merge: 合并远程分支并修复代码冲突
合并了远程分支 cb72262 的功能更新,同时保留了 ESLint 修复:
**冲突解决详情:**
1. AccountTableFilters.vue
- ✅ 保留 emit 模式修复(避免 vue/no-mutating-props 错误)
- ✅ 添加第三个筛选器 type(账户类型)
- ✅ 新增 antigravity 平台和 inactive 状态选项
2. UserBalanceModal.vue
- ✅ 保留 console.error 错误日志
- ✅ 添加输入验证(金额校验、余额不足检查)
- ✅ 使用 appStore.showError 向用户显示友好错误
3. AccountsView.vue
- ✅ 保留所有 console.error 错误日志(避免 no-empty 错误)
- ✅ 使用新 API:clearRateLimit 和 setSchedulable
4. UsageView.vue
- ✅ 添加 console.error 错误日志
- ✅ 添加图表功能(模型分布、使用趋势)
- ✅ 添加粒度选择(按天/按小时)
- ✅ 保留 XLSX 动态导入优化
**测试结果:**
- ✅ Go tests: PASS
- ✅ golangci-lint: 0 issues
- ✅ ESLint: 0 errors
- ✅ TypeScript: PASS
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,7 +52,7 @@ const load = async () => { loading.value = true; try { const res = await adminAP
|
||||
const handleSave = async () => {
|
||||
if (!props.user) return; submitting.value = true
|
||||
try {
|
||||
await adminAPI.users.update(props.user.id, { allowed_groups: selectedIds.value.length > 0 ? selectedIds.value : null })
|
||||
await adminAPI.users.update(props.user.id, { allowed_groups: selectedIds.value })
|
||||
appStore.showSuccess(t('admin.users.allowedGroupsUpdated')); emit('success'); emit('close')
|
||||
} catch (error) { console.error('Failed to update allowed groups:', error) } finally { submitting.value = false }
|
||||
}
|
||||
|
||||
@@ -37,10 +37,22 @@ watch(() => props.show, (v) => { if(v) { form.amount = 0; form.notes = '' } })
|
||||
|
||||
const calculateNewBalance = () => (props.user ? (props.operation === 'add' ? props.user.balance + form.amount : props.user.balance - form.amount) : 0)
|
||||
const handleBalanceSubmit = async () => {
|
||||
if (!props.user) return; submitting.value = true
|
||||
if (!props.user) return
|
||||
if (!form.amount || form.amount <= 0) {
|
||||
appStore.showError(t('admin.users.amountRequired'))
|
||||
return
|
||||
}
|
||||
if (props.operation === 'subtract' && form.amount > props.user.balance) {
|
||||
appStore.showError(t('admin.users.insufficientBalance'))
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
await adminAPI.users.updateBalance(props.user.id, form.amount, props.operation, form.notes)
|
||||
appStore.showSuccess(t('common.success')); emit('success'); emit('close')
|
||||
} catch (error) { console.error('Failed to update balance:', error) } finally { submitting.value = false }
|
||||
} catch (e: any) {
|
||||
console.error('Failed to update balance:', e)
|
||||
appStore.showError(e.response?.data?.detail || t('common.error'))
|
||||
} finally { submitting.value = false }
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<input v-model="form.password" type="text" required class="input pr-10" :placeholder="t('admin.users.enterPassword')" />
|
||||
</div>
|
||||
<button type="button" @click="generateRandomPassword" class="btn btn-secondary px-3">
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /></svg>
|
||||
<Icon name="refresh" size="md" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,6 +52,7 @@ import { reactive, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'; import { adminAPI } from '@/api/admin'
|
||||
import { useForm } from '@/composables/useForm'
|
||||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
|
||||
const props = defineProps<{ show: boolean }>()
|
||||
const emit = defineEmits(['close', 'success']); const { t } = useI18n()
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" @click="generatePassword" class="btn btn-secondary px-3">
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /></svg>
|
||||
<Icon name="refresh" size="md" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,6 +59,7 @@ import { adminAPI } from '@/api/admin'
|
||||
import type { User, UserAttributeValuesMap } from '@/types'
|
||||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||||
import UserAttributeForm from '@/components/user/UserAttributeForm.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
|
||||
const props = defineProps<{ show: boolean, user: User | null }>()
|
||||
const emit = defineEmits(['close', 'success'])
|
||||
@@ -86,6 +87,14 @@ const copyPassword = async () => {
|
||||
}
|
||||
const handleUpdateUser = async () => {
|
||||
if (!props.user) return
|
||||
if (!form.email.trim()) {
|
||||
appStore.showError(t('admin.users.emailRequired'))
|
||||
return
|
||||
}
|
||||
if (form.concurrency < 1) {
|
||||
appStore.showError(t('admin.users.concurrencyMin'))
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const data: any = { email: form.email, username: form.username, notes: form.notes, concurrency: form.concurrency }
|
||||
@@ -98,4 +107,4 @@ const handleUpdateUser = async () => {
|
||||
appStore.showError(e.response?.data?.detail || t('admin.users.failedToUpdate'))
|
||||
} finally { submitting.value = false }
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user