From d7fb9fc2b139a6069d36b1f580afd3e923162f72 Mon Sep 17 00:00:00 2001 From: haoliang <821644@qq.com> Date: Wed, 29 Apr 2026 19:54:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=89=8D=E7=AB=AFAPI?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E4=B8=8E=E5=90=8E=E7=AB=AFRESTful=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=E4=B8=8D=E5=8C=B9=E9=85=8D=EF=BC=9A=E5=8D=87=E7=BA=A7?= =?UTF-8?q?mock=E6=8F=92=E4=BB=B6=E6=94=AF=E6=8C=81=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=8C=96=E8=B7=AF=E7=94=B1=EF=BC=8C=E4=BF=AE=E6=AD=A36?= =?UTF-8?q?=E5=A4=84API=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/mock/brand.ts | 18 ++++++ frontend/mock/collect-address.ts | 19 ++++++ frontend/mock/machine.ts | 64 +++++++++++++++++++ frontend/mock/plugin.ts | 40 ++++++++++-- frontend/mock/types.ts | 2 + frontend/mock/worker.ts | 26 ++++++++ frontend/src/views/brand/BrandEditPage.vue | 2 +- frontend/src/views/brand/BrandListPage.vue | 2 +- .../CollectAddressListPage.vue | 4 +- .../src/views/machine/MachineListPage.vue | 4 +- frontend/src/views/worker/WorkerListPage.vue | 2 +- 11 files changed, 169 insertions(+), 14 deletions(-) diff --git a/frontend/mock/brand.ts b/frontend/mock/brand.ts index 55fbe95..70b7027 100644 --- a/frontend/mock/brand.ts +++ b/frontend/mock/brand.ts @@ -39,6 +39,17 @@ const mock: MockMethod[] = [ response: () => ({ code: 0, data: { items: brands } }), }, { + // 参数化路由:匹配 /mock-api/admin/brand/1, /mock-api/admin/brand/2 等 + url: '/mock-api/admin/brand/:id', + method: 'get', + response: ({ params }: any) => { + const id = Number(params.id) + const b = brands.find((b: any) => b.id === id) + return { code: 0, data: { ...b, mappings: id === 1 ? fanucMappings : [] } } + }, + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/brand/detail', method: 'get', response: ({ query }: any) => { @@ -63,6 +74,13 @@ const mock: MockMethod[] = [ response: () => ({ code: 0, message: 'success', data: null }), }, { + // 参数化路由:POST /mock-api/admin/brand/:id/copy + url: '/mock-api/admin/brand/:id/copy', + method: 'post', + response: () => ({ code: 0, message: 'success', data: { id: 4, brandName: '新复制品牌' } }), + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/brand/copy', method: 'post', response: () => ({ code: 0, message: 'success', data: { id: 4, brandName: '新复制品牌' } }), diff --git a/frontend/mock/collect-address.ts b/frontend/mock/collect-address.ts index 8b3fb8c..bfcae9c 100644 --- a/frontend/mock/collect-address.ts +++ b/frontend/mock/collect-address.ts @@ -8,20 +8,39 @@ const addresses = [ const mock: MockMethod[] = [ { url: '/mock-api/admin/collect-address', method: 'get', response: () => ({ code: 0, data: { items: addresses } }) }, + // 参数化路由:GET /mock-api/admin/collect-address/:id + { url: '/mock-api/admin/collect-address/:id', method: 'get', response: ({ params }: any) => { const id = Number(params.id); const a = addresses.find((a: any) => a.id === id); return { code: 0, data: a || null } } }, + // 兼容旧URL格式 { url: '/mock-api/admin/collect-address/detail', method: 'get', response: ({ query }: any) => { const id = Number(query.id); const a = addresses.find((a: any) => a.id === id); return { code: 0, data: a || null } } }, { url: '/mock-api/admin/collect-address', method: 'post', response: () => ({ code: 0, message: 'success', data: { id: 4, name: '新地址' } }) }, { url: '/mock-api/admin/collect-address/update', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) }, { url: '/mock-api/admin/collect-address/delete', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) }, { url: '/mock-api/admin/collect-address/toggle', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) }, + // 参数化路由:GET /mock-api/admin/collect-address/:id/machines + { url: '/mock-api/admin/collect-address/:id/machines', method: 'get', response: () => ({ code: 0, data: { items: [ + { machineId: 1, machineName: '西-1.8', deviceCode: 'fanake_1.8', workshopName: 'A栋', isOnline: 1, programName: '1566.NC', partCount: 580 }, + { machineId: 2, machineName: '西-1.10', deviceCode: 'fanake_1.10', workshopName: 'A栋', isOnline: 1, programName: 'O123.NC', partCount: 120 }, + ] } }) }, + // 兼容旧URL格式 { url: '/mock-api/admin/collect-address/machines', method: 'get', response: () => ({ code: 0, data: { items: [ { machineId: 1, machineName: '西-1.8', deviceCode: 'fanake_1.8', workshopName: 'A栋', isOnline: 1, programName: '1566.NC', partCount: 580 }, { machineId: 2, machineName: '西-1.10', deviceCode: 'fanake_1.10', workshopName: 'A栋', isOnline: 1, programName: 'O123.NC', partCount: 120 }, ] } }) }, + // 参数化路由:GET /mock-api/admin/collect-address/:id/collect-records + { url: '/mock-api/admin/collect-address/:id/collect-records', method: 'get', response: () => ({ code: 0, data: { items: [ + { requestTime: '2026-04-25T17:36:38', duration: 1200, isSuccess: 1, machineCount: 32, failMachineCount: 0, machineName: '西-1.8' }, + { requestTime: '2026-04-25T17:35:38', duration: 3500, isSuccess: 1, machineCount: 31, failMachineCount: 1, machineName: '西-1.10' }, + { requestTime: '2026-04-25T17:35:08', duration: null, isSuccess: 0, machineCount: null, failMachineCount: null, machineName: '西-1.12' }, + ] } }) }, + // 兼容旧URL格式 { url: '/mock-api/admin/collect-address/collect-records', method: 'get', response: () => ({ code: 0, data: { items: [ { requestTime: '2026-04-25T17:36:38', duration: 1200, isSuccess: 1, machineCount: 32, failMachineCount: 0, machineName: '西-1.8' }, { requestTime: '2026-04-25T17:35:38', duration: 3500, isSuccess: 1, machineCount: 31, failMachineCount: 1, machineName: '西-1.10' }, { requestTime: '2026-04-25T17:35:08', duration: null, isSuccess: 0, machineCount: null, failMachineCount: null, machineName: '西-1.12' }, ] } }) }, + // 参数化路由:GET /mock-api/admin/collect-address/:id/raw-json + { url: '/mock-api/admin/collect-address/:id/raw-json', method: 'get', response: () => ({ code: 0, data: { rawJson: '[{device:fanake_1.8,tags:[{id:Tag5,value:1566.NC}]}]' } }) }, + // 兼容旧URL格式 { url: '/mock-api/admin/collect-address/raw-json', method: 'get', response: () => ({ code: 0, data: { rawJson: '[{device:fanake_1.8,tags:[{id:Tag5,value:1566.NC}]}]' } }) }, { url: '/mock-api/admin/brand/list', method: 'get', response: () => ({ code: 0, data: { items: [{ id: 1, brandName: 'FANUC' }, { id: 2, brandName: 'SIEMENS' }] } }) }, { url: '/mock-api/admin/machine/list', method: 'get', response: ({ query }: any) => { diff --git a/frontend/mock/machine.ts b/frontend/mock/machine.ts index 9927f19..c07df5c 100644 --- a/frontend/mock/machine.ts +++ b/frontend/mock/machine.ts @@ -17,11 +17,23 @@ const mock: MockMethod[] = [ if (query.workshopId) items = items.filter((m: any) => m.workshopId === Number(query.workshopId)) if (query.isOnline !== undefined && query.isOnline !== '') items = items.filter((m: any) => m.isOnline === Number(query.isOnline)) if (query.brandId) items = items.filter((m: any) => m.brandId === Number(query.brandId)) + if (query.brandName) items = items.filter((m: any) => m.brandName === query.brandName) if (query.keyword) items = items.filter((m: any) => m.name.includes(query.keyword) || m.deviceCode.includes(query.keyword)) return { code: 0, data: { items, total: items.length, page: Number(query.page || 1), pageSize: Number(query.pageSize || 20) } } }, }, { + // 参数化路由:GET /mock-api/admin/machine/:id + url: '/mock-api/admin/machine/:id', + method: 'get', + response: ({ params }: any) => { + const id = Number(params.id) + const m = machines.find((m: any) => m.id === id) + return { code: 0, data: m || null } + }, + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/machine/detail', method: 'get', response: ({ query }: any) => { @@ -31,6 +43,16 @@ const mock: MockMethod[] = [ }, }, { + // 参数化路由:GET /mock-api/admin/machine/:id/status + url: '/mock-api/admin/machine/:id/status', + method: 'get', + response: () => ({ + code: 0, + data: { programName: '1566.NC', partCount: 580, runStatus: '运行中', operateMode: '自动', spindleSpeedSet: 3000, feedSpeedSet: 500, spindleSpeedActual: 2980, feedSpeedActual: 480, spindleLoad: 65, machiningStatus: 'G01', lastCollectTime: '2026-04-25T17:36:38' }, + }), + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/machine/status', method: 'get', response: () => ({ @@ -59,6 +81,19 @@ const mock: MockMethod[] = [ response: () => ({ code: 0, message: 'success', data: null }), }, { + // 参数化路由:GET /mock-api/admin/machine/:id/production/today + url: '/mock-api/admin/machine/:id/production/today', + method: 'get', + response: () => ({ + code: 0, + data: { items: [ + { programName: '1566.NC', quantity: 580, runTime: '4h20m', cuttingTime: '3h50m' }, + { programName: 'O123.NC', quantity: 120, runTime: '2h10m', cuttingTime: '1h45m' }, + ] }, + }), + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/machine/production/today', method: 'get', response: () => ({ @@ -70,6 +105,21 @@ const mock: MockMethod[] = [ }), }, { + // 参数化路由:GET /mock-api/admin/machine/:id/production/trend + url: '/mock-api/admin/machine/:id/production/trend', + method: 'get', + response: () => ({ + code: 0, + data: { items: [ + { date: '2026-04-19', quantity: 820 }, { date: '2026-04-20', quantity: 760 }, + { date: '2026-04-21', quantity: 910 }, { date: '2026-04-22', quantity: 850 }, + { date: '2026-04-23', quantity: 780 }, { date: '2026-04-24', quantity: 900 }, + { date: '2026-04-25', quantity: 700 }, + ] }, + }), + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/machine/production/trend', method: 'get', response: () => ({ @@ -83,6 +133,20 @@ const mock: MockMethod[] = [ }), }, { + // 参数化路由:GET /mock-api/admin/machine/:id/collect-records + url: '/mock-api/admin/machine/:id/collect-records', + method: 'get', + response: () => ({ + code: 0, + data: { items: [ + { collectTime: '2026-04-25T17:36:35', programName: '1566.NC', partCount: 580, runStatus: '运行中' }, + { collectTime: '2026-04-25T17:36:05', programName: '1566.NC', partCount: 579, runStatus: '运行中' }, + { collectTime: '2026-04-25T17:35:35', programName: '1566.NC', partCount: 578, runStatus: '运行中' }, + ] }, + }), + }, + { + // 兼容旧URL格式 url: '/mock-api/admin/machine/collect-records', method: 'get', response: () => ({ diff --git a/frontend/mock/plugin.ts b/frontend/mock/plugin.ts index 81ed7ac..94494f6 100644 --- a/frontend/mock/plugin.ts +++ b/frontend/mock/plugin.ts @@ -71,6 +71,26 @@ export function viteMockPlugin(options: MockPluginOptions): Plugin { } }) + // 参数化路由匹配:支持 /mock-api/admin/brand/:id 格式 + function matchRoute(pattern: string, path: string): { matched: boolean; params: Record } { + // 无参数占位符时精确匹配 + if (!pattern.includes(':')) { + return { matched: pattern === path, params: {} } + } + // 将 :param 转为正则捕获组 + const paramNames: string[] = [] + const regexStr = pattern.replace(/:([^/]+)/g, (_, name) => { + paramNames.push(name) + return '([^/]+)' + }) + const regex = new RegExp('^' + regexStr + '$') + const match = path.match(regex) + if (!match) return { matched: false, params: {} } + const params: Record = {} + paramNames.forEach((name, i) => { params[name] = match[i + 1] }) + return { matched: true, params } + } + server.middlewares.use(async (req, res, next) => { const routes = await loadMockRoutes() @@ -79,12 +99,18 @@ export function viteMockPlugin(options: MockPluginOptions): Plugin { const parsedUrl = new URL(req.url || '/', 'http://localhost') const reqPath = parsedUrl.pathname - // 匹配路由:URL 精确匹配 + method 匹配 - const matched = routes.find(r => { - if (r.url !== reqPath) return false - if (!r.method) return true - return r.method.toLowerCase() === reqMethod - }) + // 匹配路由:支持精确匹配 + 参数化路由 + method 匹配 + // 参数化路由优先级低于精确匹配,遍历时先匹配到的优先 + let matched: MockMethod | null = null + let matchedParams: Record = {} + for (const r of routes) { + const { matched: isMatched, params } = matchRoute(r.url, reqPath) + if (!isMatched) continue + if (r.method && r.method.toLowerCase() !== reqMethod) continue + matched = r + matchedParams = params + break + } if (!matched) return next() @@ -113,7 +139,7 @@ export function viteMockPlugin(options: MockPluginOptions): Plugin { // 执行 response let responseBody: any if (typeof matched.response === 'function') { - const mockReq: MockRequest = { query, body, url: req.url || '', method: reqMethod } + const mockReq: MockRequest = { query, body, url: req.url || '', method: reqMethod, params: matchedParams } responseBody = matched.response(mockReq) } else { responseBody = matched.response diff --git a/frontend/mock/types.ts b/frontend/mock/types.ts index d1a8a15..3d4e3b8 100644 --- a/frontend/mock/types.ts +++ b/frontend/mock/types.ts @@ -7,6 +7,8 @@ export interface MockRequest { body: any url: string method: string + /** URL路径参数,如 /brand/:id 中的 { id: '1' } */ + params: Record } export interface MockMethod { diff --git a/frontend/mock/worker.ts b/frontend/mock/worker.ts index 309a623..2c9cc54 100644 --- a/frontend/mock/worker.ts +++ b/frontend/mock/worker.ts @@ -20,6 +20,12 @@ const mock: MockMethod[] = [ items = items.slice(start, end) return { code: 0, data: { items, total, page, pageSize } } }}, + // 参数化路由:GET /mock-api/admin/worker/:id + { url: '/mock-api/admin/worker/:id', method: 'get', response: ({ params }: any) => { + const w = workers.find((w: any) => w.id === Number(params.id)) + return { code: 0, data: w || null } + }}, + // 兼容旧URL格式 { url: '/mock-api/admin/worker/detail', method: 'get', response: ({ query }: any) => { const w = workers.find((w: any) => w.id === Number(query.id)) return { code: 0, data: w || null } @@ -28,14 +34,34 @@ const mock: MockMethod[] = [ { url: '/mock-api/admin/worker/update', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) }, { url: '/mock-api/admin/worker/delete', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) }, { url: '/mock-api/admin/worker/batch-status', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) }, + // 参数化路由:GET /mock-api/admin/worker/:id/machines + { url: '/mock-api/admin/worker/:id/machines', method: 'get', response: () => ({ code: 0, data: { items: [ + { machineId: 1, machineName: '西-1.8', deviceCode: 'fanake_1.8', workshopName: 'A栋', brandName: 'FANUC', isOnline: 1, programName: '1566.NC' }, + { machineId: 2, machineName: '西-2.0', deviceCode: 'fanake_2.0', workshopName: 'A栋', brandName: 'FANUC', isOnline: 0, programName: null }, + ] } }) }, + // 兼容旧URL格式 { url: '/mock-api/admin/worker/machines', method: 'get', response: () => ({ code: 0, data: { items: [ { machineId: 1, machineName: '西-1.8', deviceCode: 'fanake_1.8', workshopName: 'A栋', brandName: 'FANUC', isOnline: 1, programName: '1566.NC' }, { machineId: 2, machineName: '西-2.0', deviceCode: 'fanake_2.0', workshopName: 'A栋', brandName: 'FANUC', isOnline: 0, programName: null }, ] } }) }, + // 参数化路由:GET /mock-api/admin/worker/:id/production/today + { url: '/mock-api/admin/worker/:id/production/today', method: 'get', response: () => ({ code: 0, data: { items: [ + { machineName: '西-1.8', programName: '1566.NC', quantity: 580, runTime: '4h20m', cuttingTime: '3h50m' }, + { machineName: '西-2.0', programName: '-', quantity: null, runTime: '-', cuttingTime: '-' }, + ] } }) }, + // 兼容旧URL格式 { url: '/mock-api/admin/worker/production/today', method: 'get', response: () => ({ code: 0, data: { items: [ { machineName: '西-1.8', programName: '1566.NC', quantity: 580, runTime: '4h20m', cuttingTime: '3h50m' }, { machineName: '西-2.0', programName: '-', quantity: null, runTime: '-', cuttingTime: '-' }, ] } }) }, + // 参数化路由:GET /mock-api/admin/worker/:id/production/trend + { url: '/mock-api/admin/worker/:id/production/trend', method: 'get', response: () => ({ code: 0, data: { items: [ + { date: '2026-04-19', quantity: 980 }, { date: '2026-04-20', quantity: 920 }, + { date: '2026-04-21', quantity: 1100 }, { date: '2026-04-22', quantity: 1050 }, + { date: '2026-04-23', quantity: 990 }, { date: '2026-04-24', quantity: 1080 }, + { date: '2026-04-25', quantity: 580 }, + ] } }) }, + // 兼容旧URL格式 { url: '/mock-api/admin/worker/production/trend', method: 'get', response: () => ({ code: 0, data: { items: [ { date: '2026-04-19', quantity: 980 }, { date: '2026-04-20', quantity: 920 }, { date: '2026-04-21', quantity: 1100 }, { date: '2026-04-22', quantity: 1050 }, diff --git a/frontend/src/views/brand/BrandEditPage.vue b/frontend/src/views/brand/BrandEditPage.vue index 9722e58..d0ffd33 100644 --- a/frontend/src/views/brand/BrandEditPage.vue +++ b/frontend/src/views/brand/BrandEditPage.vue @@ -44,7 +44,7 @@ const form = reactive({ brandName: '', deviceField: 'device', tagsPath: 'tags', function addMapping() { form.mappings.push({ standardField: '', fieldName: '', matchBy: 'id', dataType: 'string', isRequired: 0 }) } async function loadData() { if (!isEdit) return - const r = await request.get('/admin/brand/detail', { params: { id: route.params.id } }) + const r = await request.get(`/admin/brand/${route.params.id}`) if (r.data) { form.brandName = r.data.brandName; form.deviceField = r.data.deviceField; form.tagsPath = r.data.tagsPath; form.mappings = (r.data as any).mappings || [] } } async function handleSave() { diff --git a/frontend/src/views/brand/BrandListPage.vue b/frontend/src/views/brand/BrandListPage.vue index 99fdf6a..ac67c20 100644 --- a/frontend/src/views/brand/BrandListPage.vue +++ b/frontend/src/views/brand/BrandListPage.vue @@ -39,7 +39,7 @@ async function handleDelete(row: Brand) { await ElMessageBox.confirm('确定删 async function handleCopy(row: Brand) { try { await ElMessageBox.confirm(`确定复制品牌【${row.brandName}】?`, '提示', { type: 'warning' }) - await request.post('/admin/brand/copy', { sourceId: row.id }) + await request.post(`/admin/brand/${row.id}/copy`) ElMessage.success('复制成功') loadData() } catch { diff --git a/frontend/src/views/collect-address/CollectAddressListPage.vue b/frontend/src/views/collect-address/CollectAddressListPage.vue index 9e542db..af1d448 100644 --- a/frontend/src/views/collect-address/CollectAddressListPage.vue +++ b/frontend/src/views/collect-address/CollectAddressListPage.vue @@ -53,7 +53,7 @@ const form=reactive({name:'',url:'',brandId:undefined as number|undefined,collec // 机床列表(关联机床) const machineList=ref<{ machineId:number; machineName:string }[]>([]) // 加载某品牌的机床列表 -async function loadMachinesForBrandName(brandName:string){ if(!brandName){ machineList.value = []; return } const r: ApiResponse<{ items: { machineId:number; machineName:string }[] }> = await request.get('/admin/machine/list', { params: { brandName } }); machineList.value = r.data?.items || [] } + async function loadMachinesForBrandName(brandName:string){ if(!brandName){ machineList.value = []; return } const r: ApiResponse<{ items: { machineId:number; machineName:string }[] }> = await request.get('/admin/machine', { params: { brandName } }); machineList.value = r.data?.items || [] } function resetQuery(){query.brandId=undefined;loadData()} function goDetail(id:number){router.push((isMock.value?'/mock/collect-address/':'/collect-address/')+id)} async function loadData(){loading.value=true;try{const r:any=await request.get('/admin/collect-address');tableData.value=r.data?.items||[]}finally{loading.value=false}} @@ -61,7 +61,7 @@ function handleAdd(){editingId.value=null;Object.assign(form,{name:'',url:'',bra function handleEdit(row:any){editingId.value=row.id;Object.assign(form,row);dialogVisible.value=true} async function handleSubmit(){submitting.value=true;try{await request[editingId.value?'put':'post'](editingId.value?`/admin/collect-address/${editingId.value}`:'/admin/collect-address',{...form});ElMessage.success('保存成功');dialogVisible.value=false;loadData()}finally{submitting.value=false}} async function handleDelete(row:any){await ElMessageBox.confirm('确定删除【'+row.name+'】?此操作不可恢复。','提示',{type:'warning'});await request.delete(`/admin/collect-address/${row.id}`);ElMessage.success('已删除');loadData()} -async function loadDrops(){const r:any=await request.get('/admin/brand/list');brandList.value=r.data?.items||[]} + async function loadDrops(){const r:any=await request.get('/admin/brand');brandList.value=r.data?.items||[]} onMounted(()=>{loadData();loadDrops()}) // 监听品牌变化,自动加载对应机床 watch(() => form.brandId, async (newVal)=>{ diff --git a/frontend/src/views/machine/MachineListPage.vue b/frontend/src/views/machine/MachineListPage.vue index 5045132..b6f2b18 100644 --- a/frontend/src/views/machine/MachineListPage.vue +++ b/frontend/src/views/machine/MachineListPage.vue @@ -293,9 +293,9 @@ async function handleImport() { async function loadDrops() { const w: ApiResponse<{ items: Workshop[] }> = await request.get('/admin/workshop') - const b: ApiResponse<{ items: Brand[] }> = await request.get('/admin/brand/list') + const b: ApiResponse<{ items: Brand[] }> = await request.get('/admin/brand') const a: ApiResponse<{ items: CollectAddress[] }> = await request.get('/admin/collect-address') - const wk: ApiResponse<{ items: Worker[] }> = await request.get('/admin/worker/list') + const wk: ApiResponse<{ items: Worker[] }> = await request.get('/admin/worker') workshopList.value = w.data?.items ?? [] brandList.value = b.data?.items ?? [] addressList.value = a.data?.items ?? [] diff --git a/frontend/src/views/worker/WorkerListPage.vue b/frontend/src/views/worker/WorkerListPage.vue index 8fb2d24..db6da3f 100644 --- a/frontend/src/views/worker/WorkerListPage.vue +++ b/frontend/src/views/worker/WorkerListPage.vue @@ -100,7 +100,7 @@ function handleEdit(row: Worker){editingId.value=row.id;Object.assign(form,{code async function handleSubmit(){submitting.value=true;try{const ok = await (workerForm.value?.validate ? new Promise((resolve)=>workerForm.value!.validate((valid:boolean)=>resolve(valid))) : Promise.resolve(true)); if(!ok){return} await request[editingId.value?'put':'post'](editingId.value?`/admin/worker/${editingId.value}`:'/admin/worker',{...form});ElMessage.success('保存成功');dialogVisible.value=false;loadData()}finally{submitting.value=false}} async function handleDelete(row:any){await ElMessageBox.confirm('确定删除【'+row.name+'】?此操作不可恢复。','提示',{type:'warning'});await request.delete(`/admin/worker/${row.id}`);ElMessage.success('已删除');loadData()} async function batchStatus(isEnabled:number){await ElMessageBox.confirm('确定对选中的'+selectedRows.value.length+'项操作?','提示',{type:'warning'});for(const id of selectedRows.value.map((r:any)=>r.id)){await request.put(`/admin/worker/${id}/toggle`,{isEnabled})};ElMessage.success('操作成功');loadData()} -async function loadDrops(){const r: ApiResponse<{ items: Array<{ id: number; name: string; deviceCode?: string; workshopName?: string }> }> = await request.get('/admin/worker/available-machines'); availableMachines.value = (r.data?.items ?? []).map(m => ({ id: m.id, name: m.name, label: `${m.deviceCode || m.name} (${m.name})${m.workshopName ? ' - ' + m.workshopName : ''}` }))} +async function loadDrops(){try{const r: ApiResponse<{ items: Array<{ id: number; name: string; deviceCode?: string; workshopName?: string }> }> = await request.get('/admin/worker/available-machines'); availableMachines.value = (r.data?.items ?? []).map(m => ({ id: m.id, name: m.name, label: `${m.deviceCode || m.name} (${m.name})${m.workshopName ? ' - ' + m.workshopName : ''}` }))}catch{/* 接口不可用时保持为空,不影响其他功能 */}} function handlePageChange(page:number){pagination.value.currentPage=page;loadData()} function handleSizeChange(size:number){pagination.value.pageSize=size;pagination.value.currentPage=1;loadData()} onMounted(()=>{loadData();loadDrops()})