Showing
1 changed file
with
87 additions
and
23 deletions
| @@ -83,19 +83,38 @@ | @@ -83,19 +83,38 @@ | ||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | .config-password-toggle { | 85 | .config-password-toggle { |
| 86 | - padding: 8px 14px; | 86 | + padding: 8px 12px; |
| 87 | border: 2px solid #000000; | 87 | border: 2px solid #000000; |
| 88 | background-color: #ffffff; | 88 | background-color: #ffffff; |
| 89 | cursor: pointer; | 89 | cursor: pointer; |
| 90 | - font-size: 12px; | ||
| 91 | - font-weight: bold; | 90 | + font-size: 0; |
| 92 | transition: all 0.3s ease; | 91 | transition: all 0.3s ease; |
| 92 | + display: flex; | ||
| 93 | + align-items: center; | ||
| 94 | + justify-content: center; | ||
| 95 | + min-width: 44px; | ||
| 96 | + min-height: 38px; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + .config-password-toggle svg { | ||
| 100 | + width: 20px; | ||
| 101 | + height: 20px; | ||
| 102 | + stroke: #000000; | ||
| 103 | + fill: none; | ||
| 104 | + stroke-width: 2; | ||
| 105 | + stroke-linecap: round; | ||
| 106 | + stroke-linejoin: round; | ||
| 107 | + transition: stroke 0.3s ease; | ||
| 93 | } | 108 | } |
| 94 | 109 | ||
| 95 | .config-password-toggle:hover, | 110 | .config-password-toggle:hover, |
| 96 | .config-password-toggle.revealed { | 111 | .config-password-toggle.revealed { |
| 97 | background-color: #000000; | 112 | background-color: #000000; |
| 98 | - color: #ffffff; | 113 | + } |
| 114 | + | ||
| 115 | + .config-password-toggle:hover svg, | ||
| 116 | + .config-password-toggle.revealed svg { | ||
| 117 | + stroke: #ffffff; | ||
| 99 | } | 118 | } |
| 100 | 119 | ||
| 101 | .search-box { | 120 | .search-box { |
| @@ -1163,7 +1182,7 @@ | @@ -1163,7 +1182,7 @@ | ||
| 1163 | title: 'Insight Agent', | 1182 | title: 'Insight Agent', |
| 1164 | subtitle: '负责洞察分析的模型配置', | 1183 | subtitle: '负责洞察分析的模型配置', |
| 1165 | fields: [ | 1184 | fields: [ |
| 1166 | - { key: 'INSIGHT_ENGINE_API_KEY', label: 'API Key' }, | 1185 | + { key: 'INSIGHT_ENGINE_API_KEY', label: 'API Key', type: 'password' }, |
| 1167 | { key: 'INSIGHT_ENGINE_BASE_URL', label: 'Base URL' }, | 1186 | { key: 'INSIGHT_ENGINE_BASE_URL', label: 'Base URL' }, |
| 1168 | { key: 'INSIGHT_ENGINE_MODEL_NAME', label: '模型名称' } | 1187 | { key: 'INSIGHT_ENGINE_MODEL_NAME', label: '模型名称' } |
| 1169 | ] | 1188 | ] |
| @@ -1172,7 +1191,7 @@ | @@ -1172,7 +1191,7 @@ | ||
| 1172 | title: 'Media Agent', | 1191 | title: 'Media Agent', |
| 1173 | subtitle: '媒体内容理解与生成模型', | 1192 | subtitle: '媒体内容理解与生成模型', |
| 1174 | fields: [ | 1193 | fields: [ |
| 1175 | - { key: 'MEDIA_ENGINE_API_KEY', label: 'API Key' }, | 1194 | + { key: 'MEDIA_ENGINE_API_KEY', label: 'API Key', type: 'password' }, |
| 1176 | { key: 'MEDIA_ENGINE_BASE_URL', label: 'Base URL' }, | 1195 | { key: 'MEDIA_ENGINE_BASE_URL', label: 'Base URL' }, |
| 1177 | { key: 'MEDIA_ENGINE_MODEL_NAME', label: '模型名称' } | 1196 | { key: 'MEDIA_ENGINE_MODEL_NAME', label: '模型名称' } |
| 1178 | ] | 1197 | ] |
| @@ -1181,7 +1200,7 @@ | @@ -1181,7 +1200,7 @@ | ||
| 1181 | title: 'Query Agent', | 1200 | title: 'Query Agent', |
| 1182 | subtitle: '负责搜索与信息汇总的模型配置', | 1201 | subtitle: '负责搜索与信息汇总的模型配置', |
| 1183 | fields: [ | 1202 | fields: [ |
| 1184 | - { key: 'QUERY_ENGINE_API_KEY', label: 'API Key' }, | 1203 | + { key: 'QUERY_ENGINE_API_KEY', label: 'API Key', type: 'password' }, |
| 1185 | { key: 'QUERY_ENGINE_BASE_URL', label: 'Base URL' }, | 1204 | { key: 'QUERY_ENGINE_BASE_URL', label: 'Base URL' }, |
| 1186 | { key: 'QUERY_ENGINE_MODEL_NAME', label: '模型名称' } | 1205 | { key: 'QUERY_ENGINE_MODEL_NAME', label: '模型名称' } |
| 1187 | ] | 1206 | ] |
| @@ -1190,7 +1209,7 @@ | @@ -1190,7 +1209,7 @@ | ||
| 1190 | title: 'Report Agent', | 1209 | title: 'Report Agent', |
| 1191 | subtitle: '报告生成使用的模型配置', | 1210 | subtitle: '报告生成使用的模型配置', |
| 1192 | fields: [ | 1211 | fields: [ |
| 1193 | - { key: 'REPORT_ENGINE_API_KEY', label: 'API Key' }, | 1212 | + { key: 'REPORT_ENGINE_API_KEY', label: 'API Key', type: 'password' }, |
| 1194 | { key: 'REPORT_ENGINE_BASE_URL', label: 'Base URL' }, | 1213 | { key: 'REPORT_ENGINE_BASE_URL', label: 'Base URL' }, |
| 1195 | { key: 'REPORT_ENGINE_MODEL_NAME', label: '模型名称' } | 1214 | { key: 'REPORT_ENGINE_MODEL_NAME', label: '模型名称' } |
| 1196 | ] | 1215 | ] |
| @@ -1199,7 +1218,7 @@ | @@ -1199,7 +1218,7 @@ | ||
| 1199 | title: 'Forum Host', | 1218 | title: 'Forum Host', |
| 1200 | subtitle: '多智能体协同使用的模型配置', | 1219 | subtitle: '多智能体协同使用的模型配置', |
| 1201 | fields: [ | 1220 | fields: [ |
| 1202 | - { key: 'FORUM_HOST_API_KEY', label: 'API Key' }, | 1221 | + { key: 'FORUM_HOST_API_KEY', label: 'API Key', type: 'password' }, |
| 1203 | { key: 'FORUM_HOST_BASE_URL', label: 'Base URL' }, | 1222 | { key: 'FORUM_HOST_BASE_URL', label: 'Base URL' }, |
| 1204 | { key: 'FORUM_HOST_MODEL_NAME', label: '模型名称' } | 1223 | { key: 'FORUM_HOST_MODEL_NAME', label: '模型名称' } |
| 1205 | ] | 1224 | ] |
| @@ -1208,7 +1227,7 @@ | @@ -1208,7 +1227,7 @@ | ||
| 1208 | title: 'Keyword Optimizer', | 1227 | title: 'Keyword Optimizer', |
| 1209 | subtitle: 'SQL / 关键词优化模型配置', | 1228 | subtitle: 'SQL / 关键词优化模型配置', |
| 1210 | fields: [ | 1229 | fields: [ |
| 1211 | - { key: 'KEYWORD_OPTIMIZER_API_KEY', label: 'API Key' }, | 1230 | + { key: 'KEYWORD_OPTIMIZER_API_KEY', label: 'API Key', type: 'password' }, |
| 1212 | { key: 'KEYWORD_OPTIMIZER_BASE_URL', label: 'Base URL' }, | 1231 | { key: 'KEYWORD_OPTIMIZER_BASE_URL', label: 'Base URL' }, |
| 1213 | { key: 'KEYWORD_OPTIMIZER_MODEL_NAME', label: '模型名称' } | 1232 | { key: 'KEYWORD_OPTIMIZER_MODEL_NAME', label: '模型名称' } |
| 1214 | ] | 1233 | ] |
| @@ -1217,8 +1236,8 @@ | @@ -1217,8 +1236,8 @@ | ||
| 1217 | title: '外部检索工具', | 1236 | title: '外部检索工具', |
| 1218 | subtitle: '联动搜索引擎、网站抓取等在线服务', | 1237 | subtitle: '联动搜索引擎、网站抓取等在线服务', |
| 1219 | fields: [ | 1238 | fields: [ |
| 1220 | - { key: 'TAVILY_API_KEY', label: 'Tavily API Key' }, | ||
| 1221 | - { key: 'BOCHA_WEB_SEARCH_API_KEY', label: 'Bocha API Key' } | 1239 | + { key: 'TAVILY_API_KEY', label: 'Tavily API Key', type: 'password' }, |
| 1240 | + { key: 'BOCHA_WEB_SEARCH_API_KEY', label: 'Bocha API Key', type: 'password' } | ||
| 1222 | ] | 1241 | ] |
| 1223 | } | 1242 | } |
| 1224 | ]; | 1243 | ]; |
| @@ -1251,6 +1270,9 @@ | @@ -1251,6 +1270,9 @@ | ||
| 1251 | checkStatus(); | 1270 | checkStatus(); |
| 1252 | setInterval(checkStatus, 5000); | 1271 | setInterval(checkStatus, 5000); |
| 1253 | 1272 | ||
| 1273 | + // 初始化密码切换功能(事件委托,只需调用一次) | ||
| 1274 | + attachConfigPasswordToggles(); | ||
| 1275 | + | ||
| 1254 | // 初始化Report Engine锁定状态检查 | 1276 | // 初始化Report Engine锁定状态检查 |
| 1255 | checkReportLockStatus(); | 1277 | checkReportLockStatus(); |
| 1256 | reportLockCheckInterval = setInterval(checkReportLockStatus, 10000); // 每10秒检查一次 | 1278 | reportLockCheckInterval = setInterval(checkReportLockStatus, 10000); // 每10秒检查一次 |
| @@ -1520,10 +1542,19 @@ | @@ -1520,10 +1542,19 @@ | ||
| 1520 | autocomplete="off" | 1542 | autocomplete="off" |
| 1521 | > | 1543 | > |
| 1522 | `; | 1544 | `; |
| 1545 | + // 眼睛图标 - 闭眼状态(默认隐藏密码) | ||
| 1546 | + const eyeOffIcon = ` | ||
| 1547 | + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
| 1548 | + <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path> | ||
| 1549 | + <line x1="1" y1="1" x2="23" y2="23"></line> | ||
| 1550 | + </svg> | ||
| 1551 | + `; | ||
| 1523 | control = ` | 1552 | control = ` |
| 1524 | <div class="config-password-wrapper"> | 1553 | <div class="config-password-wrapper"> |
| 1525 | ${inputElement} | 1554 | ${inputElement} |
| 1526 | - <button type="button" class="config-password-toggle" data-target="${field.key}">显示</button> | 1555 | + <button type="button" class="config-password-toggle" data-target="${field.key}" title="显示/隐藏密码"> |
| 1556 | + ${eyeOffIcon} | ||
| 1557 | + </button> | ||
| 1527 | </div> | 1558 | </div> |
| 1528 | `; | 1559 | `; |
| 1529 | } else { | 1560 | } else { |
| @@ -1562,24 +1593,57 @@ | @@ -1562,24 +1593,57 @@ | ||
| 1562 | }).join(''); | 1593 | }).join(''); |
| 1563 | 1594 | ||
| 1564 | container.innerHTML = sections; | 1595 | container.innerHTML = sections; |
| 1565 | - attachConfigPasswordToggles(); | 1596 | + // 不再需要每次调用 attachConfigPasswordToggles |
| 1597 | + // 事件委托已在页面初始化时设置 | ||
| 1566 | } | 1598 | } |
| 1567 | 1599 | ||
| 1568 | function attachConfigPasswordToggles() { | 1600 | function attachConfigPasswordToggles() { |
| 1569 | - const toggles = document.querySelectorAll('.config-password-toggle'); | ||
| 1570 | - toggles.forEach(toggle => { | 1601 | + // 定义眼睛图标的SVG |
| 1602 | + const eyeOffIcon = ` | ||
| 1603 | + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
| 1604 | + <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path> | ||
| 1605 | + <line x1="1" y1="1" x2="23" y2="23"></line> | ||
| 1606 | + </svg> | ||
| 1607 | + `; | ||
| 1608 | + const eyeOnIcon = ` | ||
| 1609 | + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
| 1610 | + <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path> | ||
| 1611 | + <circle cx="12" cy="12" r="3"></circle> | ||
| 1612 | + </svg> | ||
| 1613 | + `; | ||
| 1614 | + | ||
| 1615 | + // 使用事件委托,只在容器上绑定一次事件 | ||
| 1616 | + const container = document.getElementById('configFormContainer'); | ||
| 1617 | + if (!container) { | ||
| 1618 | + return; | ||
| 1619 | + } | ||
| 1620 | + | ||
| 1621 | + // 防止重复绑定 | ||
| 1622 | + if (container.dataset.passwordToggleAttached === 'true') { | ||
| 1623 | + return; | ||
| 1624 | + } | ||
| 1625 | + | ||
| 1626 | + container.addEventListener('click', (event) => { | ||
| 1627 | + // 查找是否点击了密码切换按钮或其内部的SVG | ||
| 1628 | + const toggle = event.target.closest('.config-password-toggle'); | ||
| 1629 | + if (!toggle) { | ||
| 1630 | + return; | ||
| 1631 | + } | ||
| 1632 | + | ||
| 1571 | const key = toggle.dataset.target; | 1633 | const key = toggle.dataset.target; |
| 1572 | - const input = document.querySelector(`.config-field-input[data-config-key="${key}"]`); | 1634 | + const input = container.querySelector(`.config-field-input[data-config-key="${key}"]`); |
| 1573 | if (!input) { | 1635 | if (!input) { |
| 1574 | return; | 1636 | return; |
| 1575 | } | 1637 | } |
| 1576 | - toggle.addEventListener('click', () => { | ||
| 1577 | - const reveal = input.getAttribute('type') === 'password'; | ||
| 1578 | - input.setAttribute('type', reveal ? 'text' : 'password'); | ||
| 1579 | - toggle.textContent = reveal ? '隐藏' : '显示'; | ||
| 1580 | - toggle.classList.toggle('revealed', reveal); | ||
| 1581 | - }); | 1638 | + |
| 1639 | + const reveal = input.getAttribute('type') === 'password'; | ||
| 1640 | + input.setAttribute('type', reveal ? 'text' : 'password'); | ||
| 1641 | + toggle.innerHTML = reveal ? eyeOnIcon : eyeOffIcon; | ||
| 1642 | + toggle.classList.toggle('revealed', reveal); | ||
| 1582 | }); | 1643 | }); |
| 1644 | + | ||
| 1645 | + // 标记已绑定,防止重复 | ||
| 1646 | + container.dataset.passwordToggleAttached = 'true'; | ||
| 1583 | } | 1647 | } |
| 1584 | 1648 | ||
| 1585 | function collectConfigUpdates() { | 1649 | function collectConfigUpdates() { |
-
Please register or login to post a comment