diff --git a/blueway/数据集管理.jsx b/blueway/数据集管理.jsx deleted file mode 100644 index 1a3e8fa..0000000 --- a/blueway/数据集管理.jsx +++ /dev/null @@ -1,506 +0,0 @@ -// 【重要】必须使用 const Component 作为组件变量名 -// 数据集管理 - 模仿 GitLab 项目列表风格(纯 React,不依赖 antd,兼容 Axhub) - -const Component = function () { - var useState = React.useState; - var useCallback = React.useCallback; - var useMemo = React.useMemo; - - // GitLab 风格色值(Pajamas 近似) - var colors = { - pageBg: '#fafafa', - cardBg: '#ffffff', - border: '#e5e5e5', - borderLight: '#ebebeb', - text: '#333333', - textSecondary: '#666666', - textMuted: '#999999', - primary: '#1f75cb', - primaryHover: '#1068bf', - link: '#1f75cb', - success: '#108548', - warning: '#c17d10', - danger: '#dd2b0e', - hoverBg: '#f5f5f5', - tabActive: '#333333', - tabInactive: '#666666' - }; - - var filterState = useState({ - keyword: '', - datasetType: '', - status: '' - }); - var filters = filterState[0]; - var setFilters = filterState[1]; - - var activeTabState = useState('all'); - var activeTab = activeTabState[0]; - var setActiveTab = activeTabState[1]; - - var sortState = useState('updated_desc'); - var sortBy = sortState[0]; - var setSortBy = sortState[1]; - - var listState = useState([ - { id: '1', name: '销售数据集', type: '表格', recordCount: 12000, createTime: '2025-02-20 10:00', updateTime: '2025-02-24 14:00', status: '已发布', remark: '销售业务数据', isStarred: true, owner: '当前用户' }, - { id: '2', name: '客户画像数据集', type: '表格', recordCount: 8500, createTime: '2025-02-19 14:30', updateTime: '2025-02-23 09:00', status: '草稿', remark: '客户标签与画像', isStarred: false, owner: '当前用户' }, - { id: '3', name: '订单明细数据集', type: '表格', recordCount: 25600, createTime: '2025-02-18 09:15', updateTime: '2025-02-22 16:30', status: '已发布', remark: '订单明细与物流', isStarred: true, owner: '当前用户' }, - { id: '4', name: '库存数据集', type: '文件', recordCount: 5200, createTime: '2025-02-17 11:00', updateTime: '2025-02-21 10:00', status: '已下线', remark: '仓库库存快照', isStarred: false, owner: '其他用户' } - ]); - var dataList = listState[0]; - var setDataList = listState[1]; - - var pageState = useState(1); - var page = pageState[0]; - var setPage = pageState[1]; - var pageSizeState = useState(20); - var pageSize = pageSizeState[0]; - - var modalState = useState({ open: false, record: null, mode: 'view' }); - var modalOpen = modalState[0].open; - var modalRecord = modalState[0].record; - var modalMode = modalState[0].mode; - var setModalState = modalState[1]; - - var dropdownOpenIdState = useState(null); - var dropdownOpenId = dropdownOpenIdState[0]; - var setDropdownOpenId = dropdownOpenIdState[1]; - - var confirmState = useState({ open: false, record: null }); - var confirmOpen = confirmState[0].open; - var confirmRecord = confirmState[0].record; - var setConfirmState = confirmState[1]; - - var toastState = useState({ show: false, text: '' }); - var toastShow = toastState[0].show; - var toastText = toastState[0].text; - var setToastState = toastState[1]; - - var typeOptions = useMemo(function () { - return [ - { value: '表格', label: '表格' }, - { value: '文件', label: '文件' }, - { value: 'API', label: 'API' } - ]; - }, []); - - var statusOptions = useMemo(function () { - return [ - { value: '草稿', label: '草稿' }, - { value: '已发布', label: '已发布' }, - { value: '已下线', label: '已下线' } - ]; - }, []); - - var sortOptions = useMemo(function () { - return [ - { value: 'updated_desc', label: '最近更新' }, - { value: 'created_desc', label: '最近创建' }, - { value: 'name_asc', label: '名称 A-Z' }, - { value: 'name_desc', label: '名称 Z-A' } - ]; - }, []); - - var filteredList = useMemo(function () { - var list = dataList.slice(); - if (activeTab === 'mine') list = list.filter(function (r) { return r.owner === '当前用户'; }); - if (activeTab === 'published') list = list.filter(function (r) { return r.status === '已发布'; }); - if (activeTab === 'draft') list = list.filter(function (r) { return r.status === '草稿'; }); - if (activeTab === 'starred') list = list.filter(function (r) { return r.isStarred; }); - if (filters.keyword) { - var kw = filters.keyword.trim().toLowerCase(); - list = list.filter(function (r) { - return (r.name && r.name.toLowerCase().indexOf(kw) !== -1) || - (r.remark && r.remark.toLowerCase().indexOf(kw) !== -1); - }); - } - if (filters.datasetType) list = list.filter(function (r) { return r.type === filters.datasetType; }); - if (filters.status) list = list.filter(function (r) { return r.status === filters.status; }); - if (sortBy === 'updated_desc') list.sort(function (a, b) { return (b.updateTime || b.createTime || '').localeCompare(a.updateTime || a.createTime || ''); }); - else if (sortBy === 'created_desc') list.sort(function (a, b) { return (b.createTime || '').localeCompare(a.createTime || ''); }); - else if (sortBy === 'name_asc') list.sort(function (a, b) { return (a.name || '').localeCompare(b.name || ''); }); - else if (sortBy === 'name_desc') list.sort(function (a, b) { return (b.name || '').localeCompare(a.name || ''); }); - return list; - }, [dataList, activeTab, filters, sortBy]); - - var displayList = useMemo(function () { - var start = (page - 1) * pageSize; - return filteredList.slice(start, start + pageSize); - }, [filteredList, page, pageSize]); - - var totalCount = filteredList.length; - - var handleQuery = useCallback(function () { setPage(1); }, []); - var handleReset = useCallback(function () { - setFilters({ keyword: '', datasetType: '', status: '' }); - setPage(1); - }, []); - - var openModal = useCallback(function (record, mode) { - setModalState({ open: true, record: record || null, mode: mode || 'view' }); - setDropdownOpenId(null); - }, []); - var closeModal = useCallback(function () { - setModalState({ open: false, record: null, mode: 'view' }); - }, []); - - var showToast = useCallback(function (text) { - setToastState({ show: true, text: text }); - setTimeout(function () { setToastState({ show: false, text: '' }); }, 2000); - }, []); - - var toggleStar = useCallback(function (record, e) { - if (e) e.stopPropagation(); - setDataList(dataList.map(function (r) { - if (r.id === record.id) return Object.assign({}, r, { isStarred: !r.isStarred }); - return r; - })); - }, [dataList]); - - var openConfirm = useCallback(function (record) { - setConfirmState({ open: true, record: record }); - setDropdownOpenId(null); - }, []); - var closeConfirm = useCallback(function () { - setConfirmState({ open: false, record: null }); - }, []); - var handleDelete = useCallback(function (record) { - setDataList(dataList.filter(function (r) { return r.id !== record.id; })); - closeConfirm(); - showToast('已删除'); - }, [dataList]); - - function statusTag(status) { - var bg = status === '已发布' ? '#e7f5ec' : (status === '草稿' ? '#f0f0f0' : '#fdf3e6'); - var color = status === '已发布' ? colors.success : (status === '草稿' ? colors.textSecondary : colors.warning); - return React.createElement('span', { - style: { - display: 'inline-block', - padding: '2px 8px', - borderRadius: 4, - fontSize: 12, - background: bg, - color: color - } - }, status); - } - - var tabs = [ - { key: 'all', label: '全部' }, - { key: 'mine', label: '我创建的' }, - { key: 'starred', label: '已加星' }, - { key: 'published', label: '已发布' }, - { key: 'draft', label: '草稿' } - ]; - - return React.createElement('div', { - style: { minHeight: '100vh', background: colors.pageBg, padding: '24px 24px 48px', position: 'relative' } - }, - // 标题栏 - React.createElement('div', { - style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16, flexWrap: 'wrap', gap: 12 } - }, - React.createElement('h1', { style: { margin: 0, fontSize: 24, fontWeight: 600, color: colors.text } }, '数据集'), - React.createElement('button', { - type: 'button', - onClick: function () { openModal(null, 'add'); }, - style: { - padding: '6px 16px', - border: 'none', - borderRadius: 6, - background: colors.primary, - color: '#fff', - fontSize: 14, - cursor: 'pointer', - fontWeight: 500 - } - }, '新建数据集') - ), - - // 搜索与筛选 - React.createElement('div', { - style: { background: colors.cardBg, border: '1px solid ' + colors.border, borderRadius: 6, padding: '12px 16px', marginBottom: 0 } - }, - React.createElement('div', { style: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 12 } }, - React.createElement('div', { style: { display: 'flex', width: 280 } }, - React.createElement('input', { - type: 'text', - placeholder: '搜索数据集…', - value: filters.keyword, - onChange: function (e) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.keyword = e.target.value; return g; }); }, - onKeyDown: function (e) { if (e.key === 'Enter') handleQuery(); }, - style: { - flex: 1, - padding: '6px 12px', - border: '1px solid ' + colors.border, - borderRight: 'none', - borderRadius: '6px 0 0 6px', - fontSize: 14, - outline: 'none' - } - }), - React.createElement('button', { - type: 'button', - onClick: handleQuery, - style: { - padding: '6px 12px', - border: '1px solid ' + colors.border, - borderRadius: '0 6px 6px 0', - background: colors.pageBg, - cursor: 'pointer', - fontSize: 14 - } - }, '搜索') - ), - React.createElement('select', { - value: filters.datasetType, - onChange: function (e) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.datasetType = e.target.value; return g; }); }, - style: { width: 100, padding: '6px 8px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14 } - }, - React.createElement('option', { value: '' }, '类型'), - typeOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); }) - ), - React.createElement('select', { - value: filters.status, - onChange: function (e) { setFilters(function (f) { var g = {}; for (var k in f) g[k] = f[k]; g.status = e.target.value; return g; }); }, - style: { width: 100, padding: '6px 8px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14 } - }, - React.createElement('option', { value: '' }, '状态'), - statusOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); }) - ), - React.createElement('button', { type: 'button', onClick: handleQuery, style: { padding: '6px 14px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '搜索'), - React.createElement('button', { type: 'button', onClick: handleReset, style: { padding: '6px 14px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '重置'), - React.createElement('span', { style: { marginLeft: 'auto', color: colors.textMuted, fontSize: 12 } }, - '排序:', - React.createElement('select', { - value: sortBy, - onChange: function (e) { setSortBy(e.target.value); }, - style: { width: 120, marginLeft: 4, padding: '4px 8px', border: 'none', background: 'transparent', fontSize: 12, color: colors.textSecondary, cursor: 'pointer' } - }, - sortOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); }) - ) - ) - ) - ), - - // Tab - React.createElement('div', { - style: { background: colors.cardBg, border: '1px solid ' + colors.border, borderTop: 'none', borderRadius: '0 0 6px 6px', padding: '0 16px', marginBottom: 16, display: 'flex', gap: 8 } - }, - tabs.map(function (t) { - var isActive = activeTab === t.key; - return React.createElement('button', { - key: t.key, - type: 'button', - onClick: function () { setActiveTab(t.key); setPage(1); }, - style: { - padding: '12px 4px', - margin: 0, - border: 'none', - background: 'none', - cursor: 'pointer', - fontSize: 14, - color: isActive ? colors.tabActive : colors.tabInactive, - fontWeight: isActive ? 600 : 400, - borderBottom: '2px solid ' + (isActive ? colors.primary : 'transparent'), - marginBottom: -1 - } - }, t.label); - }) - ), - - // 列表 - React.createElement('div', { - style: { background: colors.cardBg, border: '1px solid ' + colors.border, borderRadius: 6, overflow: 'hidden' } - }, - displayList.length === 0 - ? React.createElement('div', { style: { padding: 48, textAlign: 'center', color: colors.textMuted } }, activeTab === 'all' && !filters.keyword ? '暂无数据集,点击「新建数据集」创建' : '没有匹配的数据集') - : displayList.map(function (row) { - var menuOpen = dropdownOpenId === row.id; - return React.createElement('div', { - key: row.id, - style: { - display: 'flex', - alignItems: 'flex-start', - padding: '12px 16px', - borderBottom: '1px solid ' + colors.borderLight, - cursor: 'pointer', - transition: 'background 0.15s' - }, - onMouseEnter: function (e) { e.currentTarget.style.background = colors.hoverBg; }, - onMouseLeave: function (e) { e.currentTarget.style.background = 'transparent'; }, - onClick: function (e) { - if (e.target.closest('button') || e.target.closest('[data-dropdown]')) return; - openModal(row, 'view'); - } - }, - React.createElement('div', { - style: { - width: 40, height: 40, borderRadius: 6, background: colors.pageBg, border: '1px solid ' + colors.border, - marginRight: 12, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18, color: colors.primary - } - }, '\uD83D\uDCCA'), - React.createElement('div', { style: { flex: 1, minWidth: 0 } }, - React.createElement('div', { style: { display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8, marginBottom: 4 } }, - React.createElement('span', { style: { fontSize: 14, fontWeight: 600, color: colors.link } }, row.name), - statusTag(row.status), - React.createElement('span', { style: { fontSize: 12, color: colors.textMuted } }, row.type + ' · ' + (row.recordCount != null ? row.recordCount.toLocaleString() + ' 条' : '-')) - ), - row.remark ? React.createElement('div', { style: { fontSize: 13, color: colors.textSecondary, marginBottom: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, row.remark) : null, - React.createElement('div', { style: { fontSize: 12, color: colors.textMuted } }, '更新于 ' + (row.updateTime || row.createTime || '-')) - ), - React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }, onClick: function (e) { e.stopPropagation(); } }, - React.createElement('button', { - type: 'button', - title: row.isStarred ? '取消加星' : '加星', - onClick: function (e) { toggleStar(row, e); }, - style: { border: 'none', background: 'none', cursor: 'pointer', padding: 4, fontSize: 16, color: row.isStarred ? '#c17d10' : colors.textMuted } - }, '\u2605'), - React.createElement('div', { style: { position: 'relative' }, 'data-dropdown': true }, - React.createElement('button', { - type: 'button', - onClick: function (e) { e.stopPropagation(); setDropdownOpenId(menuOpen ? null : row.id); }, - style: { border: 'none', background: 'none', cursor: 'pointer', padding: '2px 8px', fontSize: 16, color: colors.textSecondary } - }, '\u22EE'), - menuOpen ? React.createElement('div', { - style: { - position: 'absolute', - right: 0, - top: '100%', - marginTop: 4, - background: colors.cardBg, - border: '1px solid ' + colors.border, - borderRadius: 6, - boxShadow: '0 4px 12px rgba(0,0,0,0.1)', - minWidth: 100, - zIndex: 10 - } - }, - React.createElement('div', { style: { padding: '6px 12px', cursor: 'pointer', fontSize: 13 }, onClick: function () { openModal(row, 'view'); setDropdownOpenId(null); } }, '查看'), - React.createElement('div', { style: { padding: '6px 12px', cursor: 'pointer', fontSize: 13 }, onClick: function () { openModal(row, 'edit'); setDropdownOpenId(null); } }, '编辑'), - React.createElement('div', { style: { borderTop: '1px solid ' + colors.borderLight } }), - React.createElement('div', { style: { padding: '6px 12px', cursor: 'pointer', fontSize: 13, color: colors.danger }, onClick: function () { openConfirm(row); } }, '删除') - ) : null - ) - ) - ); - }) - ), - - // 分页 - totalCount > 0 ? React.createElement('div', { - style: { display: 'flex', justifyContent: 'flex-end', alignItems: 'center', padding: '16px 0', gap: 16, fontSize: 13, color: colors.textSecondary } - }, - React.createElement('span', null, '共 ' + totalCount + ' 项'), - React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } }, - React.createElement('button', { - type: 'button', - disabled: page <= 1, - onClick: function () { setPage(page - 1); }, - style: { padding: '4px 10px', border: '1px solid ' + colors.border, borderRadius: 4, background: colors.cardBg, cursor: page <= 1 ? 'not-allowed' : 'pointer', opacity: page <= 1 ? 0.6 : 1 } - }, '上一页'), - React.createElement('span', null, page + ' / ' + Math.max(1, Math.ceil(totalCount / pageSize))), - React.createElement('button', { - type: 'button', - disabled: page >= Math.ceil(totalCount / pageSize), - onClick: function () { setPage(page + 1); }, - style: { padding: '4px 10px', border: '1px solid ' + colors.border, borderRadius: 4, background: colors.cardBg, cursor: page >= Math.ceil(totalCount / pageSize) ? 'not-allowed' : 'pointer', opacity: page >= Math.ceil(totalCount / pageSize) ? 0.6 : 1 } - }, '下一页') - ) - ) : null, - - // 弹窗(查看/编辑/新增) - modalOpen && React.createElement('div', { - style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center' }, - onClick: function (e) { if (e.target === e.currentTarget) closeModal(); } - }, - React.createElement('div', { - style: { background: colors.cardBg, borderRadius: 8, width: 520, maxWidth: '90vw', maxHeight: '90vh', overflow: 'auto', boxShadow: '0 6px 16px rgba(0,0,0,0.2)' }, - onClick: function (e) { e.stopPropagation(); } - }, - React.createElement('div', { style: { padding: '16px 24px', borderBottom: '1px solid ' + colors.border, fontSize: 16, fontWeight: 600 } }, - modalMode === 'view' ? '查看数据集' : (modalMode === 'edit' ? '编辑数据集' : '新建数据集') - ), - (modalRecord || modalMode === 'add') && React.createElement('div', { style: { padding: 24 } }, - React.createElement('div', { style: { marginBottom: 16 } }, - React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '数据集名称'), - modalMode === 'view' ? (modalRecord && modalRecord.name) : React.createElement('input', { - type: 'text', - placeholder: '请输入名称', - defaultValue: modalRecord && modalRecord.name, - style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box' } - }) - ), - React.createElement('div', { style: { marginBottom: 16 } }, - React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '类型'), - modalMode === 'view' ? (modalRecord && modalRecord.type) : React.createElement('select', { - defaultValue: modalRecord && modalRecord.type, - style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box' } - }, typeOptions.map(function (o) { return React.createElement('option', { key: o.value, value: o.value }, o.label); })) - ), - React.createElement('div', { style: { marginBottom: 16 } }, - React.createElement('div', { style: { marginBottom: 4, color: colors.textSecondary, fontSize: 13 } }, '备注'), - modalMode === 'view' ? (modalRecord && modalRecord.remark) || '-' : React.createElement('textarea', { - placeholder: '选填', - defaultValue: modalRecord && modalRecord.remark, - rows: 3, - style: { width: '100%', padding: '8px 12px', border: '1px solid ' + colors.border, borderRadius: 6, fontSize: 14, boxSizing: 'border-box', resize: 'vertical' } - }) - ) - ), - React.createElement('div', { style: { padding: '12px 24px', borderTop: '1px solid ' + colors.border, display: 'flex', justifyContent: 'flex-end', gap: 8 } }, - modalMode === 'view' - ? React.createElement('button', { type: 'button', onClick: closeModal, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '关闭') - : [ - React.createElement('button', { key: 'cancel', type: 'button', onClick: closeModal, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '取消'), - React.createElement('button', { - key: 'save', - type: 'button', - onClick: function () { showToast(modalMode === 'add' ? '创建成功' : '已保存'); closeModal(); }, - style: { padding: '6px 16px', border: 'none', borderRadius: 6, background: colors.primary, color: '#fff', cursor: 'pointer', fontSize: 14 } - }, modalMode === 'add' ? '创建' : '保存') - ] - ) - ) - ), - - // 删除确认弹窗 - confirmOpen && confirmRecord && React.createElement('div', { - style: { position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.45)', zIndex: 1001, display: 'flex', alignItems: 'center', justifyContent: 'center' }, - onClick: function (e) { if (e.target === e.currentTarget) closeConfirm(); } - }, - React.createElement('div', { - style: { background: colors.cardBg, borderRadius: 8, padding: 24, width: 400, maxWidth: '90vw', boxShadow: '0 6px 16px rgba(0,0,0,0.2)' }, - onClick: function (e) { e.stopPropagation(); } - }, - React.createElement('div', { style: { fontSize: 16, fontWeight: 600, marginBottom: 8 } }, '删除数据集'), - React.createElement('div', { style: { color: colors.textSecondary, marginBottom: 24 } }, '确定要删除「' + (confirmRecord.name || '') + '」吗?此操作不可恢复。'), - React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } }, - React.createElement('button', { type: 'button', onClick: closeConfirm, style: { padding: '6px 16px', border: '1px solid ' + colors.border, borderRadius: 6, background: colors.cardBg, cursor: 'pointer', fontSize: 14 } }, '取消'), - React.createElement('button', { - type: 'button', - onClick: function () { handleDelete(confirmRecord); }, - style: { padding: '6px 16px', border: 'none', borderRadius: 6, background: colors.danger, color: '#fff', cursor: 'pointer', fontSize: 14 } - }, '删除') - ) - ) - ), - - // 轻提示 - toastShow && React.createElement('div', { - style: { - position: 'fixed', - left: '50%', - bottom: 48, - transform: 'translateX(-50%)', - padding: '8px 16px', - background: 'rgba(0,0,0,0.75)', - color: '#fff', - borderRadius: 6, - fontSize: 14, - zIndex: 1002 - } - }, toastText) - ); -};