From 4271e7d2d941c419076b4b91e57c263bb3bf5433 Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Tue, 21 Apr 2026 15:55:28 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0API=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=92=8C=E5=8F=82=E6=95=B0=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E8=B7=AF=E7=94=B1=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将`updateAnchor`方法的请求方式改为PUT,`deleteAnchor`方法改为DELETE并使用params传递数据 - 在路由组件中添加`normalizeRouteComponent`和`resolveRouteComponent`函数,增强动态路由解析能力 - 更新多个组件中的ID处理逻辑,确保ID始终为字符串类型 - 修改样式以统一选择框的宽度 --- .gitignore | 1 + .../trade/operation/setting/anchor/index.ts | 6 +- .../operation/setting/live-account/index.ts | 88 +++++++ .../operation/setting/scheduling/index.ts | 114 ++++++++ src/router/backEnd.ts | 182 ++++++------- .../setting/anchor/component/editAnchor.vue | 32 +-- .../trade/operation/setting/anchor/index.vue | 13 +- .../component/editLiveAccount.vue | 159 +++++++++++ .../operation/setting/live-account/index.vue | 247 ++++++++++++++++++ .../scheduling/component/editSchedule.vue | 225 ++++++++++++++++ .../operation/setting/scheduling/index.vue | 237 +++++++++++++++++ 11 files changed, 1189 insertions(+), 115 deletions(-) create mode 100644 src/api/trade/operation/setting/live-account/index.ts create mode 100644 src/api/trade/operation/setting/scheduling/index.ts create mode 100644 src/views/trade/operation/setting/live-account/component/editLiveAccount.vue create mode 100644 src/views/trade/operation/setting/live-account/index.vue create mode 100644 src/views/trade/operation/setting/scheduling/component/editSchedule.vue create mode 100644 src/views/trade/operation/setting/scheduling/index.vue diff --git a/.gitignore b/.gitignore index 0f09833..c590da0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store node_modules /dist +src/**/*.vue.js # local env files diff --git a/src/api/trade/operation/setting/anchor/index.ts b/src/api/trade/operation/setting/anchor/index.ts index 3f58794..ec814b0 100644 --- a/src/api/trade/operation/setting/anchor/index.ts +++ b/src/api/trade/operation/setting/anchor/index.ts @@ -47,7 +47,7 @@ export function addAnchor(data: AnchorParams) { export function updateAnchor(data: AnchorParams) { return request({ url: '/erp/anchor/controller/updateAnchor', - method: 'post', + method: 'put', data: data, }); } @@ -55,8 +55,8 @@ export function updateAnchor(data: AnchorParams) { export function deleteAnchor(data: { id: string }) { return request({ url: '/erp/anchor/controller/deleteAnchor', - method: 'post', - data: data, + method: 'delete', + params: data, }); } diff --git a/src/api/trade/operation/setting/live-account/index.ts b/src/api/trade/operation/setting/live-account/index.ts new file mode 100644 index 0000000..467c3cf --- /dev/null +++ b/src/api/trade/operation/setting/live-account/index.ts @@ -0,0 +1,88 @@ +import request from '/@/utils/request'; + +export interface LiveAccountParams { + pageNum: number; + pageSize: number; + platform?: string; + accountName?: string; + accountId?: string; + status?: number; +} + +export interface LiveAccountSaveParams { + id?: string; + platform: string; + accountName: string; + accountId: string; + status?: number; + remark?: string; +} + +export interface LiveAccount { + id: string; + platform: string; + accountName: string; + accountId: string; + status: number; + statusName: string; + remark: string; + createdAt: number; + updatedAt: number; +} + +export interface LiveAccountListResult { + list: LiveAccount[]; + total: number; +} + +export interface LiveAccountListResponse { + code: number; + message: string; + data: LiveAccountListResult; +} + +export interface LiveAccountDetailResponse { + code: number; + message: string; + data: LiveAccount; +} + +export function getLiveAccountList(params: LiveAccountParams): Promise { + return request({ + url: '/erp/live/account/controller/listLiveAccounts', + method: 'get', + params, + }) as Promise; +} + +export function getLiveAccountDetail(params: { id: string }): Promise { + return request({ + url: '/erp/live/account/controller/getLiveAccount', + method: 'get', + params, + }) as Promise; +} + +export function createLiveAccount(data: LiveAccountSaveParams) { + return request({ + url: '/erp/live/account/controller/createLiveAccount', + method: 'post', + data, + }); +} + +export function updateLiveAccount(data: LiveAccountSaveParams) { + return request({ + url: '/erp/live/account/controller/updateLiveAccount', + method: 'put', + data, + }); +} + +export function deleteLiveAccount(params: { id: string }) { + return request({ + url: '/erp/live/account/controller/deleteLiveAccount', + method: 'delete', + params, + }); +} diff --git a/src/api/trade/operation/setting/scheduling/index.ts b/src/api/trade/operation/setting/scheduling/index.ts new file mode 100644 index 0000000..130fa1b --- /dev/null +++ b/src/api/trade/operation/setting/scheduling/index.ts @@ -0,0 +1,114 @@ +import request from '/@/utils/request'; + +export interface ScheduleListParams { + pageNum: number; + pageSize: number; + anchorId?: string; + accountId?: string; + status?: number; +} + +export interface ScheduleSaveParams { + id?: string; + anchorId: string; + accountId: string; + productId?: number; + orderId?: number; + startTime: string; + endTime: string; + status?: number; + remark?: string; +} + +export interface ScheduleItem { + id: string; + anchorId: string; + anchorName: string; + accountId: string; + accountName: string; + platform: string; + startTime: number; + endTime: number; + status: number; + statusName: string; + productId: number; + orderId: number; + remark: string; + createdAt: number; + updatedAt: number; +} + +export interface ScheduleDetail { + id: string; + tenantId: number; + creator: string; + createdAt: string; + updater: string; + updatedAt: string; + deletedAt: string | null; + anchorId: string; + accountId: string; + startTime: string; + endTime: string; + status: number; + productId: number; + orderId: number; + remark: string; +} + +export interface ScheduleListResult { + list: ScheduleItem[]; + total: number; +} + +export interface ScheduleListResponse { + code: number; + message: string; + data: ScheduleListResult; +} + +export interface ScheduleDetailResponse { + code: number; + message: string; + data: ScheduleDetail; +} + +export function getScheduleList(params: ScheduleListParams): Promise { + return request({ + url: '/erp/schedule/controller/listSchedules', + method: 'get', + params, + }) as Promise; +} + +export function getScheduleDetail(params: { id: string }): Promise { + return request({ + url: '/erp/schedule/controller/getSchedule', + method: 'get', + params, + }) as Promise; +} + +export function createSchedule(data: ScheduleSaveParams) { + return request({ + url: '/erp/schedule/controller/createSchedule', + method: 'post', + data, + }); +} + +export function updateSchedule(data: ScheduleSaveParams) { + return request({ + url: '/erp/schedule/controller/updateSchedule', + method: 'put', + data, + }); +} + +export function deleteSchedule(params: { id: string }) { + return request({ + url: '/erp/schedule/controller/deleteSchedule', + method: 'delete', + params, + }); +} diff --git a/src/router/backEnd.ts b/src/router/backEnd.ts index c561557..17375d6 100644 --- a/src/router/backEnd.ts +++ b/src/router/backEnd.ts @@ -10,8 +10,17 @@ import { useRoutesList } from '/@/stores/routesList'; import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'; import { getUserMenus } from '/@/api/system/menu/index'; +// 扩展Window接口,添加nextLoading属性 +declare global { + interface Window { + nextLoading?: boolean; + } +} + const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}'); const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}'); +const parentView = layouModules['../layout/routerView/parent.vue']; +const notFoundView = viewsModules['../views/error/404.vue']; // 后端控制路由 @@ -22,6 +31,57 @@ const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}'); */ const dynamicViewsModules: Record = Object.assign({}, { ...layouModules }, { ...viewsModules }); +const normalizeRouteComponent = (component?: string) => { + if (!component) return ''; + return component + .trim() + .replace(/^\/@\//, '') + .replace(/^\//, '') + .replace(/^views\//, '') + .replace(/\.(vue|tsx)$/i, '') + .replace(/\/index$/i, ''); +}; + +const resolveRouteComponent = (component?: string, path?: string, isParent = false) => { + const normalizedComponent = normalizeRouteComponent(component); + const normalizedPath = normalizeRouteComponent(path); + const candidates = [normalizedComponent, normalizedPath].filter(Boolean); + + for (const candidate of candidates) { + const exactViewIndexVueKey = `../views/${candidate}/index.vue`; + if (dynamicViewsModules[exactViewIndexVueKey]) { + return dynamicViewsModules[exactViewIndexVueKey]; + } + + const exactViewVueKey = `../views/${candidate}.vue`; + if (dynamicViewsModules[exactViewVueKey]) { + return dynamicViewsModules[exactViewVueKey]; + } + + const exactViewTsxKey = `../views/${candidate}.tsx`; + if (dynamicViewsModules[exactViewTsxKey]) { + return dynamicViewsModules[exactViewTsxKey]; + } + + const exactLayoutVueKey = `../${candidate}.vue`; + if (dynamicViewsModules[exactLayoutVueKey]) { + return dynamicViewsModules[exactLayoutVueKey]; + } + + const exactLayoutTsxKey = `../${candidate}.tsx`; + if (dynamicViewsModules[exactLayoutTsxKey]) { + return dynamicViewsModules[exactLayoutTsxKey]; + } + + if (candidate === 'layout/routerView/parent') { + return parentView; + } + } + + if (isParent) return parentView; + return notFoundView || parentView; +}; + /** * 后端控制路由:初始化方法,防止刷新时路由丢失 * @method NextLoading 界面 loading 动画开始执行 @@ -31,25 +91,18 @@ const dynamicViewsModules: Record = Object.assign({}, { ...lay * @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 */ export async function initBackEndControlRoutes() { - // 界面 loading 动画开始执行 if (window.nextLoading === undefined) NextLoading.start(); - // 无 token 停止执行下一步 if (!Session.get('token')) return false; - // 触发初始化用户信息 pinia - // https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP + await useUserInfo().setUserInfos(); await useUserInfo().setPermissions(); - // 获取路由菜单数据 await getBackEndControlRoutes(); - let menuRoute = Session.get('userMenu'); - // 存储接口原始路由(未处理component),根据需求选择使用 + const menuRoute = Session.get('userMenu'); + useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(menuRoute))); - // 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由 dynamicRoutes[0].children = [...defaultDynamicRouteChildren]; dynamicRoutes[0].children?.push(...(await backEndComponent(menuRoute))); - // 添加动态路由 await setAddRoute(); - // 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组 await setFilterMenuAndCacheTagsViewRoutes(); } @@ -79,7 +132,7 @@ export function setCacheTagsViewRoutes() { * @returns 返回替换后的路由数组 */ export function setFilterRouteEnd() { - let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)); + const filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)); filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower]; return filterRouteEnd; } @@ -103,8 +156,8 @@ export async function setAddRoute() { * @returns 返回后端路由菜单数据 */ export async function getBackEndControlRoutes() { - let menuRoute = Session.get('userMenu'); - let permissions = Session.get('permissions'); + const menuRoute = Session.get('userMenu'); + const permissions = Session.get('permissions'); if (!menuRoute || !permissions) { await refreshBackEndControlRoutes(); } @@ -116,7 +169,6 @@ export async function getBackEndControlRoutes() { * @returns 返回后端路由菜单数据 */ export async function refreshBackEndControlRoutes() { - // 获取路由 await getUserMenus().then((res: any) => { Session.set('userMenu', res.data.menuList); Session.set('permissions', res.data.permissions); @@ -141,51 +193,21 @@ export function setBackEndControlRefreshRoutes() { export function backEndComponent(routes: any) { if (!routes) return []; return routes.map((item: any) => { - // 递归处理子路由 - if (item.children && item.children.length > 0) { + const hasChildren = Array.isArray(item.children) && item.children.length > 0; + + if (hasChildren) { item.children = backEndComponent(item.children); - // 找到第一个非隐藏的子路由作为重定向 const firstVisibleChild = item.children.find((ci: any) => !ci.meta?.isHide); if (firstVisibleChild) { item.redirect = firstVisibleChild.path; } - // 确保父级路由有component - if (!item.component || item.component === false) { - item.component = dynamicViewsModules['../layout/routerView/parent.vue']; - } + + const routeComponent = resolveRouteComponent(item.component as string, item.path as string, false); + item.component = routeComponent === notFoundView ? parentView : routeComponent; } else { - // 确保叶子路由有component - let componentFound = false; - if (item.component && item.component !== false) { - const comp = dynamicImport(dynamicViewsModules, item.component as string); - if (comp) { - item.component = comp; - componentFound = true; - } - } - if (!componentFound) { - // 尝试根据path生成component路径 - const path = item.path.replace(/^\//, ''); - let comp = dynamicImport(dynamicViewsModules, path); - if (!comp) { - comp = dynamicImport(dynamicViewsModules, path + '/index'); - } - if (!comp) { - // 尝试直接拼接views路径 - const viewsPath = 'views/' + path + '/index.vue'; - const directKey = Object.keys(dynamicViewsModules).find((key) => key.includes(viewsPath)); - if (directKey) { - comp = dynamicViewsModules[directKey]; - } - } - if (comp) { - item.component = comp; - } else { - // 如果还是找不到,使用一个默认组件 - item.component = dynamicViewsModules['../layout/routerView/parent.vue']; - } - } + item.component = resolveRouteComponent(item.component as string, item.path as string, false); } + return item; }); } @@ -197,43 +219,27 @@ export function backEndComponent(routes: any) { * @returns 返回处理成函数后的 component */ export function dynamicImport(dynamicViewsModules: Record, component: string) { + const normalizedComponent = normalizeRouteComponent(component); + if (!normalizedComponent) return false; + + const directViewIndexVueKey = `../views/${normalizedComponent}/index.vue`; + if (dynamicViewsModules[directViewIndexVueKey]) return dynamicViewsModules[directViewIndexVueKey]; + + const directViewVueKey = `../views/${normalizedComponent}.vue`; + if (dynamicViewsModules[directViewVueKey]) return dynamicViewsModules[directViewVueKey]; + + const directViewTsxKey = `../views/${normalizedComponent}.tsx`; + if (dynamicViewsModules[directViewTsxKey]) return dynamicViewsModules[directViewTsxKey]; + + const directLayoutVueKey = `../${normalizedComponent}.vue`; + if (dynamicViewsModules[directLayoutVueKey]) return dynamicViewsModules[directLayoutVueKey]; + + const directLayoutTsxKey = `../${normalizedComponent}.tsx`; + if (dynamicViewsModules[directLayoutTsxKey]) return dynamicViewsModules[directLayoutTsxKey]; + const keys = Object.keys(dynamicViewsModules); - - // 尝试多种匹配方式 - let matchKeys = keys.filter((key) => { - const k = key.replace(/..\/views|../, ''); - return k.startsWith(`${component}`) || k.startsWith(`/${component}`); - }); - - // 如果没有匹配到,尝试带index的路径 - if (matchKeys?.length === 0) { - matchKeys = keys.filter((key) => { - const k = key.replace(/..\/views|../, ''); - return k.startsWith(`${component}/index`) || k.startsWith(`/${component}/index`); - }); - } - - // 尝试直接匹配完整路径 - if (matchKeys?.length === 0) { - matchKeys = keys.filter((key) => { - return key.includes(component); - }); - } - - // 尝试更宽松的匹配方式 - if (matchKeys?.length === 0) { - matchKeys = keys.filter((key) => { - return key.replace(/..\/views\//, '').includes(component.replace(/\//g, '')); - }); - } - - if (matchKeys?.length === 1) { - const matchKey = matchKeys[0]; - return dynamicViewsModules[matchKey]; - } - if (matchKeys?.length > 1) { - return false; - } + const fuzzyKey = keys.find((key) => key.includes(`/${normalizedComponent}/`) || key.includes(`/${normalizedComponent}.`)); + if (fuzzyKey) return dynamicViewsModules[fuzzyKey]; return false; } diff --git a/src/views/trade/operation/setting/anchor/component/editAnchor.vue b/src/views/trade/operation/setting/anchor/component/editAnchor.vue index 8f15a70..1cdf3fe 100644 --- a/src/views/trade/operation/setting/anchor/component/editAnchor.vue +++ b/src/views/trade/operation/setting/anchor/component/editAnchor.vue @@ -32,13 +32,7 @@ - + @@ -96,9 +90,7 @@ const rules: FormRules = { { required: true, message: '联系电话不能为空', trigger: 'blur' }, { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }, ], - code: [ - { required: true, message: '工号不能为空', trigger: 'blur' }, - ], + code: [{ required: true, message: '工号不能为空', trigger: 'blur' }], }; const formRef = ref(); @@ -110,10 +102,10 @@ const openDialog = async (row?: DialogFormData) => { if (row && row.id) { try { state.loading = true; - const res = await getAnchorOne({ id: row.id }); + const res = await getAnchorOne({ id: String(row.id) }); if (res.data) { state.formData = { - id: res.data.id, + id: String(res.data.id), name: res.data.name || '', phone: res.data.phone || '', code: res.data.code || '', @@ -122,7 +114,6 @@ const openDialog = async (row?: DialogFormData) => { }; } } catch (error) { - console.error('获取主播详情失败:', error); ElMessage.error('获取主播详情失败'); } finally { state.loading = false; @@ -152,18 +143,23 @@ const onSubmit = async () => { state.loading = true; - if (state.formData.id) { - await updateAnchor(state.formData); + // 确保id是字符串类型 + const submitData = { + ...state.formData, + id: state.formData.id ? String(state.formData.id) : undefined, + }; + + if (submitData.id) { + await updateAnchor(submitData); ElMessage.success('修改成功'); } else { - await addAnchor(state.formData); + await addAnchor(submitData); ElMessage.success('添加成功'); } closeDialog(); emit('refresh'); } catch (error) { - console.error('操作失败:', error); ElMessage.error('操作失败'); } finally { state.loading = false; @@ -193,4 +189,4 @@ defineExpose({ color: #999; margin-top: 5px; } - \ No newline at end of file + diff --git a/src/views/trade/operation/setting/anchor/index.vue b/src/views/trade/operation/setting/anchor/index.vue index 769527e..f276564 100644 --- a/src/views/trade/operation/setting/anchor/index.vue +++ b/src/views/trade/operation/setting/anchor/index.vue @@ -12,7 +12,7 @@ - + @@ -159,11 +159,13 @@ const getList = async () => { status: searchForm.status, }); if (res && res.data) { - tableData.data = res.data.list || []; + tableData.data = (res.data.list || []).map((item: any) => ({ + ...item, + id: String(item.id), + })); tableData.total = res.data.total || 0; } } catch (error) { - console.error('获取主播列表失败:', error); ElMessage.error('获取主播列表失败'); } finally { tableData.loading = false; @@ -185,12 +187,11 @@ const handleDelete = async (row: TableDataItem) => { cancelButtonText: '取消', type: 'warning', }); - await deleteAnchor({ id: row.id }); + await deleteAnchor({ id: String(row.id) }); ElMessage.success('删除成功'); getList(); } catch (error) { if (error !== 'cancel') { - console.error('删除失败:', error); ElMessage.error('删除失败'); } } @@ -213,4 +214,4 @@ onMounted(() => { .mt20 { margin-top: 20px; } - \ No newline at end of file + diff --git a/src/views/trade/operation/setting/live-account/component/editLiveAccount.vue b/src/views/trade/operation/setting/live-account/component/editLiveAccount.vue new file mode 100644 index 0000000..9702ddc --- /dev/null +++ b/src/views/trade/operation/setting/live-account/component/editLiveAccount.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/src/views/trade/operation/setting/live-account/index.vue b/src/views/trade/operation/setting/live-account/index.vue new file mode 100644 index 0000000..d99f1d6 --- /dev/null +++ b/src/views/trade/operation/setting/live-account/index.vue @@ -0,0 +1,247 @@ + + + + + diff --git a/src/views/trade/operation/setting/scheduling/component/editSchedule.vue b/src/views/trade/operation/setting/scheduling/component/editSchedule.vue new file mode 100644 index 0000000..c512b5a --- /dev/null +++ b/src/views/trade/operation/setting/scheduling/component/editSchedule.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/src/views/trade/operation/setting/scheduling/index.vue b/src/views/trade/operation/setting/scheduling/index.vue new file mode 100644 index 0000000..7b7d04e --- /dev/null +++ b/src/views/trade/operation/setting/scheduling/index.vue @@ -0,0 +1,237 @@ + + + + +