feat(admin): 客服手动录单支持平台与操作人,并可后台配置可选项

- 录单列表新增「平台」「操作人」两列,录单表单与编辑弹窗新增同名字段下拉
- 新增 cs_manual_entry_options 表与 4 个接口(读取/新增/重命名/删除),页面右上角弹窗维护平台与操作人
- 迁移 000087:cs_manual_entries 增加 platform/operator 列与索引,注册 cs_manual_entry:option_manage 权限并授予 ops/super_admin
- 记录保存名称快照,删除选项不影响历史记录;同类型重名返回 409,跨类型同名允许
- 顶部关键词查询支持命中平台/操作人
- 新增单测:选项 CRUD 边界与录单平台/操作人读写
This commit is contained in:
yml2213
2026-10-13 16:22:57 +08:00
parent 9a52dbdb7b
commit f33dc97f6f
12 changed files with 1017 additions and 30 deletions
@@ -2,9 +2,14 @@ import { apiClient } from '@/shared/api/client'
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
/** 录单可选项类型:平台 / 操作人 */
export type ManualEntryOptionType = 'platform' | 'operator'
export interface ManualEntryItem {
id: number
entry_no: string
platform: string
operator: string
admin_id: number
admin_name: string
admin_username: string
@@ -12,6 +17,19 @@ export interface ManualEntryItem {
created_at: string
}
export interface ManualEntryOption {
id: number
type: ManualEntryOptionType
name: string
sort_order: number
created_at: string
}
export interface ManualEntryOptions {
platforms: ManualEntryOption[]
operators: ManualEntryOption[]
}
export interface ManualEntryAgentStats {
admin_id: number
admin_name: string
@@ -20,11 +38,15 @@ export interface ManualEntryAgentStats {
export interface CreateManualEntryRequest {
entry_no: string
platform?: string
operator?: string
remark?: string
}
export interface UpdateManualEntryRequest {
entry_no: string
platform?: string
operator?: string
remark?: string
}
@@ -79,3 +101,37 @@ export async function fetchManualEntryTodayStats() {
)
return data.data
}
/** 获取平台 / 操作人可选项(录单下拉数据源) */
export async function fetchManualEntryOptions() {
const { data } = await apiClient.get<ApiResponse<ManualEntryOptions>>(
'/admin/manual-entries/options'
)
return data.data
}
export async function createManualEntryOption(payload: {
type: ManualEntryOptionType
name: string
}) {
const { data } = await apiClient.post<ApiResponse<ManualEntryOption>>(
'/admin/manual-entries/options',
payload
)
return data.data
}
export async function updateManualEntryOption(id: number, name: string) {
const { data } = await apiClient.put<ApiResponse<ManualEntryOption>>(
`/admin/manual-entries/options/${id}`,
{ name }
)
return data.data
}
export async function deleteManualEntryOption(id: number) {
const { data } = await apiClient.delete<ApiResponse<{ deleted: boolean }>>(
`/admin/manual-entries/options/${id}`
)
return data.data
}