mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 20:05:54 +00:00
Merge branch 'main' into new
This commit is contained in:
@@ -304,7 +304,25 @@ const selectQuickOption = (value) => {
|
||||
// 更新自定义过期时间
|
||||
const updateCustomExpiryPreview = () => {
|
||||
if (localForm.customExpireDate) {
|
||||
localForm.expiresAt = new Date(localForm.customExpireDate).toISOString()
|
||||
try {
|
||||
// 手动解析日期时间字符串,确保它被正确解释为本地时间
|
||||
const [datePart, timePart] = localForm.customExpireDate.split('T')
|
||||
const [year, month, day] = datePart.split('-').map(Number)
|
||||
const [hours, minutes] = timePart.split(':').map(Number)
|
||||
|
||||
// 使用构造函数创建本地时间的 Date 对象,然后转换为 UTC ISO 字符串
|
||||
const localDate = new Date(year, month - 1, day, hours, minutes, 0, 0)
|
||||
|
||||
// 验证日期有效性
|
||||
if (isNaN(localDate.getTime())) {
|
||||
console.error('Invalid date:', localForm.customExpireDate)
|
||||
return
|
||||
}
|
||||
|
||||
localForm.expiresAt = localDate.toISOString()
|
||||
} catch (error) {
|
||||
console.error('Failed to parse custom expire date:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4841,11 +4841,23 @@ const handleGroupRefresh = async () => {
|
||||
// 处理 API Key 管理模态框刷新
|
||||
const handleApiKeyRefresh = async () => {
|
||||
// 刷新账户信息以更新 API Key 数量
|
||||
if (props.account?.id) {
|
||||
if (!props.account?.id) {
|
||||
return
|
||||
}
|
||||
|
||||
const refreshers = [
|
||||
typeof accountsStore.fetchDroidAccounts === 'function'
|
||||
? accountsStore.fetchDroidAccounts
|
||||
: null,
|
||||
typeof accountsStore.fetchAllAccounts === 'function' ? accountsStore.fetchAllAccounts : null
|
||||
].filter(Boolean)
|
||||
|
||||
for (const refresher of refreshers) {
|
||||
try {
|
||||
await accountsStore.fetchAccounts()
|
||||
await refresher()
|
||||
return
|
||||
} catch (error) {
|
||||
console.error('Failed to refresh account data:', error)
|
||||
console.error('刷新账户列表失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,28 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="p-1 text-gray-400 transition-colors hover:text-gray-600 dark:hover:text-gray-300"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<i class="fas fa-times text-lg sm:text-xl" />
|
||||
</button>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="flex items-center gap-2 rounded-lg border border-purple-200 bg-white/90 px-3 py-1.5 text-xs font-semibold text-purple-600 shadow-sm transition-all duration-200 hover:border-purple-300 hover:bg-purple-50 hover:text-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-200 disabled:cursor-not-allowed disabled:opacity-60 dark:border-purple-600/60 dark:bg-purple-900/20 dark:text-purple-200 dark:hover:border-purple-500 dark:hover:bg-purple-900/40 dark:hover:text-purple-100 dark:focus:ring-purple-500/40 sm:text-sm"
|
||||
:disabled="loading || apiKeys.length === 0 || copyingAll"
|
||||
@click="copyAllApiKeys"
|
||||
>
|
||||
<i
|
||||
:class="[
|
||||
'text-sm sm:text-base',
|
||||
copyingAll ? 'fas fa-spinner fa-spin' : 'fas fa-clipboard-list'
|
||||
]"
|
||||
/>
|
||||
<span>复制全部 Key</span>
|
||||
</button>
|
||||
<button
|
||||
class="flex h-9 w-9 items-center justify-center rounded-full bg-gray-100 text-gray-400 transition-colors hover:text-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:text-gray-200 sm:h-10 sm:w-10"
|
||||
title="关闭"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<i class="fas fa-times text-base sm:text-lg" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
@@ -393,6 +409,7 @@ const resetting = ref(null)
|
||||
const apiKeys = ref([])
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const copyingAll = ref(false)
|
||||
|
||||
// 新增:筛选和搜索相关状态
|
||||
const statusFilter = ref('all') // 'all' | 'active' | 'error'
|
||||
@@ -703,14 +720,71 @@ const exportKeys = (type) => {
|
||||
showToast(`成功导出 ${keysToExport.length} 个 API Key`, 'success')
|
||||
}
|
||||
|
||||
// 写入剪贴板(带回退逻辑)
|
||||
const writeToClipboard = async (text) => {
|
||||
const canUseClipboardApi =
|
||||
typeof navigator !== 'undefined' &&
|
||||
navigator.clipboard &&
|
||||
typeof navigator.clipboard.writeText === 'function' &&
|
||||
(typeof window === 'undefined' || window.isSecureContext !== false)
|
||||
|
||||
if (canUseClipboardApi) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof document === 'undefined') {
|
||||
throw new Error('clipboard unavailable')
|
||||
}
|
||||
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.value = text
|
||||
textarea.setAttribute('readonly', '')
|
||||
textarea.style.position = 'fixed'
|
||||
textarea.style.opacity = '0'
|
||||
textarea.style.pointerEvents = 'none'
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select()
|
||||
|
||||
try {
|
||||
const success = document.execCommand('copy')
|
||||
document.body.removeChild(textarea)
|
||||
if (!success) {
|
||||
throw new Error('execCommand failed')
|
||||
}
|
||||
} catch (error) {
|
||||
document.body.removeChild(textarea)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 复制 API Key
|
||||
const copyApiKey = async (key) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(key)
|
||||
await writeToClipboard(key)
|
||||
showToast('API Key 已复制', 'success')
|
||||
} catch (error) {
|
||||
console.error('Failed to copy:', error)
|
||||
showToast('复制失败', 'error')
|
||||
showToast('复制失败,请手动复制', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// 复制全部 API Key
|
||||
const copyAllApiKeys = async () => {
|
||||
if (!apiKeys.value.length || copyingAll.value) {
|
||||
return
|
||||
}
|
||||
|
||||
copyingAll.value = true
|
||||
try {
|
||||
const allKeysText = apiKeys.value.map((item) => item.key).join('\n')
|
||||
await writeToClipboard(allKeysText)
|
||||
showToast(`已复制 ${apiKeys.value.length} 条 API Key`, 'success')
|
||||
} catch (error) {
|
||||
console.error('Failed to copy all keys:', error)
|
||||
showToast('复制全部 API Key 失败,请手动复制', 'error')
|
||||
} finally {
|
||||
copyingAll.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user