// 【重要】必须使用 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) ); };