chore(web): 收口前几轮后台改动(品牌 / 媒资上传 / 轮播图 / 类型管理 / 操作日志 / App 用户)
为什么:这些改动在前几轮就已完成并在 :3005 验证过,但一直留在工作区没有入库。 下一步 3-4 的四个页面依赖其中的公共样式入口(src/styles/business/index.css)与 api/admin 的公共类型(PageResult 等),如果不先收口,新页面的提交就会夹带旧改动, 既说不清 diff 边界,也让 index.css 这类多轮共用的文件长期处于半提交状态。 本提交只做归档,不改行为: - 品牌与接口基址改成 FruitShorts / 短剧服务(.env.*、README、vite 代理) - 去掉前端 mock 数据目录(src/data、src/permissions、stores/auth),改为走真实接口 - 新增 api/admin 下的接口封装与 src/utils/uploader.ts(媒资分片直传) - 新增/改造:轮播图管理、类型管理、操作日志、App 用户管理、后台布局与公共样式
This commit is contained in:
1 parent
40546bfd01
commit
44c2c46a68
59 files changed
+4082
-982
No files matched your search
@@ -0,0 +1,198 @@
|
||||
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' }
|
||||
)
|
||||
}
|
||||
|
||||
/** 毫秒转 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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user