mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 18:49:15 +00:00
1
This commit is contained in:
@@ -425,16 +425,13 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="(key, index) in paginatedApiKeys" :key="key.id">
|
||||
<template v-for="key in paginatedApiKeys" :key="key.id">
|
||||
<!-- API Key 主行 - 添加斑马条纹和增强分隔 -->
|
||||
<tr
|
||||
:class="[
|
||||
'table-row transition-all duration-150',
|
||||
index % 2 === 0
|
||||
? 'bg-white dark:bg-gray-800/40'
|
||||
: 'bg-gray-50/70 dark:bg-gray-700/30',
|
||||
'table-row',
|
||||
'border-b-2 border-gray-200/80 dark:border-gray-700/50',
|
||||
'hover:bg-blue-50/60 hover:shadow-sm dark:hover:bg-blue-900/20'
|
||||
'hover:shadow-sm'
|
||||
]"
|
||||
>
|
||||
<td
|
||||
@@ -458,8 +455,9 @@
|
||||
<div class="min-w-0">
|
||||
<!-- 名称 -->
|
||||
<div
|
||||
class="truncate text-sm font-semibold text-gray-900 dark:text-gray-100"
|
||||
:title="key.name"
|
||||
class="cursor-pointer truncate text-sm font-semibold text-gray-900 hover:text-blue-600 dark:text-gray-100 dark:hover:text-blue-400"
|
||||
title="点击复制"
|
||||
@click.stop="copyText(key.name)"
|
||||
>
|
||||
{{ key.name }}
|
||||
</div>
|
||||
@@ -1265,7 +1263,11 @@
|
||||
@change="updateSelectAllState"
|
||||
/>
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||
<h4
|
||||
class="cursor-pointer text-sm font-semibold text-gray-900 hover:text-blue-600 dark:text-gray-100 dark:hover:text-blue-400"
|
||||
title="点击复制"
|
||||
@click.stop="copyText(key.name)"
|
||||
>
|
||||
{{ key.name }}
|
||||
</h4>
|
||||
<p class="mt-0.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
@@ -1854,8 +1856,9 @@
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<div
|
||||
class="truncate text-sm font-semibold text-gray-900 dark:text-gray-100"
|
||||
:title="key.name"
|
||||
class="cursor-pointer truncate text-sm font-semibold text-gray-900 hover:text-blue-600 dark:text-gray-100 dark:hover:text-blue-400"
|
||||
title="点击复制"
|
||||
@click.stop="copyText(key.name)"
|
||||
>
|
||||
{{ key.name }}
|
||||
</div>
|
||||
@@ -2114,14 +2117,26 @@
|
||||
@close="showUsageDetailModal = false"
|
||||
@open-timeline="openTimeline"
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
:cancel-text="confirmModalConfig.cancelText"
|
||||
:confirm-text="confirmModalConfig.confirmText"
|
||||
:message="confirmModalConfig.message"
|
||||
:show="showConfirmModal"
|
||||
:title="confirmModalConfig.title"
|
||||
:type="confirmModalConfig.type"
|
||||
@cancel="handleCancel"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { showToast } from '@/utils/toast'
|
||||
import { apiClient } from '@/config/api'
|
||||
import { showToast } from '@/utils/tools'
|
||||
import { copyText } from '@/utils/tools'
|
||||
import * as httpApi from '@/utils/http_apis'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import * as XLSX from 'xlsx-js-style'
|
||||
import CreateApiKeyModal from '@/components/apikeys/CreateApiKeyModal.vue'
|
||||
@@ -2135,6 +2150,7 @@ import UsageDetailModal from '@/components/apikeys/UsageDetailModal.vue'
|
||||
import LimitProgressBar from '@/components/apikeys/LimitProgressBar.vue'
|
||||
import CustomDropdown from '@/components/common/CustomDropdown.vue'
|
||||
import ActionDropdown from '@/components/common/ActionDropdown.vue'
|
||||
import ConfirmModal from '@/components/common/ConfirmModal.vue'
|
||||
|
||||
// 响应式数据
|
||||
const router = useRouter()
|
||||
@@ -2304,6 +2320,39 @@ const renewingApiKey = ref(null)
|
||||
const newApiKeyData = ref(null)
|
||||
const batchApiKeyData = ref([])
|
||||
|
||||
// ConfirmModal 状态
|
||||
const showConfirmModal = ref(false)
|
||||
const confirmModalConfig = ref({
|
||||
title: '',
|
||||
message: '',
|
||||
type: 'primary',
|
||||
confirmText: '确认',
|
||||
cancelText: '取消'
|
||||
})
|
||||
const confirmResolve = ref(null)
|
||||
|
||||
const showConfirm = (
|
||||
title,
|
||||
message,
|
||||
confirmText = '确认',
|
||||
cancelText = '取消',
|
||||
type = 'primary'
|
||||
) => {
|
||||
return new Promise((resolve) => {
|
||||
confirmModalConfig.value = { title, message, confirmText, cancelText, type }
|
||||
confirmResolve.value = resolve
|
||||
showConfirmModal.value = true
|
||||
})
|
||||
}
|
||||
const handleConfirm = () => {
|
||||
showConfirmModal.value = false
|
||||
confirmResolve.value?.(true)
|
||||
}
|
||||
const handleCancel = () => {
|
||||
showConfirmModal.value = false
|
||||
confirmResolve.value?.(false)
|
||||
}
|
||||
|
||||
// 计算排序后的API Keys(现在由后端处理,这里直接返回)
|
||||
const sortedApiKeys = computed(() => {
|
||||
// 后端已经处理了筛选、搜索和排序,直接返回
|
||||
@@ -2396,15 +2445,15 @@ const loadAccounts = async (forceRefresh = false) => {
|
||||
droidData,
|
||||
groupsData
|
||||
] = await Promise.all([
|
||||
apiClient.get('/admin/claude-accounts'),
|
||||
apiClient.get('/admin/claude-console-accounts'),
|
||||
apiClient.get('/admin/gemini-accounts'),
|
||||
apiClient.get('/admin/gemini-api-accounts'), // 加载 Gemini-API 账号
|
||||
apiClient.get('/admin/openai-accounts'),
|
||||
apiClient.get('/admin/openai-responses-accounts'), // 加载 OpenAI-Responses 账号
|
||||
apiClient.get('/admin/bedrock-accounts'),
|
||||
apiClient.get('/admin/droid-accounts'),
|
||||
apiClient.get('/admin/account-groups')
|
||||
httpApi.getClaudeAccounts(),
|
||||
httpApi.getClaudeConsoleAccounts(),
|
||||
httpApi.getGeminiAccounts(),
|
||||
httpApi.getGeminiApiAccounts(),
|
||||
httpApi.getOpenAIAccounts(),
|
||||
httpApi.getOpenAIResponsesAccounts(),
|
||||
httpApi.getBedrockAccounts(),
|
||||
httpApi.getDroidAccounts(),
|
||||
httpApi.getAccountGroups()
|
||||
])
|
||||
|
||||
// 合并Claude OAuth账户和Claude Console账户
|
||||
@@ -2510,7 +2559,7 @@ const loadAccounts = async (forceRefresh = false) => {
|
||||
// 加载已使用的模型列表
|
||||
const loadUsedModels = async () => {
|
||||
try {
|
||||
const data = await apiClient.get('/admin/api-keys/used-models')
|
||||
const data = await httpApi.get('/admin/api-keys/used-models')
|
||||
if (data.success) {
|
||||
availableModels.value = data.data || []
|
||||
}
|
||||
@@ -2599,7 +2648,7 @@ const loadApiKeys = async (clearStatsCache = true) => {
|
||||
params.set('timeRange', globalDateFilter.preset)
|
||||
}
|
||||
|
||||
const data = await apiClient.get(`/admin/api-keys?${params.toString()}`)
|
||||
const data = await httpApi.getApiKeysWithParams(params.toString())
|
||||
if (data.success) {
|
||||
// 更新数据
|
||||
apiKeys.value = data.data?.items || []
|
||||
@@ -2680,7 +2729,7 @@ const loadPageStats = async () => {
|
||||
requestBody.endDate = endDate
|
||||
}
|
||||
|
||||
const response = await apiClient.post('/admin/api-keys/batch-stats', requestBody)
|
||||
const response = await httpApi.post('/admin/api-keys/batch-stats', requestBody)
|
||||
|
||||
if (response.success && response.data) {
|
||||
// 更新缓存
|
||||
@@ -2734,7 +2783,7 @@ const loadPageLastUsage = async () => {
|
||||
keyIds.forEach((id) => lastUsageLoading.value.add(id))
|
||||
|
||||
try {
|
||||
const response = await apiClient.post('/admin/api-keys/batch-last-usage', { keyIds })
|
||||
const response = await httpApi.post('/admin/api-keys/batch-last-usage', { keyIds })
|
||||
|
||||
if (response.success && response.data) {
|
||||
// 更新缓存
|
||||
@@ -2765,7 +2814,7 @@ const loadDeletedApiKeys = async () => {
|
||||
activeTab.value = 'deleted'
|
||||
deletedApiKeysLoading.value = true
|
||||
try {
|
||||
const data = await apiClient.get('/admin/api-keys/deleted')
|
||||
const data = await httpApi.get('/admin/api-keys/deleted')
|
||||
if (data.success) {
|
||||
deletedApiKeys.value = data.apiKeys || []
|
||||
}
|
||||
@@ -2844,7 +2893,7 @@ let costSortStatusTimer = null
|
||||
// 获取费用排序索引状态
|
||||
const fetchCostSortStatus = async () => {
|
||||
try {
|
||||
const data = await apiClient.get('/admin/api-keys/cost-sort-status')
|
||||
const data = await httpApi.get('/admin/api-keys/cost-sort-status')
|
||||
if (data.success) {
|
||||
costSortStatus.value = data.data || {}
|
||||
|
||||
@@ -3193,7 +3242,7 @@ const loadApiKeyModelStats = async (keyId, forceReload = false) => {
|
||||
|
||||
url += '?' + params.toString()
|
||||
|
||||
const data = await apiClient.get(url)
|
||||
const data = await httpApi.get(url)
|
||||
if (data.success) {
|
||||
apiKeyModelStats.value[keyId] = data.data || []
|
||||
}
|
||||
@@ -3845,27 +3894,19 @@ const toggleApiKeyStatus = async (key) => {
|
||||
|
||||
// 禁用时需要二次确认
|
||||
if (key.isActive) {
|
||||
if (window.showConfirm) {
|
||||
confirmed = await window.showConfirm(
|
||||
'禁用 API Key',
|
||||
`确定要禁用 API Key "${key.name}" 吗?禁用后所有使用此 Key 的请求将返回 401 错误。`,
|
||||
'确定禁用',
|
||||
'取消'
|
||||
)
|
||||
} else {
|
||||
// 降级方案
|
||||
confirmed = confirm(
|
||||
`确定要禁用 API Key "${key.name}" 吗?禁用后所有使用此 Key 的请求将返回 401 错误。`
|
||||
)
|
||||
}
|
||||
confirmed = await showConfirm(
|
||||
'禁用 API Key',
|
||||
`确定要禁用 API Key "${key.name}" 吗?禁用后所有使用此 Key 的请求将返回 401 错误。`,
|
||||
'确定禁用',
|
||||
'取消',
|
||||
'warning'
|
||||
)
|
||||
}
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
const data = await apiClient.put(`/admin/api-keys/${key.id}`, {
|
||||
isActive: !key.isActive
|
||||
})
|
||||
const data = await httpApi.updateApiKey(key.id, { isActive: !key.isActive })
|
||||
|
||||
if (data.success) {
|
||||
showToast(`API Key 已${key.isActive ? '禁用' : '激活'}`, 'success')
|
||||
@@ -3885,24 +3926,18 @@ const toggleApiKeyStatus = async (key) => {
|
||||
// 更新API Key图标
|
||||
// 删除API Key
|
||||
const deleteApiKey = async (keyId) => {
|
||||
let confirmed = false
|
||||
|
||||
if (window.showConfirm) {
|
||||
confirmed = await window.showConfirm(
|
||||
'删除 API Key',
|
||||
'确定要删除这个 API Key 吗?此操作不可恢复。',
|
||||
'确定删除',
|
||||
'取消'
|
||||
)
|
||||
} else {
|
||||
// 降级方案
|
||||
confirmed = confirm('确定要删除这个 API Key 吗?此操作不可恢复。')
|
||||
}
|
||||
const confirmed = await showConfirm(
|
||||
'删除 API Key',
|
||||
'确定要删除这个 API Key 吗?此操作不可恢复。',
|
||||
'确定删除',
|
||||
'取消',
|
||||
'danger'
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
const data = await apiClient.delete(`/admin/api-keys/${keyId}`)
|
||||
const data = await httpApi.deleteApiKey(keyId)
|
||||
if (data.success) {
|
||||
showToast('API Key 已删除', 'success')
|
||||
// 从选中列表中移除
|
||||
@@ -3922,24 +3957,18 @@ const deleteApiKey = async (keyId) => {
|
||||
|
||||
// 恢复API Key
|
||||
const restoreApiKey = async (keyId) => {
|
||||
let confirmed = false
|
||||
|
||||
if (window.showConfirm) {
|
||||
confirmed = await window.showConfirm(
|
||||
'恢复 API Key',
|
||||
'确定要恢复这个 API Key 吗?恢复后可以重新使用。',
|
||||
'确定恢复',
|
||||
'取消'
|
||||
)
|
||||
} else {
|
||||
// 降级方案
|
||||
confirmed = confirm('确定要恢复这个 API Key 吗?恢复后可以重新使用。')
|
||||
}
|
||||
const confirmed = await showConfirm(
|
||||
'恢复 API Key',
|
||||
'确定要恢复这个 API Key 吗?恢复后可以重新使用。',
|
||||
'确定恢复',
|
||||
'取消',
|
||||
'primary'
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
const data = await apiClient.post(`/admin/api-keys/${keyId}/restore`)
|
||||
const data = await httpApi.restoreApiKey(keyId)
|
||||
if (data.success) {
|
||||
showToast('API Key 已成功恢复', 'success')
|
||||
// 刷新已删除列表
|
||||
@@ -3956,24 +3985,18 @@ const restoreApiKey = async (keyId) => {
|
||||
|
||||
// 彻底删除API Key
|
||||
const permanentDeleteApiKey = async (keyId) => {
|
||||
let confirmed = false
|
||||
|
||||
if (window.showConfirm) {
|
||||
confirmed = await window.showConfirm(
|
||||
'彻底删除 API Key',
|
||||
'确定要彻底删除这个 API Key 吗?此操作不可恢复,所有相关数据将被永久删除。',
|
||||
'确定彻底删除',
|
||||
'取消'
|
||||
)
|
||||
} else {
|
||||
// 降级方案
|
||||
confirmed = confirm('确定要彻底删除这个 API Key 吗?此操作不可恢复,所有相关数据将被永久删除。')
|
||||
}
|
||||
const confirmed = await showConfirm(
|
||||
'彻底删除 API Key',
|
||||
'确定要彻底删除这个 API Key 吗?此操作不可恢复,所有相关数据将被永久删除。',
|
||||
'确定彻底删除',
|
||||
'取消',
|
||||
'danger'
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
const data = await apiClient.delete(`/admin/api-keys/${keyId}/permanent`)
|
||||
const data = await httpApi.permanentDeleteApiKey(keyId)
|
||||
if (data.success) {
|
||||
showToast('API Key 已彻底删除', 'success')
|
||||
// 刷新已删除列表
|
||||
@@ -3994,24 +4017,18 @@ const clearAllDeletedApiKeys = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
let confirmed = false
|
||||
|
||||
if (window.showConfirm) {
|
||||
confirmed = await window.showConfirm(
|
||||
'清空所有已删除的 API Keys',
|
||||
`确定要彻底删除全部 ${count} 个已删除的 API Keys 吗?此操作不可恢复,所有相关数据将被永久删除。`,
|
||||
'确定清空全部',
|
||||
'取消'
|
||||
)
|
||||
} else {
|
||||
// 降级方案
|
||||
confirmed = confirm(`确定要彻底删除全部 ${count} 个已删除的 API Keys 吗?此操作不可恢复。`)
|
||||
}
|
||||
const confirmed = await showConfirm(
|
||||
'清空所有已删除的 API Keys',
|
||||
`确定要彻底删除全部 ${count} 个已删除的 API Keys 吗?此操作不可恢复,所有相关数据将被永久删除。`,
|
||||
'确定清空全部',
|
||||
'取消',
|
||||
'danger'
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
const data = await apiClient.delete('/admin/api-keys/deleted/clear-all')
|
||||
const data = await httpApi.del('/admin/api-keys/deleted/clear-all')
|
||||
if (data.success) {
|
||||
showToast(data.message || '已清空所有已删除的 API Keys', 'success')
|
||||
|
||||
@@ -4040,21 +4057,20 @@ const batchDeleteApiKeys = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
let confirmed = false
|
||||
const message = `确定要删除选中的 ${selectedCount} 个 API Key 吗?此操作不可恢复。`
|
||||
|
||||
if (window.showConfirm) {
|
||||
confirmed = await window.showConfirm('批量删除 API Keys', message, '确定删除', '取消')
|
||||
} else {
|
||||
confirmed = confirm(message)
|
||||
}
|
||||
const confirmed = await showConfirm(
|
||||
'批量删除 API Keys',
|
||||
`确定要删除选中的 ${selectedCount} 个 API Key 吗?此操作不可恢复。`,
|
||||
'确定删除',
|
||||
'取消',
|
||||
'danger'
|
||||
)
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
const keyIds = [...selectedApiKeys.value]
|
||||
|
||||
try {
|
||||
const data = await apiClient.delete('/admin/api-keys/batch', {
|
||||
const data = await httpApi.del('/admin/api-keys/batch', {
|
||||
data: { keyIds }
|
||||
})
|
||||
|
||||
@@ -4136,7 +4152,7 @@ const closeExpiryEdit = () => {
|
||||
const handleSaveExpiry = async ({ keyId, expiresAt, activateNow }) => {
|
||||
try {
|
||||
// 使用新的PATCH端点来修改过期时间
|
||||
const data = await apiClient.patch(`/admin/api-keys/${keyId}/expiration`, {
|
||||
const data = await httpApi.updateApiKeyExpiration(keyId, {
|
||||
expiresAt: expiresAt || null,
|
||||
activateNow: activateNow || false
|
||||
})
|
||||
@@ -4873,27 +4889,41 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.dark .table-container::-webkit-scrollbar-track {
|
||||
background: #374151;
|
||||
background: var(--bg-gradient-mid);
|
||||
}
|
||||
|
||||
.dark .table-container::-webkit-scrollbar-thumb {
|
||||
background: #4b5563;
|
||||
background: var(--bg-gradient-end);
|
||||
}
|
||||
|
||||
.dark .table-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #6b7280;
|
||||
background: var(--text-secondary);
|
||||
}
|
||||
|
||||
.table-row {
|
||||
transition: background-color 0.2s ease;
|
||||
/* 统一 hover 背景 - 所有 td 使用主题色 */
|
||||
.table-container tbody tr:hover > td {
|
||||
background-color: rgba(var(--primary-rgb), 0.06) !important;
|
||||
}
|
||||
|
||||
.table-row:hover {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
.dark .table-container tbody tr:hover > td {
|
||||
background-color: rgba(var(--primary-rgb), 0.16) !important;
|
||||
}
|
||||
|
||||
.dark .table-row:hover {
|
||||
background-color: rgba(255, 255, 255, 0.02);
|
||||
/* 所有 td 的斑马纹背景 */
|
||||
.table-container tbody tr:nth-child(odd) > td {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.table-container tbody tr:nth-child(even) > td {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:nth-child(odd) > td {
|
||||
background-color: var(--bg-gradient-start);
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:nth-child(even) > td {
|
||||
background-color: var(--bg-gradient-mid);
|
||||
}
|
||||
|
||||
/* 固定操作列在右侧,兼容浅色和深色模式 */
|
||||
@@ -4910,33 +4940,7 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.dark .table-container thead .operations-column {
|
||||
background: linear-gradient(to bottom, #374151, #1f2937);
|
||||
}
|
||||
|
||||
/* tbody 中的操作列背景处理 - 使用纯色避免滚动时重叠 */
|
||||
.table-container tbody tr:nth-child(odd) .operations-column {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.table-container tbody tr:nth-child(even) .operations-column {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:nth-child(odd) .operations-column {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:nth-child(even) .operations-column {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
/* hover 状态下的操作列背景 */
|
||||
.table-container tbody tr:hover .operations-column {
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:hover .operations-column {
|
||||
background-color: #1e3a5f;
|
||||
background: linear-gradient(to bottom, var(--bg-gradient-mid), var(--bg-gradient-start));
|
||||
}
|
||||
|
||||
.table-container tbody .operations-column {
|
||||
@@ -4963,39 +4967,7 @@ onUnmounted(() => {
|
||||
|
||||
.dark .table-container thead .checkbox-column,
|
||||
.dark .table-container thead .name-column {
|
||||
background: linear-gradient(to bottom, #374151, #1f2937);
|
||||
}
|
||||
|
||||
/* tbody 中的左侧固定列背景处理 - 使用纯色避免滚动时重叠 */
|
||||
.table-container tbody tr:nth-child(odd) .checkbox-column,
|
||||
.table-container tbody tr:nth-child(odd) .name-column {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.table-container tbody tr:nth-child(even) .checkbox-column,
|
||||
.table-container tbody tr:nth-child(even) .name-column {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:nth-child(odd) .checkbox-column,
|
||||
.dark .table-container tbody tr:nth-child(odd) .name-column {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:nth-child(even) .checkbox-column,
|
||||
.dark .table-container tbody tr:nth-child(even) .name-column {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
/* hover 状态下的左侧固定列背景 */
|
||||
.table-container tbody tr:hover .checkbox-column,
|
||||
.table-container tbody tr:hover .name-column {
|
||||
background-color: #eff6ff;
|
||||
}
|
||||
|
||||
.dark .table-container tbody tr:hover .checkbox-column,
|
||||
.dark .table-container tbody tr:hover .name-column {
|
||||
background-color: #1e3a5f;
|
||||
background: linear-gradient(to bottom, var(--bg-gradient-mid), var(--bg-gradient-start));
|
||||
}
|
||||
|
||||
/* 名称列右侧阴影(分隔效果) */
|
||||
|
||||
Reference in New Issue
Block a user