fix: 管理后台空值修复(users无status字段兜底正常/adminlog时间字段timestamp/知识分类中文/额度列表字段全面修正)+ 弹窗移动端宽度自适应

This commit is contained in:
2026-09-13 18:17:43 +08:00
parent 6a3c048072
commit d221a45a5e
5 changed files with 78 additions and 31 deletions
+2 -2
View File
@@ -14,7 +14,7 @@
<el-table-column label="维度" width="90"> <el-table-column label="维度" width="90">
<template #default="{ row }">{{ DIMS[row.dim - 1] || row.dim }}</template> <template #default="{ row }">{{ DIMS[row.dim - 1] || row.dim }}</template>
</el-table-column> </el-table-column>
<el-table-column prop="category" label="分类" width="90" /> <el-table-column label="分类" width="90"><template #default="{ row }">{{ CATS[row.category] || row.category || '—' }}</template></el-table-column>
<el-table-column label="检测题" width="80"> <el-table-column label="检测题" width="80">
<template #default="{ row }"> <template #default="{ row }">
<el-tag :type="(row.options || []).length ? 'success' : 'info'" size="small">{{ (row.options || []).length ? '有' : '无' }}</el-tag> <el-tag :type="(row.options || []).length ? 'success' : 'info'" size="small">{{ (row.options || []).length ? '有' : '无' }}</el-tag>
@@ -36,7 +36,7 @@
</el-table> </el-table>
<el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" /> <el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" />
<el-dialog v-model="dlg" :title="form._id ? '编辑知识' : '新增知识'" width="640px" top="4vh"> <el-dialog v-model="dlg" :title="form._id ? '编辑知识' : '新增知识'" width="min(640px, 94%)" top="4vh">
<el-form label-width="76px"> <el-form label-width="76px">
<el-form-item label="标题"><el-input v-model="form.title" /></el-form-item> <el-form-item label="标题"><el-input v-model="form.title" /></el-form-item>
<el-form-item label="维度/分类"> <el-form-item label="维度/分类">
+3 -2
View File
@@ -19,13 +19,13 @@
<el-table-column prop="userAgent" label="UA" min-width="180" show-overflow-tooltip /> <el-table-column prop="userAgent" label="UA" min-width="180" show-overflow-tooltip />
</template> </template>
<template v-else> <template v-else>
<el-table-column label="时间" width="160"><template #default="{ row }">{{ fmt(row.createdAt) }}</template></el-table-column> <el-table-column label="时间" width="160"><template #default="{ row }">{{ fmt(row.timestamp || row.createdAt) }}</template></el-table-column>
<el-table-column prop="adminName" label="管理员" width="100" /> <el-table-column prop="adminName" label="管理员" width="100" />
<el-table-column prop="action" label="动作" width="90" /> <el-table-column prop="action" label="动作" width="90" />
<el-table-column prop="resource" label="对象" width="120" /> <el-table-column prop="resource" label="对象" width="120" />
<el-table-column prop="ip" label="IP" width="120" /> <el-table-column prop="ip" label="IP" width="120" />
<el-table-column label="详情" min-width="200"> <el-table-column label="详情" min-width="200">
<template #default="{ row }">{{ JSON.stringify(row.details || row.meta || {}).slice(0, 100) }}</template> <template #default="{ row }">{{ row.details ? JSON.stringify(row.details).slice(0, 100) : '—' }}</template>
</el-table-column> </el-table-column>
</template> </template>
</el-table> </el-table>
@@ -38,6 +38,7 @@ import { ref, onMounted } from 'vue'
import { apiGet } from '../api' import { apiGet } from '../api'
const tab = ref('login'), list = ref([]), total = ref(0), page = ref(1), pageSize = 20, loading = ref(false) const tab = ref('login'), list = ref([]), total = ref(0), page = ref(1), pageSize = 20, loading = ref(false)
function fmt(t) { return t ? new Date(t).toLocaleString('zh-CN', { hour12: false }) : '—' } function fmt(t) { return t ? new Date(t).toLocaleString('zh-CN', { hour12: false }) : '—' }
// adminlog 的时间字段是 timestamp,loginlog 是 createdAt
async function load() { async function load() {
loading.value = true loading.value = true
try { try {
+69 -23
View File
@@ -1,36 +1,82 @@
<template> <template>
<div> <div class="page">
<div class="head"><h2>问答额度管理</h2> <div class="toolbar">
<el-button type="primary" @click="addQuota">新增额度</el-button> <el-input v-model="kw" placeholder="搜索 openid" style="width: 220px" clearable @keyup.enter="search" />
<el-button type="primary" @click="search">搜索</el-button>
<el-button type="success" @click="addDlg = true">增加额度</el-button>
</div> </div>
<el-table :data="list"> <el-table :data="list" stripe v-loading="loading">
<el-table-column prop="user" label="用户" width="160" /> <el-table-column label="OpenID" min-width="160">
<el-table-column prop="total" label="总额度" width="90" /> <template #default="{ row }">
<el-table-column prop="used" label="已用" width="80" /> <span :title="row.openid">{{ openidShort(row.openid) }}</span>
<el-table-column prop="remaining" label="剩余" width="80" /> </template>
<el-table-column prop="date" label="日期" width="120" /> </el-table-column>
<el-table-column label="类型" width="90">
<template #default="{ row }">{{ row.openid.startsWith('guest:') ? '游客' : '注册用户' }}</template>
</el-table-column>
<el-table-column prop="freeQuota" label="免费额度" width="90" />
<el-table-column prop="sharedQuota" label="分享获得" width="90" />
<el-table-column prop="usedQuota" label="累计使用" width="90" />
<el-table-column prop="dailyUsed" label="今日已用" width="90" />
<el-table-column label="剩余可用" width="90">
<template #default="{ row }">
<el-tag size="small" :type="remaining(row) > 0 ? 'success' : 'danger'">{{ remaining(row) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="最后使用" width="150">
<template #default="{ row }">{{ fmt(row.lastUsedDate) }}</template>
</el-table-column>
</el-table> </el-table>
<el-dialog v-model="dialog" title="增加额度" width="400"> <el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" />
<el-form label-width="80">
<el-form-item label="用户ID"><el-input v-model="form.userId" /></el-form-item> <el-dialog v-model="addDlg" title="增加问答额度" width="min(380px, 92%)" style="max-width: 380px">
<el-form-item label="增加次数"><el-input-number v-model="form.amount" :min="1" :max="100" /></el-form-item> <el-form label-width="90px">
<el-form-item label="OpenID"><el-input v-model="addOpenid" placeholder="完整 openid" /></el-form-item>
<el-form-item label="增加次数"><el-input-number v-model="addAmount" :min="1" :max="100" /></el-form-item>
</el-form> </el-form>
<template #footer><el-button @click="dialog=false">取消</el-button><el-button type="primary" @click="save">确认</el-button></template> <template #footer>
<el-button @click="addDlg = false">取消</el-button>
<el-button type="primary" @click="add">确认</el-button>
</template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, reactive } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { apiGet, apiPost } from '../api' import { apiGet, apiPost } from '../api'
const list = ref([]); const dialog = ref(false) const list = ref([]), total = ref(0), page = ref(1), pageSize = 20, kw = ref(''), loading = ref(false)
const form = reactive({ userId: '', amount: 5 }) const addDlg = ref(false), addOpenid = ref(''), addAmount = ref(5)
async function load() { const r = await apiGet('/api/admin/ai-chat/quota-list'); list.value = r.list || r.data || [] } function openidShort(o) { return o && o.length > 18 ? o.slice(0, 16) + '…' : (o || '—') }
function addQuota() { dialog.value = true } function fmt(t) { return t ? new Date(t).toLocaleString('zh-CN', { hour12: false }) : '—' }
async function save() { function remaining(row) {
try { await apiPost('/api/admin/ai-chat/add-quota', { userId: form.userId, amount: form.amount }); ElMessage.success('已增加'); dialog.value = false; load() } const todayUsed = row.lastUsedDate && new Date(row.lastUsedDate).toDateString() === new Date().toDateString() ? row.dailyUsed : 0
catch (e) { ElMessage.error(e.message) } return Math.max(0, (row.freeQuota || 0) + (row.sharedQuota || 0) - todayUsed)
}
function search() { page.value = 1; load() }
async function load() {
loading.value = true
try {
const r = await apiGet(`/api/admin/ai-chat/quota-list?page=${page.value}&pageSize=${pageSize}&keyword=${encodeURIComponent(kw.value)}`)
list.value = r.list || []
total.value = r.total || 0
} finally { loading.value = false }
}
async function add() {
if (!addOpenid.value.trim()) return ElMessage.warning('请输入 openid')
try {
await apiPost('/api/admin/ai-chat/add-quota', { openid: addOpenid.value.trim(), amount: addAmount.value })
ElMessage.success('已增加')
addDlg.value = false
addOpenid.value = ''
load()
} catch (e) { ElMessage.error(e.message || '操作失败') }
} }
onMounted(load) onMounted(load)
</script> </script>
<style scoped>.head{display:flex;justify-content:space-between;align-items:center}</style>
<style scoped>
.page { background: #fff; border-radius: 12px; padding: 16px; box-shadow: 0 1px 4px rgba(0,0,0,.06); }
.toolbar { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
</style>
+1 -1
View File
@@ -27,7 +27,7 @@
</el-table> </el-table>
<el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" /> <el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" />
<el-dialog v-model="dlg" :title="form._id ? '编辑资讯' : '新增资讯'" width="560px"> <el-dialog v-model="dlg" :title="form._id ? '编辑资讯' : '新增资讯'" width="min(560px, 94%)" style="max-width: 560px">
<el-form label-width="76px"> <el-form label-width="76px">
<el-form-item label="标题"><el-input v-model="form.title" /></el-form-item> <el-form-item label="标题"><el-input v-model="form.title" /></el-form-item>
<el-form-item label="摘要"><el-input v-model="form.summary" type="textarea" :rows="2" /></el-form-item> <el-form-item label="摘要"><el-input v-model="form.summary" type="textarea" :rows="2" /></el-form-item>
+3 -3
View File
@@ -15,8 +15,8 @@
<el-table-column label="最近登录" width="160"> <el-table-column label="最近登录" width="160">
<template #default="{ row }">{{ fmt(row.lastLoginAt) }}</template> <template #default="{ row }">{{ fmt(row.lastLoginAt) }}</template>
</el-table-column> </el-table-column>
<el-table-column prop="status" label="状态" width="80"> <el-table-column label="状态" width="80">
<template #default="{ row }"><el-tag :type="row.status === 'active' ? 'success' : 'info'" size="small">{{ row.status }}</el-tag></template> <template #default="{ row }"><el-tag :type="(row.status || 'active') === 'active' ? 'success' : 'info'" size="small">{{ (row.status || 'active') === 'active' ? '正常' : row.status }}</el-tag></template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="110"> <el-table-column label="操作" width="110">
<template #default="{ row }"> <template #default="{ row }">
@@ -25,7 +25,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" /> <el-pagination background layout="total, prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="load" style="margin-top: 12px" />
<el-dialog v-model="quotaDlg" title="增加问答额度" width="360px"> <el-dialog v-model="quotaDlg" title="增加问答额度" width="min(360px, 92%)" style="max-width: 360px">
<el-form label-width="90px"> <el-form label-width="90px">
<el-form-item label="用户">{{ quotaUser?.nickName }}</el-form-item> <el-form-item label="用户">{{ quotaUser?.nickName }}</el-form-item>
<el-form-item label="OpenID">{{ (quotaUser?.openid || '').slice(0, 16) }}…</el-form-item> <el-form-item label="OpenID">{{ (quotaUser?.openid || '').slice(0, 16) }}…</el-form-item>