Files
light-code/web/src/apps/muse/base/table/index.tsx
2025-10-20 05:45:19 +08:00

281 lines
7.3 KiB
TypeScript

import React, { useState, useMemo } from 'react';
import { Table } from './Table';
import { DetailModal } from './DetailModal';
import { mockMarks, Mark } from '../mock/collection';
import { TableColumn, ActionButton } from './types';
export const Base = () => {
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [data, setData] = useState<Mark[]>(mockMarks);
const [detailModalVisible, setDetailModalVisible] = useState(false);
const [currentRecord, setCurrentRecord] = useState<Mark | null>(null);
// 表格列配置
const columns: TableColumn<Mark>[] = [
{
key: 'title',
title: '标题',
dataIndex: 'title',
width: 300,
sortable: true,
render: (value: string, record: Mark) => (
<div>
<div className="title-text" style={{ fontWeight: 600, marginBottom: 4 }}>
{value}
</div>
<div style={{ fontSize: '12px', color: '#666' }}>
{record.description.slice(0, 60)}...
</div>
</div>
)
},
{
key: 'markType',
title: '类型',
dataIndex: 'markType',
width: 100,
sortable: true,
render: (value: string) => (
<span
style={{
padding: '4px 8px',
borderRadius: '4px',
backgroundColor: getTypeColor(value),
color: '#fff',
fontSize: '12px'
}}
>
{value}
</span>
)
},
{
key: 'tags',
title: '标签',
dataIndex: 'tags',
width: 200,
render: (tags: string[]) => (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
{tags.slice(0, 3).map((tag, index) => (
<span
key={index}
style={{
padding: '2px 6px',
backgroundColor: '#f0f0f0',
borderRadius: '2px',
fontSize: '11px',
color: '#666'
}}
>
{tag}
</span>
))}
{tags.length > 3 && (
<span style={{ fontSize: '11px', color: '#999' }}>
+{tags.length - 3}
</span>
)}
</div>
)
},
{
key: 'uname',
title: '创建者',
dataIndex: 'uname',
width: 120,
sortable: true
},
{
key: 'createdAt',
title: '创建时间',
dataIndex: 'createdAt',
width: 180,
sortable: true,
render: (value: Date) => new Date(value).toLocaleString('zh-CN')
},
{
key: 'config.visibility',
title: '可见性',
dataIndex: 'config.visibility',
width: 100,
render: (value: string) => (
<span style={{
color: value === 'public' ? '#52c41a' : value === 'private' ? '#ff4d4f' : '#faad14'
}}>
{value === 'public' ? '公开' : value === 'private' ? '私有' : '受限'}
</span>
)
}
];
// 操作按钮配置
const actions: ActionButton[] = [
{
key: 'view',
label: '详情',
type: 'primary',
icon: '👁',
onClick: (record: Mark) => {
handleViewDetail(record);
}
},
{
key: 'edit',
label: '编辑',
icon: '✏️',
onClick: (record: Mark) => {
handleEdit(record);
}
},
{
key: 'delete',
label: '删除',
type: 'danger',
icon: '🗑️',
onClick: (record: Mark) => {
handleDelete(record);
}
}
];
// 获取类型颜色
const getTypeColor = (type: string): string => {
const colors: Record<string, string> = {
markdown: '#1890ff',
json: '#52c41a',
html: '#fa8c16',
image: '#eb2f96',
video: '#722ed1',
audio: '#13c2c2',
code: '#666',
link: '#1890ff',
file: '#999'
};
return colors[type] || '#999';
};
// 处理详情查看
const handleViewDetail = (record: Mark) => {
setCurrentRecord(record);
setDetailModalVisible(true);
};
// 处理编辑
const handleEdit = (record: Mark) => {
alert(`编辑: ${record.title}`);
// 这里可以打开编辑对话框或跳转到编辑页面
};
// 处理删除
const handleDelete = (record: Mark) => {
if (window.confirm(`确定要删除"${record.title}"吗?`)) {
setData(prevData => prevData.filter(item => item.id !== record.id));
// 如果当前选中的项包含被删除的项,也要从选中列表中移除
setSelectedRowKeys(prev => prev.filter(key => key !== record.id));
}
};
// 处理批量删除
const handleBatchDelete = () => {
if (selectedRowKeys.length === 0) return;
if (window.confirm(`确定要删除选中的 ${selectedRowKeys.length} 项吗?`)) {
setData(prevData => prevData.filter(item => !selectedRowKeys.includes(item.id)));
setSelectedRowKeys([]);
}
};
// 排序处理
const handleSort = (field: string, order: 'asc' | 'desc' | null) => {
if (!order) {
setData(mockMarks); // 重置为原始顺序
return;
}
const sortedData = [...data].sort((a, b) => {
const getNestedValue = (obj: any, path: string) => {
return path.split('.').reduce((o, p) => o?.[p], obj);
};
const aVal = getNestedValue(a, field);
const bVal = getNestedValue(b, field);
if (aVal < bVal) return order === 'asc' ? -1 : 1;
if (aVal > bVal) return order === 'asc' ? 1 : -1;
return 0;
});
setData(sortedData);
};
// 分页配置
const paginationConfig = {
current: currentPage,
pageSize: pageSize,
total: data.length,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: number, range: [number, number]) =>
`${range[0]}-${range[1]} 条,共 ${total}`,
onChange: (page: number, size: number) => {
setCurrentPage(page);
setPageSize(size);
}
};
return (
<div style={{ padding: '24px' }}>
<div style={{ marginBottom: '16px' }}>
<h2></h2>
<p style={{ color: '#666', margin: '8px 0' }}>
</p>
</div>
{selectedRowKeys.length > 0 && (
<div style={{
marginBottom: '16px',
padding: '12px',
backgroundColor: '#e6f7ff',
borderRadius: '4px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
}}>
<span> {selectedRowKeys.length} </span>
<button
className="btn btn-danger"
onClick={handleBatchDelete}
>
</button>
</div>
)}
<Table
data={data}
columns={columns}
actions={actions}
rowSelection={{
selectedRowKeys,
onChange: (keys, rows) => {
setSelectedRowKeys(keys);
}
}}
pagination={paginationConfig}
onSort={handleSort}
/>
<DetailModal
visible={detailModalVisible}
data={currentRecord}
onClose={() => {
setDetailModalVisible(false);
setCurrentRecord(null);
}}
/>
</div>
);
};