feat(web): 08 后台四个页面落地(内容治理 / 订单与权益 / 创作者审核 / 数据看板)
为什么:08 运营后台的接口在前几轮已经实现,但运营在界面上点不到—— 评论审核、举报工单、敏感词维护、订单补发/退款、权益回收、金币补发、对账、 创作者入驻与内容提交审核、运营大盘,都只能靠 curl。本轮把这四个页面补齐, 08 才算真正闭环。 - 内容治理:评论审核(通过/驳回/删除)、举报工单处理(可联动封禁)、敏感词库增删改查 - 订单与权益:订单检索与流水、补发、退款、权益检索/人工补发/回收、金币流水与人工补币、对账运行与历史 - 创作者审核:入驻审核(通过/驳回原因码/冻结)+ 资质抽屉;内容提交审核(通过/驳回) - 数据看板:overview 五组指标 + transcode 队列与失败任务,只读;取不到的指标显示「暂无数据源」而不是 0 - 新增 console.css(工具条/抽屉/看板卡片,含深色模式),四个页面共用;时间列统一 yyyy-MM-dd HH:mm:ss - 按钮按权限点显隐(没有 *:update / *:audit 的角色看到的是只读态),表格列与后端字段一一对应
This commit is contained in:
1 parent
44c2c46a68
commit
f037d243cd
13 files changed
+2680
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import request from '@/axios'
|
||||
import type { PageResult } from './types'
|
||||
|
||||
export interface OrderItem {
|
||||
id: number
|
||||
orderNo: string
|
||||
userId: number
|
||||
productCode: string
|
||||
productName: string
|
||||
type: string
|
||||
platform: string
|
||||
amount: number
|
||||
currency: string
|
||||
coinAmount: number
|
||||
/** CREATED / PAID / DELIVERED / REFUNDED / CLOSED */
|
||||
status: string
|
||||
paidAt?: string
|
||||
deliveredAt?: string
|
||||
refundReason: string
|
||||
failReason: string
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export interface OrderEventItem {
|
||||
id: number
|
||||
orderNo: string
|
||||
event: string
|
||||
fromStatus: string
|
||||
toStatus: string
|
||||
source: string
|
||||
payload: string
|
||||
operatorId: number
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export interface EntitlementItem {
|
||||
id: number
|
||||
userId: number
|
||||
dramaId: number
|
||||
episodeId: number
|
||||
/** EPISODE / DRAMA */
|
||||
type: string
|
||||
/** ORDER / ADMIN / ACTIVITY */
|
||||
source: string
|
||||
/** ACTIVE / REVOKED / EXPIRED */
|
||||
status: string
|
||||
orderNo: string
|
||||
startAt?: string
|
||||
revokeReason: string
|
||||
}
|
||||
|
||||
export interface LedgerItem {
|
||||
id: number
|
||||
userId: number
|
||||
changeAmount: number
|
||||
balanceAfter: number
|
||||
bizType: string
|
||||
bizId: string
|
||||
remark: string
|
||||
operatorId: number
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export interface ReconcileItem {
|
||||
id: number
|
||||
bizDate: string
|
||||
platform: string
|
||||
totalCount: number
|
||||
matchedCount: number
|
||||
diffCount: number
|
||||
amountSum: number
|
||||
status: string
|
||||
detail: string
|
||||
finishedAt?: string
|
||||
}
|
||||
|
||||
export const pageOrdersApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<OrderItem>>({ url: '/admin/orders/page', params })
|
||||
}
|
||||
|
||||
export const listOrderEventsApi = (orderNo: string) => {
|
||||
return request.get<OrderEventItem[]>({ url: `/admin/orders/${orderNo}/events` })
|
||||
}
|
||||
|
||||
export const compensateOrderApi = (orderNo: string) => {
|
||||
return request.post<OrderItem>({ url: `/admin/orders/${orderNo}/compensate` })
|
||||
}
|
||||
|
||||
export const refundOrderApi = (orderNo: string, reason: string) => {
|
||||
return request.post<OrderItem>({ url: `/admin/orders/${orderNo}/refund`, data: { reason } })
|
||||
}
|
||||
|
||||
export const pageEntitlementsApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<EntitlementItem>>({ url: '/admin/entitlements/page', params })
|
||||
}
|
||||
|
||||
export const grantEntitlementApi = (data: Record<string, unknown>) => {
|
||||
return request.post<EntitlementItem>({ url: '/admin/entitlements', data })
|
||||
}
|
||||
|
||||
export const revokeEntitlementApi = (id: number, reason: string) => {
|
||||
return request.put<void>({ url: `/admin/entitlements/${id}/revoke`, params: { reason } })
|
||||
}
|
||||
|
||||
export const listWalletLedgerApi = (userId: number, limit = 20) => {
|
||||
return request.get<LedgerItem[]>({
|
||||
url: `/admin/wallets/${userId}/ledger`,
|
||||
params: { limit }
|
||||
})
|
||||
}
|
||||
|
||||
export const adjustCoinsApi = (data: Record<string, unknown>) => {
|
||||
return request.post<LedgerItem>({ url: '/admin/wallets/coins', data })
|
||||
}
|
||||
|
||||
export const pageReconcileApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<ReconcileItem>>({ url: '/admin/reconcile/page', params })
|
||||
}
|
||||
|
||||
export const runReconcileApi = (params: Record<string, unknown>) => {
|
||||
return request.post<ReconcileItem>({ url: '/admin/reconcile/run', params })
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import request from '@/axios'
|
||||
import type { PageResult } from './types'
|
||||
|
||||
export interface CreatorItem {
|
||||
id: number
|
||||
userId: number
|
||||
code: string
|
||||
name: string
|
||||
/** INDIVIDUAL / COMPANY */
|
||||
type: string
|
||||
contactName: string
|
||||
contactMobile: string
|
||||
contactEmail: string
|
||||
countryCode: string
|
||||
/** pending / approved / rejected / frozen */
|
||||
status: string
|
||||
auditNote: string
|
||||
auditBy: number
|
||||
auditAt?: string
|
||||
settlementCurrency: string
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export interface CreatorDocumentItem {
|
||||
id: number
|
||||
creatorId: number
|
||||
/** ID_CARD / BUSINESS_LICENSE / CONTRACT */
|
||||
type: string
|
||||
fileUrl: string
|
||||
status: string
|
||||
auditNote: string
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export interface SubmissionItem {
|
||||
id: number
|
||||
creatorId: number
|
||||
dramaId: number
|
||||
/** CREATE / UPDATE / PUBLISH / OFFLINE */
|
||||
type: string
|
||||
/** DRAFT / SUBMITTED / REVIEWING / APPROVED / REJECTED / WITHDRAWN */
|
||||
status: string
|
||||
version: number
|
||||
submitAt?: string
|
||||
reviewerId: number
|
||||
reviewAt?: string
|
||||
reviewNote: string
|
||||
rejectCode: string
|
||||
createdAt?: string
|
||||
}
|
||||
|
||||
export const pageCreatorsApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<CreatorItem>>({ url: '/admin/creators/page', params })
|
||||
}
|
||||
|
||||
export const listCreatorDocumentsApi = (id: number) => {
|
||||
return request.get<CreatorDocumentItem[]>({ url: `/admin/creators/${id}/documents` })
|
||||
}
|
||||
|
||||
/** action: APPROVE / REJECT / FREEZE */
|
||||
export const auditCreatorApi = (id: number, data: Record<string, unknown>) => {
|
||||
return request.put<CreatorItem>({ url: `/admin/creators/${id}/audit`, data })
|
||||
}
|
||||
|
||||
export const pageSubmissionsApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<SubmissionItem>>({ url: '/admin/submissions/page', params })
|
||||
}
|
||||
|
||||
/** action: APPROVE / REJECT / WITHDRAW */
|
||||
export const auditSubmissionApi = (id: number, data: Record<string, unknown>) => {
|
||||
return request.put<SubmissionItem>({ url: `/admin/submissions/${id}/audit`, data })
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import request from '@/axios'
|
||||
|
||||
/** 指标口径见后端 DashboardService 注释与 docs/api-phase1.md「2.8 数据看板」 */
|
||||
export interface DashboardOverview {
|
||||
generatedAt: string
|
||||
user: Record<string, number>
|
||||
content: Record<string, number>
|
||||
playback: Record<string, number>
|
||||
revenue: Record<string, number>
|
||||
cost: Record<string, number | null>
|
||||
notes: string[]
|
||||
}
|
||||
|
||||
export interface TranscodeBoard {
|
||||
generatedAt: string
|
||||
queue: Record<string, number>
|
||||
failedTasks: {
|
||||
id: number
|
||||
episodeId: number
|
||||
status: string
|
||||
retryCount: number
|
||||
errorMsg: string
|
||||
createdAt?: string
|
||||
}[]
|
||||
notes: string[]
|
||||
}
|
||||
|
||||
export const getDashboardOverviewApi = () => {
|
||||
return request.get<DashboardOverview>({ url: '/admin/dashboard/overview' })
|
||||
}
|
||||
|
||||
export const getTranscodeBoardApi = () => {
|
||||
return request.get<TranscodeBoard>({ url: '/admin/dashboard/transcode' })
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import request from '@/axios'
|
||||
import type { PageResult, CommentItem, ReportItem, SensitiveWordItem } from './types'
|
||||
|
||||
export const pageCommentsApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<CommentItem>>({ url: '/admin/comments/page', params })
|
||||
}
|
||||
|
||||
export const listCommentAuditRecordsApi = (id: number) => {
|
||||
return request.get<Record<string, unknown>[]>({
|
||||
url: `/admin/comments/${id}/audit-records`
|
||||
})
|
||||
}
|
||||
|
||||
/** result: PASS / REJECT */
|
||||
export const auditCommentApi = (id: number, data: Record<string, unknown>) => {
|
||||
return request.put<void>({ url: `/admin/comments/${id}/audit`, data })
|
||||
}
|
||||
|
||||
export const deleteCommentApi = (id: number) => {
|
||||
return request.delete<void>({ url: `/admin/comments/${id}` })
|
||||
}
|
||||
|
||||
export const pageReportsApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<ReportItem>>({ url: '/admin/reports/page', params })
|
||||
}
|
||||
|
||||
/** status: handled / rejected;banTarget=true 时联动封禁被举报用户 */
|
||||
export const handleReportApi = (id: number, data: Record<string, unknown>) => {
|
||||
return request.put<void>({ url: `/admin/reports/${id}/handle`, data })
|
||||
}
|
||||
|
||||
export const pageSensitiveWordsApi = (params: Record<string, unknown>) => {
|
||||
return request.get<PageResult<SensitiveWordItem>>({
|
||||
url: '/admin/sensitive-words/page',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const createSensitiveWordApi = (data: Record<string, unknown>) => {
|
||||
return request.post<number>({ url: '/admin/sensitive-words', data })
|
||||
}
|
||||
|
||||
export const updateSensitiveWordApi = (id: number, data: Record<string, unknown>) => {
|
||||
return request.put<void>({ url: `/admin/sensitive-words/${id}`, data })
|
||||
}
|
||||
|
||||
export const deleteSensitiveWordApi = (id: number) => {
|
||||
return request.delete<void>({ url: `/admin/sensitive-words/${id}` })
|
||||
}
|
||||
Reference in new issue
Block a user