|
|
@@ -41,6 +41,7 @@ import {
|
|
|
Tag,
|
|
|
Tooltip,
|
|
|
Typography,
|
|
|
+ Checkbox,
|
|
|
Card,
|
|
|
Form
|
|
|
} from '@douyinfe/semi-ui';
|
|
|
@@ -172,17 +173,108 @@ const ChannelsTable = () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // Define all columns
|
|
|
- const columns = [
|
|
|
+ // Define column keys for selection
|
|
|
+ const COLUMN_KEYS = {
|
|
|
+ ID: 'id',
|
|
|
+ NAME: 'name',
|
|
|
+ GROUP: 'group',
|
|
|
+ TYPE: 'type',
|
|
|
+ STATUS: 'status',
|
|
|
+ RESPONSE_TIME: 'response_time',
|
|
|
+ BALANCE: 'balance',
|
|
|
+ PRIORITY: 'priority',
|
|
|
+ WEIGHT: 'weight',
|
|
|
+ OPERATE: 'operate',
|
|
|
+ };
|
|
|
+
|
|
|
+ // State for column visibility
|
|
|
+ const [visibleColumns, setVisibleColumns] = useState({});
|
|
|
+ const [showColumnSelector, setShowColumnSelector] = useState(false);
|
|
|
+
|
|
|
+ // Load saved column preferences from localStorage
|
|
|
+ useEffect(() => {
|
|
|
+ const savedColumns = localStorage.getItem('channels-table-columns');
|
|
|
+ if (savedColumns) {
|
|
|
+ try {
|
|
|
+ const parsed = JSON.parse(savedColumns);
|
|
|
+ // Make sure all columns are accounted for
|
|
|
+ const defaults = getDefaultColumnVisibility();
|
|
|
+ const merged = { ...defaults, ...parsed };
|
|
|
+ setVisibleColumns(merged);
|
|
|
+ } catch (e) {
|
|
|
+ console.error('Failed to parse saved column preferences', e);
|
|
|
+ initDefaultColumns();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ initDefaultColumns();
|
|
|
+ }
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ // Update table when column visibility changes
|
|
|
+ useEffect(() => {
|
|
|
+ if (Object.keys(visibleColumns).length > 0) {
|
|
|
+ // Save to localStorage
|
|
|
+ localStorage.setItem(
|
|
|
+ 'channels-table-columns',
|
|
|
+ JSON.stringify(visibleColumns),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }, [visibleColumns]);
|
|
|
+
|
|
|
+ // Get default column visibility
|
|
|
+ const getDefaultColumnVisibility = () => {
|
|
|
+ return {
|
|
|
+ [COLUMN_KEYS.ID]: true,
|
|
|
+ [COLUMN_KEYS.NAME]: true,
|
|
|
+ [COLUMN_KEYS.GROUP]: true,
|
|
|
+ [COLUMN_KEYS.TYPE]: true,
|
|
|
+ [COLUMN_KEYS.STATUS]: true,
|
|
|
+ [COLUMN_KEYS.RESPONSE_TIME]: true,
|
|
|
+ [COLUMN_KEYS.BALANCE]: true,
|
|
|
+ [COLUMN_KEYS.PRIORITY]: true,
|
|
|
+ [COLUMN_KEYS.WEIGHT]: true,
|
|
|
+ [COLUMN_KEYS.OPERATE]: true,
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ // Initialize default column visibility
|
|
|
+ const initDefaultColumns = () => {
|
|
|
+ const defaults = getDefaultColumnVisibility();
|
|
|
+ setVisibleColumns(defaults);
|
|
|
+ };
|
|
|
+
|
|
|
+ // Handle column visibility change
|
|
|
+ const handleColumnVisibilityChange = (columnKey, checked) => {
|
|
|
+ const updatedColumns = { ...visibleColumns, [columnKey]: checked };
|
|
|
+ setVisibleColumns(updatedColumns);
|
|
|
+ };
|
|
|
+
|
|
|
+ // Handle "Select All" checkbox
|
|
|
+ const handleSelectAll = (checked) => {
|
|
|
+ const allKeys = Object.keys(COLUMN_KEYS).map((key) => COLUMN_KEYS[key]);
|
|
|
+ const updatedColumns = {};
|
|
|
+
|
|
|
+ allKeys.forEach((key) => {
|
|
|
+ updatedColumns[key] = checked;
|
|
|
+ });
|
|
|
+
|
|
|
+ setVisibleColumns(updatedColumns);
|
|
|
+ };
|
|
|
+
|
|
|
+ // Define all columns with keys
|
|
|
+ const allColumns = [
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.ID,
|
|
|
title: t('ID'),
|
|
|
dataIndex: 'id',
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.NAME,
|
|
|
title: t('名称'),
|
|
|
dataIndex: 'name',
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.GROUP,
|
|
|
title: t('分组'),
|
|
|
dataIndex: 'group',
|
|
|
render: (text, record, index) => (
|
|
|
@@ -201,6 +293,7 @@ const ChannelsTable = () => {
|
|
|
),
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.TYPE,
|
|
|
title: t('类型'),
|
|
|
dataIndex: 'type',
|
|
|
render: (text, record, index) => {
|
|
|
@@ -212,6 +305,7 @@ const ChannelsTable = () => {
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.STATUS,
|
|
|
title: t('状态'),
|
|
|
dataIndex: 'status',
|
|
|
render: (text, record, index) => {
|
|
|
@@ -237,6 +331,7 @@ const ChannelsTable = () => {
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.RESPONSE_TIME,
|
|
|
title: t('响应时间'),
|
|
|
dataIndex: 'response_time',
|
|
|
render: (text, record, index) => (
|
|
|
@@ -244,6 +339,7 @@ const ChannelsTable = () => {
|
|
|
),
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.BALANCE,
|
|
|
title: t('已用/剩余'),
|
|
|
dataIndex: 'expired_time',
|
|
|
render: (text, record, index) => {
|
|
|
@@ -283,6 +379,7 @@ const ChannelsTable = () => {
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.PRIORITY,
|
|
|
title: t('优先级'),
|
|
|
dataIndex: 'priority',
|
|
|
render: (text, record, index) => {
|
|
|
@@ -334,6 +431,7 @@ const ChannelsTable = () => {
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.WEIGHT,
|
|
|
title: t('权重'),
|
|
|
dataIndex: 'weight',
|
|
|
render: (text, record, index) => {
|
|
|
@@ -385,6 +483,7 @@ const ChannelsTable = () => {
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
+ key: COLUMN_KEYS.OPERATE,
|
|
|
title: '',
|
|
|
dataIndex: 'operate',
|
|
|
fixed: 'right',
|
|
|
@@ -595,6 +694,89 @@ const ChannelsTable = () => {
|
|
|
searchModel: '',
|
|
|
};
|
|
|
|
|
|
+ // Filter columns based on visibility settings
|
|
|
+ const getVisibleColumns = () => {
|
|
|
+ return allColumns.filter((column) => visibleColumns[column.key]);
|
|
|
+ };
|
|
|
+
|
|
|
+ // Column selector modal
|
|
|
+ const renderColumnSelector = () => {
|
|
|
+ return (
|
|
|
+ <Modal
|
|
|
+ title={t('列设置')}
|
|
|
+ visible={showColumnSelector}
|
|
|
+ onCancel={() => setShowColumnSelector(false)}
|
|
|
+ footer={
|
|
|
+ <div className="flex justify-end">
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ onClick={() => initDefaultColumns()}
|
|
|
+ className="!rounded-full"
|
|
|
+ >
|
|
|
+ {t('重置')}
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ theme="light"
|
|
|
+ onClick={() => setShowColumnSelector(false)}
|
|
|
+ className="!rounded-full"
|
|
|
+ >
|
|
|
+ {t('取消')}
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type='primary'
|
|
|
+ onClick={() => setShowColumnSelector(false)}
|
|
|
+ className="!rounded-full"
|
|
|
+ >
|
|
|
+ {t('确定')}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ size="middle"
|
|
|
+ centered={true}
|
|
|
+ >
|
|
|
+ <div style={{ marginBottom: 20 }}>
|
|
|
+ <Checkbox
|
|
|
+ checked={Object.values(visibleColumns).every((v) => v === true)}
|
|
|
+ indeterminate={
|
|
|
+ Object.values(visibleColumns).some((v) => v === true) &&
|
|
|
+ !Object.values(visibleColumns).every((v) => v === true)
|
|
|
+ }
|
|
|
+ onChange={(e) => handleSelectAll(e.target.checked)}
|
|
|
+ >
|
|
|
+ {t('全选')}
|
|
|
+ </Checkbox>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ className="flex flex-wrap max-h-96 overflow-y-auto rounded-lg p-4"
|
|
|
+ style={{ border: '1px solid var(--semi-color-border)' }}
|
|
|
+ >
|
|
|
+ {allColumns.map((column) => {
|
|
|
+ // Skip columns without title
|
|
|
+ if (!column.title) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ key={column.key}
|
|
|
+ className="w-1/2 mb-4 pr-2"
|
|
|
+ >
|
|
|
+ <Checkbox
|
|
|
+ checked={!!visibleColumns[column.key]}
|
|
|
+ onChange={(e) =>
|
|
|
+ handleColumnVisibilityChange(column.key, e.target.checked)
|
|
|
+ }
|
|
|
+ >
|
|
|
+ {column.title}
|
|
|
+ </Checkbox>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ </div>
|
|
|
+ </Modal>
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
const removeRecord = (record) => {
|
|
|
let newDataSource = [...channels];
|
|
|
if (record.id != null) {
|
|
|
@@ -1397,6 +1579,16 @@ const ChannelsTable = () => {
|
|
|
>
|
|
|
{t('刷新')}
|
|
|
</Button>
|
|
|
+
|
|
|
+ <Button
|
|
|
+ theme='light'
|
|
|
+ type='tertiary'
|
|
|
+ icon={<IconSetting />}
|
|
|
+ onClick={() => setShowColumnSelector(true)}
|
|
|
+ className="!rounded-full w-full md:w-auto"
|
|
|
+ >
|
|
|
+ {t('列设置')}
|
|
|
+ </Button>
|
|
|
</div>
|
|
|
|
|
|
<div className="flex flex-col md:flex-row items-center gap-4 w-full md:w-auto order-1 md:order-2">
|
|
|
@@ -1481,6 +1673,7 @@ const ChannelsTable = () => {
|
|
|
|
|
|
return (
|
|
|
<>
|
|
|
+ {renderColumnSelector()}
|
|
|
<EditTagModal
|
|
|
visible={showEditTag}
|
|
|
tag={editingTag}
|
|
|
@@ -1501,7 +1694,7 @@ const ChannelsTable = () => {
|
|
|
bordered={false}
|
|
|
>
|
|
|
<Table
|
|
|
- columns={columns}
|
|
|
+ columns={getVisibleColumns()}
|
|
|
dataSource={pageData}
|
|
|
scroll={{ x: 'max-content' }}
|
|
|
pagination={{
|