重命名前端目录
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
const ADMIN_TOKEN_KEY = 'order-site-admin-token'
|
||||
const ADMIN_EXPIRES_AT_KEY = 'order-site-admin-token-expires-at'
|
||||
const ADMIN_USER_ID_KEY = 'order-site-admin-user-id'
|
||||
const ADMIN_USERNAME_KEY = 'order-site-admin-username'
|
||||
const ADMIN_ROLE_KEY = 'order-site-admin-role'
|
||||
|
||||
const ADMIN_ROLE_LEVEL: Record<'support' | 'operator' | 'admin', number> = {
|
||||
support: 1,
|
||||
operator: 2,
|
||||
admin: 3,
|
||||
}
|
||||
|
||||
export function getAdminToken() {
|
||||
return localStorage.getItem(ADMIN_TOKEN_KEY) || ''
|
||||
}
|
||||
|
||||
export function getAdminTokenExpiresAt() {
|
||||
return localStorage.getItem(ADMIN_EXPIRES_AT_KEY) || ''
|
||||
}
|
||||
|
||||
export function getAdminUserId() {
|
||||
const value = Number(localStorage.getItem(ADMIN_USER_ID_KEY) || 0)
|
||||
return Number.isFinite(value) && value > 0 ? value : 0
|
||||
}
|
||||
|
||||
export function getAdminUsername() {
|
||||
return localStorage.getItem(ADMIN_USERNAME_KEY) || ''
|
||||
}
|
||||
|
||||
export function getAdminRole() {
|
||||
const role = localStorage.getItem(ADMIN_ROLE_KEY)
|
||||
if (role === 'admin' || role === 'operator' || role === 'support') {
|
||||
return role
|
||||
}
|
||||
|
||||
return 'support'
|
||||
}
|
||||
|
||||
export function hasAdminRole(role: 'admin' | 'operator' | 'support') {
|
||||
if (!hasAdminSession()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return ADMIN_ROLE_LEVEL[getAdminRole()] >= ADMIN_ROLE_LEVEL[role]
|
||||
}
|
||||
|
||||
export function setAdminSession(
|
||||
token: string,
|
||||
expiresAt: string,
|
||||
user?: { userId: number; username: string; role: 'admin' | 'operator' | 'support' },
|
||||
) {
|
||||
localStorage.setItem(ADMIN_TOKEN_KEY, token)
|
||||
localStorage.setItem(ADMIN_EXPIRES_AT_KEY, expiresAt)
|
||||
|
||||
if (user?.userId) {
|
||||
localStorage.setItem(ADMIN_USER_ID_KEY, String(user.userId))
|
||||
}
|
||||
|
||||
if (user?.username) {
|
||||
localStorage.setItem(ADMIN_USERNAME_KEY, user.username)
|
||||
}
|
||||
|
||||
if (user?.role) {
|
||||
localStorage.setItem(ADMIN_ROLE_KEY, user.role)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearAdminSession() {
|
||||
localStorage.removeItem(ADMIN_TOKEN_KEY)
|
||||
localStorage.removeItem(ADMIN_EXPIRES_AT_KEY)
|
||||
localStorage.removeItem(ADMIN_USER_ID_KEY)
|
||||
localStorage.removeItem(ADMIN_USERNAME_KEY)
|
||||
localStorage.removeItem(ADMIN_ROLE_KEY)
|
||||
}
|
||||
|
||||
export function hasAdminSession() {
|
||||
const token = getAdminToken()
|
||||
const expiresAt = getAdminTokenExpiresAt()
|
||||
|
||||
if (!token) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isExpired(expiresAt)) {
|
||||
clearAdminSession()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function isExpired(expiresAt: string) {
|
||||
if (!expiresAt) {
|
||||
return false
|
||||
}
|
||||
|
||||
const expiresAtMs = Date.parse(expiresAt)
|
||||
return Number.isFinite(expiresAtMs) && expiresAtMs <= Date.now()
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
export function formatStatusWithRaw(status: string, labelMap: Record<string, string> = {}) {
|
||||
const normalized = String(status || '').trim()
|
||||
|
||||
if (!normalized) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
const key = normalized.toLowerCase()
|
||||
const label = labelMap[key]
|
||||
|
||||
if (!label || label === normalized) {
|
||||
return normalized
|
||||
}
|
||||
|
||||
return `${label} (${normalized})`
|
||||
}
|
||||
|
||||
export function formatAuditAction(action: string) {
|
||||
const labelMap: Record<string, string> = {
|
||||
admin_user_created: '创建后台用户',
|
||||
admin_user_role_updated: '修改用户角色',
|
||||
admin_user_status_updated: '修改用户状态',
|
||||
admin_user_password_reset: '重置用户密码',
|
||||
task_regenerate_claim_link: '重发领取链接',
|
||||
task_kuaishou_cloud_rebind_role: '换绑角色',
|
||||
task_closed: '关闭任务',
|
||||
task_mark_manual_review: '转人工处理',
|
||||
task_complete_manual_dispatch: '完成人工履约',
|
||||
platform_shop_config_updated: '更新店铺配置',
|
||||
platform_fulfillment_config_updated: '更新履约配置',
|
||||
}
|
||||
|
||||
return formatStatusWithRaw(action, labelMap)
|
||||
}
|
||||
|
||||
export function formatAuditTargetType(targetType: string) {
|
||||
const labelMap: Record<string, string> = {
|
||||
admin_user: '后台用户',
|
||||
task: '交付任务',
|
||||
platform_config: '平台配置',
|
||||
}
|
||||
|
||||
return formatStatusWithRaw(targetType, labelMap)
|
||||
}
|
||||
|
||||
export function formatTaskEventType(eventType: string) {
|
||||
const labelMap: Record<string, string> = {
|
||||
manual_dispatch_completed: '人工履约已回写',
|
||||
claim_redeem_code_used: '兑换码已使用',
|
||||
claim_redeem_code_invalid: '兑换码错误',
|
||||
claim_redeem_completed: '兑换链路完成',
|
||||
kuaishou_cloud_rebind_requested: '发起角色换绑',
|
||||
kuaishou_cloud_rebind_old_number_returned: '换绑旧号已退还',
|
||||
kuaishou_cloud_rebind_prepared: '换绑资源已准备',
|
||||
kuaishou_cloud_rebind_failed: '换绑失败',
|
||||
}
|
||||
|
||||
return formatStatusWithRaw(eventType, labelMap)
|
||||
}
|
||||
|
||||
export function formatRedeemOutcomeLabel(value: string) {
|
||||
const normalized = String(value || '').trim()
|
||||
|
||||
switch (normalized) {
|
||||
case 'success':
|
||||
return '兑换成功'
|
||||
case 'code_used':
|
||||
return '兑换码已使用'
|
||||
case 'code_invalid':
|
||||
return '兑换码错误'
|
||||
case 'captcha_rejected':
|
||||
return '验证码错误'
|
||||
case 'failed':
|
||||
return '兑换失败'
|
||||
default:
|
||||
return normalized || '-'
|
||||
}
|
||||
}
|
||||
|
||||
export function formatRedeemResolutionStatus(value: string) {
|
||||
return value === 'success' ? '已完成' : value === 'failed' ? '失败收口' : value || '-'
|
||||
}
|
||||
|
||||
export function formatKuaishouRoleInfoLabel(value: string) {
|
||||
const normalized = String(value || '').trim()
|
||||
|
||||
switch (normalized) {
|
||||
case 'name':
|
||||
return '角色名称'
|
||||
case 'rid':
|
||||
return '角色 ID'
|
||||
default:
|
||||
return normalized || '-'
|
||||
}
|
||||
}
|
||||
|
||||
export function formatTaskEventPayload(payload: Record<string, unknown>) {
|
||||
const parts = [
|
||||
payload.outcome ? `结果 ${payload.outcome}` : '',
|
||||
payload.resultCode ? `代码 ${payload.resultCode}` : '',
|
||||
payload.resultMessage ? `说明 ${payload.resultMessage}` : '',
|
||||
payload.codeMasked ? `凭据 ${payload.codeMasked}` : '',
|
||||
payload.previousCodeMasked ? `旧码 ${payload.previousCodeMasked}` : '',
|
||||
payload.nextCodeMasked ? `新码 ${payload.nextCodeMasked}` : '',
|
||||
payload.ticketCodeMasked ? `卡券号 ${payload.ticketCodeMasked}` : '',
|
||||
payload.vnId ? `虚拟号 #${payload.vnId}` : '',
|
||||
payload.oldVnId ? `旧虚拟号 #${payload.oldVnId}` : '',
|
||||
payload.vnPhoneMasked ? `号码 ${payload.vnPhoneMasked}` : '',
|
||||
payload.oldVnPhoneMasked ? `旧号码 ${payload.oldVnPhoneMasked}` : '',
|
||||
payload.oldRoleName ? `旧角色 ${payload.oldRoleName}` : '',
|
||||
payload.oldRoleId ? `旧角色ID ${payload.oldRoleId}` : '',
|
||||
payload.attempt ? `第 ${payload.attempt} 次` : '',
|
||||
payload.bindUrl ? `绑定链接 ${payload.bindUrl}` : '',
|
||||
payload.sendType ? `发送类型 ${payload.sendType}` : '',
|
||||
payload.note ? `备注 ${payload.note}` : '',
|
||||
payload.previousOutcome
|
||||
? `切换原因 ${formatRedeemOutcomeLabel(String(payload.previousOutcome || ''))}`
|
||||
: '',
|
||||
payload.deliveryReference ? `单号 ${payload.deliveryReference}` : '',
|
||||
payload.platformOrderId ? `平台单 ${payload.platformOrderId}` : '',
|
||||
payload.trigger ? `触发 ${payload.trigger}` : '',
|
||||
payload.reason ? `原因 ${payload.reason}` : '',
|
||||
payload.purchaseTriggered === true ? '已触发购买' : '',
|
||||
payload.usedKnapsack === true ? '使用背包库存' : '',
|
||||
payload.responseStatus ? `HTTP ${payload.responseStatus}` : '',
|
||||
payload.requestId ? `请求 ${payload.requestId}` : '',
|
||||
payload.errorMessage ? `错误 ${payload.errorMessage}` : '',
|
||||
].filter(Boolean)
|
||||
|
||||
if (parts.length > 0) {
|
||||
return parts.join(' · ')
|
||||
}
|
||||
|
||||
return JSON.stringify(payload || {})
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
export const adminPayStatusOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '未支付', value: 'unpaid' },
|
||||
{ label: '已支付', value: 'paid' },
|
||||
{ label: '失败', value: 'failed' },
|
||||
{ label: '已退款', value: 'refunded' },
|
||||
]
|
||||
|
||||
export const adminTaskStatusOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '待支付', value: 'pending_payment' },
|
||||
{ label: '已支付', value: 'paid' },
|
||||
{ label: '已生成链接', value: 'link_generated' },
|
||||
{ label: '已创建会话', value: 'claimed' },
|
||||
{ label: '已确认角色', value: 'role_confirmed' },
|
||||
{ label: '兑换中', value: 'redeeming' },
|
||||
{ label: '已兑换', value: 'redeemed' },
|
||||
{ label: '待重试', value: 'retry_pending' },
|
||||
{ label: '人工处理', value: 'manual_review' },
|
||||
{ label: '已过期', value: 'expired' },
|
||||
{ label: '已关闭', value: 'closed' },
|
||||
]
|
||||
|
||||
export const adminUserRoleOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '管理员', value: 'admin' },
|
||||
{ label: '普通运营', value: 'operator' },
|
||||
{ label: '客服', value: 'support' },
|
||||
]
|
||||
|
||||
export const adminUserStatusOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '生效中', value: 'active' },
|
||||
{ label: '已停用', value: 'disabled' },
|
||||
]
|
||||
|
||||
export const adminAuditActionOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '创建后台用户', value: 'admin_user_created' },
|
||||
{ label: '修改用户角色', value: 'admin_user_role_updated' },
|
||||
{ label: '修改用户状态', value: 'admin_user_status_updated' },
|
||||
{ label: '重置用户密码', value: 'admin_user_password_reset' },
|
||||
{ label: '换绑角色', value: 'task_kuaishou_cloud_rebind_role' },
|
||||
{ label: '关闭任务', value: 'task_closed' },
|
||||
{ label: '转人工处理', value: 'task_mark_manual_review' },
|
||||
]
|
||||
|
||||
export const adminAuditTargetTypeOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '后台用户', value: 'admin_user' },
|
||||
{ label: '交付任务', value: 'task' },
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
export { formatDateTime as formatAdminDateTime } from '@/utils/date-time'
|
||||
@@ -0,0 +1,54 @@
|
||||
const ISO_DATE_TIME_PATTERN =
|
||||
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$/
|
||||
|
||||
export function isIsoDateTimeString(value: unknown): value is string {
|
||||
return typeof value === 'string' && ISO_DATE_TIME_PATTERN.test(value.trim())
|
||||
}
|
||||
|
||||
export function formatDateTime(value: string | null | undefined, fallback = '-') {
|
||||
const normalized = String(value || '').trim()
|
||||
|
||||
if (!normalized) {
|
||||
return fallback
|
||||
}
|
||||
|
||||
const date = new Date(normalized)
|
||||
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return normalized.replace('T', ' ').replace('Z', '')
|
||||
}
|
||||
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
export function normalizeDateTimeValue(value: unknown): unknown {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => normalizeDateTimeValue(item))
|
||||
}
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, currentValue]) => [
|
||||
key,
|
||||
normalizeDateTimeValue(currentValue),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
if (isIsoDateTimeString(value)) {
|
||||
return formatDateTime(value, '')
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
export function stringifyDisplayJson(value: unknown, space = 2) {
|
||||
return JSON.stringify(normalizeDateTimeValue(value), null, space)
|
||||
}
|
||||
Reference in New Issue
Block a user