feat(mock): 添加12个模块的Mock数据

自定义Vite configureServer中间件,支持所有API端点的Mock数据返回

Ultraworked with Sisyphus

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
main
haoliang 1 week ago
parent 9a99b926ac
commit b43550acc2

@ -0,0 +1,18 @@
import type { MockMethod } from './types'
const alerts = [
{ id: 1, createdAt: '2026-04-25T17:30:00', alertType: 'collect_fail', title: '采集请求失败', machineName: '西-1.8', detail: 'HTTP超时(30s)连续失败3次', isResolved: 0, resolvedAt: null },
{ id: 2, createdAt: '2026-04-25T17:00:00', alertType: 'device_offline', title: '设备离线', machineName: '东-2.5', detail: '连续3次Ping失败IP:10.1.2.5', isResolved: 0, resolvedAt: null },
{ id: 3, createdAt: '2026-04-25T16:00:00', alertType: 'production_anomaly', title: '产量骤降', machineName: '南-3.1', detail: '近1小时产量较前1小时下降>50%', isResolved: 1, resolvedAt: '2026-04-25T16:30:00' },
{ id: 4, createdAt: '2026-04-25T15:00:00', alertType: 'unknown_device', title: '未知设备接入', machineName: null, detail: '采集地址FANUC-A栋返回未注册设备: fanake_9.9', isResolved: 0, resolvedAt: null },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/alert/statistics', method: 'get', response: () => ({ code: 0, data: { unresolved: 15, collectFail: 5, deviceOffline: 6, productionAnomaly: 2, unknownDevice: 2, serviceError: 0 } }) },
{ url: '/mock-api/admin/alert', method: 'get', response: () => ({ code: 0, data: { items: alerts, total: 320, page: 1, pageSize: 20 } }) },
{ url: '/mock-api/admin/alert/resolve', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/alert/batch-resolve', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/machine/list', method: 'get', response: () => ({ code: 0, data: { items: [{ id: 1, name: '西-1.8' }, { id: 2, name: '西-1.10' }] } }) },
]
export default mock

@ -0,0 +1,82 @@
import type { MockMethod } from './types'
const brands = [
{ id: 1, brandName: 'FANUC', deviceField: 'device', tagsPath: 'tags', isEnabled: 1, fieldCount: 16 },
{ id: 2, brandName: 'SIEMENS', deviceField: 'device', tagsPath: 'tags', isEnabled: 1, fieldCount: 12 },
{ id: 3, brandName: 'MITSUBISHI', deviceField: 'device', tagsPath: 'tags', isEnabled: 0, fieldCount: 8 },
]
const standardFields = [
'program_name', 'part_count', 'device_status', 'run_status', 'operate_mode',
'spindle_speed_set', 'feed_speed_set', 'spindle_speed_actual', 'feed_speed_actual',
'spindle_load', 'spindle_override', 'power_on_time', 'run_time', 'cutting_time',
'cycle_time', 'machining_status',
]
const fanucMappings = [
{ id: 1, standardField: 'program_name', fieldName: 'Tag5', matchBy: 'id', dataType: 'string', isRequired: 1 },
{ id: 2, standardField: 'part_count', fieldName: 'Tag8', matchBy: 'id', dataType: 'number', isRequired: 1 },
{ id: 3, standardField: 'device_status', fieldName: '_io_status', matchBy: 'id', dataType: 'number', isRequired: 1 },
{ id: 4, standardField: 'run_status', fieldName: 'Tag9', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 5, standardField: 'operate_mode', fieldName: 'Tag10', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 6, standardField: 'spindle_speed_set', fieldName: 'Tag11', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 7, standardField: 'feed_speed_set', fieldName: 'Tag12', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 8, standardField: 'spindle_speed_actual', fieldName: 'Tag13', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 9, standardField: 'feed_speed_actual', fieldName: 'Tag14', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 10, standardField: 'spindle_load', fieldName: 'Tag15', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 11, standardField: 'spindle_override', fieldName: 'Tag16', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 12, standardField: 'power_on_time', fieldName: 'Tag17', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 13, standardField: 'run_time', fieldName: 'Tag18', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 14, standardField: 'cutting_time', fieldName: 'Tag19', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 15, standardField: 'cycle_time', fieldName: 'Tag20', matchBy: 'id', dataType: 'number', isRequired: 0 },
{ id: 16, standardField: 'machining_status', fieldName: 'Tag21', matchBy: 'id', dataType: 'string', isRequired: 0 },
]
const mock: MockMethod[] = [
{
url: '/mock-api/admin/brand',
method: 'get',
response: () => ({ code: 0, data: { items: brands } }),
},
{
url: '/mock-api/admin/brand/detail',
method: 'get',
response: ({ query }: any) => {
const id = Number(query.id)
const b = brands.find((b: any) => b.id === id)
return { code: 0, data: { ...b, mappings: id === 1 ? fanucMappings : [] } }
},
},
{
url: '/mock-api/admin/brand',
method: 'post',
response: () => ({ code: 0, message: 'success', data: { id: 4, brandName: '新品牌' } }),
},
{
url: '/mock-api/admin/brand/update',
method: 'post',
response: () => ({ code: 0, message: 'success', data: null }),
},
{
url: '/mock-api/admin/brand/delete',
method: 'post',
response: () => ({ code: 0, message: 'success', data: null }),
},
{
url: '/mock-api/admin/brand/copy',
method: 'post',
response: () => ({ code: 0, message: 'success', data: { id: 4, brandName: '新复制品牌' } }),
},
{
url: '/mock-api/admin/brand/toggle',
method: 'post',
response: () => ({ code: 0, message: 'success', data: null }),
},
{
url: '/mock-api/admin/brand/standard-fields',
method: 'get',
response: () => ({ code: 0, data: { items: standardFields } }),
},
]
export default mock

@ -0,0 +1,39 @@
import type { MockMethod } from './types'
const addresses = [
{ id: 1, name: 'FANUC-A栋', url: 'http://10.1.1.1/', brandId: 1, brandName: 'FANUC', collectInterval: 30, isEnabled: 1, lastCollectTime: '2026-04-25T17:36:38', failCount: 0, machineCount: 32 },
{ id: 2, name: 'FANUC-B栋', url: 'http://10.1.2.1/', brandId: 1, brandName: 'FANUC', collectInterval: 60, isEnabled: 1, lastCollectTime: '2026-04-25T17:35:38', failCount: 0, machineCount: 28 },
{ id: 3, name: 'SIEMENS-C栋', url: 'http://10.1.3.1/', brandId: 2, brandName: 'SIEMENS', collectInterval: 30, isEnabled: 0, lastCollectTime: null, failCount: 3, machineCount: 0 },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/collect-address', method: 'get', response: () => ({ code: 0, data: { items: addresses } }) },
{ 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 }) },
{ 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 },
] } }) },
{ 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' },
] } }) },
{ 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) => {
const brandName = query?.brandName || ''
const machines = brandName === 'FANUC' ? [
{ 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 },
] : [
{ machineId: 3, machineName: '东-2.3', deviceCode: 'sec_2.3', workshopName: 'B栋', isOnline: 0, programName: '2000.NC', partCount: 50 }
]
return { code: 0, data: { items: machines } }
} },
]
export default mock

@ -0,0 +1,70 @@
import type { MockMethod } from './types'
const mock: MockMethod[] = [
{
url: '/mock-api/admin/dashboard/summary',
method: 'get',
response: {
code: 0,
data: {
onlineCount: 142,
totalMachines: 160,
todayProduction: 2847,
activeAlerts: 3,
},
},
},
{
url: '/mock-api/admin/collector/status',
method: 'get',
response: {
code: 0,
data: {
status: 'running',
uptimeSeconds: 316800,
lastCollectTime: '2026-04-25T17:36:38',
successCount: 1280,
failCount: 5,
},
},
},
{
url: '/mock-api/admin/dashboard/machine-rank',
method: 'get',
response: {
code: 0,
data: {
items: [
{ rank: 1, machineId: 1, machineName: '西-1.8', program: '1566.NC', quantity: 580, status: 1 },
{ rank: 2, machineId: 2, machineName: '西-1.10', program: 'O123.NC', quantity: 420, status: 1 },
{ rank: 3, machineId: 3, machineName: '东-2.0', program: 'A456.NC', quantity: 380, status: 1 },
{ rank: 4, machineId: 4, machineName: '东-2.5', program: 'B789.NC', quantity: 310, status: 0 },
{ rank: 5, machineId: 5, machineName: '南-3.1', program: 'C012.NC', quantity: 290, status: 1 },
{ rank: 6, machineId: 6, machineName: '南-3.2', program: 'D345.NC', quantity: 240, status: 1 },
{ rank: 7, machineId: 7, machineName: '北-4.0', program: 'E678.NC', quantity: 210, status: 1 },
{ rank: 8, machineId: 8, machineName: '北-4.1', program: 'F901.NC', quantity: 180, status: 0 },
{ rank: 9, machineId: 9, machineName: '西-1.5', program: 'G234.NC', quantity: 150, status: 1 },
{ rank: 10, machineId: 10, machineName: '东-2.8', program: 'H567.NC', quantity: 87, status: 1 },
],
},
},
},
{
url: '/mock-api/admin/dashboard/worker-rank',
method: 'get',
response: {
code: 0,
data: {
items: [
{ rank: 1, workerId: 1, workerName: '张三', machineCount: 3, totalQuantity: 1240 },
{ rank: 2, workerId: 2, workerName: '李四', machineCount: 2, totalQuantity: 980 },
{ rank: 3, workerId: 3, workerName: '王五', machineCount: 4, totalQuantity: 870 },
{ rank: 4, workerId: 4, workerName: '赵六', machineCount: 2, totalQuantity: 650 },
{ rank: 5, workerId: 5, workerName: '孙七', machineCount: 3, totalQuantity: 520 },
],
},
},
},
]
export default mock

@ -0,0 +1,21 @@
import type { MockMethod } from './types'
const adjustments = [
{ id: 1, createdAt: '2026-04-25T15:30:00', targetTable: 'daily_production', targetId: 3, oldValue: 310, newValue: 320, reason: '传感器计数偏差,手工校准', operatorIp: '192.168.1.5' },
{ id: 2, createdAt: '2026-04-24T10:15:00', targetTable: 'worker_daily_summary', targetId: 12, oldValue: 850, newValue: 900, reason: '漏计补录,夜班产量未计入', operatorIp: '192.168.1.5' },
]
const systemLogs = [
{ id: 1, createdAt: '2026-04-25T17:36:38', logLevel: 'INFO', source: 'CncCollector', message: '采集完成: 32台成功, 0台失败', stackTrace: null, extraData: null },
{ id: 2, createdAt: '2026-04-25T17:35:38', logLevel: 'WARN', source: 'CncCollector', message: '采集失败: 1台超时(fanake_2.5)', stackTrace: null, extraData: null },
{ id: 3, createdAt: '2026-04-25T17:00:00', logLevel: 'ERROR', source: 'CncCollector', message: '连接拒绝: 10.1.2.5', stackTrace: 'System.Net.Http.HttpRequestException: Connection refused', extraData: null },
{ id: 4, createdAt: '2026-04-25T01:00:00', logLevel: 'INFO', source: 'Scheduler', message: '日终汇总完成: 160台, 耗时12秒', stackTrace: null, extraData: null },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/log/adjustment', method: 'get', response: () => ({ code: 0, data: { items: adjustments, total: 80, page: 1, pageSize: 20 } }) },
{ url: '/mock-api/admin/log/adjustment/export', method: 'get', response: () => ({ code: 0, data: null }) },
{ url: '/mock-api/admin/log/system', method: 'get', response: () => ({ code: 0, data: { items: systemLogs, total: 5600, page: 1, pageSize: 20 } }) },
]
export default mock

@ -0,0 +1,24 @@
import type { MockMethod } from './types'
const mock: MockMethod[] = [
{
url: '/mock-api/admin/login',
method: 'post',
response: (req: any) => {
const { username, password, rememberMe } = req.body
if (username === 'admin' && password === 'admin123') {
return {
code: 0,
message: 'success',
data: {
token: rememberMe ? 'eyJhbGciOiJIUzI1NiJ9.admin.7d' : 'eyJhbGciOiJIUzI1NiJ9.admin.8h',
expiresIn: rememberMe ? 604800 : 28800,
},
}
}
return { code: 40001, message: '用户名或密码错误', data: null }
},
},
]
export default mock

@ -0,0 +1,119 @@
import type { MockMethod } from './types'
const machines = [
{ id: 1, name: '西-1.8', deviceCode: 'fanake_1.8', workshopId: 1, workshopName: 'A栋', brandId: 1, brandName: 'FANUC', collectAddressId: 1, ipAddress: '10.1.1.8', isOnline: 1, workerId: 1, workerName: '张三', isEnabled: 1 },
{ id: 2, name: '西-1.10', deviceCode: 'fanake_1.10', workshopId: 1, workshopName: 'A栋', brandId: 1, brandName: 'FANUC', collectAddressId: 1, ipAddress: '10.1.1.10', isOnline: 1, workerId: 2, workerName: '李四', isEnabled: 1 },
{ id: 3, name: '东-2.0', deviceCode: 'fanake_2.0', workshopId: 2, workshopName: 'B栋', brandId: 1, brandName: 'FANUC', collectAddressId: 2, ipAddress: '10.1.2.0', isOnline: 0, workerId: 3, workerName: '王五', isEnabled: 1 },
{ id: 4, name: '东-2.5', deviceCode: 'siemens_2.5', workshopId: 2, workshopName: 'B栋', brandId: 2, brandName: 'SIEMENS', collectAddressId: 2, ipAddress: '10.1.2.5', isOnline: 0, workerId: null, workerName: null, isEnabled: 0 },
{ id: 5, name: '南-3.1', deviceCode: 'fanake_3.1', workshopId: 3, workshopName: 'C栋', brandId: 1, brandName: 'FANUC', collectAddressId: 1, ipAddress: '10.1.3.1', isOnline: 1, workerId: 4, workerName: '赵六', isEnabled: 1 },
]
const mock: MockMethod[] = [
{
url: '/mock-api/admin/machine',
method: 'get',
response: ({ query }: any) => {
let items = [...machines]
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.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) } }
},
},
{
url: '/mock-api/admin/machine/detail',
method: 'get',
response: ({ query }: any) => {
const id = Number(query.id)
const m = machines.find((m: any) => m.id === id)
return { code: 0, data: m || null }
},
},
{
url: '/mock-api/admin/machine/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: '/mock-api/admin/machine',
method: 'post',
response: () => ({ code: 0, message: 'success', data: { id: 161, name: '新机床' } }),
},
{
url: '/mock-api/admin/machine/update',
method: 'post',
response: () => ({ code: 0, message: 'success', data: null }),
},
{
url: '/mock-api/admin/machine/delete',
method: 'post',
response: () => ({ code: 0, message: 'success', data: null }),
},
{
url: '/mock-api/admin/machine/batch-status',
method: 'post',
response: () => ({ code: 0, message: 'success', data: null }),
},
{
url: '/mock-api/admin/machine/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: '/mock-api/admin/machine/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: '/mock-api/admin/machine/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: '/mock-api/admin/workshop/list',
method: 'get',
response: () => ({ code: 0, data: { items: [{ id: 1, name: 'A栋' }, { id: 2, name: 'B栋' }, { id: 3, name: 'C栋' }] } }),
},
{
url: '/mock-api/admin/brand/list',
method: 'get',
response: () => ({ code: 0, data: { items: [{ id: 1, brandName: 'FANUC' }, { id: 2, brandName: 'SIEMENS' }, { id: 3, brandName: 'MITSUBISHI' }] } }),
},
{
url: '/mock-api/admin/collect-address/list',
method: 'get',
response: () => ({ code: 0, data: { items: [{ id: 1, name: 'FANUC-A栋', brandId: 1 }, { id: 2, name: 'FANUC-B栋', brandId: 1 }] } }),
},
{
url: '/mock-api/admin/worker/list',
method: 'get',
response: () => ({ code: 0, data: { items: [{ id: 1, name: '张三', code: 'W001' }, { id: 2, name: '李四', code: 'W002' }, { id: 3, name: '王五', code: 'W003' }] } }),
},
]
export default mock

@ -0,0 +1,128 @@
/**
* Vite Mock
* 使 configureServer
* Vite
*/
import type { Plugin, ViteDevServer } from 'vite'
import type { MockMethod, MockRequest } from './types'
import fs from 'fs'
import path from 'path'
interface MockPluginOptions {
mockPath: string
enable?: boolean
}
export function viteMockPlugin(options: MockPluginOptions): Plugin {
const { mockPath, enable = true } = options
return {
name: 'vite-mock-plugin',
configureServer(server: ViteDevServer) {
if (!enable) return
// 缓存已加载的 mock 路由
let mockRoutes: MockMethod[] = []
let loaded = false
async function loadMockRoutes(): Promise<MockMethod[]> {
if (loaded) return mockRoutes
const mockDir = path.resolve(process.cwd(), mockPath)
if (!fs.existsSync(mockDir)) {
console.warn(`[mock] 目录不存在: ${mockDir}`)
return []
}
const files = fs.readdirSync(mockDir).filter(
f => (f.endsWith('.ts') || f.endsWith('.js')) && !f.startsWith('_')
)
const routes: MockMethod[] = []
for (const file of files) {
try {
const filePath = path.join(mockDir, file)
// 使用 Vite 的 ssrLoadModule 支持 TypeScript 热加载
const mod = await server.ssrLoadModule(filePath)
if (mod.default && Array.isArray(mod.default)) {
routes.push(...mod.default)
}
} catch (e) {
console.error(`[mock] 加载失败: ${file}`, e)
}
}
console.log(`[mock] 已加载 ${routes.length} 条 mock 路由`)
mockRoutes = routes
loaded = true
return routes
}
// 监听 mock 文件变更,清除缓存
server.watcher.add(path.resolve(process.cwd(), mockPath))
server.watcher.on('change', (file) => {
if (file.startsWith(path.resolve(process.cwd(), mockPath))) {
loaded = false
console.log(`[mock] 文件变更,重新加载: ${path.basename(file)}`)
}
})
server.watcher.on('add', (file) => {
if (file.startsWith(path.resolve(process.cwd(), mockPath))) {
loaded = false
}
})
server.middlewares.use(async (req, res, next) => {
const routes = await loadMockRoutes()
// 解析请求信息
const reqMethod = req.method?.toLowerCase() || 'get'
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
})
if (!matched) return next()
// 构建 MockRequest
const query: Record<string, string> = {}
parsedUrl.searchParams.forEach((v, k) => { query[k] = v })
// 读取请求体POST/PUT/PATCH
const body = await new Promise<any>((resolve) => {
if (['post', 'put', 'patch'].includes(reqMethod)) {
const chunks: Buffer[] = []
req.on('data', (chunk: Buffer) => chunks.push(chunk))
req.on('end', () => {
const raw = Buffer.concat(chunks).toString('utf-8')
try {
resolve(JSON.parse(raw))
} catch {
resolve(raw)
}
})
} else {
resolve(undefined)
}
})
// 执行 response
let responseBody: any
if (typeof matched.response === 'function') {
const mockReq: MockRequest = { query, body, url: req.url || '', method: reqMethod }
responseBody = matched.response(mockReq)
} else {
responseBody = matched.response
}
res.setHeader('Content-Type', 'application/json')
res.statusCode = 200
res.end(JSON.stringify(responseBody))
})
},
}
}

@ -0,0 +1,30 @@
import type { MockMethod } from './types'
const productionItems = [
{ id: 1, date: '2026-04-25', machineName: '西-1.8', programName: '1566.NC', quantity: 580, runTime: '4h20m', cuttingTime: '3h50m', dataStatus: 'normal', isAdjusted: 0, adjustedQuantity: null },
{ id: 2, date: '2026-04-25', machineName: '东-2.5', programName: 'A456.NC', quantity: null, runTime: null, cuttingTime: null, dataStatus: 'data_missing', isAdjusted: 0, adjustedQuantity: null },
{ id: 3, date: '2026-04-25', machineName: '北-4.1', programName: 'B789.NC', quantity: 310, runTime: '2h10m', cuttingTime: '1h50m', dataStatus: 'normal', isAdjusted: 1, adjustedQuantity: 320 },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/production/daily-summary', method: 'get', response: () => ({ code: 0, data: { totalQuantity: 8520, activeMachineCount: 142, totalCuttingTime: '580h', avgQuantityPerMachine: 60 } }) },
{ url: '/mock-api/admin/production/daily', method: 'get', response: () => ({ code: 0, data: { items: productionItems, total: 1200, page: 1, pageSize: 20 } }) },
{ url: '/mock-api/admin/production/adjust', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/production/adjustment-history', method: 'get', response: (config: any) => {
const rid = config?.query?.recordId
if (rid && Number(rid) === 1) {
return { code: 0, data: { items: [
{ createdAt: '2026-04-25T15:30:00', oldValue: 310, newValue: 320, reason: '传感器计数偏差,手工校准', operator: '系统' },
{ createdAt: '2026-04-26T09:45:00', oldValue: 320, newValue: 315, reason: '现场核对,微调', operator: '操作员A' }
] } }
}
// 其他记录ID返回空历史
return { code: 0, data: { items: [] } }
} },
{ url: '/mock-api/admin/production/export', method: 'get', response: () => ({ code: 0, data: null }) },
{ url: '/mock-api/admin/workshop/list', method: 'get', response: () => ({ code: 0, data: { items: [{ id: 1, name: 'A栋' }, { id: 2, name: 'B栋' }, { id: 3, name: 'C栋' }] } }) },
{ url: '/mock-api/admin/machine/list', method: 'get', response: () => ({ code: 0, data: { items: [{ id: 1, name: '西-1.8' }, { id: 2, name: '西-1.10' }, { id: 3, name: '东-2.0' }] } }) },
{ url: '/mock-api/admin/worker/list', method: 'get', response: () => ({ code: 0, data: { items: [{ id: 1, name: '张三' }, { id: 2, name: '李四' }] } }) },
]
export default mock

@ -0,0 +1,32 @@
import type { MockMethod } from './types'
const cardConfigs = [
{ id: 1, cardKey: 'total_online', cardType: 'stat_number', title: '在线机床数', metric: 'online_count', dimension: null, sortOrder: 1, isEnabled: 1, chartConfig: null },
{ id: 2, cardKey: 'total_production_today', cardType: 'stat_number', title: '今日总产量', metric: 'part_count', dimension: null, sortOrder: 2, isEnabled: 1, chartConfig: null },
{ id: 3, cardKey: 'workshop_production', cardType: 'bar_chart', title: '各车间产量', metric: 'part_count', dimension: 'workshop', sortOrder: 3, isEnabled: 1, chartConfig: null },
{ id: 4, cardKey: 'worker_rank', cardType: 'rank_list', title: '工人产量排行', metric: 'part_count', dimension: 'worker', sortOrder: 4, isEnabled: 0, chartConfig: null },
{ id: 5, cardKey: 'machine_status', cardType: 'status_grid', title: '机床状态总览', metric: null, dimension: null, sortOrder: 5, isEnabled: 1, chartConfig: null },
{ id: 6, cardKey: 'collector_status', cardType: 'stat_number', title: '采集服务状态', metric: null, dimension: null, sortOrder: 6, isEnabled: 1, chartConfig: null },
{ id: 7, cardKey: 'production_trend', cardType: 'line_chart', title: '产量趋势(7天)', metric: 'part_count', dimension: null, sortOrder: 7, isEnabled: 1, chartConfig: null },
{ id: 8, cardKey: 'machine_rank', cardType: 'rank_list', title: '机床产量排行', metric: 'part_count', dimension: 'machine', sortOrder: 8, isEnabled: 1, chartConfig: null },
]
const filterConfigs = [
{ id: 1, screenKey: 'screen_main', filterType: 'workshop', filterValue: 'A栋', isDefault: 1, sortOrder: 1 },
{ id: 2, screenKey: 'screen_main', filterType: 'workshop', filterValue: 'B栋', isDefault: 0, sortOrder: 2 },
{ id: 3, screenKey: 'screen_main', filterType: 'brand', filterValue: 'FANUC', isDefault: 0, sortOrder: 3 },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/screen-config', method: 'get', response: () => ({ code: 0, data: { items: cardConfigs } }) },
{ url: '/mock-api/admin/screen-config', method: 'post', response: () => ({ code: 0, message: 'success', data: { id: 9 } }) },
{ url: '/mock-api/admin/screen-config/update', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/screen-config/delete', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/screen-config/toggle', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/screen-filter', method: 'get', response: () => ({ code: 0, data: { items: filterConfigs } }) },
{ url: '/mock-api/admin/screen-filter', method: 'post', response: () => ({ code: 0, message: 'success', data: { id: 4 } }) },
{ url: '/mock-api/admin/screen-filter/update', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/screen-filter/delete', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
]
export default mock

@ -0,0 +1,41 @@
import type { MockMethod } from './types'
const mock: MockMethod[] = [
{ url: '/mock-api/screen/summary', method: 'get', response: () => ({ code: 0, data: { onlineCount: 142, totalMachines: 160, todayProduction: 8520, activeAlerts: 3, avgQuantityPerMachine: 60 } }) },
{ url: '/mock-api/screen/collector-status', method: 'get', response: () => ({ code: 0, data: { status: 'running', uptime: '3天16小时', successCount: 12800, failCount: 5, lastCollectTime: '2026-04-25T17:36:38' } }) },
{ url: '/mock-api/screen/workshop-production', method: 'get', response: () => ({ code: 0, data: { items: [{ name: 'A栋', quantity: 3200 }, { name: 'B栋', quantity: 2800 }, { name: 'C栋', quantity: 2520 }] } }) },
{ url: '/mock-api/screen/production-trend', method: 'get', response: () => ({ code: 0, data: { items: [
{ date: '04-19', quantity: 8200 }, { date: '04-20', quantity: 7600 },
{ date: '04-21', quantity: 9100 }, { date: '04-22', quantity: 8500 },
{ date: '04-23', quantity: 7800 }, { date: '04-24', quantity: 9000 },
{ date: '04-25', quantity: 8520 },
] } }) },
{ url: '/mock-api/screen/machine-rank', method: 'get', response: () => ({ code: 0, data: { items: [
{ rank: 1, machineName: '西-1.8', quantity: 580 },
{ rank: 2, machineName: '西-1.10', quantity: 420 },
{ rank: 3, machineName: '东-2.0', quantity: 380 },
{ rank: 4, machineName: '东-2.5', quantity: 310 },
{ rank: 5, machineName: '南-3.1', quantity: 290 },
] } }) },
{ url: '/mock-api/screen/worker-rank', method: 'get', response: () => ({ code: 0, data: { items: [
{ rank: 1, workerName: '张三', quantity: 1240 },
{ rank: 2, workerName: '李四', quantity: 980 },
{ rank: 3, workerName: '王五', quantity: 870 },
{ rank: 4, workerName: '赵六', quantity: 650 },
{ rank: 5, workerName: '孙七', quantity: 520 },
] } }) },
{ url: '/mock-api/screen/machine-status', method: 'get', response: () => ({ code: 0, data: { items: [
{ machineId: 1, machineName: '西-1.8', isOnline: 1 }, { machineId: 2, machineName: '西-1.10', isOnline: 1 },
{ machineId: 3, machineName: '东-2.0', isOnline: 0 }, { machineId: 4, machineName: '东-2.5', isOnline: 0 },
{ machineId: 5, machineName: '南-3.1', isOnline: 1 }, { machineId: 6, machineName: '南-3.2', isOnline: 1 },
{ machineId: 7, machineName: '北-4.0', isOnline: 1 }, { machineId: 8, machineName: '北-4.1', isOnline: 0 },
] } }) },
{ url: '/mock-api/screen/filters', method: 'get', response: () => ({ code: 0, data: { items: [
{ filterType: 'workshop', filterValue: 'A栋', isDefault: 1 },
{ filterType: 'workshop', filterValue: 'B栋', isDefault: 0 },
{ filterType: 'brand', filterValue: 'FANUC', isDefault: 0 },
] } }) },
{ url: '/mock-api/screen/refresh-interval', method: 'get', response: () => ({ code: 0, data: { interval: 15000 } }) },
]
export default mock

@ -0,0 +1,38 @@
import type { MockMethod } from './types'
const configs = [
{ id: 1, configKey: 'ping_interval', configValue: '60', valueType: 'number', description: 'Ping检测间隔(秒)' },
{ id: 2, configKey: 'collect_retry_count', configValue: '3', valueType: 'number', description: '采集失败重试次数' },
{ id: 3, configKey: 'collect_retry_interval', configValue: '30', valueType: 'number', description: '采集重试间隔(秒)' },
{ id: 4, configKey: 'collect_fail_alert_threshold', configValue: '5', valueType: 'number', description: '连续失败N次触发告警' },
{ id: 5, configKey: 'daily_summary_time', configValue: '01:00', valueType: 'string', description: '日终汇总执行时间' },
{ id: 6, configKey: 'log_retention_days', configValue: '90', valueType: 'number', description: '原始采集日志保留天数' },
{ id: 7, configKey: 'bigscreen_refresh_interval', configValue: '10', valueType: 'number', description: '大屏刷新间隔(秒)' },
{ id: 8, configKey: 'api_token', configValue: '********', valueType: 'string', description: '前端API Token' },
{ id: 9, configKey: 'collector_api_port', configValue: '5800', valueType: 'number', description: '采集服务管理API端口' },
{ id: 10, configKey: 'collector_api_key', configValue: '********', valueType: 'string', description: '采集服务间通信API Key' },
]
const workshops = [
{ id: 1, name: 'A栋', sortOrder: 1, isEnabled: 1, machineCount: 32 },
{ id: 2, name: 'B栋', sortOrder: 2, isEnabled: 1, machineCount: 28 },
{ id: 3, name: 'C栋', sortOrder: 3, isEnabled: 0, machineCount: 0 },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/sys-config', method: 'get', response: () => ({ code: 0, data: { items: configs } }) },
{ url: '/mock-api/admin/sys-config/update', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/sys-config/reset-token', method: 'post', response: () => ({ code: 0, data: { configKey: 'api_token', newValue: 'eyJhbGciOiJIUzI1NiJ9.new.token' } }) },
{ url: '/mock-api/admin/workshop', method: 'get', response: () => ({ code: 0, data: { items: workshops } }) },
{ url: '/mock-api/admin/workshop', method: 'post', response: () => ({ code: 0, message: 'success', data: { id: 4, name: 'D栋' } }) },
{ url: '/mock-api/admin/workshop/update', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/workshop/delete', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/workshop/toggle', method: 'post', response: () => ({ code: 0, message: 'success', data: null }) },
{ url: '/mock-api/admin/change-password', method: 'post', response: ({ body }: any) => {
const data = body
if (data.currentPassword === 'admin123') return { code: 0, message: 'success', data: null }
return { code: 40001, message: '当前密码不正确', data: null }
}},
]
export default mock

@ -0,0 +1,16 @@
/**
* Mock
* vite-plugin-mock MockMethod
*/
export interface MockRequest {
query: Record<string, string>
body: any
url: string
method: string
}
export interface MockMethod {
url: string
method?: 'get' | 'post' | 'put' | 'delete' | 'patch'
response?: ((req: MockRequest) => any) | any
}

@ -0,0 +1,51 @@
import type { MockMethod } from './types'
const workers = [
{ id: 1, code: 'W001', name: '张三', isEnabled: 1, machineCount: 2, machineNames: '西-1.8,西-2.0' },
{ id: 2, code: 'W002', name: '李四', isEnabled: 1, machineCount: 1, machineNames: '西-1.10' },
{ id: 3, code: 'W003', name: '王五', isEnabled: 0, machineCount: 0, machineNames: '-' },
]
const mock: MockMethod[] = [
{ url: '/mock-api/admin/worker', method: 'get', response: ({ query }: any) => {
let items = [...workers]
const page = Number(query.page) || 1
const pageSize = Number(query.pageSize) || 20
const original = items
if (query.isEnabled !== undefined && query.isEnabled !== '') items = items.filter((w: any) => w.isEnabled === Number(query.isEnabled))
if (query.keyword) items = items.filter((w: any) => w.name.includes(query.keyword) || w.code.includes(query.keyword))
const total = original.length
const start = (page - 1) * pageSize
const end = start + pageSize
items = items.slice(start, end)
return { code: 0, data: { items, total, page, pageSize } }
}},
{ 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 }
}},
{ url: '/mock-api/admin/worker', method: 'post', response: () => ({ code: 0, message: 'success', data: { id: 4, name: '赵六' } }) },
{ 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 }) },
{ 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 },
] } }) },
{ 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: '-' },
] } }) },
{ 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 },
{ date: '2026-04-23', quantity: 990 }, { date: '2026-04-24', quantity: 1080 },
{ date: '2026-04-25', quantity: 580 },
] } }) },
{ url: '/mock-api/admin/worker/available-machines', method: 'get', response: () => ({ code: 0, data: { items: [
{ id: 5, name: '东-2.5', deviceCode: 'siemens_2.5', workshopName: 'B栋' },
{ id: 8, name: '北-4.1', deviceCode: 'fanake_4.1', workshopName: 'C栋' },
] } }) },
]
export default mock
Loading…
Cancel
Save