Files
ai-dimension/admin/src/views/Users.vue
T

169 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="page">
<div class="toolbar">
<el-input v-model="q" placeholder="搜索昵称" style="width: 220px" clearable @keyup.enter="search" />
<el-button type="primary" @click="search">搜索</el-button>
</div>
<el-table :data="list" stripe v-loading="loading">
<el-table-column prop="nickName" label="昵称" min-width="100" />
<el-table-column label="OpenID" min-width="130">
<template #default="{ row }">{{ (row.openid || '').slice(0, 12) }}…</template>
</el-table-column>
<el-table-column label="星尘" width="70" align="center">
<template #default="{ row }">
<span :style="{ fontWeight: 700, color: (row.vpayCoins || 0) > 0 ? '#5b5ff8' : '#999' }">{{ row.vpayCoins || 0 }}</span>
</template>
</el-table-column>
<el-table-column label="注册时间" width="150">
<template #default="{ row }">{{ fmt(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="最近登录" width="150">
<template #default="{ row }">{{ fmt(row.lastLoginAt) }}</template>
</el-table-column>
<el-table-column label="状态" width="60">
<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 label="操作" width="220">
<template #default="{ row }">
<el-button size="small" type="primary" @click="openAdjust(row)">调整星尘</el-button>
<el-button size="small" @click="viewOrders(row)">订单</el-button>
<el-button size="small" @click="openQuota(row)">加额度</el-button>
</template>
</el-table-column>
</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-dialog v-model="adjustDlg" title="调整星尘余额" width="min(400px, 92%)" style="max-width: 400px">
<div class="adjust-user-info">
<b>{{ adjustUser?.nickName || '未命名' }}</b>
<span>当前星尘:{{ adjustUser?.vpayCoins || 0 }}</span>
</div>
<el-form label-width="70px" style="margin-top: 12px">
<el-form-item label="变动值">
<el-input-number v-model="adjustAmount" :min="-100000" :max="100000" />
<span class="adjust-hint">正=赠送 负=扣减</span>
</el-form-item>
<el-form-item label="原因"><el-input v-model="adjustReason" placeholder="如:活动赠送/异常补偿" /></el-form-item>
</el-form>
<template #footer>
<el-button @click="adjustDlg = false">取消</el-button>
<el-button type="primary" @click="doAdjust">确认</el-button>
</template>
</el-dialog>
<!-- 用户订单 -->
<el-dialog v-model="ordersDlg" :title="(ordersUser?.nickName || '用户') + '的订单'" width="min(520px, 94%)" style="max-width: 520px">
<el-table :data="ordersList" stripe v-loading="ordersLoading" size="small">
<el-table-column prop="orderNo" label="订单号" min-width="180" show-overflow-tooltip />
<el-table-column prop="productName" label="商品" width="100" />
<el-table-column label="金额" width="60"><template #default="{ row }">"¥" + row.amount</template></el-table-column>
<el-table-column label="状态" width="70">
<template #default="{ row }">
<el-tag :type="row.status === 'paid' ? 'success' : row.status === 'refunded' ? 'danger' : 'warning'" size="small">
{{ row.status === 'paid' ? '已支付' : row.status === 'refunded' ? '已退款' : row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="时间" width="140"><template #default="{ row }">{{ fmt(row.createdAt) }}</template></el-table-column>
</el-table>
<p v-if="!ordersLoading && !ordersList.length" style="text-align:center;color:#999;padding:20px 0">暂无订单</p>
</el-dialog>
<!-- 加问答额度(保留原有) -->
<el-dialog v-model="quotaDlg" title="增加问答额度" width="min(360px, 92%)" style="max-width: 360px">
<el-form label-width="90px">
<el-form-item label="用户">{{ quotaUser?.nickName }}</el-form-item>
<el-form-item label="增加次数">
<el-input-number v-model="quotaAmount" :min="1" :max="100" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="quotaDlg = false">取消</el-button>
<el-button type="primary" @click="addQuota">确认</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { apiGet, apiPost } from '../api'
const list = ref([]), total = ref(0), page = ref(1), pageSize = 20, q = ref(''), loading = ref(false)
const quotaDlg = ref(false), quotaUser = ref(null), quotaAmount = ref(5)
const adjustDlg = ref(false), adjustUser = ref(null), adjustAmount = ref(100), adjustReason = ref('')
function fmt(t) { return t ? new Date(t).toLocaleString('zh-CN', { hour12: false }) : '—' }
function search() { page.value = 1; load() }
async function load() {
loading.value = true
try {
const r = await apiGet(`/api/admin/users?page=${page.value}&limit=${pageSize}&search=${encodeURIComponent(q.value)}`)
list.value = r.items || []
total.value = r.pagination?.total || 0
} finally { loading.value = false }
}
// 调整星尘
function openAdjust(row) {
adjustUser.value = row
adjustAmount.value = 100
adjustReason.value = ''
adjustDlg.value = true
}
async function doAdjust() {
if (!adjustUser.value) return
try {
await apiPost('/api/admin/vpay/adjust', {
openid: adjustUser.value.openid,
amount: adjustAmount.value,
reason: adjustReason.value || '管理员调整',
})
ElMessage.success(`已${adjustAmount.value > 0 ? '赠送' : '扣减'} ${Math.abs(adjustAmount.value)} 星尘`)
adjustDlg.value = false
load() // 刷新列表更新余额列
} catch (e) { ElMessage.error(e.message || '调整失败') }
}
// 查看该用户的订单(弹窗展示)
const ordersDlg = ref(false), ordersUser = ref(null), ordersList = ref([]), ordersLoading = ref(false)
async function viewOrders(row) {
ordersUser.value = row
ordersDlg.value = true
ordersLoading.value = true
try {
const r = await apiGet(`/api/admin/vpay/orders?keyword=${encodeURIComponent(row.openid)}&limit=20`)
ordersList.value = r.items || []
} catch (e) { ordersList.value = [] }
finally { ordersLoading.value = false }
}
// 加问答额度(保留原有)
function openQuota(row) { quotaUser.value = row; quotaAmount.value = 5; quotaDlg.value = true }
async function addQuota() {
try {
await apiPost('/api/admin/ai-chat/add-quota', { openid: quotaUser.value.openid, amount: quotaAmount.value })
ElMessage.success('已增加')
quotaDlg.value = false
} catch (e) { ElMessage.error(e.message || '操作失败') }
}
onMounted(load)
</script>
<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; }
.adjust-user-info {
display: flex; align-items: center; gap: 12px;
background: #f8f9fb; border-radius: 8px; padding: 10px 14px;
}
.adjust-user-info b { font-size: 15px; color: #1f2333; }
.adjust-user-info span { font-size: 13px; color: #5b5ff8; font-weight: 600; }
.adjust-hint { margin-left: 8px; font-size: 11px; color: #999; }
</style>