feat: phase1 UI prep — 后台 App 用户列表支持系统筛选 + 国家中文名 + 标签语义优化
新增: 系统下拉 (ANDROID/IOS)、国家中文名映射、用户列昵称/UID 冒号标签 详情抽屉: 同步系统/国家/标签展示
This commit is contained in:
1 parent
daf681ae1a
commit
bedae02946
4 files changed
+111
-25
No files matched your search
@@ -11,6 +11,8 @@ export interface AppUserPageParams {
|
|||||||
status?: string
|
status?: string
|
||||||
countryCode?: string
|
countryCode?: string
|
||||||
registerSource?: string
|
registerSource?: string
|
||||||
|
/** 客户端平台:ANDROID / IOS / WEB */
|
||||||
|
platform?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WatchHistoryPageParams {
|
export interface WatchHistoryPageParams {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-user-toolbar .app-user-status,
|
.app-user-toolbar .app-user-status,
|
||||||
.app-user-toolbar .app-user-country {
|
.app-user-toolbar .app-user-country,
|
||||||
|
.app-user-toolbar .app-user-platform {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
flex: 0 0 150px;
|
flex: 0 0 150px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ import { useRbac } from '@/stores/rbac'
|
|||||||
import {
|
import {
|
||||||
APP_USER_STATUS,
|
APP_USER_STATUS,
|
||||||
COUNTRY_OPTIONS,
|
COUNTRY_OPTIONS,
|
||||||
|
countryText,
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDuration,
|
formatDuration,
|
||||||
|
platformText,
|
||||||
statusMeta,
|
statusMeta,
|
||||||
systemText,
|
|
||||||
useAppUsers
|
useAppUsers
|
||||||
} from './composables/useAppUsers'
|
} from './composables/useAppUsers'
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ const {
|
|||||||
keyword,
|
keyword,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
countryFilter,
|
countryFilter,
|
||||||
|
platformFilter,
|
||||||
currentPage,
|
currentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
detailVisible,
|
detailVisible,
|
||||||
@@ -54,6 +56,11 @@ const SOURCE_TEXT = {
|
|||||||
FAVORITE: '收藏'
|
FAVORITE: '收藏'
|
||||||
}
|
}
|
||||||
const BAN_TYPE_TEXT = { BAN: '封禁', LIMIT: '限制', MUTE: '禁言' }
|
const BAN_TYPE_TEXT = { BAN: '封禁', LIMIT: '限制', MUTE: '禁言' }
|
||||||
|
const PLATFORM_OPTIONS = [
|
||||||
|
{ value: '', label: '全部系统' },
|
||||||
|
{ value: 'ANDROID', label: 'Android' },
|
||||||
|
{ value: 'IOS', label: 'iOS' }
|
||||||
|
]
|
||||||
const APPEAL_TEXT = {
|
const APPEAL_TEXT = {
|
||||||
none: '未申诉',
|
none: '未申诉',
|
||||||
pending: '待处理',
|
pending: '待处理',
|
||||||
@@ -117,6 +124,20 @@ function canBeBanned(row) {
|
|||||||
:value="item.value"
|
:value="item.value"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
<el-select
|
||||||
|
v-model="platformFilter"
|
||||||
|
class="app-user-platform"
|
||||||
|
clearable
|
||||||
|
placeholder="全部系统"
|
||||||
|
@change="queryUsers"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in PLATFORM_OPTIONS"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
<el-select
|
<el-select
|
||||||
v-model="countryFilter"
|
v-model="countryFilter"
|
||||||
class="app-user-country"
|
class="app-user-country"
|
||||||
@@ -166,24 +187,24 @@ function canBeBanned(row) {
|
|||||||
<template v-else>{{ avatarText(row) }}</template>
|
<template v-else>{{ avatarText(row) }}</template>
|
||||||
</span>
|
</span>
|
||||||
<div class="app-user-meta">
|
<div class="app-user-meta">
|
||||||
<span class="app-user-name" :title="row.nickname">{{
|
<span class="app-user-name" :title="row.nickname || ''">
|
||||||
row.nickname || '未设置昵称'
|
昵称: {{ row.nickname || '未设置昵称' }}
|
||||||
}}</span>
|
</span>
|
||||||
<span class="app-user-uid" :title="row.uid">{{ row.uid }}</span>
|
<span class="app-user-uid">UID: {{ row.uid }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="系统" width="130" show-overflow-tooltip>
|
<el-table-column label="系统" width="110" show-overflow-tooltip>
|
||||||
<template #default="{ row }">{{ systemText(row) }}</template>
|
<template #default="{ row }">{{ platformText(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="手机 / 邮箱" min-width="150" show-overflow-tooltip>
|
<el-table-column label="手机 / 邮箱" min-width="150" show-overflow-tooltip>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span class="app-user-mono">{{ row.maskedTarget || '—' }}</span>
|
<span class="app-user-mono">{{ row.maskedTarget || '—' }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="国家" width="90">
|
<el-table-column label="国家" width="110">
|
||||||
<template #default="{ row }">{{ row.countryCode || '—' }}</template>
|
<template #default="{ row }">{{ countryText(row.countryCode) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="状态" width="90">
|
<el-table-column label="状态" width="90">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -249,8 +270,8 @@ function canBeBanned(row) {
|
|||||||
<template v-else>{{ avatarText(detail) }}</template>
|
<template v-else>{{ avatarText(detail) }}</template>
|
||||||
</span>
|
</span>
|
||||||
<div class="app-user-meta">
|
<div class="app-user-meta">
|
||||||
<span class="app-user-name">{{ detail.nickname || '未设置昵称' }}</span>
|
<span class="app-user-name"> 昵称: {{ detail.nickname || '未设置昵称' }} </span>
|
||||||
<span class="app-user-uid">{{ detail.uid }}</span>
|
<span class="app-user-uid">UID: {{ detail.uid }}</span>
|
||||||
</div>
|
</div>
|
||||||
<el-tag :type="statusMeta(detail.status).tag" effect="light" style="margin-left: auto">
|
<el-tag :type="statusMeta(detail.status).tag" effect="light" style="margin-left: auto">
|
||||||
{{ statusMeta(detail.status).label }}
|
{{ statusMeta(detail.status).label }}
|
||||||
@@ -263,7 +284,10 @@ function canBeBanned(row) {
|
|||||||
{{ detail.maskedTarget || '—' }}
|
{{ detail.maskedTarget || '—' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="国家 / 地区">
|
<el-descriptions-item label="国家 / 地区">
|
||||||
{{ detail.countryCode || '—' }} / {{ detail.regionCode || '—' }}
|
{{ countryText(detail.countryCode) }}
|
||||||
|
<template v-if="detail.regionCode && detail.regionCode !== detail.countryCode">
|
||||||
|
/ {{ countryText(detail.regionCode) }}
|
||||||
|
</template>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="语言">{{ detail.language || '—' }}</el-descriptions-item>
|
<el-descriptions-item label="语言">{{ detail.language || '—' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="时区">{{ detail.timezone || '—' }}</el-descriptions-item>
|
<el-descriptions-item label="时区">{{ detail.timezone || '—' }}</el-descriptions-item>
|
||||||
@@ -273,7 +297,7 @@ function canBeBanned(row) {
|
|||||||
<el-descriptions-item label="最后登录">{{
|
<el-descriptions-item label="最后登录">{{
|
||||||
detail.lastLoginAt || '—'
|
detail.lastLoginAt || '—'
|
||||||
}}</el-descriptions-item>
|
}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="系统">{{ systemText(detail) }}</el-descriptions-item>
|
<el-descriptions-item label="系统">{{ platformText(detail) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="会员">
|
<el-descriptions-item label="会员">
|
||||||
{{ detail.vipLevel ? `等级 ${detail.vipLevel}` : '未开通(第二阶段)' }}
|
{{ detail.vipLevel ? `等级 ${detail.vipLevel}` : '未开通(第二阶段)' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
@@ -305,8 +329,8 @@ function canBeBanned(row) {
|
|||||||
<el-table-column label="完成" width="70">
|
<el-table-column label="完成" width="70">
|
||||||
<template #default="{ row }">{{ row.finished ? '是' : '否' }}</template>
|
<template #default="{ row }">{{ row.finished ? '是' : '否' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="系统" width="120" show-overflow-tooltip>
|
<el-table-column label="系统" width="100" show-overflow-tooltip>
|
||||||
<template #default="{ row }">{{ systemText(row) }}</template>
|
<template #default="{ row }">{{ platformText(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="来源" width="80">
|
<el-table-column label="来源" width="80">
|
||||||
<template #default="{ row }">{{
|
<template #default="{ row }">{{
|
||||||
|
|||||||
@@ -29,6 +29,69 @@ export const COUNTRY_OPTIONS = [
|
|||||||
{ value: 'CN', label: '中国' }
|
{ value: 'CN', label: '中国' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/** 国家码 → 中文名,与后端 CountryProfile 保持一致;未收录的仍显示原码 */
|
||||||
|
const COUNTRY_TEXT = {
|
||||||
|
ID: '印度尼西亚',
|
||||||
|
MY: '马来西亚',
|
||||||
|
SG: '新加坡',
|
||||||
|
TH: '泰国',
|
||||||
|
VN: '越南',
|
||||||
|
PH: '菲律宾',
|
||||||
|
KH: '柬埔寨',
|
||||||
|
MM: '缅甸',
|
||||||
|
LA: '老挝',
|
||||||
|
BN: '文莱',
|
||||||
|
CN: '中国',
|
||||||
|
TW: '中国台湾',
|
||||||
|
HK: '中国香港',
|
||||||
|
MO: '中国澳门',
|
||||||
|
JP: '日本',
|
||||||
|
KR: '韩国',
|
||||||
|
IN: '印度',
|
||||||
|
PK: '巴基斯坦',
|
||||||
|
BD: '孟加拉国',
|
||||||
|
LK: '斯里兰卡',
|
||||||
|
NP: '尼泊尔',
|
||||||
|
US: '美国',
|
||||||
|
CA: '加拿大',
|
||||||
|
MX: '墨西哥',
|
||||||
|
BR: '巴西',
|
||||||
|
AR: '阿根廷',
|
||||||
|
CL: '智利',
|
||||||
|
CO: '哥伦比亚',
|
||||||
|
PE: '秘鲁',
|
||||||
|
VE: '委内瑞拉',
|
||||||
|
EC: '厄瓜多尔',
|
||||||
|
GT: '危地马拉',
|
||||||
|
DO: '多米尼加',
|
||||||
|
GB: '英国',
|
||||||
|
DE: '德国',
|
||||||
|
FR: '法国',
|
||||||
|
ES: '西班牙',
|
||||||
|
IT: '意大利',
|
||||||
|
NL: '荷兰',
|
||||||
|
PL: '波兰',
|
||||||
|
RU: '俄罗斯',
|
||||||
|
UA: '乌克兰',
|
||||||
|
TR: '土耳其',
|
||||||
|
SA: '沙特阿拉伯',
|
||||||
|
AE: '阿联酋',
|
||||||
|
IL: '以色列',
|
||||||
|
EG: '埃及',
|
||||||
|
NG: '尼日利亚',
|
||||||
|
ZA: '南非',
|
||||||
|
KE: '肯尼亚',
|
||||||
|
MA: '摩洛哥',
|
||||||
|
AU: '澳大利亚',
|
||||||
|
NZ: '新西兰'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 国家列统一展示中文名,无值时显示横线 */
|
||||||
|
export function countryText(code) {
|
||||||
|
const value = (code || '').toUpperCase()
|
||||||
|
return COUNTRY_TEXT[value] || value || '—'
|
||||||
|
}
|
||||||
|
|
||||||
export function statusMeta(status) {
|
export function statusMeta(status) {
|
||||||
return (
|
return (
|
||||||
APP_USER_STATUS.find((item) => item.value === status) || { label: status || '-', tag: 'info' }
|
APP_USER_STATUS.find((item) => item.value === status) || { label: status || '-', tag: 'info' }
|
||||||
@@ -43,14 +106,6 @@ export function platformText(row) {
|
|||||||
return PLATFORM_TEXT[platform] || platform || '—'
|
return PLATFORM_TEXT[platform] || platform || '—'
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 登录会话上的系统,例如 Android 16。没有记录时显示横线。 */
|
|
||||||
export function systemText(row) {
|
|
||||||
const name = PLATFORM_TEXT[row?.platform] || row?.platform || ''
|
|
||||||
const version = row?.osVersion || ''
|
|
||||||
const text = `${name} ${version}`.trim()
|
|
||||||
return text || '—'
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 后端返回 ISO 时间串,统一格式化成后台可读的本地时间 */
|
/** 后端返回 ISO 时间串,统一格式化成后台可读的本地时间 */
|
||||||
export function formatDateTime(value) {
|
export function formatDateTime(value) {
|
||||||
return value && dayjs(value).isValid() ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '—'
|
return value && dayjs(value).isValid() ? dayjs(value).format('YYYY-MM-DD HH:mm:ss') : '—'
|
||||||
@@ -72,6 +127,7 @@ export function useAppUsers() {
|
|||||||
const keyword = ref('')
|
const keyword = ref('')
|
||||||
const statusFilter = ref(undefined)
|
const statusFilter = ref(undefined)
|
||||||
const countryFilter = ref(undefined)
|
const countryFilter = ref(undefined)
|
||||||
|
const platformFilter = ref(undefined)
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
|
|
||||||
@@ -94,7 +150,8 @@ export function useAppUsers() {
|
|||||||
pageSize: pageSize.value,
|
pageSize: pageSize.value,
|
||||||
keyword: keyword.value.trim() || undefined,
|
keyword: keyword.value.trim() || undefined,
|
||||||
status: statusFilter.value,
|
status: statusFilter.value,
|
||||||
countryCode: countryFilter.value
|
countryCode: countryFilter.value,
|
||||||
|
platform: platformFilter.value || undefined
|
||||||
})
|
})
|
||||||
users.value = res.data?.list || []
|
users.value = res.data?.list || []
|
||||||
total.value = res.data?.total || 0
|
total.value = res.data?.total || 0
|
||||||
@@ -112,6 +169,7 @@ export function useAppUsers() {
|
|||||||
keyword.value = ''
|
keyword.value = ''
|
||||||
statusFilter.value = undefined
|
statusFilter.value = undefined
|
||||||
countryFilter.value = undefined
|
countryFilter.value = undefined
|
||||||
|
platformFilter.value = undefined
|
||||||
queryUsers()
|
queryUsers()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +256,7 @@ export function useAppUsers() {
|
|||||||
keyword,
|
keyword,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
countryFilter,
|
countryFilter,
|
||||||
|
platformFilter,
|
||||||
currentPage,
|
currentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
detailVisible,
|
detailVisible,
|
||||||
|
|||||||
Reference in new issue
Block a user