配置 eslint 与 prettier
This commit is contained in:
5
.prettierignore
Normal file
5
.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 忽略打包输出目录
|
||||||
|
dist
|
||||||
|
|
||||||
|
# 忽略依赖目录
|
||||||
|
node_modules
|
||||||
25
.prettierrc
Normal file
25
.prettierrc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"arrowParens": "always",
|
||||||
|
"bracketSameLine": false,
|
||||||
|
"objectWrap": "preserve",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"semi": false,
|
||||||
|
"experimentalOperatorPosition": "end",
|
||||||
|
"experimentalTernaries": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"quoteProps": "preserve",
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleAttributePerLine": false,
|
||||||
|
"htmlWhitespaceSensitivity": "ignore",
|
||||||
|
"vueIndentScriptAndStyle": false,
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"insertPragma": false,
|
||||||
|
"printWidth": 80,
|
||||||
|
"requirePragma": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": true,
|
||||||
|
"embeddedLanguageFormatting": "auto",
|
||||||
|
"cursorOffset": -1
|
||||||
|
}
|
||||||
3
commitlint.config.js
Normal file
3
commitlint.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
}
|
||||||
53
eslint.config.mjs
Normal file
53
eslint.config.mjs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* ESLint Flat 配置(ESLint 9)
|
||||||
|
* 适用于:Vue 3 + TypeScript + Prettier
|
||||||
|
*/
|
||||||
|
import pluginVue from 'eslint-plugin-vue' // Vue 官方 ESLint 插件
|
||||||
|
import {
|
||||||
|
defineConfigWithVueTs, // 用于组合 Vue + TS 的 Flat 配置
|
||||||
|
vueTsConfigs, // 官方提供的 TypeScript 推荐规则集合
|
||||||
|
} from '@vue/eslint-config-typescript'
|
||||||
|
import prettierPlugin from 'eslint-plugin-prettier' // 将 Prettier 作为 ESLint 规则使用
|
||||||
|
import prettierConfig from 'eslint-config-prettier' // 关闭与 Prettier 冲突的 ESLint 规则
|
||||||
|
|
||||||
|
export default defineConfigWithVueTs(
|
||||||
|
// 1. 全局忽略配置(必须放在最前面)
|
||||||
|
{
|
||||||
|
// 不对构建产物和依赖目录进行 ESLint 校验
|
||||||
|
ignores: ['dist/**', 'node_modules/**'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2. Vue 相关基础规则(包含模板语法支持)
|
||||||
|
pluginVue.configs['flat/essential'],
|
||||||
|
|
||||||
|
// 3. TypeScript 推荐规则(脚本逻辑部分)
|
||||||
|
vueTsConfigs.recommended,
|
||||||
|
|
||||||
|
// 4. Prettier 相关配置:只负责格式化,不干扰语义规则
|
||||||
|
{
|
||||||
|
// 注册需要用到的插件
|
||||||
|
plugins: {
|
||||||
|
prettier: prettierPlugin, // Prettier 插件
|
||||||
|
},
|
||||||
|
|
||||||
|
// 自定义规则
|
||||||
|
rules: {
|
||||||
|
// 关闭与 Prettier 冲突的 ESLint 规则
|
||||||
|
...prettierConfig.rules,
|
||||||
|
|
||||||
|
// 将 Prettier 的格式化结果作为 ESLint 的告警输出
|
||||||
|
'prettier/prettier': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5. 自定义规则
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
// Vue 规则
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
|
||||||
|
// TypeScript 规则
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
6
global.d.ts
vendored
6
global.d.ts
vendored
@@ -1,4 +1,4 @@
|
|||||||
// declare module '@/*';
|
// declare module '@/*';
|
||||||
declare module '@/components/*';
|
declare module '@/components/*'
|
||||||
declare module '@/views/*';
|
declare module '@/views/*'
|
||||||
declare module '@/api/*';
|
declare module '@/api/*'
|
||||||
|
|||||||
10
lint-staged.config.cjs
Normal file
10
lint-staged.config.cjs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module.exports = {
|
||||||
|
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
|
||||||
|
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
||||||
|
'prettier --write--parser json',
|
||||||
|
],
|
||||||
|
'package.json': ['prettier --write'],
|
||||||
|
'*.vue': ['eslint --fix', 'prettier --write', 'stylelint --fix'],
|
||||||
|
'*.{scss,less,style,html}': ['stylelint --fix', 'prettier --write'],
|
||||||
|
'*.md': ['prettier --write'],
|
||||||
|
}
|
||||||
2248
package-lock.json
generated
2248
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@@ -6,7 +6,10 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host --open",
|
"dev": "vite --host --open",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint \"src/**/*.{ts,vue}\"",
|
||||||
|
"lint:fix": "eslint \"src/**/*.{ts,vue}\" --fix",
|
||||||
|
"format": "prettier --write \"src/**/*.{ts,vue,js,json,css,md}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/lodash": "^4.17.21",
|
"@types/lodash": "^4.17.21",
|
||||||
@@ -24,14 +27,29 @@
|
|||||||
"@types/event-source-polyfill": "^1.0.5",
|
"@types/event-source-polyfill": "^1.0.5",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^24.0.3",
|
"@types/node": "^24.0.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
||||||
|
"@typescript-eslint/parser": "^8.53.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.3",
|
"@vitejs/plugin-vue": "^5.2.3",
|
||||||
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/tsconfig": "^0.7.0",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"eslint": "^9.39.2",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
|
"eslint-plugin-vue": "^10.6.2",
|
||||||
"event-source-polyfill": "^1.0.31",
|
"event-source-polyfill": "^1.0.31",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
|
"lint-staged": "^16.2.7",
|
||||||
|
"prettier": "^3.7.4",
|
||||||
"sass-embedded": "^1.89.2",
|
"sass-embedded": "^1.89.2",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"unplugin-icons": "^22.2.0",
|
"unplugin-icons": "^22.2.0",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
"vue-tsc": "^2.2.8"
|
"vue-tsc": "^2.2.8"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,js,vue}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/App.vue
14
src/App.vue
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-config-provider :locale="zhCN">
|
<a-config-provider :locale="zhCN">
|
||||||
<router-view />
|
<router-view />
|
||||||
</a-config-provider>
|
</a-config-provider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||||
import 'dayjs/locale/zh-cn';
|
import 'dayjs/locale/zh-cn'
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
40
src/api/common.d.ts
vendored
40
src/api/common.d.ts
vendored
@@ -1,11 +1,11 @@
|
|||||||
export type ID = number | string;
|
export type ID = number | string
|
||||||
export type IDS = (number | string)[];
|
export type IDS = (number | string)[]
|
||||||
|
|
||||||
export interface BaseEntity {
|
export interface BaseEntity {
|
||||||
createBy?: string;
|
createBy?: string
|
||||||
createTime?: string;
|
createTime?: string
|
||||||
updateBy?: string;
|
updateBy?: string
|
||||||
updateTime?: string;
|
updateTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,24 +16,24 @@ export interface BaseEntity {
|
|||||||
* @param isAsc 是否升序
|
* @param isAsc 是否升序
|
||||||
*/
|
*/
|
||||||
export interface PageQuery {
|
export interface PageQuery {
|
||||||
isAsc?: string;
|
isAsc?: string
|
||||||
orderByColumn?: string;
|
orderByColumn?: string
|
||||||
pageNum?: number;
|
pageNum?: number
|
||||||
pageSize?: number;
|
pageSize?: number
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiResponse<T = any> {
|
export interface ApiResponse<T = any> {
|
||||||
code?: number;
|
code?: number
|
||||||
msg?: string;
|
msg?: string
|
||||||
|
|
||||||
data?: T;
|
data?: T
|
||||||
rows: T[];
|
rows: T[]
|
||||||
total?: number;
|
total?: number
|
||||||
|
|
||||||
token?: string;
|
token?: string
|
||||||
|
|
||||||
img?: string;
|
img?: string
|
||||||
uuid?: string;
|
uuid?: string
|
||||||
captchaOnOff?: boolean;
|
captchaOnOff?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +1,93 @@
|
|||||||
import axios from "axios";
|
import axios from 'axios'
|
||||||
import router from "@/router";
|
import router from '@/router'
|
||||||
import { getToken, removeToken } from "@/utils/auth";
|
import { getToken, removeToken } from '@/utils/auth'
|
||||||
import { Modal, notification } from "ant-design-vue";
|
import { Modal, notification } from 'ant-design-vue'
|
||||||
import type { AxiosInstance, AxiosRequestConfig } from "axios";
|
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
|
||||||
import type { ApiResponse } from "@/api/common";
|
import type { ApiResponse } from '@/api/common'
|
||||||
|
|
||||||
const errCodeMap: { [key: string]: string } = {
|
const errCodeMap: { [key: string]: string } = {
|
||||||
"403": "当前操作没有权限",
|
'403': '当前操作没有权限',
|
||||||
"404": "访问资源不存在",
|
'404': '访问资源不存在',
|
||||||
default: "系统未知错误,请反馈给管理员",
|
default: '系统未知错误,请反馈给管理员',
|
||||||
};
|
}
|
||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service: AxiosInstance = axios.create({
|
const service: AxiosInstance = axios.create({
|
||||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
});
|
})
|
||||||
|
|
||||||
// 请求拦截器
|
// 请求拦截器
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
config.headers = config.headers || {};
|
config.headers = config.headers || {}
|
||||||
config.headers["Accept-Language"] = "zh-CN";
|
config.headers['Accept-Language'] = 'zh-CN'
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken()
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers["Authorization"] = `Bearer ${token}`;
|
config.headers['Authorization'] = `Bearer ${token}`
|
||||||
}
|
}
|
||||||
return config;
|
return config
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error)
|
||||||
}
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
// 响应拦截器
|
// 响应拦截器
|
||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
(response) => {
|
(response) => {
|
||||||
if (response.config.responseType === "blob") {
|
if (response.config.responseType === 'blob') {
|
||||||
return response.data;
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = response.data.code || 200;
|
const code = response.data.code || 200
|
||||||
const data = response.data;
|
const data = response.data
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 200:
|
case 200:
|
||||||
return data;
|
return data
|
||||||
case 401:
|
case 401:
|
||||||
Modal.error({
|
Modal.error({
|
||||||
title: "系统提示",
|
title: '系统提示',
|
||||||
content: "登录状态已过期,请重新登录",
|
content: '登录状态已过期,请重新登录',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
removeToken();
|
removeToken()
|
||||||
router.replace({ name: 'Login' });
|
router.replace({ name: 'Login' })
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
return Promise.reject(data);
|
return Promise.reject(data)
|
||||||
case 500:
|
case 500:
|
||||||
return Promise.reject(new Error(data.msg));
|
return Promise.reject(new Error(data.msg))
|
||||||
default:
|
default:
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "请求错误",
|
message: '请求错误',
|
||||||
description: errCodeMap[code] || data?.msg || errCodeMap.default,
|
description: errCodeMap[code] || data?.msg || errCodeMap.default,
|
||||||
});
|
})
|
||||||
return Promise.reject(data);
|
return Promise.reject(data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
if (!error.__handled) {
|
if (!error.__handled) {
|
||||||
const message =
|
const message =
|
||||||
error.code === "ECONNABORTED"
|
error.code === 'ECONNABORTED'
|
||||||
? "请求超时,请稍后重试"
|
? '请求超时,请稍后重试'
|
||||||
: error.message || "网络异常";
|
: error.message || '网络异常'
|
||||||
|
|
||||||
notification.error({
|
notification.error({
|
||||||
message: "网络错误",
|
message: '网络错误',
|
||||||
description: message,
|
description: message,
|
||||||
});
|
})
|
||||||
|
|
||||||
error.__handled = true;
|
error.__handled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error)
|
||||||
}
|
},
|
||||||
);
|
)
|
||||||
|
|
||||||
function request<T = any>(config: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
function request<T = any>(config: AxiosRequestConfig): Promise<ApiResponse<T>> {
|
||||||
return service(config) as Promise<ApiResponse<T>>;
|
return service(config) as Promise<ApiResponse<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default request;
|
export default request
|
||||||
|
|||||||
@@ -2,51 +2,51 @@ import request from '@/api/request'
|
|||||||
|
|
||||||
// 查询字典数据列表
|
// 查询字典数据列表
|
||||||
export function listData(params: any) {
|
export function listData(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/list',
|
url: '/system/dict/data/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据详细
|
// 查询字典数据详细
|
||||||
export function getData(dictCode: string) {
|
export function getData(dictCode: string) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/' + dictCode,
|
url: '/system/dict/data/' + dictCode,
|
||||||
method: 'get'
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据字典类型查询字典数据信息
|
// 根据字典类型查询字典数据信息
|
||||||
export function getDicts(dictType: string) {
|
export function getDicts(dictType: string) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/type/' + dictType,
|
url: '/system/dict/data/type/' + dictType,
|
||||||
method: 'get'
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增字典数据
|
// 新增字典数据
|
||||||
export function addData(data: any) {
|
export function addData(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data',
|
url: '/system/dict/data',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改字典数据
|
// 修改字典数据
|
||||||
export function updateData(data: any) {
|
export function updateData(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data',
|
url: '/system/dict/data',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除字典数据
|
// 删除字典数据
|
||||||
export function delData(dictCode: string) {
|
export function delData(dictCode: string) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/dict/data/' + dictCode,
|
url: '/system/dict/data/' + dictCode,
|
||||||
method: 'delete'
|
method: 'delete',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './data'
|
export * from './data'
|
||||||
export * from './type'
|
export * from './type'
|
||||||
|
|||||||
@@ -3,59 +3,59 @@ import type { ID } from '@/api/common'
|
|||||||
|
|
||||||
// 查询字典类型列表
|
// 查询字典类型列表
|
||||||
export function listType(params: any) {
|
export function listType(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type/list",
|
url: '/system/dict/type/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典类型详细
|
// 查询字典类型详细
|
||||||
export function getType(dictId: ID) {
|
export function getType(dictId: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type/" + dictId,
|
url: '/system/dict/type/' + dictId,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增字典类型
|
// 新增字典类型
|
||||||
export function addType(data: any) {
|
export function addType(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type",
|
url: '/system/dict/type',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改字典类型
|
// 修改字典类型
|
||||||
export function updateType(data: any) {
|
export function updateType(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type",
|
url: '/system/dict/type',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除字典类型
|
// 删除字典类型
|
||||||
export function delType(dictId: ID) {
|
export function delType(dictId: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type/" + dictId,
|
url: '/system/dict/type/' + dictId,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新字典缓存
|
// 刷新字典缓存
|
||||||
export function refreshCache() {
|
export function refreshCache() {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type/refreshCache",
|
url: '/system/dict/type/refreshCache',
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取字典选择框列表
|
// 获取字典选择框列表
|
||||||
export function getOptions() {
|
export function getOptions() {
|
||||||
return request({
|
return request({
|
||||||
url: "/system/dict/type/optionselect",
|
url: '/system/dict/type/optionselect',
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import request from '@/api/request'
|
import request from '@/api/request'
|
||||||
import type { LoginInfo } from './model';
|
import type { LoginInfo } from './model'
|
||||||
|
|
||||||
// 用户登录
|
// 用户登录
|
||||||
export function login(data: LoginInfo) {
|
export function login(data: LoginInfo) {
|
||||||
return request({
|
return request({
|
||||||
url: '/login',
|
url: '/login',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
export function getCaptcha() {
|
export function getCaptcha() {
|
||||||
return request({
|
return request({
|
||||||
url: '/captchaImage',
|
url: '/captchaImage',
|
||||||
method: 'get'
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
export function logout() {
|
export function logout() {
|
||||||
return request({
|
return request({
|
||||||
url: '/logout',
|
url: '/logout',
|
||||||
method: 'post'
|
method: 'post',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/api/system/model.d.ts
vendored
10
src/api/system/model.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
export interface LoginInfo {
|
export interface LoginInfo {
|
||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
uuid: string
|
uuid: string
|
||||||
code: string
|
code: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询设备管理详细
|
// 查询设备管理详细
|
||||||
export function getEquipmentByCode(code: string) {
|
export function getEquipmentByCode(code: string) {
|
||||||
return request({
|
return request({
|
||||||
url: '/basic/equipment/code/' + code,
|
url: '/basic/equipment/code/' + code,
|
||||||
method: 'get'
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询设备进站记录列表
|
// 查询设备进站记录列表
|
||||||
export function listEquipmentEntry(params: any) {
|
export function listEquipmentEntry(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/equipment-entry-log/list",
|
url: '/mes/equipment-entry-log/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增设备进站记录
|
// 新增设备进站记录
|
||||||
export function addEquipmentEntry(data: any) {
|
export function addEquipmentEntry(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/equipment-entry-log",
|
url: '/mes/equipment-entry-log',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除设备进站记录
|
// 删除设备进站记录
|
||||||
export function delEquipmentEntry(id: ID) {
|
export function delEquipmentEntry(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/equipment-entry-log/" + id,
|
url: '/mes/equipment-entry-log/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,72 +1,72 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { LotTraceOrderData, LotTraceOrderQuery } from "./model";
|
import type { LotTraceOrderData, LotTraceOrderQuery } from './model'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询随工单列表
|
// 查询随工单列表
|
||||||
export function listLotTraceOrder(params: LotTraceOrderQuery) {
|
export function listLotTraceOrder(params: LotTraceOrderQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order/list",
|
url: '/mes/lot-trace-order/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高级查询随工单列表
|
// 高级查询随工单列表
|
||||||
export function advListLotTraceOrder(params: LotTraceOrderQuery) {
|
export function advListLotTraceOrder(params: LotTraceOrderQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order/advList",
|
url: '/mes/lot-trace-order/advList',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询随工单详细
|
// 查询随工单详细
|
||||||
export function getLotTraceOrder(id: ID) {
|
export function getLotTraceOrder(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order/" + id,
|
url: '/mes/lot-trace-order/' + id,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增随工单
|
// 新增随工单
|
||||||
export function addLotTraceOrder(data: LotTraceOrderData) {
|
export function addLotTraceOrder(data: LotTraceOrderData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order",
|
url: '/mes/lot-trace-order',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改随工单
|
// 修改随工单
|
||||||
export function updateLotTraceOrder(data: LotTraceOrderData) {
|
export function updateLotTraceOrder(data: LotTraceOrderData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order",
|
url: '/mes/lot-trace-order',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除随工单
|
// 删除随工单
|
||||||
export function delLotTraceOrder(id: ID) {
|
export function delLotTraceOrder(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order/" + id,
|
url: '/mes/lot-trace-order/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭随工单
|
// 关闭随工单
|
||||||
export function closeLotTraceOrder(id: ID) {
|
export function closeLotTraceOrder(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/lot-trace-order/close/" + id,
|
url: '/mes/lot-trace-order/close/' + id,
|
||||||
method: "put",
|
method: 'put',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增质量异常联络单
|
// 新增质量异常联络单
|
||||||
export function addQualityAbnormalContact(data: any) {
|
export function addQualityAbnormalContact(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/qualityAbnormalContact",
|
url: '/mes/qualityAbnormalContact',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,54 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询库位列表
|
// 查询库位列表
|
||||||
export function listStorageLocation(params: any) {
|
export function listStorageLocation(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/wip/storageLocation/list",
|
url: '/wip/storageLocation/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高级查询库位列表
|
// 高级查询库位列表
|
||||||
export function advListStorageLocation(params: any) {
|
export function advListStorageLocation(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/wip/storageLocation/advList",
|
url: '/wip/storageLocation/advList',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询库位详细
|
// 查询库位详细
|
||||||
export function getStorageLocation(id: ID) {
|
export function getStorageLocation(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/wip/storageLocation/" + id,
|
url: '/wip/storageLocation/' + id,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增库位
|
// 新增库位
|
||||||
export function addStorageLocation(data: any) {
|
export function addStorageLocation(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/wip/storageLocation",
|
url: '/wip/storageLocation',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改库位
|
// 修改库位
|
||||||
export function updateStorageLocation(data: any) {
|
export function updateStorageLocation(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/wip/storageLocation",
|
url: '/wip/storageLocation',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除库位
|
// 删除库位
|
||||||
export function delStorageLocation(id: ID) {
|
export function delStorageLocation(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/wip/storageLocation/" + id,
|
url: '/wip/storageLocation/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询站点绑定治具列表
|
// 查询站点绑定治具列表
|
||||||
export function listStationBindMask(params: any) {
|
export function listStationBindMask(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/mask/list",
|
url: '/mes/station/mask/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为站点绑定治具
|
// 为站点绑定治具
|
||||||
export function batchBindMasksToStation(stationId: ID, maskIds: ID[]) {
|
export function batchBindMasksToStation(stationId: ID, maskIds: ID[]) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/" + stationId + "/bind/mask",
|
url: '/mes/station/' + stationId + '/bind/mask',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data: maskIds,
|
data: maskIds,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为站点绑定治具组
|
// 为站点绑定治具组
|
||||||
export function bindMaskCombinationToStations(data: any) {
|
export function bindMaskCombinationToStations(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/bind/mask-combination",
|
url: '/mes/station/bind/mask-combination',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 站点解绑治具
|
// 站点解绑治具
|
||||||
export function unbindStationMask(stationMaskId: ID) {
|
export function unbindStationMask(stationMaskId: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/bind-mask/" + stationMaskId,
|
url: '/mes/station/bind-mask/' + stationMaskId,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,84 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { MaskCombinationData, MaskCombinationQuery } from "./model";
|
import type { MaskCombinationData, MaskCombinationQuery } from './model'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询治具组合列表
|
// 查询治具组合列表
|
||||||
export function listMaskCombination(params: MaskCombinationQuery) {
|
export function listMaskCombination(params: MaskCombinationQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination/list",
|
url: '/tpm/mask/combination/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询治具组合包含的治具列表
|
// 查询治具组合包含的治具列表
|
||||||
export function listCombinationAssignMask(id: ID, params: MaskCombinationQuery) {
|
export function listCombinationAssignMask(
|
||||||
return request({
|
id: ID,
|
||||||
url: "/tpm/mask/combination/" + id + "/masks",
|
params: MaskCombinationQuery,
|
||||||
method: "get",
|
) {
|
||||||
params,
|
return request({
|
||||||
});
|
url: '/tpm/mask/combination/' + id + '/masks',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高级查询治具组合列表
|
// 高级查询治具组合列表
|
||||||
export function advListMaskCombination(params: MaskCombinationQuery) {
|
export function advListMaskCombination(params: MaskCombinationQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination/advList",
|
url: '/tpm/mask/combination/advList',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询治具组合详细
|
// 查询治具组合详细
|
||||||
export function getMaskCombination(id: ID) {
|
export function getMaskCombination(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination/" + id,
|
url: '/tpm/mask/combination/' + id,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增治具组合
|
// 新增治具组合
|
||||||
export function addMaskCombination(data: MaskCombinationData) {
|
export function addMaskCombination(data: MaskCombinationData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination",
|
url: '/tpm/mask/combination',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改治具组合
|
// 修改治具组合
|
||||||
export function updateMaskCombination(data: MaskCombinationData) {
|
export function updateMaskCombination(data: MaskCombinationData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination",
|
url: '/tpm/mask/combination',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除治具组合
|
// 删除治具组合
|
||||||
export function delMaskCombination(id: ID) {
|
export function delMaskCombination(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination/" + id,
|
url: '/tpm/mask/combination/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增治具组合与治具关联关系
|
// 新增治具组合与治具关联关系
|
||||||
export function addMaskCombinationAssignment(data: MaskCombinationData) {
|
export function addMaskCombinationAssignment(data: MaskCombinationData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination/assignment",
|
url: '/tpm/mask/combination/assignment',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除治具组合与治具关联关系
|
// 删除治具组合与治具关联关系
|
||||||
export function delMaskCombinationAssignment(id: ID) {
|
export function delMaskCombinationAssignment(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/tpm/mask/combination/assignment/" + id,
|
url: '/tpm/mask/combination/assignment/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import { BaseEntity, PageQuery, type ID } from "@/api/common";
|
import { BaseEntity, PageQuery, type ID } from '@/api/common'
|
||||||
/**
|
/**
|
||||||
* 治具组合查询参数
|
* 治具组合查询参数
|
||||||
*/
|
*/
|
||||||
export interface MaskCombinationQuery extends PageQuery {
|
export interface MaskCombinationQuery extends PageQuery {
|
||||||
combinationName?: string;
|
combinationName?: string
|
||||||
combinationCode?: string;
|
combinationCode?: string
|
||||||
combinationStatus?: string;
|
combinationStatus?: string
|
||||||
remark?: string;
|
remark?: string
|
||||||
searchValue?: string;
|
searchValue?: string
|
||||||
tempId?: string;
|
tempId?: ID
|
||||||
timeRange?: [string, string];
|
timeRange?: [string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 治具组合数据
|
* 治具组合数据
|
||||||
*/
|
*/
|
||||||
export interface MaskCombinationData extends BaseEntity {
|
export interface MaskCombinationData extends BaseEntity {
|
||||||
combinationName?: string;
|
combinationName?: string
|
||||||
combinationCode?: string;
|
combinationCode?: string
|
||||||
combinationStatus?: string;
|
combinationStatus?: string
|
||||||
remark?: string;
|
remark?: string
|
||||||
searchValue?: string;
|
searchValue?: string
|
||||||
tempId?: string;
|
tempId?: ID
|
||||||
timeRange?: [string, string];
|
timeRange?: [string, string]
|
||||||
}
|
}
|
||||||
|
|||||||
354
src/api/traceOrderManage/model.d.ts
vendored
354
src/api/traceOrderManage/model.d.ts
vendored
@@ -1,188 +1,188 @@
|
|||||||
import { BaseEntity, PageQuery, type ID } from "@/api/common";
|
import { BaseEntity, PageQuery, type ID } from '@/api/common'
|
||||||
/**
|
/**
|
||||||
* 随工单查询参数
|
* 随工单查询参数
|
||||||
*/
|
*/
|
||||||
export interface LotTraceOrderQuery extends PageQuery {
|
export interface LotTraceOrderQuery extends PageQuery {
|
||||||
/** 编码 */
|
/** 编码 */
|
||||||
code?: string;
|
code?: string
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string
|
||||||
/** 主生产计划的ID */
|
/** 主生产计划的ID */
|
||||||
mpsId?: number;
|
mpsId?: number
|
||||||
/** 主生产计划编码 */
|
/** 主生产计划编码 */
|
||||||
mpsCode?: string;
|
mpsCode?: string
|
||||||
/** 订单类型 */
|
/** 订单类型 */
|
||||||
orderType?: string;
|
orderType?: string
|
||||||
/** 主生产计划明细ID */
|
/** 主生产计划明细ID */
|
||||||
mpsDetailId?: number;
|
mpsDetailId?: number
|
||||||
/** 批号 */
|
/** 批号 */
|
||||||
batchNo?: string;
|
batchNo?: string
|
||||||
/** 主生产计划明细序号 */
|
/** 主生产计划明细序号 */
|
||||||
mpsDetailSeq?: number;
|
mpsDetailSeq?: number
|
||||||
/** 目标产品ID */
|
/** 目标产品ID */
|
||||||
tarMaterialId?: number;
|
tarMaterialId?: number
|
||||||
/** 主物料ID */
|
/** 主物料ID */
|
||||||
masterMaterialId?: number;
|
masterMaterialId?: number
|
||||||
/** 生产版本ID */
|
/** 生产版本ID */
|
||||||
prodVersionId?: number;
|
prodVersionId?: number
|
||||||
/** 计划数量 */
|
/** 计划数量 */
|
||||||
planQty?: number;
|
planQty?: number
|
||||||
/** OK数量 */
|
/** OK数量 */
|
||||||
okQty?: number;
|
okQty?: number
|
||||||
/** NG数量 */
|
/** NG数量 */
|
||||||
ngQty?: number;
|
ngQty?: number
|
||||||
/** 未完成数量 */
|
/** 未完成数量 */
|
||||||
unfinishedQty?: number;
|
unfinishedQty?: number
|
||||||
/** 单位ID */
|
/** 单位ID */
|
||||||
unitId?: number;
|
unitId?: number
|
||||||
/** 计划开始时间范围 */
|
/** 计划开始时间范围 */
|
||||||
planStartTimeRange?: [string, string];
|
planStartTimeRange?: [string, string]
|
||||||
/** 计划结束时间范围 */
|
/** 计划结束时间范围 */
|
||||||
planEndTimeRange?: [string, string];
|
planEndTimeRange?: [string, string]
|
||||||
/** 扩展字段1 */
|
/** 扩展字段1 */
|
||||||
extStr1?: string;
|
extStr1?: string
|
||||||
/** 扩展字段2 */
|
/** 扩展字段2 */
|
||||||
extStr2?: string;
|
extStr2?: string
|
||||||
/** 扩展字段3 */
|
/** 扩展字段3 */
|
||||||
extStr3?: string;
|
extStr3?: string
|
||||||
/** 扩展字段4 */
|
/** 扩展字段4 */
|
||||||
extStr4?: string;
|
extStr4?: string
|
||||||
/** 扩展字段5 */
|
/** 扩展字段5 */
|
||||||
extStr5?: string;
|
extStr5?: string
|
||||||
/** 扩展字段6 */
|
/** 扩展字段6 */
|
||||||
extStr6?: string;
|
extStr6?: string
|
||||||
/** 扩展字段7 */
|
/** 扩展字段7 */
|
||||||
extStr7?: string;
|
extStr7?: string
|
||||||
/** 扩展字段8 */
|
/** 扩展字段8 */
|
||||||
extStr8?: string;
|
extStr8?: string
|
||||||
/** 扩展字段9 */
|
/** 扩展字段9 */
|
||||||
extStr9?: string;
|
extStr9?: string
|
||||||
/** 扩展字段10 */
|
/** 扩展字段10 */
|
||||||
extStr10?: string;
|
extStr10?: string
|
||||||
/** 扩展字段11 */
|
/** 扩展字段11 */
|
||||||
extStr11?: string;
|
extStr11?: string
|
||||||
/** 扩展字段12 */
|
/** 扩展字段12 */
|
||||||
extStr12?: string;
|
extStr12?: string
|
||||||
/** 扩展字段13 */
|
/** 扩展字段13 */
|
||||||
extStr13?: string;
|
extStr13?: string
|
||||||
/** 扩展字段14 */
|
/** 扩展字段14 */
|
||||||
extStr14?: string;
|
extStr14?: string
|
||||||
/** 扩展字段15 */
|
/** 扩展字段15 */
|
||||||
extStr15?: string;
|
extStr15?: string
|
||||||
/** 扩展字段16 */
|
/** 扩展字段16 */
|
||||||
extStr16?: string;
|
extStr16?: string
|
||||||
/** 扩展整型1 */
|
/** 扩展整型1 */
|
||||||
extInt1?: number;
|
extInt1?: number
|
||||||
/** 扩展整型2 */
|
/** 扩展整型2 */
|
||||||
extInt2?: number;
|
extInt2?: number
|
||||||
/** 扩展小数1 */
|
/** 扩展小数1 */
|
||||||
extDec1?: number;
|
extDec1?: number
|
||||||
/** 扩展小数2 */
|
/** 扩展小数2 */
|
||||||
extDec2?: number;
|
extDec2?: number
|
||||||
/** 扩展日期1范围 */
|
/** 扩展日期1范围 */
|
||||||
extDate1Range?: [string, string];
|
extDate1Range?: [string, string]
|
||||||
/** 扩展日期2范围 */
|
/** 扩展日期2范围 */
|
||||||
extDate2Range?: [string, string];
|
extDate2Range?: [string, string]
|
||||||
/** 删除标志 */
|
/** 删除标志 */
|
||||||
delStatus?: string;
|
delStatus?: string
|
||||||
/** 创建时间范围 */
|
/** 创建时间范围 */
|
||||||
createTimeRange?: [string, string];
|
createTimeRange?: [string, string]
|
||||||
/** 更新时间范围 */
|
/** 更新时间范围 */
|
||||||
updateTimeRange?: [string, string];
|
updateTimeRange?: [string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 随工单数据
|
* 随工单数据
|
||||||
*/
|
*/
|
||||||
export interface LotTraceOrderData extends BaseEntity {
|
export interface LotTraceOrderData extends BaseEntity {
|
||||||
/** 主键ID */
|
/** 主键ID */
|
||||||
id?: ID;
|
id?: ID
|
||||||
/** 编码 */
|
/** 编码 */
|
||||||
code?: string;
|
code?: string
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string
|
||||||
/** 主生产计划的ID */
|
/** 主生产计划的ID */
|
||||||
mpsId?: number;
|
mpsId?: number
|
||||||
/** 主生产计划编码 */
|
/** 主生产计划编码 */
|
||||||
mpsCode?: string;
|
mpsCode?: string
|
||||||
/** 订单类型 */
|
/** 订单类型 */
|
||||||
orderType?: string;
|
orderType?: string
|
||||||
/** 主生产计划明细ID */
|
/** 主生产计划明细ID */
|
||||||
mpsDetailId?: number;
|
mpsDetailId?: number
|
||||||
/** 批号 */
|
/** 批号 */
|
||||||
batchNo?: string;
|
batchNo?: string
|
||||||
/** 主生产计划明细序号 */
|
/** 主生产计划明细序号 */
|
||||||
mpsDetailSeq?: number;
|
mpsDetailSeq?: number
|
||||||
/** 目标产品ID */
|
/** 目标产品ID */
|
||||||
tarMaterialId?: number;
|
tarMaterialId?: number
|
||||||
/** 目标产品名称 */
|
/** 目标产品名称 */
|
||||||
tarMaterialName?: string;
|
tarMaterialName?: string
|
||||||
/** 目标产品编码 */
|
/** 目标产品编码 */
|
||||||
tarMaterialCode?: string;
|
tarMaterialCode?: string
|
||||||
/** 主物料ID */
|
/** 主物料ID */
|
||||||
masterMaterialId?: number;
|
masterMaterialId?: number
|
||||||
/** 生产版本ID */
|
/** 生产版本ID */
|
||||||
prodVersionId?: number;
|
prodVersionId?: number
|
||||||
/** 计划数量 */
|
/** 计划数量 */
|
||||||
planQty?: number;
|
planQty?: number
|
||||||
/** OK数量 */
|
/** OK数量 */
|
||||||
okQty?: number;
|
okQty?: number
|
||||||
/** NG数量 */
|
/** NG数量 */
|
||||||
ngQty?: number;
|
ngQty?: number
|
||||||
/** 未完成数量 */
|
/** 未完成数量 */
|
||||||
unfinishedQty?: number;
|
unfinishedQty?: number
|
||||||
/** 单位ID */
|
/** 单位ID */
|
||||||
unitId?: number;
|
unitId?: number
|
||||||
/** 计划开始时间 */
|
/** 计划开始时间 */
|
||||||
planStartTime?: string;
|
planStartTime?: string
|
||||||
/** 计划结束时间 */
|
/** 计划结束时间 */
|
||||||
planEndTime?: string;
|
planEndTime?: string
|
||||||
/** 扩展字段1 */
|
/** 扩展字段1 */
|
||||||
extStr1?: string;
|
extStr1?: string
|
||||||
/** 扩展字段2 */
|
/** 扩展字段2 */
|
||||||
extStr2?: string;
|
extStr2?: string
|
||||||
/** 扩展字段3 */
|
/** 扩展字段3 */
|
||||||
extStr3?: string;
|
extStr3?: string
|
||||||
/** 扩展字段4 */
|
/** 扩展字段4 */
|
||||||
extStr4?: string;
|
extStr4?: string
|
||||||
/** 扩展字段5 */
|
/** 扩展字段5 */
|
||||||
extStr5?: string;
|
extStr5?: string
|
||||||
/** 扩展字段6 */
|
/** 扩展字段6 */
|
||||||
extStr6?: string;
|
extStr6?: string
|
||||||
/** 扩展字段7 */
|
/** 扩展字段7 */
|
||||||
extStr7?: string;
|
extStr7?: string
|
||||||
/** 扩展字段8 */
|
/** 扩展字段8 */
|
||||||
extStr8?: string;
|
extStr8?: string
|
||||||
/** 扩展字段9 */
|
/** 扩展字段9 */
|
||||||
extStr9?: string;
|
extStr9?: string
|
||||||
/** 扩展字段10 */
|
/** 扩展字段10 */
|
||||||
extStr10?: string;
|
extStr10?: string
|
||||||
/** 扩展字段11 */
|
/** 扩展字段11 */
|
||||||
extStr11?: string;
|
extStr11?: string
|
||||||
/** 扩展字段12 */
|
/** 扩展字段12 */
|
||||||
extStr12?: string;
|
extStr12?: string
|
||||||
/** 扩展字段13 */
|
/** 扩展字段13 */
|
||||||
extStr13?: string;
|
extStr13?: string
|
||||||
/** 扩展字段14 */
|
/** 扩展字段14 */
|
||||||
extStr14?: string;
|
extStr14?: string
|
||||||
/** 扩展字段15 */
|
/** 扩展字段15 */
|
||||||
extStr15?: string;
|
extStr15?: string
|
||||||
/** 扩展字段16 */
|
/** 扩展字段16 */
|
||||||
extStr16?: string;
|
extStr16?: string
|
||||||
/** 扩展整型1 */
|
/** 扩展整型1 */
|
||||||
extInt1?: number;
|
extInt1?: number
|
||||||
/** 扩展整型2 */
|
/** 扩展整型2 */
|
||||||
extInt2?: number;
|
extInt2?: number
|
||||||
/** 扩展小数1 */
|
/** 扩展小数1 */
|
||||||
extDec1?: number;
|
extDec1?: number
|
||||||
/** 扩展小数2 */
|
/** 扩展小数2 */
|
||||||
extDec2?: number;
|
extDec2?: number
|
||||||
/** 扩展日期1 */
|
/** 扩展日期1 */
|
||||||
extDate1?: string;
|
extDate1?: string
|
||||||
/** 扩展日期2 */
|
/** 扩展日期2 */
|
||||||
extDate2?: string;
|
extDate2?: string
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark?: string;
|
remark?: string
|
||||||
/** 删除标志 */
|
/** 删除标志 */
|
||||||
delStatus?: string;
|
delStatus?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './infeed'
|
export * from './infeed'
|
||||||
export * from './outfeed'
|
export * from './outfeed'
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询主材进站列表
|
// 查询主材进站列表
|
||||||
export function listMainMaterialEntryLog(params: any) {
|
export function listMainMaterialEntryLog(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/list",
|
url: '/mes/station/entry-log/main-material/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询主材进站详细
|
// 查询主材进站详细
|
||||||
export function getMainMaterialEntryLog(id: ID) {
|
export function getMainMaterialEntryLog(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/" + id,
|
url: '/mes/station/entry-log/main-material/' + id,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增主材Wafer进站
|
// 新增主材Wafer进站
|
||||||
export function addWaferEntryLog(data: any) {
|
export function addWaferEntryLog(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/wafer",
|
url: '/mes/station/entry-log/main-material/wafer',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
// 通过载具新增主材Wafer进站
|
// 通过载具新增主材Wafer进站
|
||||||
export function addWaferEntryLogByCarrier(data: any) {
|
export function addWaferEntryLogByCarrier(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/wafer/by-carrier",
|
url: '/mes/station/entry-log/main-material/wafer/by-carrier',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增主材Die进站
|
// 新增主材Die进站
|
||||||
export function addDieEntryLog(data: any) {
|
export function addDieEntryLog(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/die",
|
url: '/mes/station/entry-log/main-material/die',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
// 通过载具新增主材Die进站
|
// 通过载具新增主材Die进站
|
||||||
export function addDieEntryLogByCarrier(data: any) {
|
export function addDieEntryLogByCarrier(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/die/by-carrier",
|
url: '/mes/station/entry-log/main-material/die/by-carrier',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除主材进站
|
// 删除主材进站
|
||||||
export function delMainMaterialEntryLog(id: ID) {
|
export function delMainMaterialEntryLog(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/entry-log/main-material/" + id,
|
url: '/mes/station/entry-log/main-material/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,54 +1,54 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
|
|
||||||
// 查询主材出站列表
|
// 查询主材出站列表
|
||||||
export function listMainMaterialOutboundLog(params: any) {
|
export function listMainMaterialOutboundLog(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/out-log/main-material/list",
|
url: '/mes/station/out-log/main-material/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询主材出站详细
|
// 查询主材出站详细
|
||||||
export function getMainMaterialOutboundLog(id: ID) {
|
export function getMainMaterialOutboundLog(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/out-log/main-material/" + id,
|
url: '/mes/station/out-log/main-material/' + id,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增主材出站
|
// 新增主材出站
|
||||||
export function batchAddMainMaterialOutboundLog(data: any) {
|
export function batchAddMainMaterialOutboundLog(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/out-log/main-material/batch",
|
url: '/mes/station/out-log/main-material/batch',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增主材出站
|
// 新增主材出站
|
||||||
export function addWaferDieOutboundLogBySpecifiedNg(data: any) {
|
export function addWaferDieOutboundLogBySpecifiedNg(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/out-log/main-material/wafer-die/by-specified-ng",
|
url: '/mes/station/out-log/main-material/wafer-die/by-specified-ng',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改主材出站
|
// 修改主材出站
|
||||||
export function updateMainMaterialOutboundLog(data: any) {
|
export function updateMainMaterialOutboundLog(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mes/station/out-log/main-material',
|
url: '/mes/station/out-log/main-material',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除主材出站
|
// 删除主材出站
|
||||||
export function delMainMaterialOutboundLog(id: ID) {
|
export function delMainMaterialOutboundLog(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/out-log/main-material/" + id,
|
url: '/mes/station/out-log/main-material/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,80 @@
|
|||||||
import request from "@/api/request";
|
import request from '@/api/request'
|
||||||
import type { ID } from "@/api/common";
|
import type { ID } from '@/api/common'
|
||||||
import type { MesStationQuery, MesStationData } from "./model";
|
import type { MesStationQuery, MesStationData } from './model'
|
||||||
|
|
||||||
// 查询站点列表
|
// 查询站点列表
|
||||||
export function listStation(params: MesStationQuery) {
|
export function listStation(params: MesStationQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/list",
|
url: '/mes/station/list',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 高级查询站点列表
|
// 高级查询站点列表
|
||||||
export function advListStation(params: MesStationQuery) {
|
export function advListStation(params: MesStationQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/advList",
|
url: '/mes/station/advList',
|
||||||
method: "get",
|
method: 'get',
|
||||||
params,
|
params,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询站点详细
|
// 查询站点详细
|
||||||
export function getStation(id: ID) {
|
export function getStation(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/" + id,
|
url: '/mes/station/' + id,
|
||||||
method: "get",
|
method: 'get',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断站点是否为最后一个站点
|
// 判断站点是否为最后一个站点
|
||||||
export function isLastStation(id: ID) {
|
export function isLastStation(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mes/station/' + id + '/isLast',
|
url: '/mes/station/' + id + '/isLast',
|
||||||
method: 'get'
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增站点
|
// 新增站点
|
||||||
export function addStation(data: MesStationData) {
|
export function addStation(data: MesStationData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station",
|
url: '/mes/station',
|
||||||
method: "post",
|
method: 'post',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改站点
|
// 修改站点
|
||||||
export function updateStation(data: MesStationData) {
|
export function updateStation(data: MesStationData) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station",
|
url: '/mes/station',
|
||||||
method: "put",
|
method: 'put',
|
||||||
data,
|
data,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除站点
|
// 删除站点
|
||||||
export function delStation(id: ID) {
|
export function delStation(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/" + id,
|
url: '/mes/station/' + id,
|
||||||
method: "delete",
|
method: 'delete',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 站点开工
|
// 站点开工
|
||||||
export function startStation(id: ID) {
|
export function startStation(id: ID) {
|
||||||
return request({
|
return request({
|
||||||
url: "/mes/station/" + id + "/start",
|
url: '/mes/station/' + id + '/start',
|
||||||
method: "put",
|
method: 'put',
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 站点完工
|
// 站点完工
|
||||||
export function completeStation(id: ID, location: any) {
|
export function completeStation(id: ID, location: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/mes/station/' + id + '/complete',
|
url: '/mes/station/' + id + '/complete',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: location
|
data: location,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
118
src/api/traceOrderManage/station/model.d.ts
vendored
118
src/api/traceOrderManage/station/model.d.ts
vendored
@@ -1,71 +1,71 @@
|
|||||||
import { BaseEntity, PageQuery, type ID } from "@/api/common";
|
import { BaseEntity, PageQuery, type ID } from '@/api/common'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 站点查询参数
|
* 站点查询参数
|
||||||
*/
|
*/
|
||||||
export interface MesStationQuery extends PageQuery {
|
export interface MesStationQuery extends PageQuery {
|
||||||
/** 随工单ID */
|
/** 随工单ID */
|
||||||
traceOrderId?: number;
|
traceOrderId?: ID
|
||||||
/** 随工单编码 */
|
/** 随工单编码 */
|
||||||
traceOrderCode?: string;
|
traceOrderCode?: string
|
||||||
/** 站点序号 */
|
/** 站点序号 */
|
||||||
seqNo?: number;
|
seqNo?: number
|
||||||
/** 站点名称 */
|
/** 站点名称 */
|
||||||
name?: string;
|
name?: string
|
||||||
/** 站点编码 */
|
/** 站点编码 */
|
||||||
code?: string;
|
code?: string
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string
|
||||||
/** 计划数量最小值 */
|
/** 计划数量最小值 */
|
||||||
planQtyMin?: number;
|
planQtyMin?: number
|
||||||
/** 计划数量最大值 */
|
/** 计划数量最大值 */
|
||||||
planQtyMax?: number;
|
planQtyMax?: number
|
||||||
/** 合格数量最小值 */
|
/** 合格数量最小值 */
|
||||||
okQtyMin?: number;
|
okQtyMin?: number
|
||||||
/** 合格数量最大值 */
|
/** 合格数量最大值 */
|
||||||
okQtyMax?: number;
|
okQtyMax?: number
|
||||||
/** 不合格数量最小值 */
|
/** 不合格数量最小值 */
|
||||||
ngQtyMin?: number;
|
ngQtyMin?: number
|
||||||
/** 不合格数量最大值 */
|
/** 不合格数量最大值 */
|
||||||
ngQtyMax?: number;
|
ngQtyMax?: number
|
||||||
/** 进站时间范围开始 */
|
/** 进站时间范围开始 */
|
||||||
arrivalTimeStart?: string;
|
arrivalTimeStart?: string
|
||||||
/** 进站时间范围结束 */
|
/** 进站时间范围结束 */
|
||||||
arrivalTimeEnd?: string;
|
arrivalTimeEnd?: string
|
||||||
/** 出战时间范围开始 */
|
/** 出战时间范围开始 */
|
||||||
departureTimeStart?: string;
|
departureTimeStart?: string
|
||||||
/** 出战时间范围结束 */
|
/** 出战时间范围结束 */
|
||||||
departureTimeEnd?: string;
|
departureTimeEnd?: string
|
||||||
/** 删除标志 */
|
/** 删除标志 */
|
||||||
delStatus?: string;
|
delStatus?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 站点数据
|
* 站点数据
|
||||||
*/
|
*/
|
||||||
export interface MesStationData extends BaseEntity {
|
export interface MesStationData extends BaseEntity {
|
||||||
/** 主键ID */
|
/** 主键ID */
|
||||||
id?: number;
|
id?: ID
|
||||||
/** 随工单ID */
|
/** 随工单ID */
|
||||||
traceOrderId?: number;
|
traceOrderId?: ID
|
||||||
/** 站点序号 */
|
/** 站点序号 */
|
||||||
seqNo?: number;
|
seqNo?: number
|
||||||
/** 站点名称 */
|
/** 站点名称 */
|
||||||
name?: string;
|
name?: string
|
||||||
/** 站点编码 */
|
/** 站点编码 */
|
||||||
code?: string;
|
code?: string
|
||||||
/** 状态 */
|
/** 状态 */
|
||||||
status?: string;
|
status?: string
|
||||||
/** 计划数量 */
|
/** 计划数量 */
|
||||||
planQty?: number;
|
planQty?: number
|
||||||
/** 合格数量 */
|
/** 合格数量 */
|
||||||
okQty?: number;
|
okQty?: number
|
||||||
/** 不合格数量 */
|
/** 不合格数量 */
|
||||||
ngQty?: number;
|
ngQty?: number
|
||||||
/** 进站时间 */
|
/** 进站时间 */
|
||||||
arrivalTime?: string;
|
arrivalTime?: string
|
||||||
/** 出战时间 */
|
/** 出战时间 */
|
||||||
departureTime?: string;
|
departureTime?: string
|
||||||
/** 删除标志 */
|
/** 删除标志 */
|
||||||
delStatus?: string;
|
delStatus?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,123 +1,155 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<template v-for="(item, index) in options">
|
<template v-for="(item, index) in options">
|
||||||
<template v-if="isValueMatch(item.value)">
|
<template v-if="isValueMatch(item.value)">
|
||||||
<span
|
<span
|
||||||
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
|
v-if="
|
||||||
:key="item.value" :index="index" :class="item.elTagClass">{{ item.label + " " }}</span>
|
(item.elTagType == 'default' || item.elTagType == '') &&
|
||||||
<a-tag v-else :disable-transitions="true" :key="item.value + ''" :index="index" :color="getColor(item.elTagType)"
|
(item.elTagClass == '' || item.elTagClass == null)
|
||||||
:class="item.elTagClass + ' ' + size">{{ item.label + " " }}</a-tag>
|
"
|
||||||
</template>
|
:key="item.value"
|
||||||
</template>
|
:index="index"
|
||||||
<template v-if="unmatch && showValue">
|
:class="item.elTagClass"
|
||||||
{{ unmatchArray || handleArray }}
|
>
|
||||||
</template>
|
{{ item.label + ' ' }}
|
||||||
</div>
|
</span>
|
||||||
|
<a-tag
|
||||||
|
v-else
|
||||||
|
:disable-transitions="true"
|
||||||
|
:key="item.value + ''"
|
||||||
|
:index="index"
|
||||||
|
:color="getColor(item.elTagType)"
|
||||||
|
:class="item.elTagClass + ' ' + size"
|
||||||
|
>
|
||||||
|
{{ item.label + ' ' }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-if="unmatch && showValue">
|
||||||
|
{{ unmatchArray || handleArray }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
interface DictOption {
|
interface DictOption {
|
||||||
label: string;
|
label: string
|
||||||
value: string;
|
value: string
|
||||||
elTagType?: string;
|
elTagType?: string
|
||||||
elTagClass?: string;
|
elTagClass?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录未匹配的项
|
// 记录未匹配的项
|
||||||
const unmatchArray = ref<any>([])
|
const unmatchArray = ref<any>([])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 数据
|
// 数据
|
||||||
options: {
|
options: {
|
||||||
type: Array as () => Array<DictOption>,
|
type: Array as () => Array<DictOption>,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
// 当前的值
|
// 当前的值
|
||||||
value: [Number, String, Array],
|
value: [Number, String, Array],
|
||||||
// 当未找到匹配的数据时,显示value
|
// 当未找到匹配的数据时,显示value
|
||||||
showValue: {
|
showValue: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
separator: {
|
separator: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ",",
|
default: ',',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'mini',
|
default: 'mini',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const values = computed(() => {
|
const values = computed(() => {
|
||||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '') return []
|
if (
|
||||||
if (typeof props.value === 'number' || typeof props.value === 'boolean') return [props.value]
|
props.value === null ||
|
||||||
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator)
|
typeof props.value === 'undefined' ||
|
||||||
|
props.value === ''
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
if (typeof props.value === 'number' || typeof props.value === 'boolean')
|
||||||
|
return [props.value]
|
||||||
|
return Array.isArray(props.value)
|
||||||
|
? props.value.map((item) => '' + item)
|
||||||
|
: String(props.value).split(props.separator)
|
||||||
})
|
})
|
||||||
|
|
||||||
const unmatch = computed(() => {
|
const unmatch = computed(() => {
|
||||||
unmatchArray.value = []
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
||||||
// 没有value不显示
|
unmatchArray.value = []
|
||||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '' || !Array.isArray(props.options) || props.options.length === 0) return false
|
// 没有value不显示
|
||||||
// 传入值为数组
|
if (
|
||||||
let unmatch = false // 添加一个标志来判断是否有未匹配项
|
props.value === null ||
|
||||||
values.value.forEach(item => {
|
typeof props.value === 'undefined' ||
|
||||||
if (!props.options.some(v => v.value == item)) {
|
props.value === '' ||
|
||||||
unmatchArray.value.push(item)
|
!Array.isArray(props.options) ||
|
||||||
unmatch = true // 如果有未匹配项,将标志设置为true
|
props.options.length === 0
|
||||||
}
|
)
|
||||||
})
|
return false
|
||||||
return unmatch // 返回标志的值
|
// 传入值为数组
|
||||||
|
let unmatch = false // 添加一个标志来判断是否有未匹配项
|
||||||
|
values.value.forEach((item) => {
|
||||||
|
if (!props.options.some((v) => v.value == item)) {
|
||||||
|
unmatchArray.value.push(item)
|
||||||
|
unmatch = true // 如果有未匹配项,将标志设置为true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return unmatch // 返回标志的值
|
||||||
})
|
})
|
||||||
|
|
||||||
function handleArray(array: string[]) {
|
function handleArray(array: string[]) {
|
||||||
if (array.length === 0) return ""
|
if (array.length === 0) return ''
|
||||||
return array.reduce((pre, cur) => {
|
return array.reduce((pre, cur) => {
|
||||||
return pre + " " + cur
|
return pre + ' ' + cur
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValueMatch(itemValue: string) {
|
function isValueMatch(itemValue: string) {
|
||||||
return values.value.some(val => val == itemValue)
|
return values.value.some((val) => val == itemValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getColor(tagType: string | undefined) {
|
function getColor(tagType: string | undefined) {
|
||||||
switch (tagType) {
|
switch (tagType) {
|
||||||
case 'primary':
|
case 'primary':
|
||||||
return 'processing'
|
return 'processing'
|
||||||
case 'success':
|
case 'success':
|
||||||
return 'success'
|
return 'success'
|
||||||
case 'info':
|
case 'info':
|
||||||
return 'info'
|
return 'info'
|
||||||
case 'warning':
|
case 'warning':
|
||||||
return 'warning'
|
return 'warning'
|
||||||
case 'danger':
|
case 'danger':
|
||||||
return 'error'
|
return 'error'
|
||||||
default:
|
default:
|
||||||
return 'default'
|
return 'default'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.ant-tag+.ant-tag {
|
.ant-tag + .ant-tag {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-tag{
|
.ant-tag {
|
||||||
&.large {
|
&.large {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
}
|
}
|
||||||
&.medium {
|
&.medium {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
&.mini {
|
&.mini {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,143 +1,160 @@
|
|||||||
<template>
|
<template>
|
||||||
<header class="header-container" :class="{ 'hide-shadow': hideShadow }"
|
<header
|
||||||
:style="{ height, zIndex, lineHeight: height }">
|
class="header-container"
|
||||||
<div class="opts left-opts" v-if="$slots['left-opts'] || title || $slots.title">
|
:class="{ 'hide-shadow': hideShadow }"
|
||||||
<a-button v-if="showBack" @click="back">返回</a-button>
|
:style="{ height, zIndex, lineHeight: height }"
|
||||||
<a-button v-if="showHome" @click="backToHome">首页</a-button>
|
>
|
||||||
<slot name="left-opts" />
|
<div
|
||||||
</div>
|
class="opts left-opts"
|
||||||
<div class="title" v-if="title || $slots.title">
|
v-if="$slots['left-opts'] || title || $slots.title"
|
||||||
<slot name="title">
|
>
|
||||||
{{ title }}
|
<a-button v-if="showBack" @click="back">返回</a-button>
|
||||||
</slot>
|
<a-button v-if="showHome" @click="backToHome">首页</a-button>
|
||||||
</div>
|
<slot name="left-opts" />
|
||||||
<div class="opts right-opts" v-if="$slots['right-opts'] || title || $slots.title">
|
</div>
|
||||||
<slot name="right-opts" />
|
<div class="title" v-if="title || $slots.title">
|
||||||
<a-button @click="handleLogout" type="primary" danger
|
<slot name="title">
|
||||||
v-if="showLogout && username">退出:{{ username }}</a-button>
|
{{ title }}
|
||||||
</div>
|
</slot>
|
||||||
<slot />
|
</div>
|
||||||
</header>
|
<div
|
||||||
|
class="opts right-opts"
|
||||||
|
v-if="$slots['right-opts'] || title || $slots.title"
|
||||||
|
>
|
||||||
|
<slot name="right-opts" />
|
||||||
|
<a-button
|
||||||
|
@click="handleLogout"
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
v-if="showLogout && username"
|
||||||
|
>
|
||||||
|
退出:{{ username }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
<slot />
|
||||||
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router'
|
||||||
import { Modal } from 'ant-design-vue';
|
import { Modal } from 'ant-design-vue'
|
||||||
import { useAuthStore, useUserStore } from '@/store';
|
import { useAuthStore, useUserStore } from '@/store'
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
showHome: {
|
showHome: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
showBack: {
|
showBack: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
showLogout: {
|
showLogout: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
hideShadow: {
|
hideShadow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
height: {
|
height: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
zIndex: {
|
zIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 999,
|
default: 999,
|
||||||
}
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
defineSlots<{
|
defineSlots<{
|
||||||
'default'(): any;
|
'default'(): any
|
||||||
'left-opts'(): any;
|
'left-opts'(): any
|
||||||
'title'(): any;
|
'title'(): any
|
||||||
'right-opts'(): any;
|
'right-opts'(): any
|
||||||
}>();
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits(['back']);
|
const emit = defineEmits(['back'])
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
const { username } = storeToRefs(userStore);
|
const { username } = storeToRefs(userStore)
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: `是否确认退出登录:${ username.value }`,
|
content: `是否确认退出登录:${username.value}`,
|
||||||
okText: '确定',
|
okText: '确定',
|
||||||
cancelText: '取消',
|
cancelText: '取消',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
authStore.logout();
|
authStore.logout()
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const back = () => {
|
const back = () => {
|
||||||
emit('back');
|
emit('back')
|
||||||
defaultBack();
|
defaultBack()
|
||||||
};
|
}
|
||||||
|
|
||||||
const defaultBack = () => {
|
const defaultBack = () => {
|
||||||
router.go(-1);
|
router.go(-1)
|
||||||
};
|
}
|
||||||
|
|
||||||
const backToHome = () => {
|
const backToHome = () => {
|
||||||
router.push({ name: 'Index' });
|
router.push({ name: 'Index' })
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.header-container {
|
.header-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 6px 1rem;
|
padding: 6px 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
background-color: #1f2e54;
|
background-color: #1f2e54;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
|
box-shadow:
|
||||||
|
rgba(0, 0, 0, 0.1) 0px 4px 6px -1px,
|
||||||
|
rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
|
||||||
|
|
||||||
&.hide-shadow {
|
&.hide-shadow {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
flex: 14;
|
flex: 14;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
font-size: clamp(16px, 2.5vw, 32px);
|
font-size: clamp(16px, 2.5vw, 32px);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.opts {
|
.opts {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 5;
|
flex: 5;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
&.left-opts {
|
&.left-opts {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.right-opts {
|
&.right-opts {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,45 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span>{{ name }}</span>
|
<span>{{ name }}</span>
|
||||||
<a-space class="subtitle">
|
<a-space class="subtitle">
|
||||||
<slot />
|
<slot />
|
||||||
<a-button v-if="showRefresh" size="small" @click="handleRefresh">
|
<a-button v-if="showRefresh" size="small" @click="handleRefresh">
|
||||||
<template #icon><i-lucide-rotate-ccw /></template>刷新
|
<template #icon><i-lucide-rotate-ccw /></template>
|
||||||
</a-button>
|
刷新
|
||||||
</a-space>
|
</a-button>
|
||||||
</div>
|
</a-space>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
defineProps({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
showRefresh: {
|
showRefresh: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
const slots = defineSlots<{
|
defineSlots<{
|
||||||
'default'(): any;
|
'default'(): any
|
||||||
}>();
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits(['refresh']);
|
const emit = defineEmits(['refresh'])
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
emit('refresh');
|
emit('refresh')
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom: 1px solid #c9c9c9;
|
border-bottom: 1px solid #c9c9c9;
|
||||||
padding-bottom: 7px;
|
padding-bottom: 7px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,82 +1,101 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-button class="action-btn" @click="handleHold">Hold</a-button>
|
<a-button class="action-btn" @click="handleHold">Hold</a-button>
|
||||||
|
|
||||||
<!-- Hold Modal -->
|
<!-- Hold Modal -->
|
||||||
<a-modal v-model:open="openHoldModal" title="Hold 操作" @cancel="handleCloseHold" @ok="handleSubmitHold">
|
<a-modal
|
||||||
<a-form :colon="false" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
v-model:open="openHoldModal"
|
||||||
<a-form-item label="工单编码">
|
title="Hold 操作"
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.code" readonly />
|
@cancel="handleCloseHold"
|
||||||
</a-form-item>
|
@ok="handleSubmitHold"
|
||||||
<a-form-item label="目标产品编码">
|
>
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.tarMaterialCode" readonly />
|
<a-form :colon="false" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||||
</a-form-item>
|
<a-form-item label="工单编码">
|
||||||
<a-form-item label="目标产品名称">
|
<a-input v-model:value="traceOrderStore.traceOrderInfo.code" readonly />
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.tarMaterialName" readonly />
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label="目标产品编码">
|
||||||
<a-form-item label="发起工序名称">
|
<a-input
|
||||||
<a-input v-model:value="traceOrderStore.stationInfo.operationTitle" readonly />
|
v-model:value="traceOrderStore.traceOrderInfo.tarMaterialCode"
|
||||||
</a-form-item>
|
readonly
|
||||||
<a-form-item label="产品规格">
|
/>
|
||||||
<a-input readonly />
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label="目标产品名称">
|
||||||
<a-form-item label="计划完成日期">
|
<a-input
|
||||||
<a-date-picker v-model:value="planFinishDate" placeholder="选择计划完成日期" valueFormat="YYYY-MM-DD HH:mm:ss" show-time
|
v-model:value="traceOrderStore.traceOrderInfo.tarMaterialName"
|
||||||
style="width: 100%" />
|
readonly
|
||||||
</a-form-item>
|
/>
|
||||||
</a-form>
|
</a-form-item>
|
||||||
</a-modal>
|
<a-form-item label="发起工序名称">
|
||||||
|
<a-input
|
||||||
|
v-model:value="traceOrderStore.stationInfo.operationTitle"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="产品规格">
|
||||||
|
<a-input readonly />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="计划完成日期">
|
||||||
|
<a-date-picker
|
||||||
|
v-model:value="planFinishDate"
|
||||||
|
placeholder="选择计划完成日期"
|
||||||
|
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||||
|
show-time
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
import { useDialog } from '@/utils/useDialog';
|
import { useDialog } from '@/utils/useDialog'
|
||||||
import { useTraceOrderStore } from '@/store';
|
import { useTraceOrderStore } from '@/store'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
import { addQualityAbnormalContact } from '@/api/traceOrderManage';
|
import { addQualityAbnormalContact } from '@/api/traceOrderManage'
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
// useDialog 管理弹窗状态
|
// useDialog 管理弹窗状态
|
||||||
const { visible: openHoldModal, show, hide } = useDialog();
|
const { visible: openHoldModal, show, hide } = useDialog()
|
||||||
|
|
||||||
const planFinishDate = ref('');
|
const planFinishDate = ref('')
|
||||||
|
|
||||||
const handleHold = () => {
|
const handleHold = () => {
|
||||||
if (!traceOrderStore.currentTraceOrderCode) {
|
if (!traceOrderStore.currentTraceOrderCode) {
|
||||||
message.error('请先选择工单!');
|
message.error('请先选择工单!')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
show();
|
show()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCloseHold = () => {
|
const handleCloseHold = () => {
|
||||||
planFinishDate.value = '';
|
planFinishDate.value = ''
|
||||||
hide();
|
hide()
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSubmitHold = async () => {
|
const handleSubmitHold = async () => {
|
||||||
const tmpPlanFinishDate = planFinishDate.value;
|
const tmpPlanFinishDate = planFinishDate.value
|
||||||
// 修改随工单状态
|
// 修改随工单状态
|
||||||
try {
|
try {
|
||||||
message.success('Hold 成功!')
|
message.success('Hold 成功!')
|
||||||
planFinishDate.value = '';
|
planFinishDate.value = ''
|
||||||
openHoldModal.value = false;
|
openHoldModal.value = false
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || 'Hold 失败');
|
message.error(error.message || 'Hold 失败')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加修改记录
|
// 添加修改记录
|
||||||
try {
|
try {
|
||||||
addQualityAbnormalContact({
|
addQualityAbnormalContact({
|
||||||
materialCode: traceOrderStore.traceOrderInfo.tarMaterialCode,
|
materialCode: traceOrderStore.traceOrderInfo.tarMaterialCode,
|
||||||
abnormalOperation: traceOrderStore.stationInfo.operationCode,
|
abnormalOperation: traceOrderStore.stationInfo.operationCode,
|
||||||
planFinishDate: tmpPlanFinishDate,
|
planFinishDate: tmpPlanFinishDate,
|
||||||
status: "Hold",
|
status: 'Hold',
|
||||||
})
|
})
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '添加记录异常');
|
message.error(error.message || '添加记录异常')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
22
src/main.ts
22
src/main.ts
@@ -1,21 +1,21 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from 'vue'
|
||||||
import App from "./App.vue";
|
import App from './App.vue'
|
||||||
|
|
||||||
// Pinia 状态管理
|
// Pinia 状态管理
|
||||||
import { createPinia } from "pinia";
|
import { createPinia } from 'pinia'
|
||||||
const pinia = createPinia();
|
const pinia = createPinia()
|
||||||
|
|
||||||
// Vue Router
|
// Vue Router
|
||||||
import router from "./router";
|
import router from './router'
|
||||||
|
|
||||||
// 样式文件
|
// 样式文件
|
||||||
import "ant-design-vue/dist/reset.css";
|
import 'ant-design-vue/dist/reset.css'
|
||||||
import "@/assets/styles/index.scss";
|
import '@/assets/styles/index.scss'
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App)
|
||||||
|
|
||||||
// 字典方法
|
// 字典方法
|
||||||
import { useDict } from "@/utils/dict";
|
import { useDict } from '@/utils/dict'
|
||||||
app.config.globalProperties.useDict = useDict;
|
app.config.globalProperties.useDict = useDict
|
||||||
|
|
||||||
app.use(pinia).use(router).mount("#app");
|
app.use(pinia).use(router).mount('#app')
|
||||||
|
|||||||
@@ -1,120 +1,119 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router';
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import { getToken } from "@/utils/auth";
|
import { getToken } from '@/utils/auth'
|
||||||
|
|
||||||
const whiteList = ["/login"];
|
const whiteList = ['/login']
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: '/',
|
||||||
name: "Index",
|
name: 'Index',
|
||||||
component: () => import("@/views/index.vue"),
|
component: () => import('@/views/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: '/login',
|
||||||
name: "Login",
|
name: 'Login',
|
||||||
component: () => import("@/views/login.vue"),
|
component: () => import('@/views/login.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/traceOrderManage",
|
path: '/traceOrderManage',
|
||||||
name: "TraceOrderManage",
|
name: 'TraceOrderManage',
|
||||||
component: () => import("@/views/traceOrderManage/layout.vue"),
|
component: () => import('@/views/traceOrderManage/layout.vue'),
|
||||||
redirect: { name: "TraceOrderManageIndex" },
|
redirect: { name: 'TraceOrderManageIndex' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "",
|
path: '',
|
||||||
name: "TraceOrderManageIndex",
|
name: 'TraceOrderManageIndex',
|
||||||
component: () => import("@/views/traceOrderManage/index.vue"),
|
component: () => import('@/views/traceOrderManage/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "infeed",
|
path: 'infeed',
|
||||||
name: "Infeed",
|
name: 'Infeed',
|
||||||
component: () => import("@/views/traceOrderManage/infeed/layout.vue"),
|
component: () => import('@/views/traceOrderManage/infeed/layout.vue'),
|
||||||
redirect: { name: "PrimaryMaterial" },
|
redirect: { name: 'PrimaryMaterial' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "primaryMaterial",
|
path: 'primaryMaterial',
|
||||||
name: "PrimaryMaterial",
|
name: 'PrimaryMaterial',
|
||||||
component: () =>
|
component: () =>
|
||||||
import("@/views/traceOrderManage/infeed/primaryMaterial/index.vue"),
|
import('@/views/traceOrderManage/infeed/primaryMaterial/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "rawMaterial",
|
path: 'rawMaterial',
|
||||||
name: "RawMaterial",
|
name: 'RawMaterial',
|
||||||
component: () =>
|
component: () =>
|
||||||
import("@/views/traceOrderManage/infeed/rawMaterial/index.vue"),
|
import('@/views/traceOrderManage/infeed/rawMaterial/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "mask",
|
path: 'mask',
|
||||||
name: "Mask",
|
name: 'Mask',
|
||||||
component: () => import("@/views/traceOrderManage/infeed/mask/index.vue"),
|
component: () =>
|
||||||
},
|
import('@/views/traceOrderManage/infeed/mask/index.vue'),
|
||||||
{
|
},
|
||||||
path: "equipment",
|
{
|
||||||
name: "Equipment",
|
path: 'equipment',
|
||||||
component: () =>
|
name: 'Equipment',
|
||||||
import("@/views/traceOrderManage/infeed/equipment/index.vue"),
|
component: () =>
|
||||||
},
|
import('@/views/traceOrderManage/infeed/equipment/index.vue'),
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
{
|
},
|
||||||
path: "outfeed",
|
{
|
||||||
name: "Outfeed",
|
path: 'outfeed',
|
||||||
component: () => import("@/views/traceOrderManage/outfeed/layout.vue"),
|
name: 'Outfeed',
|
||||||
redirect: { name: "JobReport" },
|
component: () => import('@/views/traceOrderManage/outfeed/layout.vue'),
|
||||||
children: [
|
redirect: { name: 'JobReport' },
|
||||||
{
|
children: [
|
||||||
path: "jobReport",
|
{
|
||||||
name: "JobReport",
|
path: 'jobReport',
|
||||||
component: () =>
|
name: 'JobReport',
|
||||||
import("@/views/traceOrderManage/outfeed/jobReport/index.vue"),
|
component: () =>
|
||||||
},
|
import('@/views/traceOrderManage/outfeed/jobReport/index.vue'),
|
||||||
{
|
},
|
||||||
path: "parameterConfiguration",
|
{
|
||||||
name: "ParameterConfiguration",
|
path: 'parameterConfiguration',
|
||||||
component: () =>
|
name: 'ParameterConfiguration',
|
||||||
import(
|
component: () =>
|
||||||
"@/views/traceOrderManage/outfeed/parameterConfiguration/index.vue"
|
import('@/views/traceOrderManage/outfeed/parameterConfiguration/index.vue'),
|
||||||
),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: 'processGuidance',
|
||||||
path: "processGuidance",
|
name: 'ProcessGuidance',
|
||||||
name: "ProcessGuidance",
|
component: () =>
|
||||||
component: () =>
|
import('@/views/traceOrderManage/outfeed/processGuidance/index.vue'),
|
||||||
import("@/views/traceOrderManage/outfeed/processGuidance/index.vue"),
|
},
|
||||||
},
|
],
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
],
|
},
|
||||||
},
|
]
|
||||||
];
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
routes
|
routes,
|
||||||
});
|
})
|
||||||
|
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
const token = getToken();
|
const token = getToken()
|
||||||
// 已登录,访问 login → 跳首页
|
// 已登录,访问 login → 跳首页
|
||||||
if (token && to.path === "/login") {
|
if (token && to.path === '/login') {
|
||||||
return next("/");
|
return next('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 白名单放行
|
// 白名单放行
|
||||||
if (whiteList.includes(to.path)) {
|
if (whiteList.includes(to.path)) {
|
||||||
return next();
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 未登录,访问受保护路由
|
// 未登录,访问受保护路由
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return next({
|
return next({
|
||||||
path: "/login",
|
path: '/login',
|
||||||
query: { redirect: to.fullPath },
|
query: { redirect: to.fullPath },
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已登录,正常访问
|
// 已登录,正常访问
|
||||||
next();
|
next()
|
||||||
});
|
})
|
||||||
|
|
||||||
export default router;
|
export default router
|
||||||
|
|||||||
10
src/shim-vue.d.ts
vendored
10
src/shim-vue.d.ts
vendored
@@ -1,5 +1,7 @@
|
|||||||
declare module "*.vue" {
|
declare module '*.vue' {
|
||||||
import type { DefineComponent } from "vue";
|
import type { DefineComponent } from 'vue'
|
||||||
const component: DefineComponent<{}, {}, any>;
|
|
||||||
export default component;
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// 系统管理
|
// 系统管理
|
||||||
export * from './system/auth';
|
export * from './system/auth'
|
||||||
export * from './system/dict';
|
export * from './system/dict'
|
||||||
export * from './system/user';
|
export * from './system/user'
|
||||||
|
|
||||||
// 工单管理
|
// 工单管理
|
||||||
export * from './traceOrderManage/traceOrder';
|
export * from './traceOrderManage/traceOrder'
|
||||||
|
|||||||
@@ -1,68 +1,74 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { message, notification } from "ant-design-vue";
|
import { message, notification } from 'ant-design-vue'
|
||||||
|
|
||||||
import { useUserStore } from "./user";
|
import { useUserStore } from './user'
|
||||||
import { login, logout as logoutApi } from "@/api/system";
|
import { login, logout as logoutApi } from '@/api/system'
|
||||||
import { setToken, getToken, removeToken, removeAccount, getRememberMe } from "@/utils/auth";
|
import {
|
||||||
import type { LoginInfo } from "@/api/system/model";
|
setToken,
|
||||||
|
getToken,
|
||||||
|
removeToken,
|
||||||
|
removeAccount,
|
||||||
|
getRememberMe,
|
||||||
|
} from '@/utils/auth'
|
||||||
|
import type { LoginInfo } from '@/api/system/model'
|
||||||
|
|
||||||
export const useAuthStore = defineStore("auth", () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const loginLoading = ref(false);
|
const loginLoading = ref(false)
|
||||||
const token = ref(getToken() || null);
|
const token = ref(getToken() || null)
|
||||||
|
|
||||||
async function authLogin(params: LoginInfo, rememberMe: boolean) {
|
async function authLogin(params: LoginInfo, rememberMe: boolean) {
|
||||||
try {
|
try {
|
||||||
loginLoading.value = true;
|
loginLoading.value = true
|
||||||
const res = await login(params);
|
const res = await login(params)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
setToken(res.token as string);
|
setToken(res.token as string)
|
||||||
await userStore.fetchUserInfo();
|
await userStore.fetchUserInfo()
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "登录成功",
|
message: '登录成功',
|
||||||
description: `欢迎回来,${params.username}`,
|
description: `欢迎回来,${params.username}`,
|
||||||
duration: 3,
|
duration: 3,
|
||||||
});
|
})
|
||||||
|
|
||||||
if (rememberMe) {
|
if (rememberMe) {
|
||||||
userStore.setUserInfo({ ...params, rememberMe });
|
userStore.setUserInfo({ ...params, rememberMe })
|
||||||
} else {
|
} else {
|
||||||
userStore.clearUserInfo();
|
userStore.clearUserInfo()
|
||||||
userStore.setUsername(params.username);
|
userStore.setUsername(params.username)
|
||||||
}
|
}
|
||||||
|
|
||||||
const redirect = route.query.redirect || "/";
|
const redirect = route.query.redirect || '/'
|
||||||
router.replace(redirect as string);
|
router.replace(redirect as string)
|
||||||
return res;
|
return res
|
||||||
} else {
|
} else {
|
||||||
// 抛出错误,让调用方处理
|
// 抛出错误,让调用方处理
|
||||||
throw new Error(res.msg || "登录失败");
|
throw new Error(res.msg || '登录失败')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error
|
||||||
} finally {
|
} finally {
|
||||||
loginLoading.value = false;
|
loginLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
// 在实际应用中,这里可以调用后端的退出登录接口
|
// 在实际应用中,这里可以调用后端的退出登录接口
|
||||||
await logoutApi();
|
await logoutApi()
|
||||||
removeToken();
|
removeToken()
|
||||||
!getRememberMe() && removeAccount();
|
if (!getRememberMe()) removeAccount()
|
||||||
await router.push("/login");
|
await router.push('/login')
|
||||||
message.success("已成功退出");
|
message.success('已成功退出')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token,
|
token,
|
||||||
loginLoading,
|
loginLoading,
|
||||||
authLogin,
|
authLogin,
|
||||||
logout,
|
logout,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,63 +1,65 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
|
|
||||||
export const useDictStore = defineStore("dict", () => {
|
export const useDictStore = defineStore('dict', () => {
|
||||||
const dict = ref<any[]>([]);
|
const dict = ref<any[]>([])
|
||||||
|
|
||||||
// 获取字典
|
// 获取字典
|
||||||
function getDict(_key: string) {
|
function getDict(_key: string) {
|
||||||
if (_key == null && _key == "") {
|
if (_key == null && _key == '') {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
dict.value.forEach(item => {
|
dict.value.forEach((item) => {
|
||||||
if (item.key == _key) {
|
if (item.key == _key) {
|
||||||
return item.value;
|
return item.value
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
console.error(e)
|
||||||
}
|
return null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置字典
|
// 设置字典
|
||||||
function setDict(_key: string, value: string | number) {
|
function setDict(_key: string, value: string | number) {
|
||||||
if (!_key) return;
|
if (!_key) return
|
||||||
dict.value.push({
|
dict.value.push({
|
||||||
key: _key,
|
key: _key,
|
||||||
value: value
|
value: value,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除字典
|
// 删除字典
|
||||||
function removeDict(_key: string) {
|
function removeDict(_key: string) {
|
||||||
var bln = false;
|
let bln = false
|
||||||
try {
|
try {
|
||||||
dict.value.forEach((item, index) => {
|
dict.value.forEach((item, index) => {
|
||||||
if (item.key == _key) {
|
if (item.key == _key) {
|
||||||
dict.value.splice(index, 1);
|
dict.value.splice(index, 1)
|
||||||
bln = true;
|
bln = true
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
bln = false;
|
console.error(e)
|
||||||
}
|
bln = false
|
||||||
return bln;
|
}
|
||||||
}
|
return bln
|
||||||
|
}
|
||||||
|
|
||||||
// 清空字典
|
// 清空字典
|
||||||
function cleanDict() {
|
function cleanDict() {
|
||||||
dict.value = new Array();
|
dict.value = []
|
||||||
}
|
}
|
||||||
// 初始字典
|
// 初始字典
|
||||||
function initDict() { }
|
function initDict() {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dict,
|
dict,
|
||||||
getDict,
|
getDict,
|
||||||
setDict,
|
setDict,
|
||||||
removeDict,
|
removeDict,
|
||||||
cleanDict,
|
cleanDict,
|
||||||
initDict,
|
initDict,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,39 +1,41 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia'
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie'
|
||||||
import { setAccount, removeAccount } from "@/utils/auth";
|
import { setAccount, removeAccount } from '@/utils/auth'
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
|
|
||||||
export const useUserStore = defineStore("user", () => {
|
export const useUserStore = defineStore('user', () => {
|
||||||
const username = ref(Cookies.get('username') || '');
|
const username = ref(Cookies.get('username') || '')
|
||||||
|
|
||||||
async function fetchUserInfo() {
|
|
||||||
// Simulate API call
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUsername(name: string) {
|
async function fetchUserInfo() {
|
||||||
username.value = name;
|
// Simulate API call
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUserInfo(params: any) {
|
function setUsername(name: string) {
|
||||||
username.value = params.username || '';
|
username.value = name
|
||||||
|
}
|
||||||
|
|
||||||
params.rememberMe && setAccount({
|
function setUserInfo(params: any) {
|
||||||
username: params.username,
|
username.value = params.username || ''
|
||||||
password: params.password,
|
|
||||||
rememberMe: params.rememberMe,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearUserInfo() {
|
if (params.rememberMe) {
|
||||||
username.value = '';
|
setAccount({
|
||||||
removeAccount();
|
username: params.username,
|
||||||
}
|
password: params.password,
|
||||||
|
rememberMe: params.rememberMe,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
function clearUserInfo() {
|
||||||
username,
|
username.value = ''
|
||||||
fetchUserInfo,
|
removeAccount()
|
||||||
setUsername,
|
}
|
||||||
setUserInfo,
|
|
||||||
clearUserInfo,
|
return {
|
||||||
}
|
username,
|
||||||
});
|
fetchUserInfo,
|
||||||
|
setUsername,
|
||||||
|
setUserInfo,
|
||||||
|
clearUserInfo,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,148 +1,148 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { message } from "ant-design-vue";
|
import { message } from 'ant-design-vue'
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
import { listStation, getStation } from "@/api/traceOrderManage/station";
|
import { listStation, getStation } from '@/api/traceOrderManage/station'
|
||||||
import {
|
import { listLotTraceOrder, getLotTraceOrder } from '@/api/traceOrderManage'
|
||||||
listLotTraceOrder,
|
|
||||||
getLotTraceOrder,
|
|
||||||
} from "@/api/traceOrderManage";
|
|
||||||
|
|
||||||
export const useTraceOrderStore = defineStore("traceOrder", () => {
|
export const useTraceOrderStore = defineStore('traceOrder', () => {
|
||||||
/* ========= 核心业务状态 ========= */
|
/* ========= 核心业务状态 ========= */
|
||||||
const currentTraceOrderId = ref<string | number>('');
|
const currentTraceOrderId = ref<string | number>('')
|
||||||
const currentTraceOrderCode = ref<string>('');
|
const currentTraceOrderCode = ref<string>('')
|
||||||
const currentStationId = ref<string | number>('');
|
const currentStationId = ref<string | number>('')
|
||||||
const currentStationCode = ref<string>('');
|
const currentStationCode = ref<string>('')
|
||||||
|
|
||||||
const traceOrderOptions = ref<any[]>([]);
|
const traceOrderOptions = ref<any[]>([])
|
||||||
const traceOrderInfo = ref<any>({});
|
const traceOrderInfo = ref<any>({})
|
||||||
const stationInfo = ref<any>({});
|
const stationInfo = ref<any>({})
|
||||||
const stationList = ref<any[]>([]);
|
const stationList = ref<any[]>([])
|
||||||
|
|
||||||
/* ========= actions ========= */
|
/* ========= actions ========= */
|
||||||
|
|
||||||
// 加载工单列表
|
// 加载工单列表
|
||||||
const fetchTraceOrderOption = debounce(async (code: string) => {
|
const fetchTraceOrderOption = debounce(async (code: string) => {
|
||||||
if (!code) return;
|
if (!code) return
|
||||||
try {
|
try {
|
||||||
const { rows } = await listLotTraceOrder({
|
const { rows } = await listLotTraceOrder({
|
||||||
code,
|
code,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
});
|
})
|
||||||
traceOrderOptions.value = rows.map((item) => {
|
traceOrderOptions.value = rows.map((item) => {
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
label: item.code,
|
label: item.code,
|
||||||
value: item.code,
|
value: item.code,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || "获取工单列表失败");
|
message.error(error.message || '获取工单列表失败')
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500)
|
||||||
|
|
||||||
// 选择工单
|
// 选择工单
|
||||||
async function selectTraceOrder(value: string, option: any) {
|
async function selectTraceOrder(value: string, option: any) {
|
||||||
currentTraceOrderId.value = option.id;
|
currentTraceOrderId.value = option.id
|
||||||
currentTraceOrderCode.value = value;
|
currentTraceOrderCode.value = value
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loadingTraceOrderInfo.value = true;
|
loadingTraceOrderInfo.value = true
|
||||||
if (!option.id) throw new Error();
|
if (!option.id) throw new Error()
|
||||||
const { data } = await getLotTraceOrder(option.id);
|
const { data } = await getLotTraceOrder(option.id)
|
||||||
traceOrderInfo.value = data;
|
traceOrderInfo.value = data
|
||||||
await fetchStationList();
|
await fetchStationList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || "获取工单信息失败");
|
message.error(error.message || '获取工单信息失败')
|
||||||
} finally {
|
} finally {
|
||||||
loadingTraceOrderInfo.value = false;
|
loadingTraceOrderInfo.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取工单信息
|
// 获取工单信息
|
||||||
const loadingTraceOrderInfo = ref(false);
|
const loadingTraceOrderInfo = ref(false)
|
||||||
async function fetchTraceOrderInfo() {
|
async function fetchTraceOrderInfo() {
|
||||||
if (!currentTraceOrderId.value) return;
|
if (!currentTraceOrderId.value) return
|
||||||
try {
|
try {
|
||||||
loadingTraceOrderInfo.value = true;
|
loadingTraceOrderInfo.value = true
|
||||||
const { data } = await getLotTraceOrder(currentTraceOrderId.value);
|
const { data } = await getLotTraceOrder(currentTraceOrderId.value)
|
||||||
traceOrderInfo.value = data;
|
traceOrderInfo.value = data
|
||||||
fetchStationList();
|
fetchStationList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '刷新工单信息失败');
|
message.error(error.message || '刷新工单信息失败')
|
||||||
} finally {
|
} finally {
|
||||||
loadingTraceOrderInfo.value = false;
|
loadingTraceOrderInfo.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取站点列表
|
|
||||||
const loadingStations = ref(false);
|
|
||||||
async function fetchStationList() {
|
|
||||||
if (!currentTraceOrderCode.value) return;
|
|
||||||
try {
|
|
||||||
loadingStations.value = true;
|
|
||||||
const res = await listStation({ traceOrderCode: currentTraceOrderCode.value });
|
|
||||||
stationList.value = (res.rows || []).map((item: any, idx: number) => {
|
|
||||||
return {
|
|
||||||
key: String(item.id ?? idx),
|
|
||||||
index: item.seqNo ?? idx + 1,
|
|
||||||
...item,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
message.error(error?.msg || error?.message || "查询站点失败");
|
|
||||||
} finally {
|
|
||||||
loadingStations.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取站点信息
|
// 获取站点列表
|
||||||
const loadingStationInfo = ref(false);
|
const loadingStations = ref(false)
|
||||||
async function fetchStationInfo() {
|
async function fetchStationList() {
|
||||||
if (!currentTraceOrderId.value) return;
|
if (!currentTraceOrderCode.value) return
|
||||||
try {
|
try {
|
||||||
loadingStationInfo.value = true;
|
loadingStations.value = true
|
||||||
const { data } = await getStation(currentStationId.value);
|
const res = await listStation({
|
||||||
stationInfo.value = data;
|
traceOrderCode: currentTraceOrderCode.value,
|
||||||
} catch (error: any) {
|
})
|
||||||
console.log(error.message || '获取站点信息失败');
|
stationList.value = (res.rows || []).map((item: any, idx: number) => {
|
||||||
} finally {
|
return {
|
||||||
loadingStationInfo.value = false;
|
key: String(item.id ?? idx),
|
||||||
}
|
index: item.seqNo ?? idx + 1,
|
||||||
}
|
...item,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error?.msg || error?.message || '查询站点失败')
|
||||||
|
} finally {
|
||||||
|
loadingStations.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 选择站点
|
// 获取站点信息
|
||||||
async function selectStation(stationId: string) {
|
const loadingStationInfo = ref(false)
|
||||||
currentStationId.value = stationId;
|
async function fetchStationInfo() {
|
||||||
stationInfo.value = stationList.value.find((s: any) => s.id === stationId) ?? {};
|
if (!currentTraceOrderId.value) return
|
||||||
currentStationCode.value = stationInfo.value.code;
|
try {
|
||||||
}
|
loadingStationInfo.value = true
|
||||||
|
const { data } = await getStation(currentStationId.value)
|
||||||
|
stationInfo.value = data
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(error.message || '获取站点信息失败')
|
||||||
|
} finally {
|
||||||
|
loadingStationInfo.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function refreshAll() {
|
// 选择站点
|
||||||
await Promise.all([fetchTraceOrderInfo(), fetchStationList()]);
|
async function selectStation(stationId: string) {
|
||||||
}
|
currentStationId.value = stationId
|
||||||
|
stationInfo.value =
|
||||||
|
stationList.value.find((s: any) => s.id === stationId) ?? {}
|
||||||
|
currentStationCode.value = stationInfo.value.code
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
async function refreshAll() {
|
||||||
currentTraceOrderId,
|
await Promise.all([fetchTraceOrderInfo(), fetchStationList()])
|
||||||
currentTraceOrderCode,
|
}
|
||||||
currentStationId,
|
|
||||||
currentStationCode,
|
return {
|
||||||
traceOrderOptions,
|
currentTraceOrderId,
|
||||||
traceOrderInfo,
|
currentTraceOrderCode,
|
||||||
stationInfo,
|
currentStationId,
|
||||||
stationList,
|
currentStationCode,
|
||||||
loadingTraceOrderInfo,
|
traceOrderOptions,
|
||||||
loadingStations,
|
traceOrderInfo,
|
||||||
loadingStationInfo,
|
stationInfo,
|
||||||
selectTraceOrder,
|
stationList,
|
||||||
selectStation,
|
loadingTraceOrderInfo,
|
||||||
refreshAll,
|
loadingStations,
|
||||||
fetchTraceOrderOption,
|
loadingStationInfo,
|
||||||
fetchTraceOrderInfo,
|
selectTraceOrder,
|
||||||
fetchStationList,
|
selectStation,
|
||||||
fetchStationInfo,
|
refreshAll,
|
||||||
};
|
fetchTraceOrderOption,
|
||||||
});
|
fetchTraceOrderInfo,
|
||||||
|
fetchStationList,
|
||||||
|
fetchStationInfo,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,48 +1,53 @@
|
|||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie'
|
||||||
import { encrypt } from '@/utils/jsencrypt';
|
import { encrypt } from '@/utils/jsencrypt'
|
||||||
|
|
||||||
// 30 天
|
// 30 天
|
||||||
export const expiresTime = 30;
|
export const expiresTime = 30
|
||||||
|
|
||||||
export const TokenKey = 'Admin-Token'
|
export const TokenKey = 'Admin-Token'
|
||||||
|
|
||||||
export function getToken() {
|
export function getToken() {
|
||||||
return Cookies.get(TokenKey)
|
return Cookies.get(TokenKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setToken(token: string) {
|
export function setToken(token: string) {
|
||||||
return Cookies.set(TokenKey, token)
|
return Cookies.set(TokenKey, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeToken() {
|
export function removeToken() {
|
||||||
return Cookies.remove(TokenKey)
|
return Cookies.remove(TokenKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountInfo {
|
export interface AccountInfo {
|
||||||
username?: string;
|
username?: string
|
||||||
password?: string;
|
password?: string
|
||||||
rememberMe?: 'true' | 'false';
|
rememberMe?: 'true' | 'false'
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRememberMe() {
|
export function getRememberMe() {
|
||||||
return Cookies.get("rememberMe");
|
return Cookies.get('rememberMe')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAccount() {
|
export function getAccount() {
|
||||||
return {
|
return {
|
||||||
username: Cookies.get("username"),
|
username: Cookies.get('username'),
|
||||||
password: Cookies.get("password"),
|
password: Cookies.get('password'),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setAccount(account: AccountInfo) {
|
export function setAccount(account: AccountInfo) {
|
||||||
account.username && Cookies.set("username", account.username, { expires: expiresTime});
|
if (account.username)
|
||||||
account.password && Cookies.set("password", encrypt(account.password) as string, { expires: expiresTime});
|
Cookies.set('username', account.username, { expires: expiresTime })
|
||||||
account.rememberMe != null && Cookies.set("rememberMe", account.rememberMe, { expires: expiresTime});
|
if (account.password)
|
||||||
|
Cookies.set('password', encrypt(account.password) as string, {
|
||||||
|
expires: expiresTime,
|
||||||
|
})
|
||||||
|
if (account.rememberMe)
|
||||||
|
Cookies.set('rememberMe', account.rememberMe, { expires: expiresTime })
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeAccount() {
|
export function removeAccount() {
|
||||||
Cookies.remove("username");
|
Cookies.remove('username')
|
||||||
Cookies.remove("password");
|
Cookies.remove('password')
|
||||||
Cookies.remove("rememberMe");
|
Cookies.remove('rememberMe')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { message } from "ant-design-vue";
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
// 复制到剪贴板
|
// 复制到剪贴板
|
||||||
export const handleCopy = async (text: string | number | undefined) => {
|
export const handleCopy = async (text: string | number | undefined) => {
|
||||||
if (!text) return;
|
if (!text) return
|
||||||
await navigator.clipboard.writeText(String(text));
|
await navigator.clipboard.writeText(String(text))
|
||||||
message.success(`已复制: ${text}`);
|
message.success(`已复制: ${text}`)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化日期时间
|
* 格式化日期时间
|
||||||
@@ -6,27 +6,30 @@ import { ref, onMounted, onBeforeUnmount } from 'vue';
|
|||||||
* @param format 格式字符串,默认 'YYYY-MM-DD HH:mm:ss'
|
* @param format 格式字符串,默认 'YYYY-MM-DD HH:mm:ss'
|
||||||
* @returns 格式化后的时间字符串
|
* @returns 格式化后的时间字符串
|
||||||
*/
|
*/
|
||||||
export function formatDateTime(date: Date | number | string, format = 'YYYY-MM-DD HH:mm:ss'): string {
|
export function formatDateTime(
|
||||||
const d = new Date(date);
|
date: Date | number | string,
|
||||||
|
format = 'YYYY-MM-DD HH:mm:ss',
|
||||||
if (isNaN(d.getTime())) {
|
): string {
|
||||||
return '';
|
const d = new Date(date)
|
||||||
}
|
|
||||||
|
|
||||||
const year = d.getFullYear();
|
if (isNaN(d.getTime())) {
|
||||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
return ''
|
||||||
const day = String(d.getDate()).padStart(2, '0');
|
}
|
||||||
const hours = String(d.getHours()).padStart(2, '0');
|
|
||||||
const minutes = String(d.getMinutes()).padStart(2, '0');
|
|
||||||
const seconds = String(d.getSeconds()).padStart(2, '0');
|
|
||||||
|
|
||||||
return format
|
const year = d.getFullYear()
|
||||||
.replace('YYYY', String(year))
|
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
.replace('MM', month)
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
.replace('DD', day)
|
const hours = String(d.getHours()).padStart(2, '0')
|
||||||
.replace('HH', hours)
|
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||||
.replace('mm', minutes)
|
const seconds = String(d.getSeconds()).padStart(2, '0')
|
||||||
.replace('ss', seconds);
|
|
||||||
|
return format
|
||||||
|
.replace('YYYY', String(year))
|
||||||
|
.replace('MM', month)
|
||||||
|
.replace('DD', day)
|
||||||
|
.replace('HH', hours)
|
||||||
|
.replace('mm', minutes)
|
||||||
|
.replace('ss', seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,7 +38,7 @@ export function formatDateTime(date: Date | number | string, format = 'YYYY-MM-D
|
|||||||
* @returns 当前时间字符串
|
* @returns 当前时间字符串
|
||||||
*/
|
*/
|
||||||
export function getCurrentTime(format = 'YYYY-MM-DD HH:mm:ss'): string {
|
export function getCurrentTime(format = 'YYYY-MM-DD HH:mm:ss'): string {
|
||||||
return formatDateTime(new Date(), format);
|
return formatDateTime(new Date(), format)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,28 +47,28 @@ export function getCurrentTime(format = 'YYYY-MM-DD HH:mm:ss'): string {
|
|||||||
* @returns 响应式时间字符串
|
* @returns 响应式时间字符串
|
||||||
*/
|
*/
|
||||||
export function useRealTime(format = 'YYYY-MM-DD HH:mm:ss') {
|
export function useRealTime(format = 'YYYY-MM-DD HH:mm:ss') {
|
||||||
const currentTime = ref(getCurrentTime(format));
|
const currentTime = ref(getCurrentTime(format))
|
||||||
let timer: NodeJS.Timeout | null = null;
|
let timer: NodeJS.Timeout | null = null
|
||||||
|
|
||||||
const startTimer = () => {
|
const startTimer = () => {
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
currentTime.value = getCurrentTime(format);
|
currentTime.value = getCurrentTime(format)
|
||||||
}, 1000);
|
}, 1000)
|
||||||
};
|
}
|
||||||
|
|
||||||
const stopTimer = () => {
|
const stopTimer = () => {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
clearInterval(timer);
|
clearInterval(timer)
|
||||||
timer = null;
|
timer = null
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
onMounted(startTimer);
|
onMounted(startTimer)
|
||||||
onBeforeUnmount(stopTimer);
|
onBeforeUnmount(stopTimer)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentTime,
|
currentTime,
|
||||||
startTimer,
|
startTimer,
|
||||||
stopTimer
|
stopTimer,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useDictStore } from "@/store";
|
import { useDictStore } from '@/store'
|
||||||
import { getDicts } from '@/api/system/dict'
|
import { getDicts } from '@/api/system/dict'
|
||||||
import { ref, toRefs } from 'vue'
|
import { ref, toRefs } from 'vue'
|
||||||
|
|
||||||
@@ -6,21 +6,26 @@ import { ref, toRefs } from 'vue'
|
|||||||
* 获取字典数据
|
* 获取字典数据
|
||||||
*/
|
*/
|
||||||
export function useDict(...args: string[]) {
|
export function useDict(...args: string[]) {
|
||||||
const res = ref<any>({})
|
const res = ref<any>({})
|
||||||
|
|
||||||
return (() => {
|
return (() => {
|
||||||
args.forEach((dictType, index) => {
|
args.forEach((dictType) => {
|
||||||
res.value[dictType] = []
|
res.value[dictType] = []
|
||||||
const dicts = useDictStore().getDict(dictType)
|
const dicts = useDictStore().getDict(dictType)
|
||||||
if (dicts) {
|
if (dicts) {
|
||||||
res.value[dictType] = dicts
|
res.value[dictType] = dicts
|
||||||
} else {
|
} else {
|
||||||
getDicts(dictType).then((resp: any) => {
|
getDicts(dictType).then((resp: any) => {
|
||||||
res.value[dictType] = resp.data.map((p: any) => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
|
res.value[dictType] = resp.data.map((p: any) => ({
|
||||||
useDictStore().setDict(dictType, res.value[dictType])
|
label: p.dictLabel,
|
||||||
})
|
value: p.dictValue,
|
||||||
}
|
elTagType: p.listClass,
|
||||||
})
|
elTagClass: p.cssClass,
|
||||||
return toRefs(res.value)
|
}))
|
||||||
})()
|
useDictStore().setDict(dictType, res.value[dictType])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return toRefs(res.value)
|
||||||
|
})()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
import JSEncrypt from "jsencrypt";
|
import JSEncrypt from 'jsencrypt'
|
||||||
|
|
||||||
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
// 密钥对生成 http://web.chacuo.net/netrsakeypair
|
||||||
|
|
||||||
const publicKey =
|
const publicKey =
|
||||||
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n" +
|
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
|
||||||
"nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==";
|
'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||||
|
|
||||||
const privateKey =
|
const privateKey =
|
||||||
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n" +
|
'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
|
||||||
"7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n" +
|
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
|
||||||
"PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n" +
|
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
|
||||||
"kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n" +
|
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
|
||||||
"cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n" +
|
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
|
||||||
"DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n" +
|
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
|
||||||
"YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n" +
|
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
|
||||||
"UP8iWi1Qw0Y=";
|
'UP8iWi1Qw0Y='
|
||||||
|
|
||||||
// 加密
|
// 加密
|
||||||
export function encrypt(txt: string) {
|
export function encrypt(txt: string) {
|
||||||
const encryptor = new JSEncrypt();
|
const encryptor = new JSEncrypt()
|
||||||
encryptor.setPublicKey(publicKey); // 设置公钥
|
encryptor.setPublicKey(publicKey) // 设置公钥
|
||||||
return encryptor.encrypt(txt); // 对数据进行加密
|
return encryptor.encrypt(txt) // 对数据进行加密
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解密
|
// 解密
|
||||||
export function decrypt(txt: string) {
|
export function decrypt(txt: string) {
|
||||||
const encryptor = new JSEncrypt();
|
const encryptor = new JSEncrypt()
|
||||||
encryptor.setPrivateKey(privateKey); // 设置私钥
|
encryptor.setPrivateKey(privateKey) // 设置私钥
|
||||||
return encryptor.decrypt(txt); // 对数据进行解密
|
return encryptor.decrypt(txt) // 对数据进行解密
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const delay = (time: number, data?: any) => {
|
export const delay = (time: number, data?: any) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(data);
|
resolve(data)
|
||||||
}, time);
|
}, time)
|
||||||
}) as any;
|
}) as any
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog控制Hook
|
* Dialog控制Hook
|
||||||
* @returns visible(显隐状态)、show(显示)、hide(隐藏)、toggle(切换)
|
* @returns visible(显隐状态)、show(显示)、hide(隐藏)、toggle(切换)
|
||||||
*/
|
*/
|
||||||
export function useDialog(initialVisible:boolean = false): {
|
export function useDialog(initialVisible: boolean = false): {
|
||||||
visible: import('vue').Ref<boolean>,
|
visible: import('vue').Ref<boolean>
|
||||||
show: () => void,
|
show: () => void
|
||||||
hide: () => void,
|
hide: () => void
|
||||||
toggle: () => void
|
toggle: () => void
|
||||||
} {
|
} {
|
||||||
const visible = ref(initialVisible);
|
const visible = ref(initialVisible)
|
||||||
|
|
||||||
const show = () => {
|
const show = () => {
|
||||||
visible.value = true;
|
visible.value = true
|
||||||
};
|
}
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
visible.value = false;
|
visible.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => {
|
||||||
visible.value = !visible.value;
|
visible.value = !visible.value
|
||||||
};
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
visible,
|
visible,
|
||||||
show,
|
show,
|
||||||
hide,
|
hide,
|
||||||
toggle
|
toggle,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,43 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading控制Hook
|
* Loading控制Hook
|
||||||
* @returns loading(加载状态)、startLoading(开始加载)、stopLoading(停止加载)、withLoading(包装异步函数)
|
* @returns loading(加载状态)、startLoading(开始加载)、stopLoading(停止加载)、withLoading(包装异步函数)
|
||||||
*/
|
*/
|
||||||
export function useLoading(initialLoading = false) {
|
export function useLoading(initialLoading = false) {
|
||||||
const loading = ref(initialLoading);
|
const loading = ref(initialLoading)
|
||||||
|
|
||||||
const startLoading = () => {
|
const startLoading = () => {
|
||||||
loading.value = true;
|
loading.value = true
|
||||||
};
|
}
|
||||||
|
|
||||||
const stopLoading = () => {
|
const stopLoading = () => {
|
||||||
loading.value = false;
|
loading.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包装异步函数,自动控制loading状态
|
* 包装异步函数,自动控制loading状态
|
||||||
* @param fn 异步函数
|
* @param fn 异步函数
|
||||||
* @returns 包装后的函数
|
* @returns 包装后的函数
|
||||||
*/
|
*/
|
||||||
const withLoading = <T extends (...args: any[]) => Promise<any>>(fn: T): T => {
|
const withLoading = <T extends (...args: any[]) => Promise<any>>(
|
||||||
return (async (...args: any[]) => {
|
fn: T,
|
||||||
startLoading();
|
): T => {
|
||||||
try {
|
return (async (...args: any[]) => {
|
||||||
const result = await fn(...args);
|
startLoading()
|
||||||
return result;
|
try {
|
||||||
} finally {
|
const result = await fn(...args)
|
||||||
stopLoading();
|
return result
|
||||||
}
|
} finally {
|
||||||
}) as T;
|
stopLoading()
|
||||||
};
|
}
|
||||||
|
}) as T
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loading,
|
loading,
|
||||||
startLoading,
|
startLoading,
|
||||||
stopLoading,
|
stopLoading,
|
||||||
withLoading
|
withLoading,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
* @returns 生成的UUID字符串
|
* @returns 生成的UUID字符串
|
||||||
*/
|
*/
|
||||||
export function generateUUID(): string {
|
export function generateUUID(): string {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||||
const r = Math.random() * 16 | 0;
|
const r = (Math.random() * 16) | 0
|
||||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
const v = c === 'x' ? r : (r & 0x3) | 0x8
|
||||||
return v.toString(16);
|
return v.toString(16)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ipc-dashboard">
|
<div class="ipc-dashboard">
|
||||||
<Header title="MES 过站平台" showLogout />
|
<Header title="MES 过站平台" showLogout />
|
||||||
|
|
||||||
<div class="menu-grid">
|
<div class="menu-grid">
|
||||||
<a-card class="menu-card" shadow="hover" @click="handleJumpTo('TraceOrderManage')">
|
<a-card
|
||||||
<div class="icon-wrap">
|
class="menu-card"
|
||||||
<i-lucide-building />
|
shadow="hover"
|
||||||
</div>
|
@click="handleJumpTo('TraceOrderManage')"
|
||||||
<div class="text">
|
>
|
||||||
<div class="title">工单管理</div>
|
<div class="icon-wrap">
|
||||||
<div class="desc">管理生产工单和进度</div>
|
<i-lucide-building />
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
<div class="text">
|
||||||
<!--
|
<div class="title">工单管理</div>
|
||||||
|
<div class="desc">管理生产工单和进度</div>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
<!--
|
||||||
<a-card class="menu-card" shadow="hover" @click="handleJumpTo('stationControl')">
|
<a-card class="menu-card" shadow="hover" @click="handleJumpTo('stationControl')">
|
||||||
<div class="icon-wrap">
|
<div class="icon-wrap">
|
||||||
<i-lucide-monitor />
|
<i-lucide-monitor />
|
||||||
@@ -82,187 +86,187 @@
|
|||||||
<div class="desc">系统参数配置</div>
|
<div class="desc">系统参数配置</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card> -->
|
</a-card> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
|
|
||||||
const handleJumpTo = (name) => {
|
const handleJumpTo = (name: string) => {
|
||||||
router.push({ name });
|
router.push({ name })
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.ipc-dashboard {
|
.ipc-dashboard {
|
||||||
background: #f5f7fa;
|
background: #f5f7fa;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-grid {
|
.menu-grid {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 8vh 6vh;
|
padding: 8vh 6vh;
|
||||||
gap: 4%;
|
gap: 4%;
|
||||||
row-gap: 6vh;
|
row-gap: 6vh;
|
||||||
|
|
||||||
.menu-card {
|
.menu-card {
|
||||||
width: 22%;
|
width: 22%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 1vw;
|
border-radius: 1vw;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
||||||
:deep(.ant-card-body) {
|
:deep(.ant-card-body) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 24px 16px;
|
padding: 24px 16px;
|
||||||
gap: 1vh;
|
gap: 1vh;
|
||||||
|
|
||||||
.icon-wrap {
|
.icon-wrap {
|
||||||
width: 5vw;
|
width: 5vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: linear-gradient(180deg, #0c6bd1 0%, #1c6fb8 100%);
|
background: linear-gradient(180deg, #0c6bd1 0%, #1c6fb8 100%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 3vw;
|
font-size: 3vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 1.2vw;
|
font-size: 1.2vw;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.desc {
|
.desc {
|
||||||
color: #8b96a7;
|
color: #8b96a7;
|
||||||
font-size: 0.9vw;
|
font-size: 0.9vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.login-dialog) {
|
:deep(.login-dialog) {
|
||||||
.ant-modal {
|
.ant-modal {
|
||||||
width: 48vw;
|
width: 48vw;
|
||||||
left: 26vw;
|
left: 26vw;
|
||||||
border-radius: 16px !important;
|
border-radius: 16px !important;
|
||||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15) !important;
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15) !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-header {
|
.ant-modal-header {
|
||||||
background: linear-gradient(135deg, #0c6bd1 0%, #1c6fb8 100%);
|
background: linear-gradient(135deg, #0c6bd1 0%, #1c6fb8 100%);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
padding: 24px 20px !important;
|
padding: 24px 20px !important;
|
||||||
|
|
||||||
.ant-modal-title {
|
.ant-modal-title {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-close-x {
|
.ant-modal-close-x {
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: rgba(255, 255, 255, 0.7);
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-body {
|
.ant-modal-body {
|
||||||
padding: 32px 28px !important;
|
padding: 32px 28px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
.ant-form-item {
|
.ant-form-item {
|
||||||
margin-bottom: 22px;
|
margin-bottom: 22px;
|
||||||
|
|
||||||
.ant-form-item-label > label {
|
.ant-form-item-label > label {
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input {
|
.ant-input {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
|
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #0c6bd1;
|
border-color: #0c6bd1;
|
||||||
box-shadow: 0 0 0 3px rgba(12, 107, 209, 0.1);
|
box-shadow: 0 0 0 3px rgba(12, 107, 209, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input-password-icon {
|
.ant-input-password-icon {
|
||||||
color: #bfbfbf;
|
color: #bfbfbf;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #0c6bd1;
|
color: #0c6bd1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-footer {
|
.ant-modal-footer {
|
||||||
padding: 20px 28px !important;
|
padding: 20px 28px !important;
|
||||||
border-top: 1px solid #f0f0f0;
|
border-top: 1px solid #f0f0f0;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
border-radius: 0 0 16px 16px;
|
border-radius: 0 0 16px 16px;
|
||||||
|
|
||||||
.ant-btn {
|
.ant-btn {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 10px 32px !important;
|
padding: 10px 32px !important;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
&:not(.btn-primary) {
|
&:not(.btn-primary) {
|
||||||
border-color: #d9d9d9;
|
border-color: #d9d9d9;
|
||||||
color: #595959;
|
color: #595959;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #0c6bd1;
|
border-color: #0c6bd1;
|
||||||
color: #0c6bd1;
|
color: #0c6bd1;
|
||||||
background: #f0f7ff;
|
background: #f0f7ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background: linear-gradient(135deg, #0c6bd1 0%, #1c6fb8 100%);
|
background: linear-gradient(135deg, #0c6bd1 0%, #1c6fb8 100%);
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 4px 16px rgba(12, 107, 209, 0.4);
|
box-shadow: 0 4px 16px rgba(12, 107, 209, 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,21 +3,21 @@ import { ref, reactive } from 'vue'
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { getCaptcha } from '@/api/system'
|
import { getCaptcha } from '@/api/system'
|
||||||
import type { LoginInfo } from '@/api/system/model'
|
import type { LoginInfo } from '@/api/system/model'
|
||||||
import type { Rule } from 'ant-design-vue/es/form';
|
import type { Rule } from 'ant-design-vue/es/form'
|
||||||
import { useAuthStore } from '@/store';
|
import { useAuthStore } from '@/store'
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia'
|
||||||
import { getAccount, getRememberMe } from '@/utils/auth';
|
import { getAccount, getRememberMe } from '@/utils/auth'
|
||||||
import { decrypt } from '@/utils/jsencrypt';
|
import { decrypt } from '@/utils/jsencrypt'
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const { loginLoading } = storeToRefs(authStore);
|
const { loginLoading } = storeToRefs(authStore)
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = reactive<LoginInfo>({
|
const formData = reactive<LoginInfo>({
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
uuid: '',
|
uuid: '',
|
||||||
code: '',
|
code: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 验证码图片
|
// 验证码图片
|
||||||
@@ -28,61 +28,55 @@ const rememberMe = ref(false)
|
|||||||
|
|
||||||
// 表单验证规则
|
// 表单验证规则
|
||||||
const rules: Record<string, Rule[]> = {
|
const rules: Record<string, Rule[]> = {
|
||||||
username: [
|
username: [{ required: true, message: '请输入用户名', trigger: 'change' }],
|
||||||
{ required: true, message: '请输入用户名', trigger: 'change' }
|
password: [{ required: true, message: '请输入密码', trigger: 'change' }],
|
||||||
],
|
code: [{ required: true, message: '请输入验证码', trigger: 'change' }],
|
||||||
password: [
|
|
||||||
{ required: true, message: '请输入密码', trigger: 'change' }
|
|
||||||
],
|
|
||||||
code: [
|
|
||||||
{ required: true, message: '请输入验证码', trigger: 'change' }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码
|
||||||
const refreshCaptcha = async () => {
|
const refreshCaptcha = async () => {
|
||||||
try {
|
try {
|
||||||
captchaImg.value = ''
|
captchaImg.value = ''
|
||||||
captchaLoading.value = true
|
captchaLoading.value = true
|
||||||
await getCaptcha().then((res: any) => {
|
await getCaptcha().then((res: any) => {
|
||||||
loadCaptchaFail.value = false;
|
loadCaptchaFail.value = false
|
||||||
if (res.captchaOnOff) {
|
if (res.captchaOnOff) {
|
||||||
captchaImg.value = "data:image/gif;base64," + res.img
|
captchaImg.value = 'data:image/gif;base64,' + res.img
|
||||||
rules.code[0].required = true;
|
rules.code[0].required = true
|
||||||
} else {
|
} else {
|
||||||
rules.code[0].required = false;
|
rules.code[0].required = false
|
||||||
}
|
}
|
||||||
formData.uuid = res.uuid
|
formData.uuid = res.uuid
|
||||||
formData.code = '' // 清空验证码输入
|
formData.code = '' // 清空验证码输入
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
loadCaptchaFail.value = true;
|
loadCaptchaFail.value = true
|
||||||
message.error('获取验证码失败')
|
message.error('获取验证码失败')
|
||||||
console.error('获取验证码失败:', error)
|
console.error('获取验证码失败:', error)
|
||||||
} finally {
|
} finally {
|
||||||
captchaLoading.value = false
|
captchaLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
const initLoginForm = () => {
|
const initLoginForm = () => {
|
||||||
const { username, password } = getAccount();
|
const { username, password } = getAccount()
|
||||||
const isRememberMe = getRememberMe();
|
const isRememberMe = getRememberMe()
|
||||||
if (isRememberMe && isRememberMe === 'true') {
|
if (isRememberMe && isRememberMe === 'true') {
|
||||||
rememberMe.value = true;
|
rememberMe.value = true
|
||||||
formData.username = username || '';
|
formData.username = username || ''
|
||||||
formData.password = decrypt(password || '') || '';
|
formData.password = decrypt(password || '') || ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 登录处理
|
// 登录处理
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
try {
|
try {
|
||||||
await authStore.authLogin(formData, rememberMe.value);
|
await authStore.authLogin(formData, rememberMe.value)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || error.msg || '登录失败');
|
message.error(error.message || error.msg || '登录失败')
|
||||||
await refreshCaptcha();
|
await refreshCaptcha()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initLoginForm()
|
initLoginForm()
|
||||||
@@ -91,113 +85,122 @@ refreshCaptcha()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<!-- 背景图 -->
|
<!-- 背景图 -->
|
||||||
<div class="login-background"></div>
|
<div class="login-background"></div>
|
||||||
|
|
||||||
<!-- 登录框 -->
|
|
||||||
<div class="login-box">
|
|
||||||
<div class="login-header">
|
|
||||||
<h2 class="login-title">
|
|
||||||
<i-lucide-cpu class="title-icon" />
|
|
||||||
宏禧 MES 过站平台
|
|
||||||
</h2>
|
|
||||||
<p class="login-subtitle">请登录您的账户</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-form
|
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
@finish="handleLogin"
|
|
||||||
class="login-form"
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<!-- 用户名 -->
|
|
||||||
<a-form-item name="username" class="form-item">
|
|
||||||
<a-input
|
|
||||||
v-model:value="formData.username"
|
|
||||||
placeholder="请输入用户名"
|
|
||||||
size="large"
|
|
||||||
class="login-input"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<i-lucide-user class="input-icon" />
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 密码 -->
|
|
||||||
<a-form-item name="password" class="form-item">
|
|
||||||
<a-input-password
|
|
||||||
v-model:value="formData.password"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
size="large"
|
|
||||||
class="login-input"
|
|
||||||
:visibility-toggle="false"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<i-lucide-lock class="input-icon" />
|
|
||||||
</template>
|
|
||||||
</a-input-password>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<!-- 验证码 -->
|
|
||||||
<a-form-item name="code" class="form-item" v-if="rules.code[0].required">
|
|
||||||
<div class="captcha-container">
|
|
||||||
<a-input
|
|
||||||
v-model:value="formData.code"
|
|
||||||
placeholder="请输入验证码"
|
|
||||||
size="large"
|
|
||||||
class="login-input captcha-input"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<i-lucide-shield-check class="input-icon" />
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
<div class="captcha-image-container" @click="refreshCaptcha">
|
|
||||||
<img
|
|
||||||
v-if="captchaImg"
|
|
||||||
:src="captchaImg"
|
|
||||||
alt="验证码"
|
|
||||||
class="captcha-image"
|
|
||||||
/>
|
|
||||||
<div v-else-if="captchaLoading" class="captcha-loading">
|
|
||||||
<i-lucide-loader class="loading-icon" />
|
|
||||||
</div>
|
|
||||||
<div v-else-if="!captchaLoading && loadCaptchaFail" class="captcha-loading">
|
|
||||||
获取失败
|
|
||||||
</div>
|
|
||||||
<div class="captcha-refresh-hint">
|
|
||||||
<i-lucide-refresh-cw class="refresh-icon" />
|
|
||||||
点击刷新
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item name="rememberMe" class="form-item">
|
<!-- 登录框 -->
|
||||||
<a-checkbox v-model:checked="rememberMe" style="color: #ddd;">记住密码</a-checkbox>
|
<div class="login-box">
|
||||||
</a-form-item>
|
<div class="login-header">
|
||||||
|
<h2 class="login-title">
|
||||||
<!-- 登录按钮 -->
|
<i-lucide-cpu class="title-icon" />
|
||||||
<a-form-item class="form-item">
|
宏禧 MES 过站平台
|
||||||
<a-button
|
</h2>
|
||||||
type="primary"
|
<p class="login-subtitle">请登录您的账户</p>
|
||||||
html-type="submit"
|
</div>
|
||||||
size="large"
|
|
||||||
:loading="loginLoading"
|
<a-form
|
||||||
class="login-button"
|
:model="formData"
|
||||||
block
|
:rules="rules"
|
||||||
>
|
@finish="handleLogin"
|
||||||
{{ loginLoading ? '登录中...' : '登录' }}
|
class="login-form"
|
||||||
</a-button>
|
layout="vertical"
|
||||||
</a-form-item>
|
>
|
||||||
</a-form>
|
<!-- 用户名 -->
|
||||||
</div>
|
<a-form-item name="username" class="form-item">
|
||||||
</div>
|
<a-input
|
||||||
|
v-model:value="formData.username"
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
size="large"
|
||||||
|
class="login-input"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<i-lucide-user class="input-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 密码 -->
|
||||||
|
<a-form-item name="password" class="form-item">
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="formData.password"
|
||||||
|
placeholder="请输入密码"
|
||||||
|
size="large"
|
||||||
|
class="login-input"
|
||||||
|
:visibility-toggle="false"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<i-lucide-lock class="input-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input-password>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 验证码 -->
|
||||||
|
<a-form-item
|
||||||
|
name="code"
|
||||||
|
class="form-item"
|
||||||
|
v-if="rules.code[0].required"
|
||||||
|
>
|
||||||
|
<div class="captcha-container">
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.code"
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
size="large"
|
||||||
|
class="login-input captcha-input"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<i-lucide-shield-check class="input-icon" />
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
<div class="captcha-image-container" @click="refreshCaptcha">
|
||||||
|
<img
|
||||||
|
v-if="captchaImg"
|
||||||
|
:src="captchaImg"
|
||||||
|
alt="验证码"
|
||||||
|
class="captcha-image"
|
||||||
|
/>
|
||||||
|
<div v-else-if="captchaLoading" class="captcha-loading">
|
||||||
|
<i-lucide-loader class="loading-icon" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="!captchaLoading && loadCaptchaFail"
|
||||||
|
class="captcha-loading"
|
||||||
|
>
|
||||||
|
获取失败
|
||||||
|
</div>
|
||||||
|
<div class="captcha-refresh-hint">
|
||||||
|
<i-lucide-refresh-cw class="refresh-icon" />
|
||||||
|
点击刷新
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item name="rememberMe" class="form-item">
|
||||||
|
<a-checkbox v-model:checked="rememberMe" style="color: #ddd">
|
||||||
|
记住密码
|
||||||
|
</a-checkbox>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 登录按钮 -->
|
||||||
|
<a-form-item class="form-item">
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
size="large"
|
||||||
|
:loading="loginLoading"
|
||||||
|
class="login-button"
|
||||||
|
block
|
||||||
|
>
|
||||||
|
{{ loginLoading ? '登录中...' : '登录' }}
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -225,319 +228,319 @@ $spacing-xl: 32px;
|
|||||||
$transition: all 0.3s ease;
|
$transition: all 0.3s ease;
|
||||||
|
|
||||||
.login-container {
|
.login-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-background {
|
.login-background {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-image: url('/bg.jpg');
|
background-image: url('/bg.jpg');
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
filter: blur(2px);
|
filter: blur(2px);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
135deg,
|
135deg,
|
||||||
rgba(74, 144, 226, 0.3) 0%,
|
rgba(74, 144, 226, 0.3) 0%,
|
||||||
rgba(58, 123, 213, 0.4) 50%,
|
rgba(58, 123, 213, 0.4) 50%,
|
||||||
rgba(91, 160, 242, 0.3) 100%
|
rgba(91, 160, 242, 0.3) 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-box {
|
.login-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
width: 420px;
|
width: 420px;
|
||||||
padding: $spacing-xl;
|
padding: $spacing-xl;
|
||||||
background: $bg-glass;
|
background: $bg-glass;
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
border: 1px solid $border-light;
|
border: 1px solid $border-light;
|
||||||
border-radius: $border-radius-lg;
|
border-radius: $border-radius-lg;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 8px 32px rgba(0, 0, 0, 0.1),
|
0 8px 32px rgba(0, 0, 0, 0.1),
|
||||||
0 4px 16px rgba(0, 0, 0, 0.1),
|
0 4px 16px rgba(0, 0, 0, 0.1),
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||||
animation: slideInUp 0.6s ease-out;
|
animation: slideInUp 0.6s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideInUp {
|
@keyframes slideInUp {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(30px);
|
transform: translateY(30px);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-header {
|
.login-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: $spacing-xl;
|
margin-bottom: $spacing-xl;
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: $spacing-sm;
|
gap: $spacing-sm;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: $white;
|
color: $white;
|
||||||
margin: 0 0 $spacing-sm 0;
|
margin: 0 0 $spacing-sm 0;
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
.title-icon {
|
.title-icon {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
color: $primary-light;
|
color: $primary-light;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-subtitle {
|
.login-subtitle {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $text-light;
|
color: $text-light;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-form {
|
.login-form {
|
||||||
.form-item {
|
.form-item {
|
||||||
margin-bottom: $spacing-lg;
|
margin-bottom: $spacing-lg;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-input {
|
.login-input {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: rgba(255, 255, 255, 0.4);
|
border-color: rgba(255, 255, 255, 0.4);
|
||||||
background: rgba(255, 255, 255, 0.15);
|
background: rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
border-color: $primary-light;
|
border-color: $primary-light;
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
|
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-input) {
|
:deep(.ant-input) {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
color: $white;
|
color: $white;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-input-password-icon) {
|
:deep(.ant-input-password-icon) {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-icon {
|
.input-icon {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-container {
|
.captcha-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: $spacing-md;
|
gap: $spacing-md;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
.captcha-input {
|
.captcha-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-image-container {
|
.captcha-image-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: $primary-light;
|
border-color: $primary-light;
|
||||||
|
|
||||||
.captcha-refresh-hint {
|
.captcha-refresh-hint {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-image {
|
.captcha-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-loading {
|
.captcha-loading {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
|
||||||
.loading-icon {
|
.loading-icon {
|
||||||
color: $white;
|
color: $white;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
animation: spin 1s linear infinite;
|
animation: spin 1s linear infinite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-refresh-hint {
|
.captcha-refresh-hint {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0, 0, 0, 0.7);
|
background: rgba(0, 0, 0, 0.7);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: $white;
|
color: $white;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
|
|
||||||
.refresh-icon {
|
.refresh-icon {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-button {
|
.login-button {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
background: linear-gradient(135deg, $primary-color 0%, $primary-dark 100%);
|
background: linear-gradient(135deg, $primary-color 0%, $primary-dark 100%);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: $transition;
|
transition: $transition;
|
||||||
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
|
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: linear-gradient(135deg, $primary-light 0%, $primary-color 100%);
|
background: linear-gradient(135deg, $primary-light 0%, $primary-color 100%);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 6px 20px rgba(74, 144, 226, 0.4);
|
box-shadow: 0 6px 20px rgba(74, 144, 226, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-icon {
|
.button-icon {
|
||||||
margin-right: $spacing-sm;
|
margin-right: $spacing-sm;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-btn-loading-icon) {
|
:deep(.ant-btn-loading-icon) {
|
||||||
margin-right: $spacing-sm;
|
margin-right: $spacing-sm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表单验证错误样式
|
// 表单验证错误样式
|
||||||
:deep(.ant-form-item-has-error) {
|
:deep(.ant-form-item-has-error) {
|
||||||
.login-input {
|
.login-input {
|
||||||
border-color: $error-color;
|
border-color: $error-color;
|
||||||
|
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2);
|
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-image-container {
|
.captcha-image-container {
|
||||||
border-color: $error-color;
|
border-color: $error-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-form-item-explain-error) {
|
:deep(.ant-form-item-explain-error) {
|
||||||
color: rgba(255, 255, 255, 0.9);
|
color: rgba(255, 255, 255, 0.9);
|
||||||
background: rgba(255, 77, 79, 0.1);
|
background: rgba(255, 77, 79, 0.1);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式设计
|
// 响应式设计
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.login-box {
|
.login-box {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
max-width: 380px;
|
max-width: 380px;
|
||||||
padding: $spacing-lg;
|
padding: $spacing-lg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha-container {
|
.captcha-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.captcha-image-container {
|
.captcha-image-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.login-box {
|
.login-box {
|
||||||
padding: $spacing-md;
|
padding: $spacing-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-header .login-title {
|
.login-header .login-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
||||||
.title-icon {
|
.title-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,86 +1,108 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getCurrentInstance } from 'vue';
|
import { getCurrentInstance } from 'vue'
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router'
|
||||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||||||
import { useTraceOrderStore } from '@/store';
|
import { useTraceOrderStore } from '@/store'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as any
|
const { proxy } = getCurrentInstance() as any
|
||||||
const { mes_station_status } = proxy.useDict("mes_station_status", "lot_trace_order_status")
|
const { mes_station_status } = proxy.useDict('mes_station_status')
|
||||||
|
|
||||||
interface TableItem {
|
interface TableItem {
|
||||||
key: string;
|
key: string
|
||||||
index: number;
|
index: number
|
||||||
operationTitle: string;
|
operationTitle: string
|
||||||
planQty: number;
|
planQty: number
|
||||||
okQty: number;
|
okQty: number
|
||||||
ngQty: number;
|
ngQty: number
|
||||||
status: string;
|
status: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: '制程', dataIndex: 'operationTitle', key: 'operationTitle', align: 'center' },
|
title: '序号',
|
||||||
{ title: '总数量', dataIndex: 'planQty', key: 'planQty', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: '合格数量', dataIndex: 'okQty', key: 'okQty', align: 'center' },
|
key: 'index',
|
||||||
{ title: '报废数量', dataIndex: 'ngQty', key: 'ngQty', align: 'center' },
|
align: 'center',
|
||||||
{ title: '状态', dataIndex: 'status', key: 'status', align: 'center' },
|
width: 80,
|
||||||
{ title: '操作', key: 'action', align: 'center' },
|
},
|
||||||
];
|
{
|
||||||
|
title: '制程',
|
||||||
|
dataIndex: 'operationTitle',
|
||||||
|
key: 'operationTitle',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: '总数量', dataIndex: 'planQty', key: 'planQty', align: 'center' },
|
||||||
|
{ title: '合格数量', dataIndex: 'okQty', key: 'okQty', align: 'center' },
|
||||||
|
{ title: '报废数量', dataIndex: 'ngQty', key: 'ngQty', align: 'center' },
|
||||||
|
{ title: '状态', dataIndex: 'status', key: 'status', align: 'center' },
|
||||||
|
{ title: '操作', key: 'action', align: 'center' },
|
||||||
|
]
|
||||||
|
|
||||||
const handleInfeed = (record: any) => {
|
const handleInfeed = (record: any) => {
|
||||||
router.push({ name: 'Infeed' });
|
router.push({ name: 'Infeed' })
|
||||||
traceOrderStore.selectStation(record.id);
|
traceOrderStore.selectStation(record.id)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOutfeed = (record: any) => {
|
const handleOutfeed = (record: any) => {
|
||||||
router.push({ name: 'Outfeed' });
|
router.push({ name: 'Outfeed' })
|
||||||
traceOrderStore.selectStation(record.id);
|
traceOrderStore.selectStation(record.id)
|
||||||
};
|
}
|
||||||
|
|
||||||
function rowClick(record: TableItem) {
|
function rowClick(record: TableItem) {
|
||||||
return {
|
return {
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
traceOrderStore.selectStation(record.id);
|
traceOrderStore.selectStation(record.id)
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<a-table
|
<a-table
|
||||||
:dataSource="traceOrderStore.stationList"
|
:dataSource="traceOrderStore.stationList"
|
||||||
:columns="columns as ColumnsType<TableItem>"
|
:columns="columns as ColumnsType<TableItem>"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:loading="traceOrderStore.loadingStations"
|
:loading="traceOrderStore.loadingStations"
|
||||||
bordered
|
bordered
|
||||||
sticky
|
sticky
|
||||||
rowKey="key"
|
rowKey="key"
|
||||||
class="custom-table"
|
class="custom-table"
|
||||||
:customRow="rowClick"
|
:customRow="rowClick"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'status'">
|
<template v-if="column.key === 'status'">
|
||||||
<DictTag :options="mes_station_status" :value="record.status" size="large" />
|
<DictTag
|
||||||
</template>
|
:options="mes_station_status"
|
||||||
<template v-if="column.key === 'action'">
|
:value="record.status"
|
||||||
<a-space>
|
size="large"
|
||||||
<a-button size="large" @click="handleInfeed(record)">进站</a-button>
|
/>
|
||||||
<a-button size="large" type="primary" danger @click="handleOutfeed(record)">出站</a-button>
|
</template>
|
||||||
</a-space>
|
<template v-if="column.key === 'action'">
|
||||||
</template>
|
<a-space>
|
||||||
</template>
|
<a-button size="large" @click="handleInfeed(record)">进站</a-button>
|
||||||
</a-table>
|
<a-button
|
||||||
</div>
|
size="large"
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
@click="handleOutfeed(record)"
|
||||||
|
>
|
||||||
|
出站
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,182 +1,214 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, reactive } from 'vue';
|
import { ref, onMounted, reactive } from 'vue'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
import { useTraceOrderStore } from '@/store';
|
import { useTraceOrderStore } from '@/store'
|
||||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||||||
import {
|
import {
|
||||||
getEquipmentByCode,
|
getEquipmentByCode,
|
||||||
listEquipmentEntry,
|
listEquipmentEntry,
|
||||||
addEquipmentEntry,
|
addEquipmentEntry,
|
||||||
delEquipmentEntry,
|
delEquipmentEntry,
|
||||||
} from '@/api/traceOrderManage/equipment';
|
} from '@/api/traceOrderManage/equipment'
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
interface EquipmentTableItem {
|
interface EquipmentTableItem {
|
||||||
key: string;
|
key: string
|
||||||
equipmentName: string;
|
equipmentName: string
|
||||||
equipmentCode: string;
|
equipmentCode: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const equipmentColumns = [
|
const equipmentColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: '设备编码', dataIndex: 'equipmentCode', key: 'equipmentCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: '设备名称', dataIndex: 'equipmentTitle', key: 'equipmentTitle', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center' },
|
key: 'index',
|
||||||
];
|
align: 'center',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备编码',
|
||||||
|
dataIndex: 'equipmentCode',
|
||||||
|
key: 'equipmentCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
dataIndex: 'equipmentTitle',
|
||||||
|
key: 'equipmentTitle',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center' },
|
||||||
|
]
|
||||||
|
|
||||||
const equipmentInfo = reactive({
|
const equipmentInfo = reactive({
|
||||||
equipmentTitle: '',
|
equipmentTitle: '',
|
||||||
equipmentCode: '',
|
equipmentCode: '',
|
||||||
});
|
})
|
||||||
|
|
||||||
const fetchEquipmentInfo = async () => {
|
const fetchEquipmentInfo = async () => {
|
||||||
const { data } = await getEquipmentByCode(equipmentInput.value)
|
const { data } = await getEquipmentByCode(equipmentInput.value)
|
||||||
if (!data) {
|
if (!data) {
|
||||||
message.error('设备不存在');
|
message.error('设备不存在')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
Object.assign(equipmentInfo, data);
|
Object.assign(equipmentInfo, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 治具表格
|
// 治具表格
|
||||||
const equipmentTableData = ref<EquipmentTableItem[]>([]);
|
const equipmentTableData = ref<EquipmentTableItem[]>([])
|
||||||
|
|
||||||
const equipmentInput = ref<string>('');
|
const equipmentInput = ref<string>('')
|
||||||
|
|
||||||
const handleBind = async () => {
|
const handleBind = async () => {
|
||||||
if (!equipmentInput.value) return;
|
if (!equipmentInput.value) return
|
||||||
try {
|
try {
|
||||||
await addEquipmentEntry({
|
await addEquipmentEntry({
|
||||||
equipmentCode: equipmentInput.value,
|
equipmentCode: equipmentInput.value,
|
||||||
stationCode: traceOrderStore.currentStationCode,
|
stationCode: traceOrderStore.currentStationCode,
|
||||||
});
|
})
|
||||||
message.success('绑定成功');
|
message.success('绑定成功')
|
||||||
equipmentInput.value = '';
|
equipmentInput.value = ''
|
||||||
fetchBoundEquipmentList();
|
fetchBoundEquipmentList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`绑定失败: ${ error.message }`);
|
message.error(`绑定失败: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUnbind = async (id: number) => {
|
const handleUnbind = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
await delEquipmentEntry(id);
|
await delEquipmentEntry(id)
|
||||||
message.success('解绑成功');
|
message.success('解绑成功')
|
||||||
fetchBoundEquipmentList();
|
fetchBoundEquipmentList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`解绑失败: ${ error.message }`);
|
message.error(`解绑失败: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取已绑定的设备列表
|
// 获取已绑定的设备列表
|
||||||
const loadingEquipmentTableData = ref(false);
|
const loadingEquipmentTableData = ref(false)
|
||||||
const fetchBoundEquipmentList = async () => {
|
const fetchBoundEquipmentList = async () => {
|
||||||
const stationId = traceOrderStore.currentStationId;
|
const stationId = traceOrderStore.currentStationId
|
||||||
if (!stationId) return;
|
if (!stationId) return
|
||||||
loadingEquipmentTableData.value = true;
|
loadingEquipmentTableData.value = true
|
||||||
try {
|
try {
|
||||||
const { rows } = await listEquipmentEntry({ stationId });
|
const { rows } = await listEquipmentEntry({ stationId })
|
||||||
equipmentTableData.value = rows;
|
equipmentTableData.value = rows
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '查询治具列表失败');
|
message.error(error.message || '查询治具列表失败')
|
||||||
} finally {
|
} finally {
|
||||||
loadingEquipmentTableData.value = false;
|
loadingEquipmentTableData.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
fetchBoundEquipmentList();
|
fetchBoundEquipmentList()
|
||||||
renderTableHeight();
|
renderTableHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算表格高度
|
// 计算表格高度
|
||||||
const customTable = ref<HTMLElement | null>(null)
|
const customTable = ref<HTMLElement | null>(null)
|
||||||
const tableHeight = ref(200)
|
const tableHeight = ref(200)
|
||||||
const renderTableHeight = () => {
|
const renderTableHeight = () => {
|
||||||
if (customTable.value) {
|
if (customTable.value) {
|
||||||
tableHeight.value = customTable.value.clientHeight - 50
|
tableHeight.value = customTable.value.clientHeight - 50
|
||||||
console.log('元素高度:', tableHeight.value)
|
console.log('元素高度:', tableHeight.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
renderTableHeight()
|
renderTableHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
|
|
||||||
fetchBoundEquipmentList()
|
fetchBoundEquipmentList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="equipment__container">
|
<div class="equipment__container">
|
||||||
<Title name="绑定设备列表" showRefresh @refresh="handleRefresh" />
|
<Title name="绑定设备列表" showRefresh @refresh="handleRefresh" />
|
||||||
<a-row class="main-content" :gutter="24">
|
<a-row class="main-content" :gutter="24">
|
||||||
<a-col :span="10" class="input__container">
|
<a-col :span="10" class="input__container">
|
||||||
<a-row :gutter="12">
|
<a-row :gutter="12">
|
||||||
<a-col :span="21">
|
<a-col :span="21">
|
||||||
<a-input size="large" v-model:value="equipmentInput" @pressEnter="fetchEquipmentInfo" placeholder="按下回车搜索" />
|
<a-input
|
||||||
</a-col>
|
size="large"
|
||||||
<a-col :span="3">
|
v-model:value="equipmentInput"
|
||||||
<a-button size="large" @click="handleBind">绑定</a-button>
|
@pressEnter="fetchEquipmentInfo"
|
||||||
</a-col>
|
placeholder="按下回车搜索"
|
||||||
</a-row>
|
/>
|
||||||
<div class="description-wrapper">
|
</a-col>
|
||||||
<a-descriptions :column="1" bordered>
|
<a-col :span="3">
|
||||||
<a-descriptions-item label="设备编码">{{ equipmentInfo.equipmentCode }}</a-descriptions-item>
|
<a-button size="large" @click="handleBind">绑定</a-button>
|
||||||
<a-descriptions-item label="设备名称">{{ equipmentInfo.equipmentTitle }}</a-descriptions-item>
|
</a-col>
|
||||||
</a-descriptions>
|
</a-row>
|
||||||
</div>
|
<div class="description-wrapper">
|
||||||
</a-col>
|
<a-descriptions :column="1" bordered>
|
||||||
<a-col :span="14">
|
<a-descriptions-item label="设备编码">
|
||||||
<div class="table-wrapper" ref="customTable">
|
{{ equipmentInfo.equipmentCode }}
|
||||||
<a-table :dataSource="equipmentTableData" :columns="equipmentColumns as ColumnsType<EquipmentTableItem>"
|
</a-descriptions-item>
|
||||||
:pagination="false" bordered sticky :scroll="{ y: tableHeight }" :loading="loadingEquipmentTableData">
|
<a-descriptions-item label="设备名称">
|
||||||
<template #bodyCell="{ column, index, record }">
|
{{ equipmentInfo.equipmentTitle }}
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
</a-descriptions-item>
|
||||||
<template v-if="column.key === 'action'">
|
</a-descriptions>
|
||||||
<a-button @click="handleUnbind(record.id)">解绑</a-button>
|
</div>
|
||||||
</template>
|
</a-col>
|
||||||
</template>
|
<a-col :span="14">
|
||||||
</a-table>
|
<div class="table-wrapper" ref="customTable">
|
||||||
</div>
|
<a-table
|
||||||
</a-col>
|
:dataSource="equipmentTableData"
|
||||||
</a-row>
|
:columns="equipmentColumns as ColumnsType<EquipmentTableItem>"
|
||||||
</div>
|
:pagination="false"
|
||||||
|
bordered
|
||||||
|
sticky
|
||||||
|
:scroll="{ y: tableHeight }"
|
||||||
|
:loading="loadingEquipmentTableData"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, index, record }">
|
||||||
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-button @click="handleUnbind(record.id)">解绑</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.equipment__container {
|
.equipment__container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input__container {
|
.input__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.description-wrapper {
|
.description-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,167 +1,169 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, inject } from 'vue';
|
import { ref, computed } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { startStation } from '@/api/traceOrderManage/station';
|
import { startStation } from '@/api/traceOrderManage/station'
|
||||||
import { useTraceOrderStore } from '@/store';
|
import { useTraceOrderStore } from '@/store'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ label: '主材', key: 'PrimaryMaterial', progress: 30 },
|
{ label: '主材', key: 'PrimaryMaterial', progress: 30 },
|
||||||
{ label: '治具', key: 'Mask', progress: 80 },
|
{ label: '治具', key: 'Mask', progress: 80 },
|
||||||
{ label: '设备', key: 'Equipment', progress: 80 },
|
{ label: '设备', key: 'Equipment', progress: 80 },
|
||||||
];
|
]
|
||||||
|
|
||||||
const activeKey = computed(() => route.name as string);
|
const activeKey = computed(() => route.name as string)
|
||||||
|
|
||||||
const handleMenuClick = (name: string) => {
|
const handleMenuClick = (name: string) => {
|
||||||
router.push({ name});
|
router.push({ name })
|
||||||
};
|
}
|
||||||
|
|
||||||
// 确认进站
|
// 确认进站
|
||||||
const infeeding = ref(false);
|
const infeeding = ref(false)
|
||||||
const handleInfeed = async () => {
|
const handleInfeed = async () => {
|
||||||
infeeding.value = true;
|
infeeding.value = true
|
||||||
try {
|
try {
|
||||||
await startStation(traceOrderStore.currentStationId);
|
await startStation(traceOrderStore.currentStationId)
|
||||||
message.success('进站成功');
|
message.success('进站成功')
|
||||||
traceOrderStore.fetchStationInfo();
|
traceOrderStore.fetchStationInfo()
|
||||||
traceOrderStore.fetchStationList();
|
traceOrderStore.fetchStationList()
|
||||||
router.push({ name: 'TraceOrderManage' });
|
router.push({ name: 'TraceOrderManage' })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '进站失败');
|
message.error(error.message || '进站失败')
|
||||||
} finally {
|
} finally {
|
||||||
infeeding.value = false;
|
infeeding.value = false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const infeedChildRef = ref<any>(null);
|
const infeedChildRef = ref<any>(null)
|
||||||
|
|
||||||
/** 父组件对外暴露的方法 */
|
/** 父组件对外暴露的方法 */
|
||||||
function renderTableHeight() {
|
function renderTableHeight() {
|
||||||
infeedChildRef.value?.renderTableHeight();
|
infeedChildRef.value?.renderTableHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-spin :spinning="infeeding">
|
<a-spin :spinning="infeeding">
|
||||||
<a-row class="infeed-layout">
|
<a-row class="infeed-layout">
|
||||||
<a-col :span="20" class="content-wrapper">
|
<a-col :span="20" class="content-wrapper">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<component :is="Component" ref="infeedChildRef" />
|
<component :is="Component" ref="infeedChildRef" />
|
||||||
</router-view>
|
</router-view>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="4" class="menu-wrapper">
|
<a-col :span="4" class="menu-wrapper">
|
||||||
<div class="menu-list">
|
<div class="menu-list">
|
||||||
<span class="infeed-title">进站</span>
|
<span class="infeed-title">进站</span>
|
||||||
<div
|
<div
|
||||||
v-for="item in menuItems"
|
v-for="item in menuItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{ active: activeKey === item.key }"
|
:class="{ active: activeKey === item.key }"
|
||||||
@click="handleMenuClick(item.key)"
|
@click="handleMenuClick(item.key)"
|
||||||
>
|
>
|
||||||
<div class="menu-content">
|
<div class="menu-content">
|
||||||
<span class="menu-title">{{ item.label }}</span>
|
<span class="menu-title">{{ item.label }}</span>
|
||||||
<a-progress
|
<a-progress
|
||||||
:percent="item.progress"
|
:percent="item.progress"
|
||||||
:show-info="false"
|
:show-info="false"
|
||||||
size="small"
|
size="small"
|
||||||
:stroke-color="activeKey === item.key ? '#1890ff' : undefined"
|
:stroke-color="activeKey === item.key ? '#1890ff' : undefined"
|
||||||
class="menu-progress"
|
class="menu-progress"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-button type="primary" size="large" @click="handleInfeed">确认进站</a-button>
|
<a-button type="primary" size="large" @click="handleInfeed">
|
||||||
</div>
|
确认进站
|
||||||
</a-col>
|
</a-button>
|
||||||
</a-row>
|
</div>
|
||||||
</a-spin>
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.infeed-layout {
|
.infeed-layout {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-list {
|
.menu-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infeed-title {
|
.infeed-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom: 1px solid #c9c9c9;
|
border-bottom: 1px solid #c9c9c9;
|
||||||
padding: 0 0 8px;
|
padding: 0 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1vh 1vw;
|
padding: 1vh 1vw;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
border: 1px solid #f0f0f0;
|
border: 1px solid #f0f0f0;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: #e6f7ff;
|
background: #e6f7ff;
|
||||||
border-color: #1890ff;
|
border-color: #1890ff;
|
||||||
|
|
||||||
.menu-title {
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
.menu-title {
|
||||||
border-color: #47a5fd;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #47a5fd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-content {
|
.menu-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-title {
|
.menu-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-progress {
|
.menu-progress {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
|
|
||||||
:deep(.ant-progress-bg) {
|
:deep(.ant-progress-bg) {
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-wrapper {
|
.menu-wrapper {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
background-color: #f3f3f3;
|
background-color: #f3f3f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,219 +1,306 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, reactive } from 'vue';
|
import { ref, onMounted, reactive } from 'vue'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||||||
import { listMaskCombination } from "@/api/traceOrderManage/maskCombination";
|
import { listMaskCombination } from '@/api/traceOrderManage/maskCombination'
|
||||||
import { listStationBindMask, bindMaskCombinationToStations, unbindStationMask } from "@/api/traceOrderManage/mask";
|
import {
|
||||||
import { useTraceOrderStore } from '@/store';
|
listStationBindMask,
|
||||||
|
bindMaskCombinationToStations,
|
||||||
|
unbindStationMask,
|
||||||
|
} from '@/api/traceOrderManage/mask'
|
||||||
|
import { useTraceOrderStore } from '@/store'
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
interface MaskTableItem {
|
interface MaskTableItem {
|
||||||
key: string;
|
key: string
|
||||||
operationTitle: string;
|
operationTitle: string
|
||||||
operationCode: string;
|
operationCode: string
|
||||||
maskName: string;
|
maskName: string
|
||||||
maskCode: string;
|
maskCode: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MaskCombinationItem {
|
interface MaskCombinationItem {
|
||||||
key: string;
|
key: string
|
||||||
combinationCode: string;
|
combinationCode: string
|
||||||
combinationName: string;
|
combinationName: string
|
||||||
combinationStatus: string;
|
combinationStatus: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const maskColumns = [
|
const maskColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: '工序编码', dataIndex: 'operationCode', key: 'operationCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: '工序名称', dataIndex: 'operationTitle', key: 'operationTitle', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: '治具编码', dataIndex: 'maskCode', key: 'maskCode', align: 'center' },
|
key: 'index',
|
||||||
{ title: '治具名称', dataIndex: 'maskName', key: 'maskName', align: 'center' },
|
align: 'center',
|
||||||
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center' },
|
width: 80,
|
||||||
];
|
},
|
||||||
|
{
|
||||||
|
title: '工序编码',
|
||||||
|
dataIndex: 'operationCode',
|
||||||
|
key: 'operationCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '工序名称',
|
||||||
|
dataIndex: 'operationTitle',
|
||||||
|
key: 'operationTitle',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '治具编码',
|
||||||
|
dataIndex: 'maskCode',
|
||||||
|
key: 'maskCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '治具名称',
|
||||||
|
dataIndex: 'maskName',
|
||||||
|
key: 'maskName',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center' },
|
||||||
|
]
|
||||||
|
|
||||||
// 治具表格
|
// 治具表格
|
||||||
const maskTableData = ref<MaskTableItem[]>([]);
|
const maskTableData = ref<MaskTableItem[]>([])
|
||||||
|
|
||||||
const maskInput = ref<string>('');
|
const maskInput = ref<string>('')
|
||||||
|
|
||||||
const openMaskModal = ref(false)
|
const openMaskModal = ref(false)
|
||||||
const maskCombinationColumns = [
|
const maskCombinationColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: '组合编码', dataIndex: 'combinationCode', key: 'combinationCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: '组合名称', dataIndex: 'combinationName', key: 'combinationName', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: '组合状态', dataIndex: 'combinationStatus', key: 'combinationStatus', align: 'center' },
|
key: 'index',
|
||||||
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center', width: 100 },
|
align: 'center',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '组合编码',
|
||||||
|
dataIndex: 'combinationCode',
|
||||||
|
key: 'combinationCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '组合名称',
|
||||||
|
dataIndex: 'combinationName',
|
||||||
|
key: 'combinationName',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '组合状态',
|
||||||
|
dataIndex: 'combinationStatus',
|
||||||
|
key: 'combinationStatus',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
const maskCombinationTableData = ref<MaskCombinationItem[]>([])
|
const maskCombinationTableData = ref<MaskCombinationItem[]>([])
|
||||||
const fetchCombinationList = async () => {
|
const fetchCombinationList = async () => {
|
||||||
try {
|
try {
|
||||||
const { rows, total } = await listMaskCombination({})
|
const { rows, total } = await listMaskCombination({})
|
||||||
if (total as number <= 0) throw new Error('未查询到组合列表');
|
if ((total as number) <= 0) throw new Error('未查询到组合列表')
|
||||||
maskCombinationTableData.value = rows;
|
maskCombinationTableData.value = rows
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '获取工单信息失败');
|
message.error(error.message || '获取工单信息失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBind = async (id: number) => {
|
const handleBind = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
await bindMaskCombinationToStations({
|
await bindMaskCombinationToStations({
|
||||||
lotTraceOrderId: traceOrderStore.currentTraceOrderId,
|
lotTraceOrderId: traceOrderStore.currentTraceOrderId,
|
||||||
maskCombinationId: id
|
maskCombinationId: id,
|
||||||
});
|
})
|
||||||
message.success('绑定成功');
|
message.success('绑定成功')
|
||||||
fetchBoundMaskList();
|
fetchBoundMaskList()
|
||||||
openMaskModal.value = false;
|
openMaskModal.value = false
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`绑定失败: ${ error.message }`);
|
message.error(`绑定失败: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUnbind = async (id: number) => {
|
const handleUnbind = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
await unbindStationMask(id);
|
await unbindStationMask(id)
|
||||||
message.success('解绑成功');
|
message.success('解绑成功')
|
||||||
fetchBoundMaskList();
|
fetchBoundMaskList()
|
||||||
openMaskModal.value = false;
|
openMaskModal.value = false
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(`解绑失败: ${ error.message }`);
|
message.error(`解绑失败: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取已绑定的治具列表
|
// 获取已绑定的治具列表
|
||||||
const loadingMask = ref(false);
|
const loadingMask = ref(false)
|
||||||
const fetchBoundMaskList = async () => {
|
const fetchBoundMaskList = async () => {
|
||||||
const traceOrderId = traceOrderStore.currentTraceOrderId;
|
const traceOrderId = traceOrderStore.currentTraceOrderId
|
||||||
if (!traceOrderId) return;
|
if (!traceOrderId) return
|
||||||
loadingMask.value = true;
|
loadingMask.value = true
|
||||||
try {
|
try {
|
||||||
const { rows, total } = await listStationBindMask({ traceOrderId })
|
const { rows, total } = await listStationBindMask({ traceOrderId })
|
||||||
if (total as number <= 0) return;
|
if ((total as number) <= 0) return
|
||||||
maskTableData.value = rows;
|
maskTableData.value = rows
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '查询治具列表失败');
|
message.error(error.message || '查询治具列表失败')
|
||||||
} finally {
|
} finally {
|
||||||
loadingMask.value = false;
|
loadingMask.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const inspectingMask = reactive({
|
const inspectingMask = reactive({
|
||||||
maskCode: '',
|
maskCode: '',
|
||||||
maskName: '',
|
maskName: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const rowClick = (record: MaskTableItem) => {
|
const rowClick = (record: MaskTableItem) => {
|
||||||
inspectingMask.maskCode = record.maskCode;
|
return {
|
||||||
inspectingMask.maskName = record.maskName;
|
onClick: () => {
|
||||||
|
inspectingMask.maskCode = record.maskCode
|
||||||
|
inspectingMask.maskName = record.maskName
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
fetchBoundMaskList();
|
fetchBoundMaskList()
|
||||||
renderTableHeight();
|
renderTableHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算表格高度
|
// 计算表格高度
|
||||||
const customTable = ref<HTMLElement | null>(null)
|
const customTable = ref<HTMLElement | null>(null)
|
||||||
const tableHeight = ref(200)
|
const tableHeight = ref(200)
|
||||||
const renderTableHeight = () => {
|
const renderTableHeight = () => {
|
||||||
if (customTable.value) {
|
if (customTable.value) {
|
||||||
tableHeight.value = customTable.value.clientHeight - 50
|
tableHeight.value = customTable.value.clientHeight - 50
|
||||||
console.log('元素高度:', tableHeight.value)
|
console.log('元素高度:', tableHeight.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
renderTableHeight()
|
renderTableHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
|
|
||||||
fetchCombinationList()
|
fetchCombinationList()
|
||||||
fetchBoundMaskList()
|
fetchBoundMaskList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mask__container">
|
<div class="mask__container">
|
||||||
<a-row class="main-content" :gutter="24">
|
<a-row class="main-content" :gutter="24">
|
||||||
<a-col :span="14" class="mask-item__container">
|
<a-col :span="14" class="mask-item__container">
|
||||||
<Title name="治具组合信息" showRefresh @refresh="handleRefresh" />
|
<Title name="治具组合信息" showRefresh @refresh="handleRefresh" />
|
||||||
<a-button size="large" @click="() => openMaskModal = true">绑定治具组合</a-button>
|
<a-button size="large" @click="() => (openMaskModal = true)">
|
||||||
<div class="table-wrapper" ref="customTable">
|
绑定治具组合
|
||||||
<a-table :dataSource="maskTableData" :columns="maskColumns as ColumnsType<MaskTableItem>"
|
</a-button>
|
||||||
:pagination="false" bordered sticky :scroll="{ y: tableHeight }" :loading="loadingMask">
|
<div class="table-wrapper" ref="customTable">
|
||||||
<template #bodyCell="{ column, index, record }">
|
<a-table
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
:dataSource="maskTableData"
|
||||||
<template v-if="column.key === 'action'">
|
:columns="maskColumns as ColumnsType<MaskTableItem>"
|
||||||
<a-button @click="handleUnbind(record.id)">解绑</a-button>
|
:pagination="false"
|
||||||
</template>
|
bordered
|
||||||
</template>
|
sticky
|
||||||
</a-table>
|
:scroll="{ y: tableHeight }"
|
||||||
</div>
|
:loading="loadingMask"
|
||||||
</a-col>
|
:customRow="rowClick"
|
||||||
<a-col :span="10" class="mask-item__container">
|
>
|
||||||
<Title name="治具校验" />
|
<template #bodyCell="{ column, index, record }">
|
||||||
<a-row>
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
<a-col :span="20">
|
<template v-if="column.key === 'action'">
|
||||||
<a-input size="large" v-model:value="maskInput" @pressEnter="" placeholder="按下回车校验" />
|
<a-button @click="handleUnbind(record.id)">解绑</a-button>
|
||||||
</a-col>
|
</template>
|
||||||
<a-col :span="3" :offset="1">
|
</template>
|
||||||
<a-button size="large" @click="">校验</a-button>
|
</a-table>
|
||||||
</a-col>
|
</div>
|
||||||
</a-row>
|
</a-col>
|
||||||
<div class="table-wrapper">
|
<a-col :span="10" class="mask-item__container">
|
||||||
<a-descriptions bordered :column="1">
|
<Title name="治具校验" />
|
||||||
<a-descriptions-item label="治具编码"></a-descriptions-item>
|
<a-row>
|
||||||
<a-descriptions-item label="治具名称"></a-descriptions-item>
|
<a-col :span="20">
|
||||||
<a-descriptions-item label="治具组合"></a-descriptions-item>
|
<a-input
|
||||||
<a-descriptions-item label="标准使用次数"></a-descriptions-item>
|
size="large"
|
||||||
<a-descriptions-item label="当前使用次数"></a-descriptions-item>
|
v-model:value="maskInput"
|
||||||
<a-descriptions-item label="关联工序"></a-descriptions-item>
|
placeholder="按下回车校验"
|
||||||
</a-descriptions>
|
/>
|
||||||
</div>
|
</a-col>
|
||||||
</a-col>
|
<a-col :span="3" :offset="1">
|
||||||
</a-row>
|
<a-button size="large">校验</a-button>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<a-descriptions bordered :column="1">
|
||||||
|
<a-descriptions-item label="治具编码"></a-descriptions-item>
|
||||||
|
<a-descriptions-item label="治具名称"></a-descriptions-item>
|
||||||
|
<a-descriptions-item label="治具组合"></a-descriptions-item>
|
||||||
|
<a-descriptions-item label="标准使用次数"></a-descriptions-item>
|
||||||
|
<a-descriptions-item label="当前使用次数"></a-descriptions-item>
|
||||||
|
<a-descriptions-item label="关联工序"></a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
<a-modal v-model:open="openMaskModal" title="绑定治具组合" width="50vw" :bodyStyle="{ padding: '20px 0' }" :footer="null">
|
<a-modal
|
||||||
<a-table :dataSource="maskCombinationTableData" :columns="maskCombinationColumns as ColumnsType<MaskCombinationItem>"
|
v-model:open="openMaskModal"
|
||||||
:pagination="false" bordered sticky :scroll="{ y: tableHeight }">
|
title="绑定治具组合"
|
||||||
<template #bodyCell="{ column, index, record }">
|
width="50vw"
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
:bodyStyle="{ padding: '20px 0' }"
|
||||||
<template v-if="column.key === 'action'">
|
:footer="null"
|
||||||
<a-button @click="handleBind(record.id)">绑定</a-button>
|
>
|
||||||
</template>
|
<a-table
|
||||||
</template>
|
:dataSource="maskCombinationTableData"
|
||||||
</a-table>
|
:columns="maskCombinationColumns as ColumnsType<MaskCombinationItem>"
|
||||||
</a-modal>
|
:pagination="false"
|
||||||
</div>
|
bordered
|
||||||
|
sticky
|
||||||
|
:scroll="{ y: tableHeight }"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, index, record }">
|
||||||
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-button @click="handleBind(record.id)">绑定</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.mask__container {
|
.mask__container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.mask-item__container {
|
.mask-item__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,187 +1,233 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue'
|
||||||
import { useTraceOrderStore } from '@/store'
|
import { useTraceOrderStore } from '@/store'
|
||||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { listMainMaterialEntryLog, addWaferEntryLogByCarrier, addDieEntryLogByCarrier, addWaferEntryLog, addDieEntryLog, delMainMaterialEntryLog } from '@/api/traceOrderManage/primaryMaterial'
|
import {
|
||||||
|
listMainMaterialEntryLog,
|
||||||
|
addWaferEntryLogByCarrier,
|
||||||
|
addDieEntryLogByCarrier,
|
||||||
|
addWaferEntryLog,
|
||||||
|
addDieEntryLog,
|
||||||
|
delMainMaterialEntryLog,
|
||||||
|
} from '@/api/traceOrderManage/primaryMaterial'
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
interface MaterialTableItem {
|
interface MaterialTableItem {
|
||||||
key: string;
|
key: string
|
||||||
carrierCode: string;
|
carrierCode: string
|
||||||
qrCode: string;
|
qrCode: string
|
||||||
dieCode: string;
|
dieCode: string
|
||||||
waferCode: string;
|
waferCode: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const materialColumns = [
|
const materialColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: '载具 ID', dataIndex: 'carrierCode', key: 'carrierCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: 'Wafer ID', dataIndex: 'waferCode', key: 'waferCode', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: 'Die ID', dataIndex: 'dieCode', key: 'dieCode', align: 'center' },
|
key: 'index',
|
||||||
{ title: '二维码', dataIndex: 'qrCode', key: 'qrCode', align: 'center' },
|
align: 'center',
|
||||||
{ title: '操作', key: 'action', align: 'center', width: 120 },
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '载具 ID',
|
||||||
|
dataIndex: 'carrierCode',
|
||||||
|
key: 'carrierCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Wafer ID',
|
||||||
|
dataIndex: 'waferCode',
|
||||||
|
key: 'waferCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: 'Die ID', dataIndex: 'dieCode', key: 'dieCode', align: 'center' },
|
||||||
|
{ title: '二维码', dataIndex: 'qrCode', key: 'qrCode', align: 'center' },
|
||||||
|
{ title: '操作', key: 'action', align: 'center', width: 120 },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 查询站点下主材信息
|
// 查询站点下主材信息
|
||||||
const loadingMaterialTableData = ref(false);
|
const loadingMaterialTableData = ref(false)
|
||||||
const fetchPrimaryMaterialList = async () => {
|
const fetchPrimaryMaterialList = async () => {
|
||||||
const stationId = traceOrderStore.currentStationId;
|
const stationId = traceOrderStore.currentStationId
|
||||||
if (!stationId) return;
|
if (!stationId) return
|
||||||
loadingMaterialTableData.value = true;
|
loadingMaterialTableData.value = true
|
||||||
try {
|
try {
|
||||||
const { rows } = await listMainMaterialEntryLog({ stationId });
|
const { rows } = await listMainMaterialEntryLog({ stationId })
|
||||||
materialTableData.value = rows;
|
materialTableData.value = rows
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '查询站点下主材信息失败');
|
message.error(error.message || '查询站点下主材信息失败')
|
||||||
} finally {
|
} finally {
|
||||||
loadingMaterialTableData.value = false;
|
loadingMaterialTableData.value = false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const carrierInput = ref<string>('');
|
const carrierInput = ref<string>('')
|
||||||
// 录入载具
|
// 录入载具
|
||||||
const insertCarrier = async () => {
|
const insertCarrier = async () => {
|
||||||
if (!carrierInput.value) return;
|
if (!carrierInput.value) return
|
||||||
const form = {
|
const form = {
|
||||||
carrierCode: carrierInput.value,
|
carrierCode: carrierInput.value,
|
||||||
stationCode: traceOrderStore.currentStationCode,
|
stationCode: traceOrderStore.currentStationCode,
|
||||||
};
|
}
|
||||||
try {
|
try {
|
||||||
if (materialType.value === "Wafer") {
|
if (materialType.value === 'Wafer') {
|
||||||
await addWaferEntryLogByCarrier(form);
|
await addWaferEntryLogByCarrier(form)
|
||||||
} else if (materialType.value === "Die") {
|
} else if (materialType.value === 'Die') {
|
||||||
await addDieEntryLogByCarrier(form);
|
await addDieEntryLogByCarrier(form)
|
||||||
} else throw new Error('主材类型异常');
|
} else throw new Error('主材类型异常')
|
||||||
carrierInput.value = '';
|
carrierInput.value = ''
|
||||||
message.success('添加成功');
|
message.success('添加成功')
|
||||||
fetchPrimaryMaterialList();
|
fetchPrimaryMaterialList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '添加载具失败');
|
message.error(error.message || '添加载具失败')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 主材料表格
|
// 主材料表格
|
||||||
const materialTableData = ref<MaterialTableItem[]>([]);
|
const materialTableData = ref<MaterialTableItem[]>([])
|
||||||
const materialInput = ref<string>('');
|
const materialInput = ref<string>('')
|
||||||
// 录入主材料
|
// 录入主材料
|
||||||
const insertMaterial = async () => {
|
const insertMaterial = async () => {
|
||||||
if (!materialInput.value) return;
|
if (!materialInput.value) return
|
||||||
const form = {
|
const form = {
|
||||||
mainMaterialCodes: [materialInput.value],
|
mainMaterialCodes: [materialInput.value],
|
||||||
stationCode: traceOrderStore.currentStationCode,
|
stationCode: traceOrderStore.currentStationCode,
|
||||||
};
|
}
|
||||||
try {
|
try {
|
||||||
if (materialType.value === "Wafer") {
|
if (materialType.value === 'Wafer') {
|
||||||
await addWaferEntryLog(form);
|
await addWaferEntryLog(form)
|
||||||
} else if (materialType.value === "Die") {
|
} else if (materialType.value === 'Die') {
|
||||||
await addDieEntryLog(form);
|
await addDieEntryLog(form)
|
||||||
} else throw new Error('主材类型异常');
|
} else throw new Error('主材类型异常')
|
||||||
materialInput.value = '';
|
materialInput.value = ''
|
||||||
message.success('添加成功');
|
message.success('添加成功')
|
||||||
fetchPrimaryMaterialList();
|
fetchPrimaryMaterialList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '添加主材失败');
|
message.error(error.message || '添加主材失败')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 移除主材料
|
// 移除主材料
|
||||||
const handleRemoveMaterial = async (row: MaterialTableItem) => {
|
const handleRemoveMaterial = async (row: MaterialTableItem) => {
|
||||||
try {
|
try {
|
||||||
await delMainMaterialEntryLog(row.id);
|
await delMainMaterialEntryLog(row.id)
|
||||||
message.success('删除成功');
|
message.success('删除成功')
|
||||||
fetchPrimaryMaterialList();
|
fetchPrimaryMaterialList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '删除主材失败');
|
message.error(error.message || '删除主材失败')
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
fetchPrimaryMaterialList();
|
fetchPrimaryMaterialList()
|
||||||
renderTableHeight();
|
renderTableHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算表格高度
|
// 计算表格高度
|
||||||
const customTable = ref<HTMLElement | null>(null)
|
const customTable = ref<HTMLElement | null>(null)
|
||||||
const tableHeight = ref(200)
|
const tableHeight = ref(200)
|
||||||
const renderTableHeight = () => {
|
const renderTableHeight = () => {
|
||||||
if (customTable.value) {
|
if (customTable.value) {
|
||||||
tableHeight.value = customTable.value.clientHeight - 50
|
tableHeight.value = customTable.value.clientHeight - 50
|
||||||
console.log('元素高度:', tableHeight.value)
|
console.log('元素高度:', tableHeight.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
renderTableHeight()
|
renderTableHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
|
|
||||||
// 初始化主材类型
|
// 初始化主材类型
|
||||||
const materialType = ref()
|
const materialType = ref()
|
||||||
if (traceOrderStore.traceOrderInfo.orderType) {
|
if (traceOrderStore.traceOrderInfo.orderType) {
|
||||||
const num = parseInt(traceOrderStore.traceOrderInfo.orderType)
|
const num = parseInt(traceOrderStore.traceOrderInfo.orderType)
|
||||||
materialType.value = num % 4 === 0 ? 'Die' : 'Wafer'
|
materialType.value = num % 4 === 0 ? 'Die' : 'Wafer'
|
||||||
} else {
|
} else {
|
||||||
materialType.value = '主材类型异常'
|
materialType.value = '主材类型异常'
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPrimaryMaterialList()
|
fetchPrimaryMaterialList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="primary-material__container">
|
<div class="primary-material__container">
|
||||||
<Title name="主材料进站" showRefresh @refresh="handleRefresh" />
|
<Title name="主材料进站" showRefresh @refresh="handleRefresh" />
|
||||||
<a-form layout="inline" size="large">
|
<a-form layout="inline" size="large">
|
||||||
<a-form-item label="主材类型">
|
<a-form-item label="主材类型">
|
||||||
<a-input v-model:value="materialType" disabled />
|
<a-input v-model:value="materialType" disabled />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="载具 ID">
|
<a-form-item label="载具 ID">
|
||||||
<a-input v-model:value="carrierInput" @pressEnter="insertCarrier" placeholder="按下回车录入" allow-clear />
|
<a-input
|
||||||
</a-form-item>
|
v-model:value="carrierInput"
|
||||||
<a-form-item>
|
@pressEnter="insertCarrier"
|
||||||
<a-button @click="insertCarrier">录入</a-button>
|
placeholder="按下回车录入"
|
||||||
</a-form-item>
|
allow-clear
|
||||||
<a-form-item label="主材 ID">
|
/>
|
||||||
<a-input v-model:value="materialInput" @pressEnter="insertMaterial" placeholder="按下回车录入" allow-clear />
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item>
|
||||||
<a-form-item>
|
<a-button @click="insertCarrier">录入</a-button>
|
||||||
<a-button @click="insertMaterial">录入</a-button>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label="主材 ID">
|
||||||
</a-form>
|
<a-input
|
||||||
<div class="table-wrapper" ref="customTable">
|
v-model:value="materialInput"
|
||||||
<a-table :dataSource="materialTableData" :columns="materialColumns as ColumnsType<MaterialTableItem>"
|
@pressEnter="insertMaterial"
|
||||||
:pagination="false" bordered sticky :scroll="{ y: tableHeight }" :loading="loadingMaterialTableData">
|
placeholder="按下回车录入"
|
||||||
<template #bodyCell="{ column, index, record }">
|
allow-clear
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
/>
|
||||||
<template v-if="column.key === 'action'">
|
</a-form-item>
|
||||||
<a-button type="text" danger @click="handleRemoveMaterial(record as MaterialTableItem)">删除</a-button>
|
<a-form-item>
|
||||||
</template>
|
<a-button @click="insertMaterial">录入</a-button>
|
||||||
</template>
|
</a-form-item>
|
||||||
</a-table>
|
</a-form>
|
||||||
</div>
|
<div class="table-wrapper" ref="customTable">
|
||||||
</div>
|
<a-table
|
||||||
|
:dataSource="materialTableData"
|
||||||
|
:columns="materialColumns as ColumnsType<MaterialTableItem>"
|
||||||
|
:pagination="false"
|
||||||
|
bordered
|
||||||
|
sticky
|
||||||
|
:scroll="{ y: tableHeight }"
|
||||||
|
:loading="loadingMaterialTableData"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, index, record }">
|
||||||
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-button
|
||||||
|
type="text"
|
||||||
|
danger
|
||||||
|
@click="handleRemoveMaterial(record as MaterialTableItem)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.primary-material__container {
|
.primary-material__container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,96 +1,136 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue'
|
||||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
|
|
||||||
interface MaterialTableItem {
|
interface MaterialTableItem {
|
||||||
key: string;
|
key: string
|
||||||
materialCode: string;
|
materialCode: string
|
||||||
materialName: string;
|
materialName: string
|
||||||
num: number;
|
num: number
|
||||||
unit: string;
|
unit: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const materialColumns = [
|
const materialColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: '物料编码', dataIndex: 'materialCode', key: 'materialCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: '物料名称', dataIndex: 'materialName', key: 'materialName', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: '数量', dataIndex: 'num', key: 'num', align: 'center' },
|
key: 'index',
|
||||||
{ title: '单位', dataIndex: 'unit', key: 'unit', align: 'center' },
|
align: 'center',
|
||||||
{ title: '操作', key: 'action', align: 'center' },
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '物料编码',
|
||||||
|
dataIndex: 'materialCode',
|
||||||
|
key: 'materialCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '物料名称',
|
||||||
|
dataIndex: 'materialName',
|
||||||
|
key: 'materialName',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: '数量', dataIndex: 'num', key: 'num', align: 'center' },
|
||||||
|
{ title: '单位', dataIndex: 'unit', key: 'unit', align: 'center' },
|
||||||
|
{ title: '操作', key: 'action', align: 'center' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// 材料表格
|
// 材料表格
|
||||||
const materialTableData = ref<MaterialTableItem[]>([
|
const materialTableData = ref<MaterialTableItem[]>([
|
||||||
{ key: '1', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '1', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '2', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '2', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '3', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '3', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '4', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '4', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '5', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '5', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '6', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '6', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '7', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '7', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '8', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '8', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '9', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{ key: '9', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
||||||
{ key: '10', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
{
|
||||||
{ key: '11', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
key: '10',
|
||||||
{ key: '12', materialCode: '123', materialName: '物料1', num: 10, unit: '片' },
|
materialCode: '123',
|
||||||
]);
|
materialName: '物料1',
|
||||||
|
num: 10,
|
||||||
|
unit: '片',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '11',
|
||||||
|
materialCode: '123',
|
||||||
|
materialName: '物料1',
|
||||||
|
num: 10,
|
||||||
|
unit: '片',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '12',
|
||||||
|
materialCode: '123',
|
||||||
|
materialName: '物料1',
|
||||||
|
num: 10,
|
||||||
|
unit: '片',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
// 确认材料
|
// 确认材料
|
||||||
const handleSubmitMaterial = (index: number) => {
|
const handleSubmitMaterial = (index: number) => {
|
||||||
message.success(`${ index + 1 } 号进站`)
|
message.success(`${index + 1} 号进站`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算表格高度
|
// 计算表格高度
|
||||||
const customTable = ref<HTMLElement | null>(null)
|
const customTable = ref<HTMLElement | null>(null)
|
||||||
const tableHeight = ref(200)
|
const tableHeight = ref(200)
|
||||||
const renderTableHeight = () => {
|
const renderTableHeight = () => {
|
||||||
if (customTable.value) {
|
if (customTable.value) {
|
||||||
tableHeight.value = customTable.value.clientHeight - 60
|
tableHeight.value = customTable.value.clientHeight - 60
|
||||||
console.log('元素高度:', tableHeight.value)
|
console.log('元素高度:', tableHeight.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
renderTableHeight()
|
renderTableHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="raw-material__container">
|
<div class="raw-material__container">
|
||||||
<Title name="材料确认" showRefresh @refresh="" />
|
<Title name="材料确认" showRefresh />
|
||||||
<div class="table-wrapper" ref="customTable">
|
<div class="table-wrapper" ref="customTable">
|
||||||
<a-table :dataSource="materialTableData" :columns="materialColumns as ColumnsType<MaterialTableItem>"
|
<a-table
|
||||||
:pagination="false" bordered sticky :scroll="{ y: tableHeight }">
|
:dataSource="materialTableData"
|
||||||
<template #bodyCell="{ column, index }">
|
:columns="materialColumns as ColumnsType<MaterialTableItem>"
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
:pagination="false"
|
||||||
<template v-if="column.key === 'action'">
|
bordered
|
||||||
<a-button @click="handleSubmitMaterial(index)">确认</a-button>
|
sticky
|
||||||
</template>
|
:scroll="{ y: tableHeight }"
|
||||||
</template>
|
>
|
||||||
</a-table>
|
<template #bodyCell="{ column, index }">
|
||||||
</div>
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
</div>
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-button @click="handleSubmitMaterial(index)">确认</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.raw-material__container {
|
.raw-material__container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,102 +1,159 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, nextTick, getCurrentInstance } from 'vue';
|
import { ref, nextTick, getCurrentInstance } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useTraceOrderStore } from '@/store';
|
import { useTraceOrderStore } from '@/store'
|
||||||
import type { LotTraceOrderData } from '@/api/traceOrderManage/model';
|
import type { LotTraceOrderData } from '@/api/traceOrderManage/model'
|
||||||
import { handleCopy } from '@/utils/copyToClipboard';
|
import { handleCopy } from '@/utils/copyToClipboard'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as any
|
const { proxy } = getCurrentInstance() as any
|
||||||
const { mes_station_status, lot_trace_order_status } = proxy.useDict("mes_station_status", "lot_trace_order_status")
|
const { mes_station_status, lot_trace_order_status } = proxy.useDict(
|
||||||
|
'mes_station_status',
|
||||||
|
'lot_trace_order_status',
|
||||||
|
)
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
const redirectTo = (routeName: string) => {
|
const redirectTo = (routeName: string) => {
|
||||||
router.push({ name: routeName });
|
router.push({ name: routeName })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 孙组件
|
// 孙组件
|
||||||
const infeedRef = ref<any>(null);
|
const infeedRef = ref<any>(null)
|
||||||
const collapsed = ref(false);
|
const collapsed = ref(false)
|
||||||
// 折叠
|
// 折叠
|
||||||
const toggleCollapse = async () => {
|
const toggleCollapse = async () => {
|
||||||
collapsed.value = !collapsed.value;
|
collapsed.value = !collapsed.value
|
||||||
await nextTick();
|
await nextTick()
|
||||||
infeedRef.value?.renderTableHeight();
|
infeedRef.value?.renderTableHeight()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<Header title="MES 过站平台" showHome showLogout>
|
<Header title="MES 过站平台" showHome showLogout>
|
||||||
<template #right-opts>
|
<template #right-opts>
|
||||||
<a-button @click="toggleCollapse">
|
<a-button @click="toggleCollapse">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i-lucide-chevron-up v-if="!collapsed" />
|
<i-lucide-chevron-up v-if="!collapsed" />
|
||||||
<i-lucide-chevron-down v-else />
|
<i-lucide-chevron-down v-else />
|
||||||
</template>
|
</template>
|
||||||
{{ collapsed ? '展开' : '折叠' }}
|
{{ collapsed ? '展开' : '折叠' }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<!-- Top Section -->
|
<!-- Top Section -->
|
||||||
<div class="top-section">
|
<div class="top-section">
|
||||||
<a-row :gutter="16" class="full-height-row">
|
<a-row :gutter="16" class="full-height-row">
|
||||||
<a-col :span="13">
|
<a-col :span="13">
|
||||||
<a-spin :spinning="traceOrderStore.loadingTraceOrderInfo">
|
<a-spin :spinning="traceOrderStore.loadingTraceOrderInfo">
|
||||||
<a-card title="工单信息" class="info-card" :bordered="false">
|
<a-card title="工单信息" class="info-card" :bordered="false">
|
||||||
<a-form :model="traceOrderStore.traceOrderInfo" :colon="false" v-show="!collapsed">
|
<a-form
|
||||||
<a-row :gutter="36">
|
:model="traceOrderStore.traceOrderInfo"
|
||||||
<a-col :span="12">
|
:colon="false"
|
||||||
<a-form-item label="工单批次">
|
v-show="!collapsed"
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.batchNo" readonly>
|
>
|
||||||
<template #suffix>
|
<a-row :gutter="36">
|
||||||
<a-button @click="handleCopy(traceOrderStore.traceOrderInfo.batchNo)" size="small">
|
<a-col :span="12">
|
||||||
<template #icon><i-lucide-copy /></template>
|
<a-form-item label="工单批次">
|
||||||
</a-button>
|
<a-input
|
||||||
</template>
|
v-model:value="traceOrderStore.traceOrderInfo.batchNo"
|
||||||
</a-input>
|
readonly
|
||||||
</a-form-item>
|
>
|
||||||
<a-form-item label="工单状态">
|
<template #suffix>
|
||||||
<a-input readonly>
|
<a-button
|
||||||
<template #prefix>
|
@click="
|
||||||
<DictTag :options="lot_trace_order_status" :value="traceOrderStore.traceOrderInfo.status" size="medium" />
|
handleCopy(
|
||||||
</template>
|
traceOrderStore.traceOrderInfo.batchNo,
|
||||||
</a-input>
|
)
|
||||||
</a-form-item>
|
"
|
||||||
<a-form-item label="总计数量">
|
size="small"
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.planQty" readonly>
|
>
|
||||||
<template #suffix>
|
<template #icon><i-lucide-copy /></template>
|
||||||
<a-button @click="handleCopy(traceOrderStore.traceOrderInfo.planQty)" size="small">
|
</a-button>
|
||||||
<template #icon><i-lucide-copy /></template>
|
</template>
|
||||||
</a-button>
|
</a-input>
|
||||||
</template>
|
</a-form-item>
|
||||||
</a-input>
|
<a-form-item label="工单状态">
|
||||||
</a-form-item>
|
<a-input readonly>
|
||||||
</a-col>
|
<template #prefix>
|
||||||
<a-col :span="12">
|
<DictTag
|
||||||
<a-form-item label="产品编码">
|
:options="lot_trace_order_status"
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.tarMaterialCode" readonly>
|
:value="traceOrderStore.traceOrderInfo.status"
|
||||||
<template #suffix>
|
size="medium"
|
||||||
<a-button @click="handleCopy(traceOrderStore.traceOrderInfo.tarMaterialCode)" size="small">
|
/>
|
||||||
<template #icon><i-lucide-copy /></template>
|
</template>
|
||||||
</a-button>
|
</a-input>
|
||||||
</template>
|
</a-form-item>
|
||||||
</a-input>
|
<a-form-item label="总计数量">
|
||||||
</a-form-item>
|
<a-input
|
||||||
<a-form-item label="产品名称">
|
v-model:value="traceOrderStore.traceOrderInfo.planQty"
|
||||||
<a-input v-model:value="traceOrderStore.traceOrderInfo.tarMaterialName" readonly>
|
readonly
|
||||||
<template #suffix>
|
>
|
||||||
<a-button @click="handleCopy(traceOrderStore.traceOrderInfo.tarMaterialName)" size="small">
|
<template #suffix>
|
||||||
<template #icon><i-lucide-copy /></template>
|
<a-button
|
||||||
</a-button>
|
@click="
|
||||||
</template>
|
handleCopy(
|
||||||
</a-input>
|
traceOrderStore.traceOrderInfo.planQty,
|
||||||
</a-form-item>
|
)
|
||||||
<!-- <a-form-item label="产品规格">
|
"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #icon><i-lucide-copy /></template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="产品编码">
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
traceOrderStore.traceOrderInfo.tarMaterialCode
|
||||||
|
"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-button
|
||||||
|
@click="
|
||||||
|
handleCopy(
|
||||||
|
traceOrderStore.traceOrderInfo
|
||||||
|
.tarMaterialCode,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #icon><i-lucide-copy /></template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="产品名称">
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
traceOrderStore.traceOrderInfo.tarMaterialName
|
||||||
|
"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-button
|
||||||
|
@click="
|
||||||
|
handleCopy(
|
||||||
|
traceOrderStore.traceOrderInfo
|
||||||
|
.tarMaterialName,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #icon><i-lucide-copy /></template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- <a-form-item label="产品规格">
|
||||||
<a-input readonly>
|
<a-input readonly>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<a-button @click="handleCopy('')" size="small">
|
<a-button @click="handleCopy('')" size="small">
|
||||||
@@ -105,168 +162,238 @@ const toggleCollapse = async () => {
|
|||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item> -->
|
</a-form-item> -->
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-space>
|
<a-space>
|
||||||
工单编码
|
工单编码
|
||||||
<a-select v-model:value="traceOrderStore.currentTraceOrderCode" show-search placeholder="输入工单编码" style="width: 300px"
|
<a-select
|
||||||
:show-arrow="false" :options="traceOrderStore.traceOrderOptions" @search="traceOrderStore.fetchTraceOrderOption" :disabled="route.name !== 'TraceOrderManageIndex'"
|
v-model:value="traceOrderStore.currentTraceOrderCode"
|
||||||
@change="(val, option) => traceOrderStore.selectTraceOrder(val as string, option as LotTraceOrderData)" />
|
show-search
|
||||||
<a-button @click="traceOrderStore.fetchTraceOrderInfo"><template #icon><i-lucide-rotate-cw /></template></a-button>
|
placeholder="输入工单编码"
|
||||||
</a-space>
|
style="width: 300px"
|
||||||
</template>
|
:show-arrow="false"
|
||||||
</a-card>
|
:options="traceOrderStore.traceOrderOptions"
|
||||||
</a-spin>
|
@search="traceOrderStore.fetchTraceOrderOption"
|
||||||
</a-col>
|
:disabled="route.name !== 'TraceOrderManageIndex'"
|
||||||
|
@change="
|
||||||
|
(val, option) =>
|
||||||
|
traceOrderStore.selectTraceOrder(
|
||||||
|
val as string,
|
||||||
|
option as LotTraceOrderData,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<a-button @click="traceOrderStore.fetchTraceOrderInfo">
|
||||||
|
<template #icon><i-lucide-rotate-cw /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
<a-col :span="8">
|
<a-col :span="8">
|
||||||
<a-spin :spinning="traceOrderStore.loadingStationInfo">
|
<a-spin :spinning="traceOrderStore.loadingStationInfo">
|
||||||
<a-card title="工序信息" class="info-card" :bordered="false">
|
<a-card title="工序信息" class="info-card" :bordered="false">
|
||||||
<a-form :model="traceOrderStore.stationInfo" :colon="false" v-show="!collapsed">
|
<a-form
|
||||||
<a-form-item label="工序名称">
|
:model="traceOrderStore.stationInfo"
|
||||||
<a-input v-model:value="traceOrderStore.stationInfo.operationTitle" readonly>
|
:colon="false"
|
||||||
<template #suffix>
|
v-show="!collapsed"
|
||||||
<a-button @click="handleCopy(traceOrderStore.stationInfo.operationTitle)" size="small">
|
>
|
||||||
<template #icon><i-lucide-copy /></template>
|
<a-form-item label="工序名称">
|
||||||
</a-button>
|
<a-input
|
||||||
</template>
|
v-model:value="traceOrderStore.stationInfo.operationTitle"
|
||||||
</a-input>
|
readonly
|
||||||
</a-form-item>
|
>
|
||||||
<a-form-item label="工序状态">
|
<template #suffix>
|
||||||
<a-input readonly>
|
<a-button
|
||||||
<template #prefix>
|
@click="
|
||||||
<DictTag :options="mes_station_status" :value="traceOrderStore.stationInfo.status" size="medium" />
|
handleCopy(
|
||||||
</template>
|
traceOrderStore.stationInfo.operationTitle,
|
||||||
</a-input>
|
)
|
||||||
</a-form-item>
|
"
|
||||||
<a-form-item label="作业编码">
|
size="small"
|
||||||
<a-input v-model:value="traceOrderStore.stationInfo.code" readonly>
|
>
|
||||||
<template #suffix>
|
<template #icon><i-lucide-copy /></template>
|
||||||
<a-button @click="handleCopy(traceOrderStore.stationInfo.code)" size="small">
|
</a-button>
|
||||||
<template #icon><i-lucide-copy /></template>
|
</template>
|
||||||
</a-button>
|
</a-input>
|
||||||
</template>
|
</a-form-item>
|
||||||
</a-input>
|
<a-form-item label="工序状态">
|
||||||
</a-form-item>
|
<a-input readonly>
|
||||||
</a-form>
|
<template #prefix>
|
||||||
<template #extra>
|
<DictTag
|
||||||
<a-button @click="traceOrderStore.fetchStationInfo"><template #icon><i-lucide-rotate-cw /></template></a-button>
|
:options="mes_station_status"
|
||||||
</template>
|
:value="traceOrderStore.stationInfo.status"
|
||||||
</a-card>
|
size="medium"
|
||||||
</a-spin>
|
/>
|
||||||
</a-col>
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="作业编码">
|
||||||
|
<a-input
|
||||||
|
v-model:value="traceOrderStore.stationInfo.code"
|
||||||
|
readonly
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<a-button
|
||||||
|
@click="handleCopy(traceOrderStore.stationInfo.code)"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<template #icon><i-lucide-copy /></template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #extra>
|
||||||
|
<a-button @click="traceOrderStore.fetchStationInfo">
|
||||||
|
<template #icon><i-lucide-rotate-cw /></template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-card>
|
||||||
|
</a-spin>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<a-col :span="3" class="action-buttons-col">
|
<a-col :span="3" class="action-buttons-col">
|
||||||
<div class="action-buttons" v-show="!collapsed">
|
<div class="action-buttons" v-show="!collapsed">
|
||||||
<a-button class="action-btn" @click="redirectTo('TraceOrderManage')" :disabled="route.name == 'TraceOrderManageIndex'">工单管理</a-button>
|
<a-button
|
||||||
<a-button class="action-btn" @click="redirectTo('PrimaryMaterial')" disabled>主材清单</a-button>
|
class="action-btn"
|
||||||
<HoldTraceOrder />
|
@click="redirectTo('TraceOrderManage')"
|
||||||
</div>
|
:disabled="route.name == 'TraceOrderManageIndex'"
|
||||||
<a-card title="操作" class="info-card" :bordered="false" v-show="collapsed">
|
>
|
||||||
<template #extra>
|
工单管理
|
||||||
<a-dropdown>
|
</a-button>
|
||||||
<template #overlay>
|
<a-button
|
||||||
<a-menu>
|
class="action-btn"
|
||||||
<a-menu-item key="1" @click="redirectTo('TraceOrderManage')">工单管理</a-menu-item>
|
@click="redirectTo('PrimaryMaterial')"
|
||||||
<a-menu-item key="2" @click="redirectTo('PrimaryMaterial')" disabled>主材清单</a-menu-item>
|
disabled
|
||||||
</a-menu>
|
>
|
||||||
</template>
|
主材清单
|
||||||
<a-button>
|
</a-button>
|
||||||
更多
|
<HoldTraceOrder />
|
||||||
<i-lucide-chevron-down />
|
</div>
|
||||||
</a-button>
|
<a-card
|
||||||
</a-dropdown>
|
title="操作"
|
||||||
</template>
|
class="info-card"
|
||||||
</a-card>
|
:bordered="false"
|
||||||
</a-col>
|
v-show="collapsed"
|
||||||
</a-row>
|
>
|
||||||
</div>
|
<template #extra>
|
||||||
|
<a-dropdown>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item
|
||||||
|
key="1"
|
||||||
|
@click="redirectTo('TraceOrderManage')"
|
||||||
|
>
|
||||||
|
工单管理
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item
|
||||||
|
key="2"
|
||||||
|
@click="redirectTo('PrimaryMaterial')"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
主材清单
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
<a-button>
|
||||||
|
更多
|
||||||
|
<i-lucide-chevron-down />
|
||||||
|
</a-button>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Bottom Section -->
|
<!-- Bottom Section -->
|
||||||
<main class="bottom-section">
|
<main class="bottom-section">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<component :is="Component" ref="infeedRef" />
|
<component :is="Component" ref="infeedRef" />
|
||||||
</router-view>
|
</router-view>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.page-container {
|
.page-container {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: #f0f2f5;
|
background-color: #f0f2f5;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-section {
|
.top-section {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-spin-nested-loading),
|
:deep(.ant-spin-nested-loading),
|
||||||
:deep(.ant-spin-container) {
|
:deep(.ant-spin-container) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-card {
|
.info-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
:deep(.ant-card-head) {
|
:deep(.ant-card-head) {
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-card-body) {
|
:deep(.ant-card-body) {
|
||||||
padding: 1px 16px;
|
padding: 1px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-form-item) {
|
:deep(.ant-form-item) {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-buttons-col {
|
.action-buttons-col {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 传递给子组件的样式
|
// 传递给子组件的样式
|
||||||
:deep(.action-buttons) {
|
:deep(.action-buttons) {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-section {
|
.bottom-section {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
22
src/views/traceOrderManage/model.d.ts
vendored
22
src/views/traceOrderManage/model.d.ts
vendored
@@ -1,16 +1,16 @@
|
|||||||
export interface WorkOrderInfo {
|
export interface WorkOrderInfo {
|
||||||
code?: string;
|
code?: string
|
||||||
batchNo?: string;
|
batchNo?: string
|
||||||
status?: string;
|
status?: string
|
||||||
tarMaterialName?: string;
|
tarMaterialName?: string
|
||||||
tarMaterialCode?: string;
|
tarMaterialCode?: string
|
||||||
planQty?: number;
|
planQty?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProcessInfo {
|
export interface ProcessInfo {
|
||||||
process?: string;
|
process?: string
|
||||||
cut?: string;
|
cut?: string
|
||||||
jobCode?: string;
|
jobCode?: string
|
||||||
status?: string;
|
status?: string
|
||||||
equipment?: string;
|
equipment?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,342 +1,511 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, computed, getCurrentInstance } from 'vue';
|
import { ref, reactive, onMounted, computed, getCurrentInstance } from 'vue'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
import { useTraceOrderStore } from '@/store';
|
import { useTraceOrderStore } from '@/store'
|
||||||
import type { ColumnsType } from 'ant-design-vue/es/table/interface';
|
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||||||
import { listMainMaterialEntryLog, listMainMaterialOutboundLog, batchAddMainMaterialOutboundLog, getMainMaterialOutboundLog, updateMainMaterialOutboundLog, delMainMaterialOutboundLog } from "@/api/traceOrderManage/primaryMaterial";
|
import {
|
||||||
|
listMainMaterialEntryLog,
|
||||||
|
listMainMaterialOutboundLog,
|
||||||
|
batchAddMainMaterialOutboundLog,
|
||||||
|
updateMainMaterialOutboundLog,
|
||||||
|
delMainMaterialOutboundLog,
|
||||||
|
} from '@/api/traceOrderManage/primaryMaterial'
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance() as any
|
const { proxy } = getCurrentInstance() as any
|
||||||
const { main_material_ok_level, main_material_ng_level } = proxy.useDict("main_material_ok_level", "main_material_ng_level")
|
const { main_material_ok_level, main_material_ng_level } = proxy.useDict(
|
||||||
|
'main_material_ok_level',
|
||||||
|
'main_material_ng_level',
|
||||||
|
)
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
interface PrimaryMaterialTableItem {
|
interface PrimaryMaterialTableItem {
|
||||||
key: string;
|
key: string
|
||||||
waferCode: string;
|
waferCode: string
|
||||||
dieCode: string;
|
dieCode: string
|
||||||
xcoordinates: string;
|
xcoordinates: string
|
||||||
ycoordinates: string;
|
ycoordinates: string
|
||||||
outputResult: 'OK' | 'NG';
|
outputResult: 'OK' | 'NG'
|
||||||
qualityLevel: string;
|
qualityLevel: string
|
||||||
createBy: string;
|
createBy: string
|
||||||
createTime: string;
|
createTime: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryMaterialColumns = [
|
const primaryMaterialColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: 'Wafer ID', dataIndex: 'waferCode', key: 'waferCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: 'Die ID', dataIndex: 'dieCode', key: 'dieCode', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: 'Die X 坐标', dataIndex: 'xcoordinates', key: 'xcoordinates', align: 'center' },
|
key: 'index',
|
||||||
{ title: 'Die Y 坐标', dataIndex: 'ycoordinates', key: 'ycoordinates', align: 'center' },
|
align: 'center',
|
||||||
{ title: '结果', dataIndex: 'outputResult', key: 'outputResult', align: 'center' },
|
width: 80,
|
||||||
{ title: '质量等级', dataIndex: 'qualityLevel', key: 'qualityLevel' },
|
},
|
||||||
{ title: '创建人', dataIndex: 'createBy', key: 'createBy' },
|
{
|
||||||
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime' },
|
title: 'Wafer ID',
|
||||||
{ title: '操作', key: 'action', align: 'center', width: 180 },
|
dataIndex: 'waferCode',
|
||||||
];
|
key: 'waferCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: 'Die ID', dataIndex: 'dieCode', key: 'dieCode', align: 'center' },
|
||||||
|
{
|
||||||
|
title: 'Die X 坐标',
|
||||||
|
dataIndex: 'xcoordinates',
|
||||||
|
key: 'xcoordinates',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Die Y 坐标',
|
||||||
|
dataIndex: 'ycoordinates',
|
||||||
|
key: 'ycoordinates',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结果',
|
||||||
|
dataIndex: 'outputResult',
|
||||||
|
key: 'outputResult',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: '质量等级', dataIndex: 'qualityLevel', key: 'qualityLevel' },
|
||||||
|
{ title: '创建人', dataIndex: 'createBy', key: 'createBy' },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime' },
|
||||||
|
{ title: '操作', key: 'action', align: 'center', width: 180 },
|
||||||
|
]
|
||||||
|
|
||||||
// 总计数据
|
// 总计数据
|
||||||
const totals = computed(() => {
|
const totals = computed(() => {
|
||||||
let okNum = 0;
|
let okNum = 0
|
||||||
let ngNum = 0;
|
let ngNum = 0
|
||||||
|
|
||||||
primaryMaterialTableData.value.forEach(({ outputResult, qualityLevel }) => {
|
primaryMaterialTableData.value.forEach(({ outputResult }) => {
|
||||||
if (outputResult === 'OK') {
|
if (outputResult === 'OK') {
|
||||||
okNum++;
|
okNum++
|
||||||
} else {
|
} else {
|
||||||
ngNum++;
|
ngNum++
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return { okNum, ngNum };
|
return { okNum, ngNum }
|
||||||
});
|
})
|
||||||
|
|
||||||
const primaryMaterialTableData = ref<PrimaryMaterialTableItem[]>([]);
|
const primaryMaterialTableData = ref<PrimaryMaterialTableItem[]>([])
|
||||||
|
|
||||||
const loadingPrimaryMaterialList = ref(false);
|
const loadingPrimaryMaterialList = ref(false)
|
||||||
const fetchPrimaryMaterialList = async () => {
|
const fetchPrimaryMaterialList = async () => {
|
||||||
const stationId = traceOrderStore.currentStationId;
|
const stationId = traceOrderStore.currentStationId
|
||||||
if (!stationId) return;
|
if (!stationId) return
|
||||||
loadingPrimaryMaterialList.value = true;
|
loadingPrimaryMaterialList.value = true
|
||||||
try {
|
try {
|
||||||
const { rows } = await listMainMaterialOutboundLog({ stationId });
|
const { rows } = await listMainMaterialOutboundLog({ stationId })
|
||||||
primaryMaterialTableData.value = rows;
|
primaryMaterialTableData.value = rows
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '查询失败');
|
message.error(error.message || '查询失败')
|
||||||
} finally {
|
} finally {
|
||||||
loadingPrimaryMaterialList.value = false;
|
loadingPrimaryMaterialList.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const openPickPrimaryMaterial = ref(false);
|
const openPickPrimaryMaterial = ref(false)
|
||||||
interface InfeedMaterialTableItem {
|
interface InfeedMaterialTableItem {
|
||||||
key: string;
|
key: string
|
||||||
waferCode: string;
|
waferCode: string
|
||||||
dieCode: string;
|
dieCode: string
|
||||||
result: 'OK' | 'NG';
|
result: 'OK' | 'NG'
|
||||||
qualityLevel: string;
|
qualityLevel: string
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
}
|
}
|
||||||
const infeedMaterialColumns = [
|
const infeedMaterialColumns = [
|
||||||
{ title: '序号', dataIndex: 'index', key: 'index', align: 'center', width: 80 },
|
{
|
||||||
{ title: 'Wafer ID', dataIndex: 'waferCode', key: 'waferCode', align: 'center' },
|
title: '序号',
|
||||||
{ title: 'Die ID', dataIndex: 'dieCode', key: 'dieCode', align: 'center' },
|
dataIndex: 'index',
|
||||||
{ title: '结果', dataIndex: 'result', key: 'result', align: 'center' },
|
key: 'index',
|
||||||
{ title: '质量等级', dataIndex: 'qualityLevel', key: 'qualityLevel' },
|
align: 'center',
|
||||||
];
|
width: 80,
|
||||||
const infeedMaterialTableData = ref<InfeedMaterialTableItem[]>([]);
|
},
|
||||||
|
{
|
||||||
|
title: 'Wafer ID',
|
||||||
|
dataIndex: 'waferCode',
|
||||||
|
key: 'waferCode',
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
{ title: 'Die ID', dataIndex: 'dieCode', key: 'dieCode', align: 'center' },
|
||||||
|
{ title: '结果', dataIndex: 'result', key: 'result', align: 'center' },
|
||||||
|
{ title: '质量等级', dataIndex: 'qualityLevel', key: 'qualityLevel' },
|
||||||
|
]
|
||||||
|
const infeedMaterialTableData = ref<InfeedMaterialTableItem[]>([])
|
||||||
|
|
||||||
// 挑选主材
|
// 挑选主材
|
||||||
const handlePickPrimaryMaterial = async () => {
|
const handlePickPrimaryMaterial = async () => {
|
||||||
const stationId = traceOrderStore.currentStationId;
|
const stationId = traceOrderStore.currentStationId
|
||||||
if (!stationId) return;
|
if (!stationId) return
|
||||||
try {
|
try {
|
||||||
const { rows } = await listMainMaterialEntryLog({ stationId });
|
const { rows } = await listMainMaterialEntryLog({ stationId })
|
||||||
infeedMaterialTableData.value = rows.map(item => {
|
infeedMaterialTableData.value = rows.map((item) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
result: 'OK',
|
result: 'OK',
|
||||||
qualityLevel: main_material_ok_level.value[0].value || '',
|
qualityLevel: main_material_ok_level.value[0].value || '',
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
openPickPrimaryMaterial.value = true;
|
openPickPrimaryMaterial.value = true
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
message.error(err.message);
|
message.error(err.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const openEditRecordModal = ref(false);
|
const openEditRecordModal = ref(false)
|
||||||
const editingRecord = reactive<any>({});
|
const editingRecord = reactive<any>({})
|
||||||
// 修改主材出站记录
|
// 修改主材出站记录
|
||||||
const handleEditRecord = (record: any) => {
|
const handleEditRecord = (record: any) => {
|
||||||
Object.assign(editingRecord, record);
|
Object.assign(editingRecord, record)
|
||||||
openEditRecordModal.value = true;
|
openEditRecordModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交修改主材出站记录
|
// 提交修改主材出站记录
|
||||||
const handleSubmitEdit = async () => {
|
const handleSubmitEdit = async () => {
|
||||||
try {
|
try {
|
||||||
await updateMainMaterialOutboundLog(editingRecord);
|
await updateMainMaterialOutboundLog(editingRecord)
|
||||||
openEditRecordModal.value = false;
|
openEditRecordModal.value = false
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
message.error(err.message || '修改失败');
|
message.error(err.message || '修改失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除主材出站记录
|
// 删除主材出站记录
|
||||||
const handleDeleteRecord = async (record: any) => {
|
const handleDeleteRecord = async (record: any) => {
|
||||||
try {
|
try {
|
||||||
await delMainMaterialOutboundLog(record.id);
|
await delMainMaterialOutboundLog(record.id)
|
||||||
message.success('删除成功');
|
message.success('删除成功')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
message.error(err.message || '删除失败');
|
message.error(err.message || '删除失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择主材
|
// 选择主材
|
||||||
const rowSelection = ref<any[]>([]);
|
const rowSelection = ref<any[]>([])
|
||||||
const selectedKeys = ref<any>([]);
|
const selectedKeys = ref<any>([])
|
||||||
const handleChangeSelection = (selectedRowKeys: any, selectedRows: InfeedMaterialTableItem[]) => {
|
const handleChangeSelection = (
|
||||||
selectedKeys.value = selectedRowKeys;
|
selectedRowKeys: any,
|
||||||
rowSelection.value = selectedRows.map(item => {
|
selectedRows: InfeedMaterialTableItem[],
|
||||||
return {
|
) => {
|
||||||
mainMaterialId: item.waferId ?? item.dieId,
|
selectedKeys.value = selectedRowKeys
|
||||||
result: item.result,
|
rowSelection.value = selectedRows.map((item) => {
|
||||||
qualityLevel: item.qualityLevel,
|
return {
|
||||||
}
|
mainMaterialId: item.waferId ?? item.dieId,
|
||||||
})
|
result: item.result,
|
||||||
|
qualityLevel: item.qualityLevel,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交主材出站
|
// 提交主材出站
|
||||||
const submitOutfeed = async () => {
|
const submitOutfeed = async () => {
|
||||||
const stationCode = traceOrderStore.currentStationCode;
|
const stationCode = traceOrderStore.currentStationCode
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await batchAddMainMaterialOutboundLog({ stationCode, outbounds: rowSelection.value });
|
await batchAddMainMaterialOutboundLog({
|
||||||
message.success("提交成功");
|
stationCode,
|
||||||
handleCancel();
|
outbounds: rowSelection.value,
|
||||||
fetchPrimaryMaterialList();
|
})
|
||||||
} catch (err: any) {
|
message.success('提交成功')
|
||||||
message.error(err.message || '提交失败');
|
handleCancel()
|
||||||
}
|
fetchPrimaryMaterialList()
|
||||||
|
} catch (err: any) {
|
||||||
|
message.error(err.message || '提交失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消选择主材
|
// 取消选择主材
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
rowSelection.value = [];
|
rowSelection.value = []
|
||||||
selectedKeys.value = [];
|
selectedKeys.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
fetchPrimaryMaterialList();
|
fetchPrimaryMaterialList()
|
||||||
renderTableHeight();
|
renderTableHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算表格高度
|
// 计算表格高度
|
||||||
const customTable = ref<HTMLElement | null>(null)
|
const customTable = ref<HTMLElement | null>(null)
|
||||||
const tableHeight = ref(200)
|
const tableHeight = ref(200)
|
||||||
const renderTableHeight = () => {
|
const renderTableHeight = () => {
|
||||||
if (customTable.value) {
|
if (customTable.value) {
|
||||||
tableHeight.value = customTable.value.clientHeight - 60
|
tableHeight.value = customTable.value.clientHeight - 60
|
||||||
console.log('元素高度:', tableHeight.value)
|
console.log('元素高度:', tableHeight.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
renderTableHeight()
|
renderTableHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
totals,
|
totals,
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
|
|
||||||
fetchPrimaryMaterialList()
|
fetchPrimaryMaterialList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="equipment__container">
|
<div class="equipment__container">
|
||||||
<Title name="主材报工" showRefresh @refresh="handleRefresh" />
|
<Title name="主材报工" showRefresh @refresh="handleRefresh" />
|
||||||
<a-button size="large" @click="handlePickPrimaryMaterial">选择主材</a-button>
|
<a-button size="large" @click="handlePickPrimaryMaterial">
|
||||||
<div class="table-wrapper" ref="customTable">
|
选择主材
|
||||||
<a-table :dataSource="primaryMaterialTableData" :columns="primaryMaterialColumns as ColumnsType<PrimaryMaterialTableItem>"
|
</a-button>
|
||||||
:pagination="false" bordered sticky :scroll="{ y: tableHeight }"
|
<div class="table-wrapper" ref="customTable">
|
||||||
:loading="loadingPrimaryMaterialList">
|
<a-table
|
||||||
<template #bodyCell="{ column, index, record }">
|
:dataSource="primaryMaterialTableData"
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
:columns="
|
||||||
<template v-if="column.key === 'outputResult'">
|
primaryMaterialColumns as ColumnsType<PrimaryMaterialTableItem>
|
||||||
<a-switch v-model:checked="record.outputResult" checked-children="NG" checkedValue="NG" un-checked-children="OK" unCheckedValue="OK" />
|
"
|
||||||
</template>
|
:pagination="false"
|
||||||
<template v-if="column.key === 'qualityLevel'">
|
bordered
|
||||||
<a-select v-model:value="record.qualityLevel" style="width: 80%" v-if="record.outputResult === 'NG'">
|
sticky
|
||||||
<a-select-option v-for="dict in main_material_ng_level" :value="dict.value">{{ dict.label }}</a-select-option>
|
:scroll="{ y: tableHeight }"
|
||||||
</a-select>
|
:loading="loadingPrimaryMaterialList"
|
||||||
<a-select v-model:value="record.qualityLevel" style="width: 80%" v-if="record.outputResult === 'OK'">
|
>
|
||||||
<a-select-option v-for="dict in main_material_ok_level" :value="dict.value">{{ dict.label }}</a-select-option>
|
<template #bodyCell="{ column, index, record }">
|
||||||
</a-select>
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
</template>
|
<template v-if="column.key === 'outputResult'">
|
||||||
<template v-if="column.key === 'action'">
|
<a-switch
|
||||||
<a-space>
|
v-model:checked="record.outputResult"
|
||||||
<a-button @click="handleEditRecord(record)">修改</a-button>
|
checked-children="NG"
|
||||||
<a-popconfirm title="确定删除该记录?" ok-text="是" cancel-text="否" @confirm="handleDeleteRecord(record)">
|
checkedValue="NG"
|
||||||
<a-button danger>删除</a-button>
|
un-checked-children="OK"
|
||||||
</a-popconfirm>
|
unCheckedValue="OK"
|
||||||
</a-space>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
<template v-if="column.key === 'qualityLevel'">
|
||||||
<template #summary>
|
<a-select
|
||||||
<a-table-summary-row>
|
v-model:value="record.qualityLevel"
|
||||||
<a-table-summary-cell>总计</a-table-summary-cell>
|
style="width: 80%"
|
||||||
<a-table-summary-cell>
|
v-if="record.outputResult === 'NG'"
|
||||||
<a-typography-text>OK: {{ totals.okNum }}</a-typography-text>
|
>
|
||||||
</a-table-summary-cell>
|
<a-select-option
|
||||||
<a-table-summary-cell>
|
v-for="dict in main_material_ng_level"
|
||||||
<a-typography-text>NG: {{ totals.ngNum }}</a-typography-text>
|
:key="dict.value"
|
||||||
</a-table-summary-cell>
|
:value="dict.value"
|
||||||
</a-table-summary-row>
|
>
|
||||||
</template>
|
{{ dict.label }}
|
||||||
</a-table>
|
</a-select-option>
|
||||||
</div>
|
</a-select>
|
||||||
|
<a-select
|
||||||
|
v-model:value="record.qualityLevel"
|
||||||
|
style="width: 80%"
|
||||||
|
v-if="record.outputResult === 'OK'"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in main_material_ok_level"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'action'">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleEditRecord(record)">修改</a-button>
|
||||||
|
<a-popconfirm
|
||||||
|
title="确定删除该记录?"
|
||||||
|
ok-text="是"
|
||||||
|
cancel-text="否"
|
||||||
|
@confirm="handleDeleteRecord(record)"
|
||||||
|
>
|
||||||
|
<a-button danger>删除</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #summary>
|
||||||
|
<a-table-summary-row>
|
||||||
|
<a-table-summary-cell>总计</a-table-summary-cell>
|
||||||
|
<a-table-summary-cell>
|
||||||
|
<a-typography-text>OK: {{ totals.okNum }}</a-typography-text>
|
||||||
|
</a-table-summary-cell>
|
||||||
|
<a-table-summary-cell>
|
||||||
|
<a-typography-text>NG: {{ totals.ngNum }}</a-typography-text>
|
||||||
|
</a-table-summary-cell>
|
||||||
|
</a-table-summary-row>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-modal v-model:open="openEditRecordModal" title="修改主材出站记录" @ok="handleSubmitEdit">
|
<a-modal
|
||||||
<a-form :model="editingRecord" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
v-model:open="openEditRecordModal"
|
||||||
<a-form-item label="Wafer ID">
|
title="修改主材出站记录"
|
||||||
<a-input v-model:value="editingRecord.waferCode" disabled />
|
@ok="handleSubmitEdit"
|
||||||
</a-form-item>
|
>
|
||||||
<a-form-item label="Die ID">
|
<a-form
|
||||||
<a-input v-model:value="editingRecord.dieCode" disabled />
|
:model="editingRecord"
|
||||||
</a-form-item>
|
:label-col="{ span: 4 }"
|
||||||
<a-form-item label="Die X 坐标">
|
:wrapper-col="{ span: 20 }"
|
||||||
<a-input v-model:value="editingRecord.xcoordinates" disabled />
|
>
|
||||||
</a-form-item>
|
<a-form-item label="Wafer ID">
|
||||||
<a-form-item label="Die Y 坐标">
|
<a-input v-model:value="editingRecord.waferCode" disabled />
|
||||||
<a-input v-model:value="editingRecord.ycoordinates" disabled />
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label="Die ID">
|
||||||
<a-form-item label="结果" :rules="[{ required: true, message: '请选择结果!' }]">
|
<a-input v-model:value="editingRecord.dieCode" disabled />
|
||||||
<a-switch v-model:checked="editingRecord.outputResult" checked-children="NG" checkedValue="NG"
|
</a-form-item>
|
||||||
un-checked-children="OK" unCheckedValue="OK" />
|
<a-form-item label="Die X 坐标">
|
||||||
</a-form-item>
|
<a-input v-model:value="editingRecord.xcoordinates" disabled />
|
||||||
<a-form-item label="质量等级" :rules="[{ required: true, message: '请选择质量等级!' }]">
|
</a-form-item>
|
||||||
<a-select v-model:value="editingRecord.qualityLevel" style="width: 80%" v-if="editingRecord.outputResult === 'NG'">
|
<a-form-item label="Die Y 坐标">
|
||||||
<a-select-option v-for="dict in main_material_ng_level" :value="dict.value">{{ dict.label
|
<a-input v-model:value="editingRecord.ycoordinates" disabled />
|
||||||
}}</a-select-option>
|
</a-form-item>
|
||||||
</a-select>
|
<a-form-item
|
||||||
<a-select v-model:value="editingRecord.qualityLevel" style="width: 80%" v-if="editingRecord.outputResult === 'OK'">
|
label="结果"
|
||||||
<a-select-option v-for="dict in main_material_ok_level" :value="dict.value">{{ dict.label
|
:rules="[{ required: true, message: '请选择结果!' }]"
|
||||||
}}</a-select-option>
|
>
|
||||||
</a-select>
|
<a-switch
|
||||||
</a-form-item>
|
v-model:checked="editingRecord.outputResult"
|
||||||
</a-form>
|
checked-children="NG"
|
||||||
</a-modal>
|
checkedValue="NG"
|
||||||
|
un-checked-children="OK"
|
||||||
|
unCheckedValue="OK"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="质量等级"
|
||||||
|
:rules="[{ required: true, message: '请选择质量等级!' }]"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model:value="editingRecord.qualityLevel"
|
||||||
|
style="width: 80%"
|
||||||
|
v-if="editingRecord.outputResult === 'NG'"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in main_material_ng_level"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-select
|
||||||
|
v-model:value="editingRecord.qualityLevel"
|
||||||
|
style="width: 80%"
|
||||||
|
v-if="editingRecord.outputResult === 'OK'"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in main_material_ok_level"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
<a-modal v-model:open="openPickPrimaryMaterial" title="选择主材出站" width="50vw" @ok="submitOutfeed" @cancel="handleCancel">
|
<a-modal
|
||||||
已选择 {{ rowSelection.length }} 条主材
|
v-model:open="openPickPrimaryMaterial"
|
||||||
<a-table :dataSource="infeedMaterialTableData" :row-selection="{ selectedRowKeys: selectedKeys, onChange: handleChangeSelection }" row-key="id"
|
title="选择主材出站"
|
||||||
:columns="infeedMaterialColumns as ColumnsType<InfeedMaterialTableItem>" :pagination="false" bordered sticky>
|
width="50vw"
|
||||||
<template #bodyCell="{ column, index, record }">
|
@ok="submitOutfeed"
|
||||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
@cancel="handleCancel"
|
||||||
<template v-if="column.key === 'result'">
|
>
|
||||||
<a-switch v-model:checked="record.result" checked-children="NG" checkedValue="NG"
|
已选择 {{ rowSelection.length }} 条主材
|
||||||
un-checked-children="OK" unCheckedValue="OK" />
|
<a-table
|
||||||
</template>
|
:dataSource="infeedMaterialTableData"
|
||||||
<template v-if="column.key === 'qualityLevel'">
|
:row-selection="{
|
||||||
<a-select v-model:value="record.qualityLevel" style="width: 80%" v-if="record.result === 'NG'">
|
selectedRowKeys: selectedKeys,
|
||||||
<a-select-option v-for="dict in main_material_ng_level" :value="dict.value">{{ dict.label
|
onChange: handleChangeSelection,
|
||||||
}}</a-select-option>
|
}"
|
||||||
</a-select>
|
row-key="id"
|
||||||
<a-select v-model:value="record.qualityLevel" style="width: 80%" v-if="record.result === 'OK'">
|
:columns="infeedMaterialColumns as ColumnsType<InfeedMaterialTableItem>"
|
||||||
<a-select-option v-for="dict in main_material_ok_level" :value="dict.value">{{ dict.label
|
:pagination="false"
|
||||||
}}</a-select-option>
|
bordered
|
||||||
</a-select>
|
sticky
|
||||||
</template>
|
>
|
||||||
</template>
|
<template #bodyCell="{ column, index, record }">
|
||||||
</a-table>
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
</a-modal>
|
<template v-if="column.key === 'result'">
|
||||||
</div>
|
<a-switch
|
||||||
|
v-model:checked="record.result"
|
||||||
|
checked-children="NG"
|
||||||
|
checkedValue="NG"
|
||||||
|
un-checked-children="OK"
|
||||||
|
unCheckedValue="OK"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'qualityLevel'">
|
||||||
|
<a-select
|
||||||
|
v-model:value="record.qualityLevel"
|
||||||
|
style="width: 80%"
|
||||||
|
v-if="record.result === 'NG'"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in main_material_ng_level"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-select
|
||||||
|
v-model:value="record.qualityLevel"
|
||||||
|
style="width: 80%"
|
||||||
|
v-if="record.result === 'OK'"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="dict in main_material_ok_level"
|
||||||
|
:key="dict.value"
|
||||||
|
:value="dict.value"
|
||||||
|
>
|
||||||
|
{{ dict.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.equipment__container {
|
.equipment__container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input__container {
|
.input__container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.description-wrapper {
|
.description-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep(.ant-switch) {
|
::v-deep(.ant-switch) {
|
||||||
background-color: #04d903;
|
background-color: #04d903;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #02eb02;
|
background-color: #02eb02;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ant-switch-checked {
|
&.ant-switch-checked {
|
||||||
background-color: #ff0000;
|
background-color: #ff0000;
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #ff2525;
|
background-color: #ff2525;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,196 +1,223 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, reactive } from 'vue';
|
import { ref, computed, reactive } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue'
|
||||||
import { listStorageLocation } from '@/api/traceOrderManage/location';
|
import { listStorageLocation } from '@/api/traceOrderManage/location'
|
||||||
import { completeStation, isLastStation as getIsLastStation } from '@/api/traceOrderManage/station';
|
import {
|
||||||
import { useTraceOrderStore } from '@/store';
|
completeStation,
|
||||||
|
isLastStation as getIsLastStation,
|
||||||
|
} from '@/api/traceOrderManage/station'
|
||||||
|
import { useTraceOrderStore } from '@/store'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
|
|
||||||
const traceOrderStore = useTraceOrderStore();
|
const traceOrderStore = useTraceOrderStore()
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ label: '主材报工', key: 'JobReport', progress: 30 },
|
{ label: '主材报工', key: 'JobReport', progress: 30 },
|
||||||
{ label: '工序参数', key: 'ParameterConfiguration', progress: 80 },
|
{ label: '工序参数', key: 'ParameterConfiguration', progress: 80 },
|
||||||
{ label: '流转指引', key: 'ProcessGuidance', progress: 80 },
|
{ label: '流转指引', key: 'ProcessGuidance', progress: 80 },
|
||||||
];
|
]
|
||||||
|
|
||||||
const activeKey = computed(() => route.name as string);
|
const activeKey = computed(() => route.name as string)
|
||||||
|
|
||||||
const handleMenuClick = (name: string) => {
|
const handleMenuClick = (name: string) => {
|
||||||
router.push({ name });
|
router.push({ name })
|
||||||
};
|
}
|
||||||
|
|
||||||
const locationOptions = ref<any>([]);
|
const locationOptions = ref<any>([])
|
||||||
const fetchLocationList = async () => {
|
const fetchLocationList = async () => {
|
||||||
try {
|
try {
|
||||||
const { rows } = await listStorageLocation({});
|
const { rows } = await listStorageLocation({})
|
||||||
locationOptions.value = rows;
|
locationOptions.value = rows
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '获取库位列表失败');
|
message.error(error.message || '获取库位列表失败')
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const openSelectLocation = ref(false);
|
const openSelectLocation = ref(false)
|
||||||
const handleOutfeed = async () => {
|
const handleOutfeed = async () => {
|
||||||
// 判断是否为末站点
|
// 判断是否为末站点
|
||||||
const { data: isLastStation } = await getIsLastStation(traceOrderStore.currentStationId);
|
const { data: isLastStation } = await getIsLastStation(
|
||||||
if (isLastStation || outfeedChildRef.value?.totals.ngNum) {
|
traceOrderStore.currentStationId,
|
||||||
fetchLocationList();
|
)
|
||||||
openSelectLocation.value = true;
|
if (isLastStation || outfeedChildRef.value?.totals.ngNum) {
|
||||||
} else {
|
fetchLocationList()
|
||||||
submitOutfeed();
|
openSelectLocation.value = true
|
||||||
}
|
} else {
|
||||||
};
|
submitOutfeed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const storage = reactive({});
|
const storage = reactive({})
|
||||||
const handleChangeLocation = (value: any, option: any) => {
|
const handleChangeLocation = (value: any, option: any) => {
|
||||||
Object.assign(storage, option);
|
Object.assign(storage, option)
|
||||||
};
|
}
|
||||||
|
|
||||||
const closeOutfeed = () => {
|
const closeOutfeed = () => {
|
||||||
openSelectLocation.value = false;
|
openSelectLocation.value = false
|
||||||
};
|
}
|
||||||
|
|
||||||
// 确认出站
|
// 确认出站
|
||||||
const outfeeding = ref(false);
|
const outfeeding = ref(false)
|
||||||
const submitOutfeed = async () => {
|
const submitOutfeed = async () => {
|
||||||
outfeeding.value = true;
|
outfeeding.value = true
|
||||||
try {
|
try {
|
||||||
await completeStation(traceOrderStore.currentStationId, storage);
|
await completeStation(traceOrderStore.currentStationId, storage)
|
||||||
openSelectLocation.value = false;
|
openSelectLocation.value = false
|
||||||
message.success('出站成功');
|
message.success('出站成功')
|
||||||
traceOrderStore.fetchStationInfo();
|
traceOrderStore.fetchStationInfo()
|
||||||
traceOrderStore.fetchStationList();
|
traceOrderStore.fetchStationList()
|
||||||
router.push({ name: 'TraceOrderManage' });
|
router.push({ name: 'TraceOrderManage' })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
message.error(error.message || '出站失败');
|
message.error(error.message || '出站失败')
|
||||||
} finally {
|
} finally {
|
||||||
outfeeding.value = false;
|
outfeeding.value = false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const outfeedChildRef = ref<any>(null);
|
const outfeedChildRef = ref<any>(null)
|
||||||
|
|
||||||
/** 父组件对外暴露的方法 */
|
/** 父组件对外暴露的方法 */
|
||||||
function renderTableHeight() {
|
function renderTableHeight() {
|
||||||
outfeedChildRef.value?.renderTableHeight();
|
outfeedChildRef.value?.renderTableHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
renderTableHeight
|
renderTableHeight,
|
||||||
});
|
})
|
||||||
|
|
||||||
fetchLocationList();
|
fetchLocationList()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-spin :spinning="outfeeding">
|
<a-spin :spinning="outfeeding">
|
||||||
<a-row class="outfeed-layout">
|
<a-row class="outfeed-layout">
|
||||||
<a-col :span="20" class="content-wrapper">
|
<a-col :span="20" class="content-wrapper">
|
||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<component :is="Component" ref="outfeedChildRef" />
|
<component :is="Component" ref="outfeedChildRef" />
|
||||||
</router-view>
|
</router-view>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="4" class="menu-wrapper">
|
<a-col :span="4" class="menu-wrapper">
|
||||||
<div class="menu-list">
|
<div class="menu-list">
|
||||||
<span class="outfeed-title">出站</span>
|
<span class="outfeed-title">出站</span>
|
||||||
<div v-for="item in menuItems" :key="item.key" class="menu-item" :class="{ active: activeKey === item.key }"
|
<div
|
||||||
@click="handleMenuClick(item.key)">
|
v-for="item in menuItems"
|
||||||
<div class="menu-content">
|
:key="item.key"
|
||||||
<span class="menu-title">{{ item.label }}</span>
|
class="menu-item"
|
||||||
<a-progress :percent="item.progress" :show-info="false" size="small"
|
:class="{ active: activeKey === item.key }"
|
||||||
:stroke-color="activeKey === item.key ? '#1890ff' : undefined" class="menu-progress" />
|
@click="handleMenuClick(item.key)"
|
||||||
</div>
|
>
|
||||||
</div>
|
<div class="menu-content">
|
||||||
<a-button type="primary" size="large" @click="handleOutfeed">确认出站</a-button>
|
<span class="menu-title">{{ item.label }}</span>
|
||||||
</div>
|
<a-progress
|
||||||
</a-col>
|
:percent="item.progress"
|
||||||
</a-row>
|
:show-info="false"
|
||||||
|
size="small"
|
||||||
|
:stroke-color="activeKey === item.key ? '#1890ff' : undefined"
|
||||||
|
class="menu-progress"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-button type="primary" size="large" @click="handleOutfeed">
|
||||||
|
确认出站
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
<a-modal title="选择出站库位" :open="openSelectLocation" @ok="submitOutfeed" @cancel="closeOutfeed">
|
<a-modal
|
||||||
<a-select style="width: 100%" @change="handleChangeLocation" :options="locationOptions" :fieldNames="{ label: 'storageLocationCode', value: 'id' }" />
|
title="选择出站库位"
|
||||||
</a-modal>
|
:open="openSelectLocation"
|
||||||
</a-spin>
|
@ok="submitOutfeed"
|
||||||
|
@cancel="closeOutfeed"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleChangeLocation"
|
||||||
|
:options="locationOptions"
|
||||||
|
:fieldNames="{ label: 'storageLocationCode', value: 'id' }"
|
||||||
|
/>
|
||||||
|
</a-modal>
|
||||||
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.outfeed-layout {
|
.outfeed-layout {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-list {
|
.menu-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.outfeed-title {
|
.outfeed-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom: 1px solid #c9c9c9;
|
border-bottom: 1px solid #c9c9c9;
|
||||||
padding: 0 0 8px;
|
padding: 0 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 1vh 1vw;
|
padding: 1vh 1vw;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
border: 1px solid #f0f0f0;
|
border: 1px solid #f0f0f0;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.02);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: #e6f7ff;
|
background: #e6f7ff;
|
||||||
border-color: #1890ff;
|
border-color: #1890ff;
|
||||||
|
|
||||||
.menu-title {
|
.menu-title {
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #47a5fd;
|
border-color: #47a5fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-content {
|
.menu-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-title {
|
.menu-title {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-progress {
|
.menu-progress {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
|
|
||||||
:deep(.ant-progress-bg) {
|
:deep(.ant-progress-bg) {
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-wrapper {
|
.menu-wrapper {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
background-color: #f3f3f3;
|
background-color: #f3f3f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts"></script>
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div></div>
|
<div></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts"></script>
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div></div>
|
<div></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||
2
src/vite-env.d.ts
vendored
2
src/vite-env.d.ts
vendored
@@ -1 +1 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|||||||
103
vite.config.ts
103
vite.config.ts
@@ -1,56 +1,55 @@
|
|||||||
import { defineConfig, loadEnv } from 'vite';
|
import { defineConfig, loadEnv } from 'vite'
|
||||||
import Components from "unplugin-vue-components/vite"; // 按需组件自动导入
|
import Components from 'unplugin-vue-components/vite' // 按需组件自动导入
|
||||||
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
|
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
|
||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue'
|
||||||
import Icons from 'unplugin-icons/vite';
|
import Icons from 'unplugin-icons/vite'
|
||||||
import IconsResolver from 'unplugin-icons/resolver';
|
import IconsResolver from 'unplugin-icons/resolver'
|
||||||
import path from 'path';
|
import path from 'path'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
|
const env = loadEnv(mode, process.cwd())
|
||||||
|
|
||||||
const env = loadEnv(mode, process.cwd());
|
return {
|
||||||
|
resolve: {
|
||||||
return {
|
alias: {
|
||||||
resolve: {
|
'@': path.resolve(__dirname, 'src'),
|
||||||
alias: {
|
},
|
||||||
"@": path.resolve(__dirname, "src"),
|
},
|
||||||
},
|
server: {
|
||||||
},
|
proxy: {
|
||||||
server: {
|
'/api': {
|
||||||
proxy: {
|
target: env.VITE_APP_BASE_URL,
|
||||||
"/api": {
|
changeOrigin: true,
|
||||||
target: env.VITE_APP_BASE_URL,
|
rewrite: (path) => path.replace(/^\/api\//, '/'),
|
||||||
changeOrigin: true,
|
},
|
||||||
rewrite: (path) => path.replace(/^\/api\//, "/"),
|
'/prod-api': {
|
||||||
},
|
target: env.VITE_APP_BASE_URL,
|
||||||
"/prod-api": {
|
changeOrigin: true,
|
||||||
target: env.VITE_APP_BASE_URL,
|
rewrite: (path) => path.replace(/^\/prod-api\//, '/'),
|
||||||
changeOrigin: true,
|
},
|
||||||
rewrite: (path) => path.replace(/^\/prod-api\//, "/"),
|
},
|
||||||
},
|
},
|
||||||
},
|
plugins: [
|
||||||
},
|
vue(),
|
||||||
plugins: [
|
Components({
|
||||||
vue(),
|
dts: true, //生成components.d.ts 全局定义文件
|
||||||
Components({
|
resolvers: [
|
||||||
dts: true, //生成components.d.ts 全局定义文件
|
AntDesignVueResolver({
|
||||||
resolvers: [
|
//对使用到的全局ant design vue组件进行类型导入
|
||||||
AntDesignVueResolver({
|
importStyle: false, // 不动态引入css,这个不强求
|
||||||
//对使用到的全局ant design vue组件进行类型导入
|
}),
|
||||||
importStyle: false, // 不动态引入css,这个不强求
|
// 自动导入 lucide 图标
|
||||||
}),
|
IconsResolver({
|
||||||
// 自动导入 lucide 图标
|
prefix: 'i', // 比如用 <i-a-arrow-down />
|
||||||
IconsResolver({
|
enabledCollections: ['lucide'],
|
||||||
prefix: "i", // 比如用 <i-a-arrow-down />
|
}),
|
||||||
enabledCollections: ["lucide"],
|
],
|
||||||
}),
|
include: [/\.vue$/, /\.vue\?vue/, /\.md$/, /\.tsx$/], //包含的文件类型
|
||||||
],
|
}),
|
||||||
include: [/\.vue$/, /\.vue\?vue/, /\.md$/, /\.tsx$/], //包含的文件类型
|
Icons({
|
||||||
}),
|
autoInstall: true, // 没安装的图标库会自动下载
|
||||||
Icons({
|
}),
|
||||||
autoInstall: true, // 没安装的图标库会自动下载
|
],
|
||||||
}),
|
}
|
||||||
],
|
})
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user