feat(密钥管理): 后台分组过滤+欠费状态识别
- 后台密钥列表:主管只看本组子账户(created_by_id=自己),超管全量可按创建人筛选;行 VO 新增 createdById/createdByUsername - 代理提取接口欠费(message=余额不足)识别为 insufficient_balance,不再静默回退直连 - 上游 LLM 网关欠费(code=insufficient_user_quota/预扣费额度失败)同样归为欠费并透传额度明细 - 前端:桌面设置面板显示"欠费",admin 后台单格药丸显示"欠费"、新增所属管理员列与超管筛选
This commit is contained in:
@@ -19,6 +19,8 @@ export interface AdminUserSecretModule {
|
||||
export interface AdminUserSecretRow {
|
||||
userId: number
|
||||
username: string
|
||||
createdById: number | null
|
||||
createdByUsername: string
|
||||
similarAsin: AdminUserSecretModule
|
||||
appearancePatent: AdminUserSecretModule
|
||||
proxy: AdminUserSecretModule
|
||||
@@ -34,9 +36,17 @@ export interface AdminUserSecretPage {
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
/** 管理员下拉项(超管筛选用)。 */
|
||||
export interface AdminOption {
|
||||
id: number
|
||||
username: string
|
||||
}
|
||||
|
||||
export interface UserSecretQuery {
|
||||
keyword?: string
|
||||
checkStatus?: string
|
||||
/** 按创建人筛选(仅超管生效)。 */
|
||||
createdById?: number
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { computed, onMounted, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { formatDateTime } from '@/utils/datetime'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
import { useAdminSessionStore } from '@/stores/admin-session'
|
||||
import { fetchUserList } from '@/api/users'
|
||||
import {
|
||||
checkUserSecret,
|
||||
deleteUserSecret,
|
||||
@@ -11,6 +13,7 @@ import {
|
||||
type AdminUserSecretRow,
|
||||
} from '@/api/user-secrets'
|
||||
|
||||
const session = useAdminSessionStore()
|
||||
const loading = ref(false)
|
||||
const rows = ref<AdminUserSecretRow[]>([])
|
||||
const total = ref(0)
|
||||
@@ -18,6 +21,9 @@ const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const keyword = ref('')
|
||||
const statusFilter = ref('')
|
||||
const createdByIdFilter = ref<number | null>(null)
|
||||
/** 管理员下拉(仅超管可见/加载)。 */
|
||||
const adminOptions = ref<Array<{ id: number; username: string }>>([])
|
||||
/** 正在检测的行 userId,用于按钮 loading 态。 */
|
||||
const checkingId = ref<number | null>(null)
|
||||
|
||||
@@ -48,7 +54,7 @@ function rowStatusMeta(status: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 单格状态药丸:未配置时统一灰色。 */
|
||||
/** 单格状态药丸:未配置时统一灰色;欠费单独标识。 */
|
||||
function moduleStatusMeta(module: AdminUserSecretModule | undefined) {
|
||||
if (!module || !module.exists) {
|
||||
return { label: '未配置', tone: 'is-unknown' }
|
||||
@@ -57,6 +63,9 @@ function moduleStatusMeta(module: AdminUserSecretModule | undefined) {
|
||||
case 'passed':
|
||||
return { label: '通过', tone: 'is-allowed' }
|
||||
case 'failed':
|
||||
if (module.checkCode === 'insufficient_balance') {
|
||||
return { label: '欠费', tone: 'is-warn' }
|
||||
}
|
||||
return { label: '失败', tone: 'is-blocked' }
|
||||
case 'error':
|
||||
return { label: '无法判定', tone: 'is-warn' }
|
||||
@@ -82,12 +91,23 @@ function rowStatusTooltip(row: AdminUserSecretRow) {
|
||||
return parts.join(';') || '暂无检测记录'
|
||||
}
|
||||
|
||||
async function loadAdminOptions() {
|
||||
if (!session.isSuperAdmin) return
|
||||
try {
|
||||
const result = await fetchUserList({ page: 1, pageSize: 999, role: 'admin' })
|
||||
adminOptions.value = result.admins || []
|
||||
} catch (error) {
|
||||
console.warn('[user-secrets] 管理员下拉加载失败', error)
|
||||
}
|
||||
}
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await fetchUserSecretList({
|
||||
keyword: keyword.value.trim() || undefined,
|
||||
checkStatus: statusFilter.value || undefined,
|
||||
createdById: createdByIdFilter.value || undefined,
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
})
|
||||
@@ -108,6 +128,7 @@ function search() {
|
||||
function reset() {
|
||||
keyword.value = ''
|
||||
statusFilter.value = ''
|
||||
createdByIdFilter.value = null
|
||||
page.value = 1
|
||||
load()
|
||||
}
|
||||
@@ -162,7 +183,10 @@ function changeSize(size: number) {
|
||||
load()
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
onMounted(() => {
|
||||
loadAdminOptions()
|
||||
load()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -183,6 +207,13 @@ onMounted(load)
|
||||
<option v-for="option in STATUS_OPTIONS" :key="option.value" :value="option.value">{{ option.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" v-if="session.isSuperAdmin" style="min-width: 170px">
|
||||
<label>所属管理员</label>
|
||||
<select v-model="createdByIdFilter">
|
||||
<option :value="null">全部管理员</option>
|
||||
<option v-for="admin in adminOptions" :key="admin.id" :value="admin.id">{{ admin.username }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label> </label>
|
||||
<div class="filter-actions">
|
||||
@@ -198,6 +229,7 @@ onMounted(load)
|
||||
<tr>
|
||||
<th style="width: 58px">序号</th>
|
||||
<th style="width: 260px">用户</th>
|
||||
<th style="width: 130px">所属管理员</th>
|
||||
<th style="width: 230px">货源查询密钥</th>
|
||||
<th style="width: 230px">外观专利密钥</th>
|
||||
<th style="width: 230px">代理设置</th>
|
||||
@@ -212,6 +244,9 @@ onMounted(load)
|
||||
<td>
|
||||
<span class="user-name">{{ row.username || '—' }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="creator-name">{{ row.createdByUsername || (row.createdById ? `UID ${row.createdById}` : '—') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="module-cell">
|
||||
<span v-if="row.similarAsin?.exists" class="mono-mask" :title="moduleTooltip(row.similarAsin)">{{ row.similarAsin.masked }}</span>
|
||||
@@ -258,10 +293,10 @@ onMounted(load)
|
||||
</tr>
|
||||
</template>
|
||||
<tr v-else-if="loading">
|
||||
<td colspan="7" class="empty-tip">加载中...</td>
|
||||
<td colspan="8" class="empty-tip">加载中...</td>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<td colspan="7" class="empty-tip">{{ keyword || statusFilter ? '暂无匹配记录' : '暂无用户密钥记录' }}</td>
|
||||
<td colspan="8" class="empty-tip">{{ keyword || statusFilter || createdByIdFilter ? '暂无匹配记录' : '暂无用户密钥记录' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -408,7 +443,7 @@ h3 {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
min-width: 1310px;
|
||||
min-width: 1440px;
|
||||
}
|
||||
.secrets-table-scroll th,
|
||||
.secrets-table-scroll td {
|
||||
@@ -443,6 +478,10 @@ h3 {
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.creator-name {
|
||||
color: #5b6f83;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.module-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user