From c6ead4e5bddb6b5f2455453aea0e28b9df1778ff Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sun, 25 May 2025 01:46:45 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=AFfeat(channels):=20enhance=20channel?= =?UTF-8?q?=20management=20UI=20and=20model=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit improves the ChannelsTable component with enhanced UI and functionality: UI Improvements: - Refactor operation column layout with primary actions exposed - Move secondary actions (delete, copy) to dropdown menu - Unify button styles with theme='light' and size="small" - Add !rounded-full design to all buttons - Add appropriate icons (IconStop, IconPlay etc.) Column Settings Modal: - Replace inline styles with Tailwind CSS - Add rounded corners design - Optimize button layout and styling - Improve responsive design Batch Operations: - Unify dropdown button styles with !rounded-full - Replace inline styles with Tailwind w-full - Maintain semantic button types (warning/secondary/danger) - Improve visual hierarchy Model Testing Enhancement: - Add comprehensive model testing modal - Implement batch testing functionality - Add model search and filtering - Add real-time test status indicators - Show response time for successful tests - Add test queue management system - Implement graceful test cancellation Other Improvements: - Optimize responsive layout for mobile devices - Add i18n support for all new features - Improve error handling and user feedback - Enhance performance with optimized state management --- web/src/components/ChannelsTable.js | 1507 ++++++++++++++------------- web/src/components/LogsTable.js | 1 + web/src/components/MjLogsTable.js | 2 +- web/src/components/TaskLogsTable.js | 2 +- web/src/i18n/locales/en.json | 14 +- web/src/index.css | 94 +- web/src/pages/Channel/index.js | 12 +- 7 files changed, 845 insertions(+), 787 deletions(-) diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index 9b1dd602..eeccb983 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -1,33 +1,25 @@ import React, { useEffect, useState } from 'react'; import { API, - isMobile, - shouldShowPrompt, showError, showInfo, showSuccess, - showWarning, timestamp2string, } from '../helpers'; import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants'; import { - getQuotaPerUnit, renderGroup, renderNumberWithPoint, renderQuota, - renderQuotaWithPrompt, - stringToColor, } from '../helpers/render'; import { Button, Divider, Dropdown, - Form, Input, InputNumber, Modal, - Popconfirm, Space, SplitButtonGroup, Switch, @@ -36,27 +28,30 @@ import { Tooltip, Typography, Checkbox, - Layout, + Card, + Select, } from '@douyinfe/semi-ui'; import EditChannel from '../pages/Channel/EditChannel'; import { IconList, IconTreeTriangleDown, - IconClose, IconFilter, IconPlus, IconRefresh, IconSetting, + IconSearch, + IconEdit, + IconDelete, + IconStop, + IconPlay, + IconMore, + IconCopy, + IconSmallTriangleRight } from '@douyinfe/semi-icons'; import { loadChannelModels } from './utils.js'; import EditTagModal from '../pages/Channel/EditTagModal.js'; -import TextNumberInput from './custom/TextNumberInput.js'; import { useTranslation } from 'react-i18next'; -function renderTimestamp(timestamp) { - return <>{timestamp2string(timestamp)}; -} - const ChannelsTable = () => { const { t } = useTranslation(); @@ -71,7 +66,7 @@ const ChannelsTable = () => { type2label[0] = { value: 0, label: t('未知类型'), color: 'grey' }; } return ( - + {type2label[type]?.label} ); @@ -95,25 +90,25 @@ const ChannelsTable = () => { switch (status) { case 1: return ( - + {t('已启用')} ); case 2: return ( - + {t('已禁用')} ); case 3: return ( - + {t('自动禁用')} ); default: return ( - + {t('未知状态')} ); @@ -125,31 +120,31 @@ const ChannelsTable = () => { time = time.toFixed(2) + t(' 秒'); if (responseTime === 0) { return ( - + {t('未测试')} ); } else if (responseTime <= 1000) { return ( - + {time} ); } else if (responseTime <= 3000) { return ( - + {time} ); } else if (responseTime <= 5000) { return ( - + {time} ); } else { return ( - + {time} ); @@ -260,24 +255,20 @@ const ChannelsTable = () => { key: COLUMN_KEYS.GROUP, title: t('分组'), dataIndex: 'group', - render: (text, record, index) => { - return ( -
- - {text - ?.split(',') - .sort((a, b) => { - if (a === 'default') return -1; - if (b === 'default') return 1; - return a.localeCompare(b); - }) - .map((item, index) => { - return renderGroup(item); - })} - -
- ); - }, + render: (text, record, index) => ( +
+ + {text + ?.split(',') + .sort((a, b) => { + if (a === 'default') return -1; + if (b === 'default') return 1; + return a.localeCompare(b); + }) + .map((item, index) => renderGroup(item))} + +
+ ), }, { key: COLUMN_KEYS.TYPE, @@ -306,9 +297,7 @@ const ChannelsTable = () => { return (
{renderStatus(text)} @@ -323,9 +312,9 @@ const ChannelsTable = () => { key: COLUMN_KEYS.RESPONSE_TIME, title: t('响应时间'), dataIndex: 'response_time', - render: (text, record, index) => { - return
{renderResponseTime(text)}
; - }, + render: (text, record, index) => ( +
{renderResponseTime(text)}
+ ), }, { key: COLUMN_KEYS.BALANCE, @@ -337,20 +326,17 @@ const ChannelsTable = () => {
- + {renderQuota(record.used_quota)} - + { - updateChannelBalance(record); - }} + shape='circle' + onClick={() => updateChannelBalance(record)} > ${renderNumberWithPoint(record.balance)} @@ -361,7 +347,7 @@ const ChannelsTable = () => { } else { return ( - + {renderQuota(record.used_quota)} @@ -387,39 +373,36 @@ const ChannelsTable = () => { innerButtons defaultValue={record.priority} min={-999} + size="small" />
); } else { return ( - <> - { - Modal.warning({ - title: t('修改子渠道优先级'), - content: - t('确定要修改所有子渠道优先级为 ') + - e.target.value + - t(' 吗?'), - onOk: () => { - if (e.target.value === '') { - return; - } - submitTagEdit('priority', { - tag: record.key, - priority: e.target.value, - }); - }, - }); - }} - innerButtons - defaultValue={record.priority} - min={-999} - /> - + { + Modal.warning({ + title: t('修改子渠道优先级'), + content: t('确定要修改所有子渠道优先级为 ') + e.target.value + t(' 吗?'), + onOk: () => { + if (e.target.value === '') { + return; + } + submitTagEdit('priority', { + tag: record.key, + priority: e.target.value, + }); + }, + }); + }} + innerButtons + defaultValue={record.priority} + min={-999} + size="small" + /> ); } }, @@ -442,6 +425,7 @@ const ChannelsTable = () => { innerButtons defaultValue={record.weight} min={0} + size="small" />
); @@ -454,10 +438,7 @@ const ChannelsTable = () => { onBlur={(e) => { Modal.warning({ title: t('修改子渠道权重'), - content: - t('确定要修改所有子渠道权重为 ') + - e.target.value + - t(' 吗?'), + content: t('确定要修改所有子渠道权重为 ') + e.target.value + t(' 吗?'), onOk: () => { if (e.target.value === '') { return; @@ -472,6 +453,7 @@ const ChannelsTable = () => { innerButtons defaultValue={record.weight} min={-999} + size="small" /> ); } @@ -483,53 +465,72 @@ const ChannelsTable = () => { dataIndex: 'operate', render: (text, record, index) => { if (record.children === undefined) { + // 创建更多操作的下拉菜单项 + const moreMenuItems = [ + { + node: 'item', + name: t('删除'), + icon: , + type: 'danger', + onClick: () => { + Modal.confirm({ + title: t('确定是否要删除此渠道?'), + content: t('此修改将不可逆'), + onOk: () => { + manageChannel(record.id, 'delete', record).then(() => { + removeRecord(record); + }); + }, + }); + }, + }, + { + node: 'item', + name: t('复制'), + icon: , + type: 'primary', + onClick: () => { + Modal.confirm({ + title: t('确定是否要复制此渠道?'), + content: t('复制渠道的所有信息'), + onOk: () => copySelectedChannel(record), + }); + }, + }, + ]; + return ( -
+ + /> - { - manageChannel(record.id, 'delete', record).then(() => { - removeRecord(record); - }); - }} - > - - + {record.status === 1 ? ( @@ -537,18 +538,21 @@ const ChannelsTable = () => { )} + - { - copySelectedChannel(record); - }} + + - - -
+ - - + - + - - + } - style={{ width: isMobile() ? '90%' : 500 }} - bodyStyle={{ padding: '24px' }} + size="middle" + centered={true} >
{
{allColumns.map((column) => { // Skip columns without title @@ -670,11 +697,7 @@ const ChannelsTable = () => { return (
{ const [searchGroup, setSearchGroup] = useState(''); const [searchModel, setSearchModel] = useState(''); const [searching, setSearching] = useState(false); - const [updatingBalance, setUpdatingBalance] = useState(false); const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); - const [showPrompt, setShowPrompt] = useState( - shouldShowPrompt('channel-test'), - ); const [channelCount, setChannelCount] = useState(pageSize); const [groupOptions, setGroupOptions] = useState([]); const [showEdit, setShowEdit] = useState(false); @@ -715,13 +734,17 @@ const ChannelsTable = () => { const [showEditTag, setShowEditTag] = useState(false); const [editingTag, setEditingTag] = useState(''); const [selectedChannels, setSelectedChannels] = useState([]); - const [showEditPriority, setShowEditPriority] = useState(false); const [enableTagMode, setEnableTagMode] = useState(false); const [showBatchSetTag, setShowBatchSetTag] = useState(false); const [batchSetTagValue, setBatchSetTagValue] = useState(''); const [showModelTestModal, setShowModelTestModal] = useState(false); const [currentTestChannel, setCurrentTestChannel] = useState(null); const [modelSearchKeyword, setModelSearchKeyword] = useState(''); + const [modelTestResults, setModelTestResults] = useState({}); + const [testingModels, setTestingModels] = useState(new Set()); + const [isBatchTesting, setIsBatchTesting] = useState(false); + const [testQueue, setTestQueue] = useState([]); + const [isProcessingQueue, setIsProcessingQueue] = useState(false); const removeRecord = (record) => { let newDataSource = [...channels]; @@ -1034,106 +1057,88 @@ const ChannelsTable = () => { } }; + const processTestQueue = async () => { + if (!isProcessingQueue || testQueue.length === 0) return; + + const { channel, model } = testQueue[0]; + + try { + setTestingModels(prev => new Set([...prev, model])); + const res = await API.get(`/api/channel/test/${channel.id}?model=${model}`); + const { success, message, time } = res.data; + + setModelTestResults(prev => ({ + ...prev, + [`${channel.id}-${model}`]: { success, time } + })); + + if (success) { + updateChannelProperty(channel.id, (ch) => { + ch.response_time = time * 1000; + ch.test_time = Date.now() / 1000; + }); + } else { + showError(message); + } + } catch (error) { + showError(error.message); + } finally { + setTestingModels(prev => { + const newSet = new Set(prev); + newSet.delete(model); + return newSet; + }); + } + + // 移除已处理的测试 + setTestQueue(prev => prev.slice(1)); + }; + + // 监听队列变化 + useEffect(() => { + if (testQueue.length > 0 && isProcessingQueue) { + processTestQueue(); + } else if (testQueue.length === 0 && isProcessingQueue) { + setIsProcessingQueue(false); + setIsBatchTesting(false); + } + }, [testQueue, isProcessingQueue]); + const testChannel = async (record, model) => { - const res = await API.get(`/api/channel/test/${record.id}?model=${model}`); - const { success, message, time } = res.data; - if (success) { - // Also update the channels state to persist the change - updateChannelProperty(record.id, (channel) => { - channel.response_time = time * 1000; - channel.test_time = Date.now() / 1000; - }); + setTestQueue(prev => [...prev, { channel: record, model }]); + if (!isProcessingQueue) { + setIsProcessingQueue(true); + } + }; - showInfo( - t('通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。') - .replace('${name}', record.name) - .replace('${time.toFixed(2)}', time.toFixed(2)), + const batchTestModels = async () => { + if (!currentTestChannel) return; + + setIsBatchTesting(true); + + const models = currentTestChannel.models + .split(',') + .filter((model) => + model.toLowerCase().includes(modelSearchKeyword.toLowerCase()) ); - } else { - showError(message); - } + + setTestQueue(models.map(model => ({ + channel: currentTestChannel, + model + }))); + setIsProcessingQueue(true); }; - const updateChannelBalance = async (record) => { - const res = await API.get(`/api/channel/update_balance/${record.id}/`); - const { success, message, balance } = res.data; - if (success) { - updateChannelProperty(record.id, (channel) => { - channel.balance = balance; - channel.balance_updated_time = Date.now() / 1000; - }); - showInfo( - t('通道 ${name} 余额更新成功!').replace('${name}', record.name), - ); + const handleCloseModal = () => { + if (isBatchTesting) { + // 清空测试队列来停止测试 + setTestQueue([]); + setIsProcessingQueue(false); + setIsBatchTesting(false); + showSuccess(t('已停止测试')); } else { - showError(message); - } - }; - - const testAllChannels = async () => { - const res = await API.get(`/api/channel/test`); - const { success, message } = res.data; - if (success) { - showInfo(t('已成功开始测试所有已启用通道,请刷新页面查看结果。')); - } else { - showError(message); - } - }; - - const deleteAllDisabledChannels = async () => { - const res = await API.delete(`/api/channel/disabled`); - const { success, message, data } = res.data; - if (success) { - showSuccess( - t('已删除所有禁用渠道,共计 ${data} 个').replace('${data}', data), - ); - await refresh(); - } else { - showError(message); - } - }; - - const updateAllChannelsBalance = async () => { - setUpdatingBalance(true); - const res = await API.get(`/api/channel/update_balance`); - const { success, message } = res.data; - if (success) { - showInfo(t('已更新完毕所有已启用通道余额!')); - } else { - showError(message); - } - setUpdatingBalance(false); - }; - - const batchDeleteChannels = async () => { - if (selectedChannels.length === 0) { - showError(t('请先选择要删除的通道!')); - return; - } - setLoading(true); - let ids = []; - selectedChannels.forEach((channel) => { - ids.push(channel.id); - }); - const res = await API.post(`/api/channel/batch`, { ids: ids }); - const { success, message, data } = res.data; - if (success) { - showSuccess(t('已删除 ${data} 个通道!').replace('${data}', data)); - await refresh(); - } else { - showError(message); - } - setLoading(false); - }; - - const fixChannelsAbilities = async () => { - const res = await API.post(`/api/channel/fix`); - const { success, message, data } = res.data; - if (success) { - showSuccess(t('已修复 ${data} 个通道!').replace('${data}', data)); - await refresh(); - } else { - showError(message); + setShowModelTestModal(false); + setModelSearchKeyword(''); } }; @@ -1146,7 +1151,7 @@ const ChannelsTable = () => { setActivePage(page); if (page === Math.ceil(channels.length / pageSize) + 1) { // In this case we have to load more data and then append them. - loadChannels(page - 1, pageSize, idSort, enableTagMode).then((r) => {}); + loadChannels(page - 1, pageSize, idSort, enableTagMode).then((r) => { }); } }; @@ -1254,6 +1259,341 @@ const ChannelsTable = () => { } }; + const testAllChannels = async () => { + const res = await API.get(`/api/channel/test`); + const { success, message } = res.data; + if (success) { + showInfo(t('已成功开始测试所有已启用通道,请刷新页面查看结果。')); + } else { + showError(message); + } + }; + + const deleteAllDisabledChannels = async () => { + const res = await API.delete(`/api/channel/disabled`); + const { success, message, data } = res.data; + if (success) { + showSuccess( + t('已删除所有禁用渠道,共计 ${data} 个').replace('${data}', data), + ); + await refresh(); + } else { + showError(message); + } + }; + + const updateAllChannelsBalance = async () => { + const res = await API.get(`/api/channel/update_balance`); + const { success, message } = res.data; + if (success) { + showInfo(t('已更新完毕所有已启用通道余额!')); + } else { + showError(message); + } + }; + + const updateChannelBalance = async (record) => { + const res = await API.get(`/api/channel/update_balance/${record.id}/`); + const { success, message, balance } = res.data; + if (success) { + updateChannelProperty(record.id, (channel) => { + channel.balance = balance; + channel.balance_updated_time = Date.now() / 1000; + }); + showInfo( + t('通道 ${name} 余额更新成功!').replace('${name}', record.name), + ); + } else { + showError(message); + } + }; + + const batchDeleteChannels = async () => { + if (selectedChannels.length === 0) { + showError(t('请先选择要删除的通道!')); + return; + } + setLoading(true); + let ids = []; + selectedChannels.forEach((channel) => { + ids.push(channel.id); + }); + const res = await API.post(`/api/channel/batch`, { ids: ids }); + const { success, message, data } = res.data; + if (success) { + showSuccess(t('已删除 ${data} 个通道!').replace('${data}', data)); + await refresh(); + } else { + showError(message); + } + setLoading(false); + }; + + const fixChannelsAbilities = async () => { + const res = await API.post(`/api/channel/fix`); + const { success, message, data } = res.data; + if (success) { + showSuccess(t('已修复 ${data} 个通道!').replace('${data}', data)); + await refresh(); + } else { + showError(message); + } + }; + + const renderHeader = () => ( +
+
+
+ + + + + + + + + + + + + + + + + + + } + > + + +
+ +
+
+ + {t('使用ID排序')} + + { + localStorage.setItem('id-sort', v + ''); + setIdSort(v); + loadChannels(0, pageSize, v, enableTagMode); + }} + /> +
+ +
+ + {t('开启批量操作')} + + { + setEnableBatchDelete(v); + }} + /> +
+ +
+ + {t('标签聚合模式')} + + { + setEnableTagMode(v); + loadChannels(0, pageSize, idSort, v); + }} + /> +
+
+
+ + + +
+
+ + + + + +
+ +
+
+ } + placeholder={t('搜索渠道的 ID,名称,密钥和API地址 ...')} + value={searchKeyword} + loading={searching} + onChange={(v) => { + setSearchKeyword(v.trim()); + }} + className="!rounded-full" + showClear + /> +
+
+ } + placeholder={t('模型关键字')} + value={searchModel} + loading={searching} + onChange={(v) => { + setSearchModel(v.trim()); + }} + className="!rounded-full" + showClear + /> +
+
+ { value={batchSetTagValue} onChange={(v) => setBatchSetTagValue(v)} size='large' + className="!rounded-full" /> -
+
- {t('已选择 ${count} 个渠道').replace( - '${count}', - selectedChannels.length, - )} + {t('已选择 ${count} 个渠道').replace('${count}', selectedChannels.length)}
{/* 模型测试弹窗 */} + + {currentTestChannel.name} {t('渠道的模型测试')} + + + {t('共')} {currentTestChannel.models.split(',').length} {t('个模型')} + +
+ ) + } visible={showModelTestModal && currentTestChannel !== null} - onCancel={() => { - setShowModelTestModal(false); - setModelSearchKeyword(''); - }} - footer={null} - maskClosable={true} + onCancel={handleCloseModal} + footer={ +
+ {isBatchTesting ? ( + + ) : ( + + )} + +
+ } + maskClosable={!isBatchTesting} centered={true} + className="!rounded-lg" + size="large" > -
+
{currentTestChannel && (
- - {t('渠道')}: {currentTestChannel.name} - - - {/* 搜索框 */} - setModelSearchKeyword(v)} - style={{ marginBottom: '16px' }} - prefix={} - showClear - /> - -
- {currentTestChannel.models - .split(',') - .filter((model) => - model - .toLowerCase() - .includes(modelSearchKeyword.toLowerCase()), - ) - .map((model, index) => { - return ( - - ); - })} +
+ setModelSearchKeyword(v)} + className="w-64 !rounded-full" + prefix={} + showClear + />
- {/* 显示搜索结果数量 */} - {modelSearchKeyword && ( - - {t('找到')}{' '} + - model - .toLowerCase() - .includes(modelSearchKeyword.toLowerCase()), - ).length - }{' '} - {t('个模型')} - - )} + title: t('模型名称'), + dataIndex: 'model', + render: (text) => ( +
+ {text} +
+ ) + }, + { + title: t('状态'), + dataIndex: 'status', + render: (text, record) => { + const testResult = modelTestResults[`${currentTestChannel.id}-${record.model}`]; + const isTesting = testingModels.has(record.model); + + if (isTesting) { + return ( + + {t('测试中')} + + ); + } + + if (!testResult) { + return ( + + {t('未开始')} + + ); + } + + return ( +
+ + {testResult.success ? t('成功') : t('失败')} + + {testResult.success && ( + + {t('请求时长: ${time}s').replace('${time}', testResult.time.toFixed(2))} + + )} +
+ ); + } + }, + { + title: '', + dataIndex: 'operate', + render: (text, record) => { + const isTesting = testingModels.has(record.model); + return ( + + ); + } + } + ]} + dataSource={currentTestChannel.models + .split(',') + .filter((model) => + model.toLowerCase().includes(modelSearchKeyword.toLowerCase()) + ) + .map((model) => ({ + model, + key: model + }))} + pagination={false} + size="middle" + /> )} diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index 985af15d..b7d12a0c 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -715,6 +715,7 @@ const LogsTable = () => {
{allColumns.map((column) => { // Skip admin-only columns for non-admin users diff --git a/web/src/components/MjLogsTable.js b/web/src/components/MjLogsTable.js index bf6e053b..f44f712f 100644 --- a/web/src/components/MjLogsTable.js +++ b/web/src/components/MjLogsTable.js @@ -730,7 +730,7 @@ const LogsTable = () => { {t('全选')}
-
+
{allColumns.map((column) => { // 为非管理员用户跳过管理员专用列 if ( diff --git a/web/src/components/TaskLogsTable.js b/web/src/components/TaskLogsTable.js index c6c25a94..e9de9f0f 100644 --- a/web/src/components/TaskLogsTable.js +++ b/web/src/components/TaskLogsTable.js @@ -573,7 +573,7 @@ const LogsTable = () => { {t('全选')}
-
+
{allColumns.map((column) => { // 为非管理员用户跳过管理员专用列 if (!isAdminUser && column.key === COLUMN_KEYS.CHANNEL) { diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index d576b375..67f46e5f 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1480,5 +1480,17 @@ "删除账户确认": "Delete Account Confirmation", "请输入您的用户名以确认删除": "Please enter your username to confirm deletion", "接受未设置价格模型": "Accept models without price settings", - "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "Accept calls even if the model has no price settings, use only when you trust the website, which may incur high costs" + "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "Accept calls even if the model has no price settings, use only when you trust the website, which may incur high costs", + "批量操作": "Batch Operations", + "未开始": "Not Started", + "测试中": "Testing", + "请求时长: ${time}s": "Request time: ${time}s", + "搜索模型...": "Search models...", + "批量测试${count}个模型": "Batch test ${count} models", + "测试中...": "Testing...", + "渠道的模型测试": "Channel Model Test", + "共": "Total", + "确定要测试所有通道吗?": "Are you sure you want to test all channels?", + "确定要更新所有已启用通道余额吗?": "Are you sure you want to update the balance of all enabled channels?", + "已选择 ${count} 个渠道": "Selected ${count} channels" } \ No newline at end of file diff --git a/web/src/index.css b/web/src/index.css index 87cc62e7..a9bcc233 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1,10 +1,13 @@ -@layer tailwind-base,semi,tailwind-components,tailwind-utils; -@layer tailwind-base{ +@layer tailwind-base, semi, tailwind-components, tailwind-utils; + +@layer tailwind-base { @tailwind base; } -@layer tailwind-components{ + +@layer tailwind-components { @tailwind components; } + @layer tailwind-utils { @tailwind utilities; } @@ -29,20 +32,7 @@ body { overflow: hidden; } -#root - > section - > header - > section - > div - > div - > div - > div.semi-navigation-header-list-outer - > div.semi-navigation-list-wrapper - > ul - > div - > a - > li - > span { +#root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li>span { font-weight: 600 !important; } @@ -51,6 +41,7 @@ body { } @media only screen and (max-width: 767px) { + /*.semi-navigation-sub-wrap .semi-navigation-sub-title, .semi-navigation-item {*/ /* padding: 0 0;*/ /*}*/ @@ -68,64 +59,24 @@ body { overflow-x: auto; scrollbar-width: none; } - #root - > section - > header - > section - > div - > div - > div - > div.semi-navigation-footer - > div - > a - > li { + + #root>section>header>section>div>div>div>div.semi-navigation-footer>div>a>li { padding: 0 0; } - #root - > section - > header - > section - > div - > div - > div - > div.semi-navigation-header-list-outer - > div.semi-navigation-list-wrapper - > ul - > div - > a - > li { + + #root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li { padding: 0 5px; } - #root - > section - > header - > section - > div - > div - > div - > div.semi-navigation-footer - > div:nth-child(1) - > a - > li { + + #root>section>header>section>div>div>div>div.semi-navigation-footer>div:nth-child(1)>a>li { padding: 0 5px; } + .semi-navigation-footer { padding-left: 0; padding-right: 0; } - .semi-table-tbody, - .semi-table-row, - .semi-table-row-cell { - display: block !important; - width: auto !important; - padding: 2px !important; - } - .semi-table-row-cell { - border-bottom: 0 !important; - } - .semi-table-tbody > .semi-table-row { - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - } + .semi-space { /*display: block!important;*/ display: flex; @@ -165,16 +116,6 @@ body { } } -.semi-table-tbody > .semi-table-row > .semi-table-row-cell { - padding: 16px 14px; -} - -.channel-table { - .semi-table-tbody > .semi-table-row > .semi-table-row-cell { - padding: 16px 8px; - } -} - /*.semi-layout {*/ /* height: 100%;*/ /*}*/ @@ -238,6 +179,7 @@ code { from { transform: translateY(-100%); } + to { transform: translateY(0); } @@ -332,4 +274,4 @@ code { .semi-datepicker-range-input { border-radius: 9999px; -} +} \ No newline at end of file diff --git a/web/src/pages/Channel/index.js b/web/src/pages/Channel/index.js index 4d40a9d1..523ea27c 100644 --- a/web/src/pages/Channel/index.js +++ b/web/src/pages/Channel/index.js @@ -1,20 +1,10 @@ import React from 'react'; import ChannelsTable from '../../components/ChannelsTable'; -import { Layout } from '@douyinfe/semi-ui'; -import { useTranslation } from 'react-i18next'; const File = () => { - const { t } = useTranslation(); return ( <> - - -

{t('管理渠道')}

-
- - - -
+ ); };