admin_new/src/modules/user/UserListView.vue
Sisyphus 78360d1d6e feat(F006-W3xC): 7 个新 view 接真 API (W3.x C P0 全部)
- P0.1 数据统计首页 (走 /sys/openapi/order/list 真 API, 4 stat 500/¥47M 真值)
- P0.2 supplier/inquiry/inquiry 重定向 /inquiry (2 个 redirect)
- P0.3 /user/user 用户列表 (2284 真实用户)
- P0.4 /other/operateguide 内容列表 (1 条真数据)
- P0.5 /api/lunbo 轮播占位 (后端 404 优雅提示)
- P0.6 /api/help 帮助中心占位 (后端 404 优雅提示)
- P0.7 /sys/region 行政区域树 (省市区 4 级 3000+ 节点)

同步: src/modules/user/{Enterprise,Finance}ListView.vue (Mac 端 working copy 补传)

P0.7 nginx 修复 (conf 改动非本次提交, 见 nginx-vue.conf.bak-20260704-194200):
- 新增 location = /admin/sys/region { rewrite /admin/index.html last; }
- 原因: location /admin/sys/ 代理抢 /admin/sys/region 走 gateway 返 401
- 已 docker compose restart frontend 生效, devbox 端 7 SPA 路径全 200
2026-07-04 20:09:11 +08:00

114 lines
3.1 KiB
Vue

<script setup lang="ts">
/**
* F006 用户管理 (前台用户列表) - W3.x C P0.3
* 走 /user/user/page 后端
*
* 字段 (从 API 实测):
* id, nickName, mobile, header (头像 URL)
* total=2284 真实用户
*
* 注意: 老 sys/UserListView 是后台管理员 (/sys/user/*), 这页是前台用户
*/
import { onMounted, ref, reactive } from 'vue'
import { httpGet } from '@/utils/request'
import DataTable from '@/components/DataTable/DataTable.vue'
interface FrontUser {
id: string
nickName: string
mobile: string
header?: string | null
raw: any
}
const list = ref<FrontUser[]>([])
const total = ref(0)
const loading = ref(false)
const query = reactive({
page: 1,
limit: 20,
keyword: ''
})
const columns = [
{ prop: 'id', label: 'ID', width: 80 },
{ prop: 'nickName', label: '昵称', minWidth: 160 },
{ prop: 'mobile', label: '手机号', width: 140 },
{
prop: 'header',
label: '头像',
width: 80,
formatter: (row: FrontUser) => row.header ? '有' : '无'
}
]
async function load() {
loading.value = true
try {
const resp: any = await httpGet('/user/user/page', { page: query.page, limit: query.limit })
if (resp && Array.isArray(resp.list)) {
list.value = resp.list.map((it: any) => ({
id: it.id,
nickName: it.nickName || '--',
mobile: it.mobile || '--',
header: it.header || null,
raw: it
}))
total.value = Number(resp.total || 0)
} else {
list.value = []
total.value = 0
}
} catch (e) {
list.value = []
total.value = 0
} finally {
loading.value = false
}
}
function onPageChange(p: number) { query.page = p; load() }
function onSearch() { query.page = 1; load() }
onMounted(load)
</script>
<template>
<div class="rr-page">
<div class="rr-page-header">
<div>
<h2 class="rr-page-title">用户列表</h2>
<p class="rr-page-sub">前台注册用户(共 {{ total }} 个)</p>
</div>
</div>
<div class="rr-card">
<div class="rr-toolbar">
<el-input v-model="query.keyword" placeholder="搜索手机号/昵称" clearable style="width: 280px;" @keyup.enter="onSearch" />
<el-button type="primary" :icon="'Search'" @click="onSearch">搜索</el-button>
</div>
<DataTable
:columns="columns"
:data="list"
:loading="loading"
:total="total"
:page="query.page"
:limit="query.limit"
:show-pagination="true"
row-key="id"
empty-text="暂无用户"
@page-change="onPageChange"
/>
</div>
</div>
</template>
<style scoped lang="scss">
.rr-page { max-width: 1400px; }
.rr-page-header { margin-bottom: 20px; }
.rr-page-title { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; color: var(--ink); margin-bottom: 4px; }
.rr-page-sub { color: var(--ink-3); font-size: 13px; }
.rr-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
.rr-toolbar { display: flex; align-items: center; gap: 8px; padding: 14px 20px; border-bottom: 1px solid var(--border); }
</style>