Files
ONE-OS/blueway/数据集管理.jsx
王冕 09cc45db36 Initial commit: ONE-OS project
Made-with: Cursor
2026-02-27 18:11:40 +08:00

507 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 【重要】必须使用 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)
);
};