// Author: fruit.com.cn import { onMounted, reactive, ref } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import { getAppUserApi, listAppUserBanRecordsApi, listAppUserWatchHistoryApi, pageAppUsersApi, updateAppUserStatusApi } from '@/api/admin/appUser' /** 账号状态与展示文案,与后端 AppUserStatus 保持一致 */ export const APP_USER_STATUS = [ { value: 'normal', label: '正常', tag: 'success' }, { value: 'limited', label: '受限', tag: 'warning' }, { value: 'banned', label: '封禁', tag: 'danger' }, { value: 'deactivated', label: '已注销', tag: 'info' } ] export const COUNTRY_OPTIONS = [ { value: 'ID', label: '印度尼西亚' }, { value: 'SG', label: '新加坡' }, { value: 'MY', label: '马来西亚' }, { value: 'PH', label: '菲律宾' }, { value: 'TH', label: '泰国' }, { value: 'VN', label: '越南' }, { value: 'CN', label: '中国' } ] export function statusMeta(status) { return ( APP_USER_STATUS.find((item) => item.value === status) || { label: status || '-', tag: 'info' } ) } const PLATFORM_TEXT = { ANDROID: 'Android', IOS: 'iOS', WEB: 'Web' } /** 登录会话上的系统,例如 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 || '—' } /** 毫秒转 mm:ss,观看流水里的进度用 */ export function formatDuration(ms) { const total = Math.max(0, Math.floor((ms || 0) / 1000)) const minute = String(Math.floor(total / 60)).padStart(2, '0') const second = String(total % 60).padStart(2, '0') return `${minute}:${second}` } export function useAppUsers() { const users = ref([]) const total = ref(0) const loading = ref(false) const keyword = ref('') const statusFilter = ref(undefined) const countryFilter = ref(undefined) const currentPage = ref(1) const pageSize = ref(10) const detailVisible = ref(false) const detailLoading = ref(false) const detail = ref(null) const banRecords = ref([]) const watchHistory = ref([]) const statusDialogVisible = ref(false) const statusSaving = ref(false) const statusTarget = ref(null) const statusForm = reactive({ status: 'banned', reason: '' }) async function load() { loading.value = true try { const res = await pageAppUsersApi({ pageIndex: currentPage.value, pageSize: pageSize.value, keyword: keyword.value.trim() || undefined, status: statusFilter.value, countryCode: countryFilter.value }) users.value = res.data?.list || [] total.value = res.data?.total || 0 } finally { loading.value = false } } function queryUsers() { currentPage.value = 1 load() } function resetFilters() { keyword.value = '' statusFilter.value = undefined countryFilter.value = undefined queryUsers() } function handleSizeChange() { currentPage.value = 1 load() } const detailTabs = reactive({ active: 'watch' }) async function openDetail(row) { detailVisible.value = true detailLoading.value = true detail.value = row banRecords.value = [] watchHistory.value = [] detailTabs.active = 'watch' try { // 三个请求互不依赖,并发拉取,抽屉打开更快 const [profile, bans, history] = await Promise.all([ getAppUserApi(row.id), listAppUserBanRecordsApi(row.id), listAppUserWatchHistoryApi(row.id, 50) ]) detail.value = profile.data || row banRecords.value = bans.data || [] watchHistory.value = history.data || [] } finally { detailLoading.value = false } } function openStatusDialog(row, preset = 'banned') { statusTarget.value = row statusForm.status = preset statusForm.reason = '' statusDialogVisible.value = true } async function confirmStatus() { const row = statusTarget.value if (!row) return const label = statusMeta(statusForm.status).label statusSaving.value = true try { await updateAppUserStatusApi(row.id, statusForm.status, statusForm.reason.trim() || undefined) ElMessage.success(`已${label},该用户全部登录会话已失效`) statusDialogVisible.value = false await load() if (detailVisible.value && detail.value?.id === row.id) { detail.value = { ...detail.value, status: statusForm.status } banRecords.value = (await listAppUserBanRecordsApi(row.id)).data || [] } } finally { statusSaving.value = false } } /** 恢复正常:不需要填原因,但仍要二次确认,避免误点 */ async function restoreUser(row) { try { await ElMessageBox.confirm( `确认把「${row.nickname || row.uid}」恢复为正常账号?`, '恢复正常', { confirmButtonText: '确认恢复', cancelButtonText: '取消', type: 'warning' } ) } catch { return } await updateAppUserStatusApi(row.id, 'normal') ElMessage.success('账号已恢复正常') await load() if (detailVisible.value && detail.value?.id === row.id) { detail.value = { ...detail.value, status: 'normal' } } } onMounted(load) return { users, total, loading, keyword, statusFilter, countryFilter, currentPage, pageSize, detailVisible, detailLoading, detail, banRecords, watchHistory, detailTabs, statusDialogVisible, statusSaving, statusTarget, statusForm, load, queryUsers, resetFilters, handleSizeChange, openDetail, openStatusDialog, confirmStatus, restoreUser } }