diff --git a/components.d.ts b/components.d.ts index 2a2070d..666a7cd 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,11 +11,13 @@ declare module 'vue' { AButton: typeof import('ant-design-vue/es')['Button'] ACol: typeof import('ant-design-vue/es')['Col'] AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider'] - Actions: typeof import('./src/components/actions/index.vue')['default'] + ActionButton: typeof import('./src/components/common/ActionButton/ActionButton.vue')['default'] + ActionButtons: typeof import('./src/components/common/ActionButtons/index.vue')['default'] AForm: typeof import('ant-design-vue/es')['Form'] AFormItem: typeof import('ant-design-vue/es')['FormItem'] AInput: typeof import('ant-design-vue/es')['Input'] AInputGroup: typeof import('ant-design-vue/es')['InputGroup'] + AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] AList: typeof import('ant-design-vue/es')['List'] AListItem: typeof import('ant-design-vue/es')['ListItem'] AModal: typeof import('ant-design-vue/es')['Modal'] @@ -27,19 +29,23 @@ declare module 'vue' { ASpace: typeof import('ant-design-vue/es')['Space'] ATable: typeof import('ant-design-vue/es')['Table'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] - DetectingDevice: typeof import('./src/components/detecting-device/index.vue')['default'] - ExecutionResult: typeof import('./src/components/common/ExecutionResult/ExecutionResult.vue')['default'] + CameraModal: typeof import('./src/components/CameraModal/index.vue')['default'] + CameraStatus: typeof import('./src/components/CameraStatus/index.vue')['default'] + ExecutionResult: typeof import('./src/components/common/ExecutionResult/index.vue')['default'] ILucideActivity: typeof import('~icons/lucide/activity')['default'] ILucideCamera: typeof import('~icons/lucide/camera')['default'] ILucideClock: typeof import('~icons/lucide/clock')['default'] ILucideCpu: typeof import('~icons/lucide/cpu')['default'] ILucideDatabase: typeof import('~icons/lucide/database')['default'] ILucideEdit: typeof import('~icons/lucide/edit')['default'] + ILucideFileText: typeof import('~icons/lucide/file-text')['default'] ILucideInbox: typeof import('~icons/lucide/inbox')['default'] ILucideLink: typeof import('~icons/lucide/link')['default'] ILucideLinkOff: typeof import('~icons/lucide/link-off')['default'] ILucideLinOff: typeof import('~icons/lucide/lin-off')['default'] ILucideLoader: typeof import('~icons/lucide/loader')['default'] + ILucideLock: typeof import('~icons/lucide/lock')['default'] + ILucideLogIn: typeof import('~icons/lucide/log-in')['default'] ILucideLoOff: typeof import('~icons/lucide/lo-off')['default'] ILucideLucideDatabase: typeof import('~icons/lucide/lucide-database')['default'] ILucideMonitor: typeof import('~icons/lucide/monitor')['default'] @@ -47,17 +53,30 @@ declare module 'vue' { ILucidePlug: typeof import('~icons/lucide/plug')['default'] ILucidePlugOff: typeof import('~icons/lucide/plug-off')['default'] ILucideRadio: typeof import('~icons/lucide/radio')['default'] + ILucideRefreshCw: typeof import('~icons/lucide/refresh-cw')['default'] ILucideSave: typeof import('~icons/lucide/save')['default'] + ILucideShieldCheck: typeof import('~icons/lucide/shield-check')['default'] ILucideSquarePen: typeof import('~icons/lucide/square-pen')['default'] ILucideTrash2: typeof import('~icons/lucide/trash2')['default'] ILucideUnlink: typeof import('~icons/lucide/unlink')['default'] ILucideUnlinkOff: typeof import('~icons/lucide/unlink-off')['default'] + ILucideUser: typeof import('~icons/lucide/user')['default'] ILucideWifi: typeof import('~icons/lucide/wifi')['default'] ILucideX: typeof import('~icons/lucide/x')['default'] ILucideZap: typeof import('~icons/lucide/zap')['default'] - LoginModal: typeof import('./src/components/common/Modal/LoginModal.vue')['default'] - PackageAutomatically: typeof import('./src/components/package-automatically/index.vue')['default'] + MesModal: typeof import('./src/components/MesModal/index.vue')['default'] + MesStatus: typeof import('./src/components/MesStatus/index.vue')['default'] + NetworkModal: typeof import('./src/components/NetworkModal/index.vue')['default'] + NetworkStatus: typeof import('./src/components/NetworkStatus/index.vue')['default'] + PlcModal: typeof import('./src/components/PlcModal/index.vue')['default'] + PlcStatus: typeof import('./src/components/PlcStatus/index.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + Settings: typeof import('./src/components/Settings/index.vue')['default'] + SettingsModal: typeof import('./src/components/SettingsModal/index.vue')['default'] + SSELogs: typeof import('./src/components/SSELogs/index.vue')['default'] + SseModal: typeof import('./src/components/SseModal/index.vue')['default'] + SseStatus: typeof import('./src/components/SseStatus/index.vue')['default'] + XxxModal: typeof import('./src/components/xxxModal/index.vue')['default'] } } diff --git a/package-lock.json b/package-lock.json index 2c954e6..763f4c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "dependencies": { "ant-design-vue": "^4.2.6", "axios": "^1.10.0", - "js-cookie": "^3.0.5", "pinia": "^3.0.3", "unplugin-vue-components": "^28.7.0", "vue": "^3.5.13", @@ -18,9 +17,13 @@ }, "devDependencies": { "@iconify-json/lucide": "^1.2.66", + "@types/event-source-polyfill": "^1.0.5", + "@types/js-cookie": "^3.0.6", "@types/node": "^24.0.3", "@vitejs/plugin-vue": "^5.2.3", "@vue/tsconfig": "^0.7.0", + "event-source-polyfill": "^1.0.31", + "js-cookie": "^3.0.5", "sass-embedded": "^1.89.2", "typescript": "~5.8.3", "unplugin-icons": "^22.2.0", @@ -925,6 +928,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/event-source-polyfill": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/@types/event-source-polyfill/-/event-source-polyfill-1.0.5.tgz", + "integrity": "sha512-iaiDuDI2aIFft7XkcwMzDWLqo7LVDixd2sR6B4wxJut9xcp/Ev9bO4EFg4rm6S9QxATLBj5OPxdeocgmhjwKaw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.0.3", "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.0.3.tgz", @@ -1600,6 +1617,13 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, + "node_modules/event-source-polyfill": { + "version": "1.0.31", + "resolved": "https://registry.npmmirror.com/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz", + "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==", + "dev": true, + "license": "MIT" + }, "node_modules/exsolve": { "version": "1.0.7", "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.7.tgz", @@ -1902,6 +1926,7 @@ "version": "3.0.5", "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, "license": "MIT", "engines": { "node": ">=14" diff --git a/package.json b/package.json index c7051f5..990ab09 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "dependencies": { "ant-design-vue": "^4.2.6", "axios": "^1.10.0", - "js-cookie": "^3.0.5", "pinia": "^3.0.3", "unplugin-vue-components": "^28.7.0", "vue": "^3.5.13", @@ -19,9 +18,13 @@ }, "devDependencies": { "@iconify-json/lucide": "^1.2.66", + "@types/event-source-polyfill": "^1.0.5", + "@types/js-cookie": "^3.0.6", "@types/node": "^24.0.3", "@vitejs/plugin-vue": "^5.2.3", "@vue/tsconfig": "^0.7.0", + "event-source-polyfill": "^1.0.31", + "js-cookie": "^3.0.5", "sass-embedded": "^1.89.2", "typescript": "~5.8.3", "unplugin-icons": "^22.2.0", diff --git a/public/bg.jpg b/public/bg.jpg new file mode 100644 index 0000000..61de169 Binary files /dev/null and b/public/bg.jpg differ diff --git a/src/api/detect/index.ts b/src/api/detect/index.ts new file mode 100644 index 0000000..15e2c2a --- /dev/null +++ b/src/api/detect/index.ts @@ -0,0 +1,39 @@ +import request from '../request'; +import type { + CheckOrderNumber, + ProcessInfo, +} from './model'; + +// 验证工单、产品编码关系是否正确 +export function checkOrderNumberApi(data: CheckOrderNumber) { + return request({ + url: '/jinghua/mes/work-order/material/verify', + method: 'post', + data + }) +} + +// 新增加工信息 +export function addProcessInfoApi(data: ProcessInfo) { + return request({ + url: '/jinghua/processInfo', + method: 'post', + data + }) +} + +// 查询 L1 数据 +export function listL1DataApi() { + return request({ + url: '/jinghua/l1Data/list', + method: 'get', + }) +} + +// 查询 L4 数据 +export function listL4DataApi() { + return request({ + url: '/jinghua/l4Data/list', + method: 'get', + }) +} diff --git a/src/api/types/check.ts b/src/api/detect/model.d.ts similarity index 92% rename from src/api/types/check.ts rename to src/api/detect/model.d.ts index 62cf927..336cb3d 100644 --- a/src/api/types/check.ts +++ b/src/api/detect/model.d.ts @@ -1,7 +1,7 @@ // 验证工单-物料 export interface CheckOrderNumber { workOrderCode: string; - materialCode: string; + productCode: string; } // 加工信息 diff --git a/src/api/modules/system.ts b/src/api/lms/index.ts similarity index 88% rename from src/api/modules/system.ts rename to src/api/lms/index.ts index b19f85a..069e933 100644 --- a/src/api/modules/system.ts +++ b/src/api/lms/index.ts @@ -1,5 +1,5 @@ import request from '../request'; -import { type LmsWorkMode } from '../types'; +import type { LmsWorkMode } from './model'; // 获取 LMS 工作模式 export const fetchLmsWorkMode = () => { diff --git a/src/api/types/system.ts b/src/api/lms/model.d.ts similarity index 100% rename from src/api/types/system.ts rename to src/api/lms/model.d.ts diff --git a/src/api/modules/check.ts b/src/api/modules/check.ts deleted file mode 100644 index 84e1390..0000000 --- a/src/api/modules/check.ts +++ /dev/null @@ -1,23 +0,0 @@ -import request from '../request.ts'; -import { - type CheckOrderNumber, - type ProcessInfo, -} from '../types'; - -// 验证工单、产品编码关系是否正确 -export function checkOrderNumberApi(data: CheckOrderNumber) { - return request({ - url: '/jinghua/mes/work-order-material/verify', - method: 'post', - data - }) -} - -// 新增加工信息 -export function addProcessInfoApi(data: ProcessInfo) { - return request({ - url: '/jinghua/processInfo', - method: 'post', - data - }) -} diff --git a/src/api/modules/index.ts b/src/api/modules/index.ts deleted file mode 100644 index 90fa52d..0000000 --- a/src/api/modules/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './user'; -export * from './laser'; -export * from './station'; -export * from './check'; -export * from './system'; \ No newline at end of file diff --git a/src/api/modules/laser.ts b/src/api/modules/laser.ts deleted file mode 100644 index 768ff07..0000000 --- a/src/api/modules/laser.ts +++ /dev/null @@ -1,32 +0,0 @@ -import request from '../request.ts'; -import { - type LaserCarvingOrderSN, - type LaserResult -} from '../types'; - -// 根据工单、产品编码、拼版数量获取 SN 号码信息 -export function fetchSNApi(data: LaserCarvingOrderSN) { - return request({ - url: '/laser/carving/getOrderSN', - method: 'get', - data - }) -} - -// 测试项目结果上传 -export function uploadTestResultApi(data: string) { - return request({ - url: '/laser/carving/uploadResult', - method: 'post', - data - }) -} - -// 根据工单号、产品编码、接收镭雕结果,更改 SN 状态 -export function updateSNStatusApi(data: LaserResult) { - return request({ - url: '/laser/carving/updateSNStatus', - method: 'put', - data - }) -} \ No newline at end of file diff --git a/src/api/modules/station.ts b/src/api/modules/station.ts deleted file mode 100644 index 1139416..0000000 --- a/src/api/modules/station.ts +++ /dev/null @@ -1,19 +0,0 @@ -import request from '../request.ts'; - -// 工序过站 -export function operationStationApi(data: string) { - return request({ - url: '/operationStation', - method: 'post', - data - }) -} - -// 包装过站 -export function packageStationApi(data: string) { - return request({ - url: '/packageStation', - method: 'post', - data - }) -} \ No newline at end of file diff --git a/src/api/modules/user.ts b/src/api/modules/user.ts deleted file mode 100644 index a19eec1..0000000 --- a/src/api/modules/user.ts +++ /dev/null @@ -1,41 +0,0 @@ -import request from '../request' - -export interface LoginInfo { - username: string - password: string -} - -// 用户登录 -export const login = (data: LoginInfo) => { - return request({ - url: '/user/login', - method: 'post', - data - }) -} - -// 获取用户信息 -export const getUserInfo = (data: any) => { - return request({ - url: '/user/info', - method: 'get', - data - }) -} - -// 更新用户信息 -export const updateUser = (id: string, data: any) => { - return request({ - url: `/user/${id}`, - method: 'put', - data - }) -} - -// 删除用户 -export const deleteUser = (id: string) => { - return request({ - url: `/user/${id}`, - method: 'delete' - }) -} \ No newline at end of file diff --git a/src/api/request.ts b/src/api/request.ts index dc498bc..33aec82 100644 --- a/src/api/request.ts +++ b/src/api/request.ts @@ -1,12 +1,9 @@ import axios from 'axios'; -import { notification } from 'ant-design-vue'; +import { Modal, notification } from 'ant-design-vue'; +import router from '@/router'; +import { getToken } from '@/utils/auth'; -interface ErrCodeMap { - [key: string]: string; -} - -const errCodeMap: ErrCodeMap = { - '401': '认证失败,无法访问系统资源', +const errCodeMap: { [key: string]: string } = { '403': '当前操作没有权限', '404': '访问资源不存在', 'default': '系统未知错误,请反馈给管理员' @@ -23,8 +20,8 @@ service.interceptors.request.use( config => { config.headers = config.headers || {}; config.headers['Accept-Language'] = 'zh-CN'; - // 可选:添加token - const token = localStorage.getItem('token'); + + const token = getToken(); if (token) { config.headers['Authorization'] = `Bearer ${token}`; } @@ -38,21 +35,28 @@ service.interceptors.request.use( // 响应拦截器 service.interceptors.response.use( response => { - // 统一处理后端自定义结构 { code, msg, data } - const res = response.data; - if (res.code === 200) { - // 成功,返回data字段 - return res.data; + console.log(response) + // 未设置状态码则默认成功状态 + const code = response.data.code || 200; + + if (code === 200) { + return response.data ?? response; + } else if (code === 401) { + Modal.error({ + title: '系统提示', + content: '登录状态已过期,请重新登录', + onOk: () => { + router.push('/login') + } + }) } else { - // 优先使用本地错误码映射 - const codeStr = String(res.code); - const errMsg = errCodeMap[codeStr] || res.msg || errCodeMap['default']; + const codeStr = String(code); + const errMsg = errCodeMap[codeStr] || response.data.msg || errCodeMap['default']; notification.error({ message: '请求错误', description: errMsg, }); - // 可在此处对401等特殊code做处理(如跳转登录) - return Promise.reject(res); + return Promise.reject(response.data); } }, error => { diff --git a/src/api/system/index.ts b/src/api/system/index.ts new file mode 100644 index 0000000..62e3dc1 --- /dev/null +++ b/src/api/system/index.ts @@ -0,0 +1,19 @@ +import request from '../request' +import type { LoginInfo } from './model'; + +// 用户登录 +export function login(data: LoginInfo) { + return request({ + url: '/login', + method: 'post', + data + }) +} + +// 获取验证码 +export function getCaptcha() { + return request({ + url: '/captchaImage', + method: 'get' + }) +} \ No newline at end of file diff --git a/src/api/system/model.d.ts b/src/api/system/model.d.ts new file mode 100644 index 0000000..06ded72 --- /dev/null +++ b/src/api/system/model.d.ts @@ -0,0 +1,6 @@ +export interface LoginInfo { + username: string + password: string + uuid: string + code: string +} \ No newline at end of file diff --git a/src/api/types/index.ts b/src/api/types/index.ts deleted file mode 100644 index c2ae317..0000000 --- a/src/api/types/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './laser'; -export * from './station'; -export * from './check'; -export * from './system'; \ No newline at end of file diff --git a/src/api/types/laser.ts b/src/api/types/laser.ts deleted file mode 100644 index 599b708..0000000 --- a/src/api/types/laser.ts +++ /dev/null @@ -1,42 +0,0 @@ -// 获取 SN 号所需参数 -export interface LaserCarvingOrderSN { - orderNumber: string; - itemCode: string; - qty: number; -} - -// 测试项目上传所需参数 -export interface commandStringItem { - number: '04'; - // 工号 - empNo: string; - // SN 号 - snNo: string; - // 工序名称 - operationName: string; - // 资源名称 - resName: string; - // 设备编码 - machineNo: string; - // 工治具编码 - fixtureCode: string; - // 测试开始时间 - testStartTime: string; - // 检测项(检测项名称:结果值) - testItem1: string; - testItem2?: string; - testItem3?: string; - testItem4?: string; - testItem5?: string; - testItem6?: string; - testItem7?: string; - // 测试次数 - testTimes: number; -} - -// 上传镭雕结果所需参数,result 代表镭雕结果,例如:{"SN001":"OK} -export interface LaserResult { - orderNumber: string; - itemCode: string; - result: any -} \ No newline at end of file diff --git a/src/api/types/station.ts b/src/api/types/station.ts deleted file mode 100644 index a54a680..0000000 --- a/src/api/types/station.ts +++ /dev/null @@ -1,39 +0,0 @@ -// 工序过站 -export interface operationCommandStringItem { - number: '05'; - // 工号 - empNo: string; - // SN 号 - snNo: string; - // 工序名称 - operationName: string; - // 资源名称 - resName: string; - // 设备编码 - machineNo: string | null; - // 工治具编码 - fixtureCode: string | null; - // 测试结果 - testResult: 'OK' | 'NG'; - // 不良原因 - reason?: string; -} - -// 包装过站 -export interface packageCommandStringItem { - number: '070'; - // 工号 - empNo: string; - // SN 号 - snNo: string | string[]; - // 工序名称 - operationName: string; - // 资源名称 - resName: string; - // 设备编码 - machineNo: string | null; - // 工治具编码 - fixtureCode: string | null; - // 装箱结果 - packageResult: 'OK'; -} \ No newline at end of file diff --git a/src/assets/styles/_variables.scss b/src/assets/styles/_variables.scss index d8f5956..3474d53 100644 --- a/src/assets/styles/_variables.scss +++ b/src/assets/styles/_variables.scss @@ -1,4 +1,110 @@ $title-font-size: clamp(1.5rem, 2vw, 2.4rem); $content-font-size: clamp(1rem, 1.5vw, 1.2rem); -$button-font-size: clamp(1rem, 1.5vw, 1.5rem); $log-font-size: 1.1rem; + + +$primary-color: #4a90e2; +$primary-light: #5ba0f2; +$primary-dark: #3a7bd5; +$success-color: #52c41a; +$success-light: #73d13d; +$success-bg: rgba(82, 196, 26, 0.2); +$success-bg-hover: rgba(82, 196, 26, 0.4); +$warning-color: #faad14; +$warning-light: #ffc53d; +$warning-bg: rgba(250, 173, 20, 0.2); +$warning-bg-hover: rgba(250, 173, 20, 0.4); +$error-color: #ff4d4f; +$error-light: #ff7875; +$error-bg: rgba(255, 77, 79, 0.2); +$error-bg-hover: rgba(255, 77, 79, 0.4); + +$white: #ffffff; +$text-size: 14px; +$text-light: #b8d4f0; +$text-dark: #333333; +$text-gray: #cccccc; +$text-success: #b7eb8f; +$text-warning: #ffd666; +$text-error: #ffccc7; + +$bg-primary: rgba(74, 144, 226, 0.2); +$bg-primary-hover: rgba(74, 144, 226, 0.4); +$bg-overlay: rgba(255, 255, 255, 0.1); +$bg-overlay-hover: rgba(255, 255, 255, 0.2); +$bg-light: rgba(255, 255, 255, 0.1); +$bg-light-hover: rgba(255, 255, 255, 0.2); +$bg-dark: rgba(0, 0, 0, 0.3); +$bg-input: rgba(255, 255, 255, 0.1); +$bg-input-focus: rgba(255, 255, 255, 0.15); + +$border-primary: 1px solid $primary-color; +$border-light: 1px solid rgba(255, 255, 255, 0.3); +$border-light-hover: 1px solid rgba(255, 255, 255, 0.5); +$border-transparent: 1px solid transparent; + +$spacing-xs: 4px; +$spacing-sm: 8px; +$spacing-md: 12px; +$spacing-lg: 15px; +$spacing-xl: 20px; +$spacing-xxl: 30px; + +$border-radius: 4px; +$border-radius-lg: 8px; + +$transition: all 0.3s ease; +$font-family: 'Microsoft YaHei', sans-serif; + +// 混合器定义 +@mixin button-base { + border-radius: $border-radius; + cursor: pointer; + transition: $transition; + border: $border-transparent; + font-weight: 500; + white-space: nowrap; + line-height: 21px; +} + +@mixin button-hover($bg-color, $border-color, $text-color) { + &:hover { + background: $bg-color; + border-color: $border-color; + color: $text-color; + } +} + +@mixin status-button($bg, $bg-hover, $border, $border-hover, $text, $text-hover) { + background: $bg; + border-color: $border; + color: $text; + + &:hover { + background: $bg-hover; + border-color: $border-hover; + color: $text-hover; + box-shadow: 0 0 12px rgba($border, 0.5); + } +} + +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin flex-between { + display: flex; + justify-content: space-between; + align-items: center; +} + +@mixin transition-ease { + transition: all 0.3s ease; +} + +@mixin icon-base($size) { + width: $size; + height: $size; +} \ No newline at end of file diff --git a/src/components/CameraStatus/index.vue b/src/components/CameraStatus/index.vue new file mode 100644 index 0000000..a8f94e1 --- /dev/null +++ b/src/components/CameraStatus/index.vue @@ -0,0 +1,108 @@ + + + + + \ No newline at end of file diff --git a/src/components/MesStatus/index.vue b/src/components/MesStatus/index.vue new file mode 100644 index 0000000..ab25e98 --- /dev/null +++ b/src/components/MesStatus/index.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file diff --git a/src/components/NetworkStatus/index.vue b/src/components/NetworkStatus/index.vue new file mode 100644 index 0000000..f5728c6 --- /dev/null +++ b/src/components/NetworkStatus/index.vue @@ -0,0 +1,121 @@ + + + + + \ No newline at end of file diff --git a/src/components/PlcStatus/index.vue b/src/components/PlcStatus/index.vue new file mode 100644 index 0000000..19d5db0 --- /dev/null +++ b/src/components/PlcStatus/index.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file diff --git a/src/components/SSELogs/index.vue b/src/components/SSELogs/index.vue new file mode 100644 index 0000000..9edcdc9 --- /dev/null +++ b/src/components/SSELogs/index.vue @@ -0,0 +1,327 @@ + + + + + \ No newline at end of file diff --git a/src/components/SettingsModal/index.vue b/src/components/SettingsModal/index.vue new file mode 100644 index 0000000..f1928d7 --- /dev/null +++ b/src/components/SettingsModal/index.vue @@ -0,0 +1,76 @@ + + + + + \ No newline at end of file diff --git a/src/components/SseStatus/index.vue b/src/components/SseStatus/index.vue new file mode 100644 index 0000000..504d6f7 --- /dev/null +++ b/src/components/SseStatus/index.vue @@ -0,0 +1,360 @@ + + + + + diff --git a/src/components/common/ActionButtons/index.vue b/src/components/common/ActionButtons/index.vue new file mode 100644 index 0000000..3b9de78 --- /dev/null +++ b/src/components/common/ActionButtons/index.vue @@ -0,0 +1,394 @@ + + + + + \ No newline at end of file diff --git a/src/components/common/ExecutionResult/ExecutionResult.vue b/src/components/common/ExecutionResult/index.vue similarity index 74% rename from src/components/common/ExecutionResult/ExecutionResult.vue rename to src/components/common/ExecutionResult/index.vue index 0c219d0..af65d62 100644 --- a/src/components/common/ExecutionResult/ExecutionResult.vue +++ b/src/components/common/ExecutionResult/index.vue @@ -1,19 +1,19 @@ \ No newline at end of file diff --git a/src/locales/zh-CN/L4Data.json b/src/locales/zh-CN/L4Data.json new file mode 100644 index 0000000..b801378 --- /dev/null +++ b/src/locales/zh-CN/L4Data.json @@ -0,0 +1,37 @@ +{ + "id": "主键", + "processInfoId": "加工信息ID", + "pltNo": "PLT No", + "processF1": "加工F1", + "processF2": "加工F2", + "goodProductF1": "良品F1", + "goodProductF2": "良品F2", + "electricalResult": "电气检测结果", + "engraveResult": "印字检测结果", + "qrCode": "二维码", + "qrCodeLevel": "二维码等级", + "pressure15Riveting": "压力1_5#_铆接", + "height15Riveting": "高度1_5#_铆接", + "pressure25Magnet1": "压力2_5#_磁石1", + "height25Magnet1": "高度2_5#_磁石1", + "pressure36Magnet2": "压力3_6#_磁石2", + "height36Magnet2": "高度3_6#_磁石2", + "torque47AxisInsert": "扭矩4_7#_轴旋入", + "height47AxisInsert": "高度4_7#_轴旋入", + "pressure58LowerCase": "压力5_8#_下壳装入", + "height58LowerCase": "高度5_8#_下壳装入", + "pressure69UpperCase": "压力6_9#_上壳装入", + "height69UpperCase": "高度6_9#_上壳装入", + "height79HeightCheck": "高度7_9#_高度检测", + "pressure79Laser": "压力7_9#_激光", + "height89Laser": "高度8_9#_激光", + "value19DcrUpper": "数値1_9#_DCR(上)", + "value29DcrLower": "数値2_9#_DCR(下)", + "value39LcrUpperLs": "数値3_9#_LCR(上)LS", + "value49LcrLowerQ": "数値4_9#_LCR(下)Q", + "value59LcrLowerLs": "数値5_9#_LCR(下)LS", + "value69LcrLowerQ": "数値6_9#_LCR(下)Q", + "value79IrR": "数値7_9#_IR R", + "value89IrI": "数値8_9#_IR I", + "createTime": "创建时间" +} diff --git a/src/router/index.ts b/src/router/index.ts index 79186cf..12fa9f7 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -3,23 +3,18 @@ import { createRouter, createWebHistory } from 'vue-router'; const routes = [ { path: '/', - name: 'IndexV2', - component: () => import('@/views/index_v2.vue') - }, - { - path: '/index', name: 'Index', component: () => import('@/views/index.vue') }, { - path: '/package-station', - name: 'PackageStation', - component: () => import('@/views/PackageStation.vue') + path: '/login', + name: 'Login', + component: () => import('@/views/login.vue') }, { - path: '/sse-test', - name: 'SSETest', - component: () => import('@/views/sse/sse-test.vue') + path: '/package-station', + name: 'PackageStation', + component: () => import('@/views/package-station/index.vue') } ]; diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 0000000..1ddb068 --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie'; + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token: string) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/src/utils/dateUtils.ts b/src/utils/dateUtils.ts index 5102736..2e6552e 100644 --- a/src/utils/dateUtils.ts +++ b/src/utils/dateUtils.ts @@ -1,6 +1,4 @@ -/** - * 时间格式化工具函数 - */ +import { ref, onMounted, onBeforeUnmount } from 'vue'; /** * 格式化日期时间 @@ -40,8 +38,6 @@ export function getCurrentTime(format = 'YYYY-MM-DD HH:mm:ss'): string { return formatDateTime(new Date(), format); } -import { ref, onMounted, onBeforeUnmount } from 'vue'; - /** * 获取实时时间(用于显示) * @param format 格式字符串 diff --git a/src/utils/useSSE.ts b/src/utils/useSSE.ts index 02b7895..92bad13 100644 --- a/src/utils/useSSE.ts +++ b/src/utils/useSSE.ts @@ -1,17 +1,27 @@ -import { ref, computed, onMounted, onUnmounted } from 'vue'; +import { ref, computed } from 'vue'; +import { EventSourcePolyfill } from 'event-source-polyfill'; import { generateUUID } from './uuidUtils'; +import { getToken } from '@/utils/auth'; -const defaultServerUrl = 'http://192.168.1.100:18081/sse'; +const defaultServerUrl = 'http://192.168.1.38:18081/sse'; + +interface EventData { + timestamp: string; + message: string; + data: any; + code: 200 | 201 | 300 | 400 | 500; +} + +interface logItem { + message: string; + type?: "success" | "error" | "log"; +} export interface SSEOptions { /** SSE服务器地址 */ serverUrl?: string; - /** 自动连接 */ - autoConnect?: boolean; /** 连接超时时间(毫秒) */ timeout?: number; - /** 是否使用凭证 */ - withCredentials?: boolean; /** 客户端ID,不传则自动生成 */ clientId?: string; /** 消息处理函数 */ @@ -20,10 +30,12 @@ export interface SSEOptions { onConnect?: () => void; /** 连接错误回调 */ onError?: (error: Event | Error) => void; - /** 是否在组件挂载时自动连接 */ - connectOnMount?: boolean; - /** 是否在组件卸载时自动断开连接 */ - disconnectOnUnmount?: boolean; + /** L1事件处理回调 */ + onL1Event?: (data: EventData) => void; + /** L4事件处理回调 */ + onL4Event?: (data: EventData) => void; + /** MES事件处理回调 */ + onMesEvent?: (data: EventData) => void; } function addSSEListener(eventSource: EventSource, type: string, handler: (data: any, event: MessageEvent) => void) { @@ -42,32 +54,16 @@ function addSSEListener(eventSource: EventSource, type: string, handler: (data: * @param options SSE连接选项 */ export function useSSE(options: SSEOptions) { - // 默认选项 - const defaultOptions: SSEOptions = { - serverUrl: defaultServerUrl, - autoConnect: true, - timeout: 10000, - withCredentials: false, - connectOnMount: true, - disconnectOnUnmount: true, - clientId: generateUUID() - }; - - // 合并选项 - const mergedOptions = { ...defaultOptions, ...options }; // 状态变量 - const logs = ref([]); + const logs = ref([]); const isConnecting = ref(false); const isConnected = ref(false); - const clientId = ref(mergedOptions.clientId || generateUUID()); - const serverUrl = ref(mergedOptions.serverUrl); - const isInCooldown = ref(false); // 连接冷却状态 - const cooldownRemaining = ref(0); // 剩余冷却时间 + const clientId = ref(options.clientId || generateUUID()); + const serverUrl = ref(options.serverUrl || defaultServerUrl); // 存储EventSource实例 let eventSource: EventSource | null = null; let connectionTimeout: number | null = null; - let cooldownTimer: number | null = null; // 计算属性 const sseStatusText = computed(() => { @@ -82,32 +78,16 @@ export function useSSE(options: SSEOptions) { return 'disconnected error'; }); - // 构建包含clientId的连接URL - const getConnectUrl = () => { - return `${serverUrl.value}/connect`; - }; - // 添加日志 - const addLog = (message: string) => { + const addLog = (message: string, type: 'success' | 'error' | 'log' = 'log') => { const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false }); const logEntry = `${timestamp} - ${message}`; - logs.value.push(logEntry); + logs.value.push({ message: logEntry, type }); return logEntry; }; - // 清空日志 - const clearLogs = () => { - logs.value = []; - }; - // 连接SSE - const connect = () => { - // 检查是否在冷却期间 - if (isInCooldown.value) { - addLog(`连接冷却中,请等待 ${cooldownRemaining.value} 秒后重试`); - return; - } - + const connect = () => { // 先断开可能存在的连接 if (eventSource) { disconnect(); @@ -121,7 +101,7 @@ export function useSSE(options: SSEOptions) { throw new Error('SSE服务器地址不能为空'); } - const connectUrl = getConnectUrl(); + const connectUrl = `${serverUrl.value}/connect`; addLog(`开始连接到 ${connectUrl}...`); // 设置连接超时 @@ -131,7 +111,7 @@ export function useSSE(options: SSEOptions) { connectionTimeout = window.setTimeout(() => { if (isConnecting.value) { - addLog('连接超时,请检查服务器地址或网络连接'); + addLog('连接超时,请检查服务器地址或网络连接', 'error'); isConnecting.value = false; if (eventSource) { eventSource.close(); @@ -139,15 +119,19 @@ export function useSSE(options: SSEOptions) { } // 调用错误回调 - if (mergedOptions.onError) { - mergedOptions.onError(new Error('连接超时')); + if (options.onError) { + options.onError(new Error('连接超时')); } } - }, mergedOptions.timeout || 10000); // 默认10秒超时 + }, options.timeout || 10000); // 默认10秒超时 // 创建EventSource - const eventSourceOptions = { withCredentials: mergedOptions.withCredentials || false }; - eventSource = new EventSource(connectUrl, eventSourceOptions); + const eventSourceOptions = { + headers: { + 'Authorization': `Bearer ${getToken()}`, + } + }; + eventSource = new EventSourcePolyfill(connectUrl, eventSourceOptions); eventSource.onopen = () => { if (connectionTimeout) { @@ -157,27 +141,42 @@ export function useSSE(options: SSEOptions) { isConnected.value = true; isConnecting.value = false; - addLog('SSE 连接已建立'); + addLog('SSE 连接已建立', 'success'); // 调用连接成功回调 - if (mergedOptions.onConnect) { - mergedOptions.onConnect(); + if (options.onConnect) { + options.onConnect(); } }; eventSource.onmessage = (event) => { - addLog(`收到消息: ${JSON.parse(event.data).message}`); + addLog(`收到消息: ${ JSON.parse(event.data).message }`); console.log(event) // 调用消息处理回调 - if (mergedOptions.onMessage) { - mergedOptions.onMessage(event.data); + if (options.onMessage) { + options.onMessage(event.data); } }; - // 改为以下格式 + // 添加事件监听器 + addSSEListener(eventSource, 'L1_EVENT', (data) => { + console.log('收到 L1_EVENT:', data); + if (options.onL1Event) { + options.onL1Event(data); + } + }); addSSEListener(eventSource, 'L4_EVENT', (data) => { - console.log('收到 L4_EVENT:', data); + console.log('收到 L4_EVENT:', data); + if (options.onL4Event) { + options.onL4Event(data); + } + }); + addSSEListener(eventSource, 'MES_EVENT', (data) => { + console.log('收到 MES_EVENT:', data); + if (options.onMesEvent) { + options.onMesEvent(data); + } }); eventSource.onerror = (err) => { @@ -186,7 +185,7 @@ export function useSSE(options: SSEOptions) { connectionTimeout = null; } - addLog('SSE 连接错误,请检查服务器地址或网络连接'); + addLog('SSE 连接错误,请检查服务器地址或网络连接', 'error'); isConnected.value = false; isConnecting.value = false; @@ -196,17 +195,17 @@ export function useSSE(options: SSEOptions) { } // 调用错误回调 - if (mergedOptions.onError) { - mergedOptions.onError(err); + if (options.onError) { + options.onError(err); } }; } catch (error) { - addLog(`连接失败: ${error}`); + addLog(`连接失败: ${error}`, 'error'); isConnecting.value = false; // 调用错误回调 - if (mergedOptions.onError && error instanceof Error) { - mergedOptions.onError(error); + if (options.onError && error instanceof Error) { + options.onError(error); } } }; @@ -223,89 +222,11 @@ export function useSSE(options: SSEOptions) { eventSource = null; } - addLog('已断开连接'); + addLog('已断开连接', 'log'); isConnected.value = false; - - // 启动5秒冷却时间 - startCooldown(); }; - // 启动连接冷却时间 - const startCooldown = () => { - isInCooldown.value = true; - cooldownRemaining.value = 5; - - const updateCooldown = () => { - if (cooldownRemaining.value > 0) { - cooldownRemaining.value--; - cooldownTimer = window.setTimeout(updateCooldown, 1000); - } else { - isInCooldown.value = false; - cooldownTimer = null; - } - }; - - updateCooldown(); - }; - - // 生命周期钩子 - onMounted(() => { - if (mergedOptions.connectOnMount && mergedOptions.autoConnect) { - connect(); - } - }); - - onUnmounted(() => { - if (mergedOptions.disconnectOnUnmount) { - disconnect(); - } - }); - - // 清理函数(兼容旧版本) - const cleanup = () => { - if (connectionTimeout) { - clearTimeout(connectionTimeout); - connectionTimeout = null; - } - - if (cooldownTimer) { - clearTimeout(cooldownTimer); - cooldownTimer = null; - } - - if (eventSource) { - eventSource.close(); - eventSource = null; - } - - isConnected.value = false; - isConnecting.value = false; - isInCooldown.value = false; - cooldownRemaining.value = 0; - }; - - // 消息分类处理函数 - const processMessage = (data: string) => { - try { - // 尝试解析JSON消息 - const message = JSON.parse(data); - // 根据消息类型分类 - return { - type: message.type || 'system', - content: message.content || data, - raw: data, - timestamp: new Date().toLocaleTimeString('zh-CN', { hour12: false }) - }; - } catch (error) { - // 非JSON格式消息,视为系统消息 - return { - type: 'system', - content: data, - raw: data, - timestamp: new Date().toLocaleTimeString('zh-CN', { hour12: false }) - }; - } - }; + return { logs, @@ -315,13 +236,8 @@ export function useSSE(options: SSEOptions) { serverUrl, sseStatusText, sseStatusClass, - isInCooldown, - cooldownRemaining, connect, disconnect, - addLog, // SSE内部日志记录 - clearLogs, - cleanup, - processMessage // 消息分类处理 + addLog }; } \ No newline at end of file diff --git a/src/views/L1-data-list/index.vue b/src/views/L1-data-list/index.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/views/L4-data-list/index.vue b/src/views/L4-data-list/index.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/views/index.vue b/src/views/index.vue new file mode 100644 index 0000000..39d3d92 --- /dev/null +++ b/src/views/index.vue @@ -0,0 +1,975 @@ + + + + + \ No newline at end of file diff --git a/src/views/login.vue b/src/views/login.vue new file mode 100644 index 0000000..f668a2d --- /dev/null +++ b/src/views/login.vue @@ -0,0 +1,527 @@ + + + + + \ No newline at end of file diff --git a/src/views/package-station/index.vue b/src/views/package-station/index.vue new file mode 100644 index 0000000..97a2d56 --- /dev/null +++ b/src/views/package-station/index.vue @@ -0,0 +1,684 @@ + + + + + \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 38269d9..18143d3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -36,7 +36,7 @@ export default defineConfig({ server: { proxy: { '/api': { - target: 'http://localhost:3000', + target: 'http://192.168.1.38:18081', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') }