feat: add column visibility settings for channels
This commit is contained in:
@@ -41,6 +41,7 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
|
Checkbox,
|
||||||
Card,
|
Card,
|
||||||
Form
|
Form
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
@@ -172,17 +173,108 @@ const ChannelsTable = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define all columns
|
// Define column keys for selection
|
||||||
const columns = [
|
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'),
|
title: t('ID'),
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.NAME,
|
||||||
title: t('名称'),
|
title: t('名称'),
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.GROUP,
|
||||||
title: t('分组'),
|
title: t('分组'),
|
||||||
dataIndex: 'group',
|
dataIndex: 'group',
|
||||||
render: (text, record, index) => (
|
render: (text, record, index) => (
|
||||||
@@ -201,6 +293,7 @@ const ChannelsTable = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.TYPE,
|
||||||
title: t('类型'),
|
title: t('类型'),
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -212,6 +305,7 @@ const ChannelsTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.STATUS,
|
||||||
title: t('状态'),
|
title: t('状态'),
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -237,6 +331,7 @@ const ChannelsTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.RESPONSE_TIME,
|
||||||
title: t('响应时间'),
|
title: t('响应时间'),
|
||||||
dataIndex: 'response_time',
|
dataIndex: 'response_time',
|
||||||
render: (text, record, index) => (
|
render: (text, record, index) => (
|
||||||
@@ -244,6 +339,7 @@ const ChannelsTable = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.BALANCE,
|
||||||
title: t('已用/剩余'),
|
title: t('已用/剩余'),
|
||||||
dataIndex: 'expired_time',
|
dataIndex: 'expired_time',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -283,6 +379,7 @@ const ChannelsTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.PRIORITY,
|
||||||
title: t('优先级'),
|
title: t('优先级'),
|
||||||
dataIndex: 'priority',
|
dataIndex: 'priority',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -334,6 +431,7 @@ const ChannelsTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.WEIGHT,
|
||||||
title: t('权重'),
|
title: t('权重'),
|
||||||
dataIndex: 'weight',
|
dataIndex: 'weight',
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -385,6 +483,7 @@ const ChannelsTable = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
key: COLUMN_KEYS.OPERATE,
|
||||||
title: '',
|
title: '',
|
||||||
dataIndex: 'operate',
|
dataIndex: 'operate',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
@@ -595,6 +694,89 @@ const ChannelsTable = () => {
|
|||||||
searchModel: '',
|
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) => {
|
const removeRecord = (record) => {
|
||||||
let newDataSource = [...channels];
|
let newDataSource = [...channels];
|
||||||
if (record.id != null) {
|
if (record.id != null) {
|
||||||
@@ -1397,6 +1579,16 @@ const ChannelsTable = () => {
|
|||||||
>
|
>
|
||||||
{t('刷新')}
|
{t('刷新')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
theme='light'
|
||||||
|
type='tertiary'
|
||||||
|
icon={<IconSetting />}
|
||||||
|
onClick={() => setShowColumnSelector(true)}
|
||||||
|
className="!rounded-full w-full md:w-auto"
|
||||||
|
>
|
||||||
|
{t('列设置')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row items-center gap-4 w-full md:w-auto order-1 md:order-2">
|
<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 (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{renderColumnSelector()}
|
||||||
<EditTagModal
|
<EditTagModal
|
||||||
visible={showEditTag}
|
visible={showEditTag}
|
||||||
tag={editingTag}
|
tag={editingTag}
|
||||||
@@ -1501,7 +1694,7 @@ const ChannelsTable = () => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={getVisibleColumns()}
|
||||||
dataSource={pageData}
|
dataSource={pageData}
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{ x: 'max-content' }}
|
||||||
pagination={{
|
pagination={{
|
||||||
|
|||||||
Reference in New Issue
Block a user