diff --git a/controller/ratio_sync.go b/controller/ratio_sync.go
index fae0c59c..490a2a74 100644
--- a/controller/ratio_sync.go
+++ b/controller/ratio_sync.go
@@ -49,7 +49,7 @@ func FetchUpstreamRatios(c *gin.Context) {
req.Timeout = 10
}
- // build upstream list from ids + custom
+ // build upstream list from ids
var upstreams []dto.UpstreamDTO
if len(req.ChannelIDs) > 0 {
// convert []int64 -> []int for model function
diff --git a/web/src/components/settings/ChannelSelectorModal.js b/web/src/components/settings/ChannelSelectorModal.js
index 35059473..573329b3 100644
--- a/web/src/components/settings/ChannelSelectorModal.js
+++ b/web/src/components/settings/ChannelSelectorModal.js
@@ -1,92 +1,116 @@
-import React from 'react';
+import React, { useState } from 'react';
import {
Modal,
Transfer,
Input,
Space,
Checkbox,
+ Avatar,
+ Highlight,
} from '@douyinfe/semi-ui';
import { IconClose } from '@douyinfe/semi-icons';
-/**
- * ChannelSelectorModal
- * 负责选择同步渠道、测试与批量测试等 UI,纯展示组件。
- * 业务状态与动作通过 props 注入,保持可复用与可测试。
- */
+const CHANNEL_STATUS_CONFIG = {
+ 1: { color: 'green', text: '启用' },
+ 2: { color: 'red', text: '禁用' },
+ 3: { color: 'amber', text: '自禁' },
+ default: { color: 'grey', text: '未知' }
+};
+
+const getChannelStatusConfig = (status) => {
+ return CHANNEL_STATUS_CONFIG[status] || CHANNEL_STATUS_CONFIG.default;
+};
+
export default function ChannelSelectorModal({
t,
visible,
onCancel,
onOk,
- // 渠道选择
allChannels = [],
selectedChannelIds = [],
setSelectedChannelIds,
- // 渠道端点
channelEndpoints,
updateChannelEndpoint,
}) {
- // Transfer 自定义渲染
- const renderSourceItem = (item) => {
+ const [searchText, setSearchText] = useState('');
+
+ const ChannelInfo = ({ item, showEndpoint = false, isSelected = false }) => {
const channelId = item.key || item.value;
const currentEndpoint = channelEndpoints[channelId];
const baseUrl = item._originalData?.base_url || '';
+ const status = item._originalData?.status || 0;
+ const statusConfig = getChannelStatusConfig(status);
return (
-
-
-
-
- {item.label}
-
+ <>
+
+ {statusConfig.text}
+
+
+
+ {isSelected ? (
+ item.label
+ ) : (
+
+ )}
-
+ >
+ );
+ };
+
+ const renderSourceItem = (item) => {
+ return (
+
+
+
+
);
};
const renderSelectedItem = (item) => {
- const channelId = item.key || item.value;
- const currentEndpoint = channelEndpoints[channelId];
- const baseUrl = item._originalData?.base_url || '';
-
return (
-
-
-
- {item.label}
-
-
-
-
- {baseUrl}
-
-
- {currentEndpoint}
-
-
-
+
+
+
);
};
- const channelFilter = (input, item) => item.label.toLowerCase().includes(input.toLowerCase());
+ const channelFilter = (input, item) => {
+ const searchLower = input.toLowerCase();
+ return item.label.toLowerCase().includes(searchLower) ||
+ (item._originalData?.base_url || '').toLowerCase().includes(searchLower);
+ };
return (
.semi-table-row {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
+}
+
+/* ==================== 同步倍率 - 渠道选择器 ==================== */
+
+.components-transfer-source-item,
+.components-transfer-selected-item {
+ display: flex;
+ align-items: center;
+ padding: 8px;
+}
+
+.semi-transfer-left-list,
+.semi-transfer-right-list {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+
+.semi-transfer-left-list::-webkit-scrollbar,
+.semi-transfer-right-list::-webkit-scrollbar {
+ display: none;
+}
+
+.components-transfer-source-item .semi-checkbox,
+.components-transfer-selected-item .semi-checkbox {
+ display: flex;
+ align-items: center;
+ width: 100%;
+}
+
+.components-transfer-source-item .semi-avatar,
+.components-transfer-selected-item .semi-avatar {
+ margin-right: 12px;
+ flex-shrink: 0;
+}
+
+.components-transfer-source-item .info,
+.components-transfer-selected-item .info {
+ flex: 1;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+}
+
+.components-transfer-source-item .name,
+.components-transfer-selected-item .name {
+ font-weight: 500;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.components-transfer-source-item .email,
+.components-transfer-selected-item .email {
+ font-size: 12px;
+ color: var(--semi-color-text-2);
+ display: flex;
+ align-items: center;
+}
+
+.components-transfer-selected-item .semi-icon-close {
+ margin-left: 8px;
+ cursor: pointer;
+ color: var(--semi-color-text-2);
+}
+
+.components-transfer-selected-item .semi-icon-close:hover {
+ color: var(--semi-color-text-0);
}
\ No newline at end of file
diff --git a/web/src/pages/Setting/Ratio/UpstreamRatioSync.js b/web/src/pages/Setting/Ratio/UpstreamRatioSync.js
index ecf5a1b9..2e12fd3b 100644
--- a/web/src/pages/Setting/Ratio/UpstreamRatioSync.js
+++ b/web/src/pages/Setting/Ratio/UpstreamRatioSync.js
@@ -42,14 +42,6 @@ export default function UpstreamRatioSync(props) {
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
- // 当前倍率快照
- const currentRatiosSnapshot = useMemo(() => ({
- model_ratio: JSON.parse(props.options.ModelRatio || '{}'),
- completion_ratio: JSON.parse(props.options.CompletionRatio || '{}'),
- cache_ratio: JSON.parse(props.options.CacheRatio || '{}'),
- model_price: JSON.parse(props.options.ModelPrice || '{}'),
- }), [props.options]);
-
// 获取所有渠道
const fetchAllChannels = async () => {
setLoading(true);