From 9d9cb926d2fdd4ba6db1daffdbc6c877a626d813 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期三, 12 三月 2025 09:13:07 +0800
Subject: [PATCH] dev-init

---
 src/views/index.vue                    |   86 
 src/views/qms/testResult/index.vue     |  313 +++
 src/views/qms/sensorRetest/index.vue   |  273 ++
 public/favicon_bak.ico                 |    0 
 src/api/qms/sensorResult/types.ts      |   96 +
 src/api/qms/sensorRetest/index.ts      |   63 
 src/api/qms/testResult/types.ts        |  201 ++
 src/api/qms/sensorResult/index.ts      |   63 
 src/views/qms/trend/index1.vue         |  349 +++
 src/views/register.vue                 |    2 
 vite.config.ts                         |   10 
 src/api/qms/device/types.ts            |  111 +
 src/layout/components/Sidebar/Logo.vue |    2 
 index.html                             |    2 
 src/api/qms/sensor/types.ts            |  181 +
 public/favicon.ico                     |    0 
 src/views/qms/retestResult/index.vue   |  461 ++++
 src/assets/logo/logo_bak.png           |    0 
 src/api/qms/batch/index.ts             |   74 
 src/api/qms/batch/types.ts             |  111 +
 src/views/qms/sensor/index.vue         |  451 ++++
 .eslintrc.cjs                          |   51 
 src/views/qms/testResult/index1.vue    |  339 +++
 src/api/qms/device/index.ts            |   63 
 src/api/qms/sensorRetest/types.ts      |  141 +
 vite/plugins/i18n.ts                   |    6 
 src/api/qms/sensor/index.ts            |   63 
 src/views/qms/testResult/index2.vue    |  372 +++
 src/api/qms/testResult/index.ts        |   89 
 src/assets/logo/logo.png               |    0 
 src/views/qms/batch/index.vue          |  299 +++
 src/views/qms/device/index.vue         |  280 ++
 .env.development                       |    4 
 /dev/null                              |   86 
 src/views/qms/trend/index.vue          |  467 ++++
 src/views/login.vue                    |    8 
 tsconfig.json                          |    2 
 src/assets/icons/svg/batch-detail.svg  |    1 
 package.json                           |    4 
 src/views/qms/sensorResult/index.vue   |  274 ++
 .env.production                        |    4 
 .eslintignore                          |   17 
 src/api/qms/retestResult/index.ts      |   63 
 src/api/qms/retestResult/types.ts      |  216 ++
 44 files changed, 5,515 insertions(+), 183 deletions(-)

diff --git a/.env.development b/.env.development
index 14e1335..a4521b0 100644
--- a/.env.development
+++ b/.env.development
@@ -1,5 +1,5 @@
 # 椤甸潰鏍囬
-VITE_APP_TITLE = RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�
+VITE_APP_TITLE = 鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺
 
 # 寮�鍙戠幆澧冮厤缃�
 VITE_APP_ENV = 'development'
@@ -19,7 +19,7 @@
 VITE_APP_PORT = 80
 
 # 鎺ュ彛鍔犲瘑鍔熻兘寮�鍏�(濡傞渶鍏抽棴 鍚庣涔熷繀椤诲搴斿叧闂�)
-VITE_APP_ENCRYPT = true
+VITE_APP_ENCRYPT = false
 # 鎺ュ彛鍔犲瘑浼犺緭 RSA 鍏挜涓庡悗绔В瀵嗙閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
 VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
 # 鎺ュ彛鍝嶅簲瑙e瘑 RSA 绉侀挜涓庡悗绔姞瀵嗗叕閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
diff --git a/.env.production b/.env.production
index 1109bc6..0bf7518 100644
--- a/.env.production
+++ b/.env.production
@@ -1,5 +1,5 @@
 # 椤甸潰鏍囬
-VITE_APP_TITLE = RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�
+VITE_APP_TITLE = 鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺
 
 # 鐢熶骇鐜閰嶇疆
 VITE_APP_ENV = 'production'
@@ -22,7 +22,7 @@
 VITE_APP_PORT = 80
 
 # 鎺ュ彛鍔犲瘑鍔熻兘寮�鍏�(濡傞渶鍏抽棴 鍚庣涔熷繀椤诲搴斿叧闂�)
-VITE_APP_ENCRYPT = true
+VITE_APP_ENCRYPT = false
 # 鎺ュ彛鍔犲瘑浼犺緭 RSA 鍏挜涓庡悗绔В瀵嗙閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
 VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
 # 鎺ュ彛鍝嶅簲瑙e瘑 RSA 绉侀挜涓庡悗绔姞瀵嗗叕閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..e74db40
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,17 @@
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+/public
+/docs
+.husky
+.local
+/bin
+.eslintrc.cjs
+prettier.config.js
+src/assets
+tailwind.config.js
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..6042c39
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,51 @@
+module.exports = {
+  env: {
+    browser: true,
+    node: true,
+    es6: true
+  },
+  parser: 'vue-eslint-parser',
+  extends: [
+    'plugin:vue/vue3-recommended',
+    './.eslintrc-auto-import.json',
+    'plugin:@typescript-eslint/recommended',
+    'prettier',
+    'plugin:prettier/recommended'
+  ],
+  parserOptions: {
+    ecmaVersion: '2020',
+    sourceType: 'module',
+    project: './tsconfig.*?.json',
+    parser: '@typescript-eslint/parser'
+  },
+  plugins: ['vue', '@typescript-eslint', 'import', 'promise', 'node', 'prettier'],
+  rules: {
+    '@typescript-eslint/no-empty-function': 'off',
+    '@typescript-eslint/no-explicit-any': 'off',
+    '@typescript-eslint/no-unused-vars': 'off',
+    '@typescript-eslint/no-this-alias': 'off',
+
+    // vue
+    'vue/multi-word-component-names': 'off',
+    'vue/valid-define-props': 'off',
+    'vue/no-v-model-argument': 'off',
+    'prefer-rest-params': 'off',
+    // prettier
+    'prettier/prettier': 'error',
+    '@typescript-eslint/ban-types': [
+      'error',
+      {
+        // 鍏抽棴绌虹被鍨嬫鏌� {}
+        extendDefaults: true,
+        types: {
+          '{}': false,
+          Function: false
+        }
+      }
+    ]
+  },
+  globals: {
+    DialogOption: 'readonly',
+    OptionType: 'readonly'
+  }
+};
diff --git a/eslint.config.js b/eslint.config.js
deleted file mode 100644
index 8b67e9e..0000000
--- a/eslint.config.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import globals from 'globals';
-import pluginJs from '@eslint/js';
-import tseslint from 'typescript-eslint';
-import pluginVue from 'eslint-plugin-vue';
-import { readFile } from 'node:fs/promises';
-import prettier from 'eslint-plugin-prettier';
-
-/**
- * https://blog.csdn.net/sayUonly/article/details/123482912
- * 鑷姩瀵煎叆鐨勯厤缃�
- */
-const autoImportFile = new URL('./.eslintrc-auto-import.json', import.meta.url);
-const autoImportGlobals = JSON.parse(await readFile(autoImportFile, 'utf8'));
-
-/** @type {import('eslint').Linter.Config[]} */
-export default [
-  {
-    /**
-     * 涓嶉渶瑕�.eslintignore鏂囦欢 鑰屾槸鍦ㄨ繖閲岄厤缃�
-     */
-    ignores: [
-      '*.sh',
-      'node_modules',
-      '*.md',
-      '*.woff',
-      '*.ttf',
-      '.vscode',
-      '.idea',
-      'dist',
-      '/public',
-      '/docs',
-      '.husky',
-      '.local',
-      '/bin',
-      '.eslintrc.cjs',
-      'prettier.config.js',
-      'src/assets',
-      'tailwind.config.js'
-    ]
-  },
-  { files: ['**/*.{js,mjs,cjs,ts,vue}'] },
-  {
-    languageOptions: {
-      globals: globals.browser
-    }
-  },
-  pluginJs.configs.recommended,
-  ...tseslint.configs.recommended,
-  ...pluginVue.configs['flat/essential'],
-  {
-    files: ['**/*.vue'],
-    languageOptions: {
-      parserOptions: {
-        parser: tseslint.parser
-      }
-    }
-  },
-  {
-    languageOptions: {
-      globals: {
-        // 鑷姩瀵煎叆鐨勯厤缃� undef
-        ...autoImportGlobals.globals,
-        DialogOption: 'readonly',
-        LayoutSetting: 'readonly'
-      }
-    },
-    plugins: { prettier },
-    rules: {
-      '@typescript-eslint/no-empty-function': 'off',
-      '@typescript-eslint/no-explicit-any': 'off',
-      '@typescript-eslint/no-unused-vars': 'off',
-      '@typescript-eslint/no-this-alias': 'off',
-
-      // vue
-      'vue/multi-word-component-names': 'off',
-      'vue/valid-define-props': 'off',
-      'vue/no-v-model-argument': 'off',
-      'prefer-rest-params': 'off',
-      // prettier
-      'prettier/prettier': 'error',
-      // 鍏佽浣跨敤绌篛bject绫诲瀷 {}
-      '@typescript-eslint/no-empty-object-type': 'off',
-      '@typescript-eslint/no-unused-expressions': 'off'
-    }
-  }
-];
diff --git a/index.html b/index.html
index aa1c86d..152dba9 100644
--- a/index.html
+++ b/index.html
@@ -6,7 +6,7 @@
     <meta name="renderer" content="webkit" />
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
     <link rel="icon" href="/favicon.ico" />
-    <title>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</title>
+    <title>鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺</title>
     <!--[if lt IE 11
       ]><script>
         window.location.href = '/html/ie.html';
diff --git a/package.json b/package.json
index 26d0862..3f75145 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "$schema": "https://json.schemastore.org/tsconfig",
-  "name": "ruoyi-vue-plus",
+  "name": "lb-qms",
   "version": "5.3.0",
-  "description": "RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�",
+  "description": "鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺",
   "author": "LionLi",
   "license": "MIT",
   "type": "module",
diff --git a/public/favicon.ico b/public/favicon.ico
index 3f919d8..8bf7e58 100644
--- a/public/favicon.ico
+++ b/public/favicon.ico
Binary files differ
diff --git a/public/favicon_bak.ico b/public/favicon_bak.ico
new file mode 100644
index 0000000..3f919d8
--- /dev/null
+++ b/public/favicon_bak.ico
Binary files differ
diff --git a/src/api/qms/batch/index.ts b/src/api/qms/batch/index.ts
new file mode 100644
index 0000000..5ac5f30
--- /dev/null
+++ b/src/api/qms/batch/index.ts
@@ -0,0 +1,74 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { BatchVO, BatchForm, BatchQuery } from '@/api/qms/batch/types';
+
+/**
+ * 鏌ヨ鎵规绠$悊鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listBatch = (query?: BatchQuery): AxiosPromise<BatchVO[]> => {
+  return request({
+    url: '/qms/batch/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ鎵�鏈変骇鍝佸瀷鍙�
+ * @param query
+ */
+export const listProdModels = () => {
+  return request({
+    url: '/qms/batch/distinctProductModels',
+    method: 'get'
+  });
+};
+
+/**
+ * 鏌ヨ鎵规绠$悊璇︾粏
+ * @param id
+ */
+export const getBatch = (id: string | number): AxiosPromise<BatchVO> => {
+  return request({
+    url: '/qms/batch/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板鎵规绠$悊
+ * @param data
+ */
+export const addBatch = (data: BatchForm) => {
+  return request({
+    url: '/qms/batch',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼鎵规绠$悊
+ * @param data
+ */
+export const updateBatch = (data: BatchForm) => {
+  return request({
+    url: '/qms/batch',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎鎵规绠$悊
+ * @param id
+ */
+export const delBatch = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/batch/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/batch/types.ts b/src/api/qms/batch/types.ts
new file mode 100644
index 0000000..338de36
--- /dev/null
+++ b/src/api/qms/batch/types.ts
@@ -0,0 +1,111 @@
+export interface BatchVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode: string;
+
+  /**
+   * 浜у搧鍨嬪彿
+   */
+  prodModel: string;
+
+  /**
+   * 鐢ㄦ埛缂栧彿
+   */
+  userId: string | number;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode: string;
+
+  /**
+   * 鏁伴噺
+   */
+  num: number;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
+
+export interface BatchForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 浜у搧鍨嬪彿
+   */
+  prodModel?: string;
+
+  /**
+   * 鐢ㄦ埛缂栧彿
+   */
+  userId?: string | number;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode?: string;
+
+  /**
+   * 鏁伴噺
+   */
+  num?: number;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+}
+
+export interface BatchQuery extends PageQuery {
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 浜у搧鍨嬪彿
+   */
+  prodModel?: string;
+
+  /**
+   * 鐢ㄦ埛缂栧彿
+   */
+  userId?: string | number;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode?: string;
+
+  /**
+   * 鏁伴噺
+   */
+  num?: number;
+
+    /**
+     * 鏃ユ湡鑼冨洿鍙傛暟
+     */
+    params?: any;
+}
+
+
+
diff --git a/src/api/qms/device/index.ts b/src/api/qms/device/index.ts
new file mode 100644
index 0000000..fd639ae
--- /dev/null
+++ b/src/api/qms/device/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { DeviceVO, DeviceForm, DeviceQuery } from '@/api/qms/device/types';
+
+/**
+ * 鏌ヨ娴嬭瘯浠垪琛�
+ * @param query
+ * @returns {*}
+ */
+
+export const listDevice = (query?: DeviceQuery): AxiosPromise<DeviceVO[]> => {
+  return request({
+    url: '/qms/device/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ娴嬭瘯浠缁�
+ * @param id
+ */
+export const getDevice = (id: string | number): AxiosPromise<DeviceVO> => {
+  return request({
+    url: '/qms/device/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板娴嬭瘯浠�
+ * @param data
+ */
+export const addDevice = (data: DeviceForm) => {
+  return request({
+    url: '/qms/device',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼娴嬭瘯浠�
+ * @param data
+ */
+export const updateDevice = (data: DeviceForm) => {
+  return request({
+    url: '/qms/device',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎娴嬭瘯浠�
+ * @param id
+ */
+export const delDevice = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/device/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/device/types.ts b/src/api/qms/device/types.ts
new file mode 100644
index 0000000..78de45d
--- /dev/null
+++ b/src/api/qms/device/types.ts
@@ -0,0 +1,111 @@
+export interface DeviceVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 璁惧绫诲瀷
+   */
+  deviceType: string;
+
+  /**
+   * 璁惧缂栧彿
+   */
+  deviceCode: string;
+
+  /**
+   * 鎵�鍦ㄧ粍
+   */
+  deviceGroup: string;
+
+  /**
+   * 鎵�鍦ㄩ儴闂�
+   */
+  deviceDept: string;
+
+  /**
+   * 鎵�鍦ㄥ伐浣�
+   */
+  deviceStation: string;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
+
+export interface DeviceForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 璁惧绫诲瀷
+   */
+  deviceType?: string;
+
+  /**
+   * 璁惧缂栧彿
+   */
+  deviceCode?: string;
+
+  /**
+   * 鎵�鍦ㄧ粍
+   */
+  deviceGroup?: string;
+
+  /**
+   * 鎵�鍦ㄩ儴闂�
+   */
+  deviceDept?: string;
+
+  /**
+   * 鎵�鍦ㄥ伐浣�
+   */
+  deviceStation?: string;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+}
+
+export interface DeviceQuery extends PageQuery {
+
+  /**
+   * 璁惧绫诲瀷
+   */
+  deviceType?: string;
+
+  /**
+   * 璁惧缂栧彿
+   */
+  deviceCode?: string;
+
+  /**
+   * 鎵�鍦ㄧ粍
+   */
+  deviceGroup?: string;
+
+  /**
+   * 鎵�鍦ㄩ儴闂�
+   */
+  deviceDept?: string;
+
+  /**
+   * 鎵�鍦ㄥ伐浣�
+   */
+  deviceStation?: string;
+
+    /**
+     * 鏃ユ湡鑼冨洿鍙傛暟
+     */
+    params?: any;
+}
+
+
+
diff --git a/src/api/qms/retestResult/index.ts b/src/api/qms/retestResult/index.ts
new file mode 100644
index 0000000..a23a713
--- /dev/null
+++ b/src/api/qms/retestResult/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { RetestResultVO, RetestResultForm, RetestResultQuery } from '@/api/qms/retestResult/types';
+
+/**
+ * 鏌ヨ澶嶆祴璁板綍鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listRetestResult = (query?: RetestResultQuery): AxiosPromise<RetestResultVO[]> => {
+  return request({
+    url: '/qms/retestResult/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ澶嶆祴璁板綍璇︾粏
+ * @param batchCode
+ */
+export const getRetestResult = (batchCode: string | number): AxiosPromise<RetestResultVO> => {
+  return request({
+    url: '/qms/retestResult/' + batchCode,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板澶嶆祴璁板綍
+ * @param data
+ */
+export const addRetestResult = (data: RetestResultForm) => {
+  return request({
+    url: '/qms/retestResult',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼澶嶆祴璁板綍
+ * @param data
+ */
+export const updateRetestResult = (data: RetestResultForm) => {
+  return request({
+    url: '/qms/retestResult',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎澶嶆祴璁板綍
+ * @param batchCode
+ */
+export const delRetestResult = (batchCode: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/retestResult/' + batchCode,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/retestResult/types.ts b/src/api/qms/retestResult/types.ts
new file mode 100644
index 0000000..d84019e
--- /dev/null
+++ b/src/api/qms/retestResult/types.ts
@@ -0,0 +1,216 @@
+export interface RetestResultVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum: string;
+
+  /**
+   * 娴嬭瘯椤圭洰
+   */
+  testItem: string;
+
+  /**
+   * 澶嶆祴娆℃暟
+   */
+  retestNum: string;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode: string;
+
+  /**
+   * 鐢ㄦ埛甯愬彿
+   */
+  userName: string;
+
+  /**
+   * 鐜娓╁害
+   */
+  temp: string;
+
+  /**
+   * 瀹為檯鐢靛帇
+   */
+  voltage: number;
+
+  /**
+   * 瀹為檯鐢垫祦
+   */
+  loadCurrent: number;
+
+  /**
+   * 鏍囧噯璺濈
+   */
+  stdDistance: number;
+
+  /**
+   * 鎰熷簲鐗�
+   */
+  inductor: string;
+
+  /**
+   * 杈撳嚭寮曡剼锛堥粦锛涚櫧锛�
+   */
+  output: string;
+
+  /**
+   * 娴嬭瘯鏁版嵁
+   */
+  testValue: number;
+
+  /**
+   * 鍒ゆ柇鏉′欢锛堟渶灏忓��<=鐪熷疄鍊�<=鏈�澶у�� 锛�
+   */
+  judgeDetail: string;
+
+  /**
+   * 娴嬭瘯缁撴灉锛圤K; NG锛�
+   */
+  testResult: string;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
+
+export interface RetestResultForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 娴嬭瘯椤圭洰
+   */
+  testItem?: string;
+
+  /**
+   * 澶嶆祴娆℃暟
+   */
+  retestNum?: string;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode?: string;
+
+  /**
+   * 鐢ㄦ埛甯愬彿
+   */
+  userName?: string;
+
+  /**
+   * 鐜娓╁害
+   */
+  temp?: string;
+
+  /**
+   * 瀹為檯鐢靛帇
+   */
+  voltage?: number;
+
+  /**
+   * 瀹為檯鐢垫祦
+   */
+  loadCurrent?: number;
+
+  /**
+   * 鏍囧噯璺濈
+   */
+  stdDistance?: number;
+
+  /**
+   * 鎰熷簲鐗�
+   */
+  inductor?: string;
+
+  /**
+   * 杈撳嚭寮曡剼锛堥粦锛涚櫧锛�
+   */
+  output?: string;
+
+  /**
+   * 娴嬭瘯鏁版嵁
+   */
+  testValue?: number;
+
+  /**
+   * 鍒ゆ柇鏉′欢锛堟渶灏忓��<=鐪熷疄鍊�<=鏈�澶у�� 锛�
+   */
+  judgeDetail?: string;
+
+  /**
+   * 娴嬭瘯缁撴灉锛圤K; NG锛�
+   */
+  testResult?: string;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+}
+
+export interface RetestResultQuery extends PageQuery {
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 娴嬭瘯椤圭洰
+   */
+  testItem?: string;
+
+  /**
+   * 澶嶆祴娆℃暟
+   */
+  retestNum?: string;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode?: string;
+
+  /**
+   * 鐢ㄦ埛甯愬彿
+   */
+  userName?: string;
+
+    /**
+     * 鏃ユ湡鑼冨洿鍙傛暟
+     */
+    params?: any;
+}
+
+
+
diff --git a/src/api/qms/sensor/index.ts b/src/api/qms/sensor/index.ts
new file mode 100644
index 0000000..aa19fe9
--- /dev/null
+++ b/src/api/qms/sensor/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SensorVO, SensorForm, SensorQuery } from '@/api/qms/sensor/types';
+
+/**
+ * 鏌ヨ浼犳劅鍣ㄥ垪琛�
+ * @param query
+ * @returns {*}
+ */
+
+export const listSensor = (query?: SensorQuery): AxiosPromise<SensorVO[]> => {
+  return request({
+    url: '/qms/sensor/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ浼犳劅鍣ㄨ缁�
+ * @param prodId
+ */
+export const getSensor = (prodId: string | number): AxiosPromise<SensorVO> => {
+  return request({
+    url: '/qms/sensor/' + prodId,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板浼犳劅鍣�
+ * @param data
+ */
+export const addSensor = (data: SensorForm) => {
+  return request({
+    url: '/qms/sensor',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼浼犳劅鍣�
+ * @param data
+ */
+export const updateSensor = (data: SensorForm) => {
+  return request({
+    url: '/qms/sensor',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎浼犳劅鍣�
+ * @param prodId
+ */
+export const delSensor = (prodId: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/sensor/' + prodId,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/sensor/types.ts b/src/api/qms/sensor/types.ts
new file mode 100644
index 0000000..9b9f3a8
--- /dev/null
+++ b/src/api/qms/sensor/types.ts
@@ -0,0 +1,181 @@
+export interface SensorVO {
+  /**
+   * 
+   */
+  prodId: string | number;
+
+  /**
+   * 浜у搧鍨嬪彿
+   */
+  prodModel: string;
+
+  /**
+   * 浜у搧鏉$爜
+   */
+  prodBarcode: string;
+
+  /**
+   * 浜у搧澶х被
+   */
+  prodClass: string;
+
+  /**
+   * 浜у搧灏忕被
+   */
+  prodSubclass: string;
+
+  /**
+   * 渚涚數鐢垫簮锛圖C-鐩存祦;AC-浜ゆ祦锛�
+   */
+  supply: string;
+
+  /**
+   * 鐢靛帇楂樺��
+   */
+  supplyHigh: number;
+
+  /**
+   * 鐢靛帇涓��
+   */
+  supplyMiddle: string | number;
+
+  /**
+   * 鐢靛帇浣庡��
+   */
+  supplyLow: number;
+
+  /**
+   * 璐熻浇鐢垫祦
+   */
+  loadCurrent: number;
+
+  /**
+   * 寮曠嚎鏁伴噺
+   */
+  wire: number;
+
+  /**
+   * 杈撳嚭淇″彿
+   */
+  outputSignal: string;
+
+  /**
+   * 杈撳嚭绫诲瀷
+   */
+  outputType: string;
+
+  /**
+   * 杈撳嚭鏋佹��
+   */
+  outputPolarity: string;
+
+  /**
+   * 鎰熷簲璺濈
+   */
+  distance: number;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
+
+export interface SensorForm extends BaseEntity {
+  /**
+   * 
+   */
+  prodId?: string | number;
+
+  /**
+   * 浜у搧鍨嬪彿
+   */
+  prodModel?: string;
+
+  /**
+   * 浜у搧鏉$爜
+   */
+  prodBarcode?: string;
+
+  /**
+   * 浜у搧澶х被
+   */
+  prodClass?: string;
+
+  /**
+   * 浜у搧灏忕被
+   */
+  prodSubclass?: string;
+
+  /**
+   * 渚涚數鐢垫簮锛圖C-鐩存祦;AC-浜ゆ祦锛�
+   */
+  supply?: string;
+
+  /**
+   * 鐢靛帇楂樺��
+   */
+  supplyHigh?: number;
+
+  /**
+   * 鐢靛帇涓��
+   */
+  supplyMiddle?: string | number;
+
+  /**
+   * 鐢靛帇浣庡��
+   */
+  supplyLow?: number;
+
+  /**
+   * 璐熻浇鐢垫祦
+   */
+  loadCurrent?: number;
+
+  /**
+   * 寮曠嚎鏁伴噺
+   */
+  wire?: number;
+
+  /**
+   * 杈撳嚭淇″彿
+   */
+  outputSignal?: string;
+
+  /**
+   * 杈撳嚭绫诲瀷
+   */
+  outputType?: string;
+
+  /**
+   * 杈撳嚭鏋佹��
+   */
+  outputPolarity?: string;
+
+  /**
+   * 鎰熷簲璺濈
+   */
+  distance?: number;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+}
+
+export interface SensorQuery extends PageQuery {
+
+  /**
+   * 浜у搧鍨嬪彿
+   */
+  prodModel?: string;
+
+    /**
+     * 鏃ユ湡鑼冨洿鍙傛暟
+     */
+    params?: any;
+}
+
+
+
diff --git a/src/api/qms/sensorResult/index.ts b/src/api/qms/sensorResult/index.ts
new file mode 100644
index 0000000..50992a5
--- /dev/null
+++ b/src/api/qms/sensorResult/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SensorResultVO, SensorResultForm, SensorResultQuery } from '@/api/qms/sensorResult/types';
+
+/**
+ * 鏌ヨ鎵规鏄庣粏鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listSensorResult = (query?: SensorResultQuery): AxiosPromise<SensorResultVO[]> => {
+  return request({
+    url: '/qms/sensorResult/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ鎵规鏄庣粏璇︾粏
+ * @param batchCode
+ */
+export const getSensorResult = (batchCode: string | number): AxiosPromise<SensorResultVO> => {
+  return request({
+    url: '/qms/sensorResult/' + batchCode,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板鎵规鏄庣粏
+ * @param data
+ */
+export const addSensorResult = (data: SensorResultForm) => {
+  return request({
+    url: '/qms/sensorResult',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼鎵规鏄庣粏
+ * @param data
+ */
+export const updateSensorResult = (data: SensorResultForm) => {
+  return request({
+    url: '/qms/sensorResult',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎鎵规鏄庣粏
+ * @param batchCode
+ */
+export const delSensorResult = (batchCode: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/sensorResult/' + batchCode,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/sensorResult/types.ts b/src/api/qms/sensorResult/types.ts
new file mode 100644
index 0000000..96878eb
--- /dev/null
+++ b/src/api/qms/sensorResult/types.ts
@@ -0,0 +1,96 @@
+export interface SensorResultVO {
+  /**
+   * 鎵规鍙�
+   */
+  batchCode: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum: string;
+
+  /**
+   * 鍒ゅ畾缁撴灉
+   */
+  judgeResult: string;
+
+  /**
+   * 鎬绘祴璇曢」鏁�
+   */
+  totalCount: number;
+
+  /**
+   * OK椤规暟
+   */
+  okCount: number;
+
+  /**
+   * NG椤规暟
+   */
+  ngCount: number;
+
+}
+
+export interface SensorResultForm extends BaseEntity {
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 鍒ゅ畾缁撴灉
+   */
+  judgeResult?: string;
+
+  /**
+   * 鎬绘祴璇曢」鏁�
+   */
+  totalCount?: number;
+
+  /**
+   * OK椤规暟
+   */
+  okCount?: number;
+
+  /**
+   * NG椤规暟
+   */
+  ngCount?: number;
+
+}
+
+export interface SensorResultQuery extends PageQuery {
+
+  /**
+   * 鍒ゅ畾缁撴灉
+   */
+  judgeResult?: string;
+
+  /**
+   * 鎬绘祴璇曢」鏁�
+   */
+  totalCount?: number;
+
+  /**
+   * OK椤规暟
+   */
+  okCount?: number;
+
+  /**
+   * NG椤规暟
+   */
+  ngCount?: number;
+
+    /**
+     * 鏃ユ湡鑼冨洿鍙傛暟
+     */
+    params?: any;
+}
+
+
+
diff --git a/src/api/qms/sensorRetest/index.ts b/src/api/qms/sensorRetest/index.ts
new file mode 100644
index 0000000..077ab9f
--- /dev/null
+++ b/src/api/qms/sensorRetest/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { SensorRetestVO, SensorRetestForm, SensorRetestQuery } from '@/api/qms/sensorRetest/types';
+
+/**
+ * 鏌ヨ澶嶆祴璁板綍鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listSensorRetest = (query?: SensorRetestQuery): AxiosPromise<SensorRetestVO[]> => {
+  return request({
+    url: '/qms/sensorRetest/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ澶嶆祴璁板綍璇︾粏
+ * @param batchCode
+ */
+export const getSensorRetest = (batchCode: string | number): AxiosPromise<SensorRetestVO> => {
+  return request({
+    url: '/qms/sensorRetest/' + batchCode,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板澶嶆祴璁板綍
+ * @param data
+ */
+export const addSensorRetest = (data: SensorRetestForm) => {
+  return request({
+    url: '/qms/sensorRetest',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼澶嶆祴璁板綍
+ * @param data
+ */
+export const updateSensorRetest = (data: SensorRetestForm) => {
+  return request({
+    url: '/qms/sensorRetest',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎澶嶆祴璁板綍
+ * @param batchCode
+ */
+export const delSensorRetest = (batchCode: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/sensorRetest/' + batchCode,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/sensorRetest/types.ts b/src/api/qms/sensorRetest/types.ts
new file mode 100644
index 0000000..72b4a4a
--- /dev/null
+++ b/src/api/qms/sensorRetest/types.ts
@@ -0,0 +1,141 @@
+export interface SensorRetestVO {
+  /**
+   * 鎵规鍙�
+   */
+  batchCode: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum: string;
+
+  /**
+   * 澶嶆祴娆℃暟
+   */
+  retestNum: string;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode: string;
+
+  /**
+   * 鐢ㄦ埛甯愬彿
+   */
+  userName: string;
+
+  /**
+   * 鍒ゅ畾缁撴灉
+   */
+  judgeResult: string;
+
+  /**
+   * 鎬绘祴璇曢」鏁�
+   */
+  totalCount: number;
+
+  /**
+   * OK椤规暟
+   */
+  okCount: number;
+
+  /**
+   * NG椤规暟
+   */
+  ngCount: number;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
+
+export interface SensorRetestForm extends BaseEntity {
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 澶嶆祴娆℃暟
+   */
+  retestNum?: string;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode?: string;
+
+  /**
+   * 鐢ㄦ埛甯愬彿
+   */
+  userName?: string;
+
+  /**
+   * 鍒ゅ畾缁撴灉
+   */
+  judgeResult?: string;
+
+  /**
+   * 鎬绘祴璇曢」鏁�
+   */
+  totalCount?: number;
+
+  /**
+   * OK椤规暟
+   */
+  okCount?: number;
+
+  /**
+   * NG椤规暟
+   */
+  ngCount?: number;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+}
+
+export interface SensorRetestQuery extends PageQuery {
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 澶嶆祴娆℃暟
+   */
+  retestNum?: string;
+
+  /**
+   * 璁惧鍙�
+   */
+  deviceCode?: string;
+
+  /**
+   * 鐢ㄦ埛甯愬彿
+   */
+  userName?: string;
+
+    /**
+     * 鏃ユ湡鑼冨洿鍙傛暟
+     */
+    params?: any;
+}
+
+
+
diff --git a/src/api/qms/testResult/index.ts b/src/api/qms/testResult/index.ts
new file mode 100644
index 0000000..34999a6
--- /dev/null
+++ b/src/api/qms/testResult/index.ts
@@ -0,0 +1,89 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { TestResultVO, TestResultForm, TestResultQuery } from '@/api/qms/testResult/types';
+
+/**
+ * 鏌ヨ娴嬭瘯缁撴灉鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listTestResult = (query?: TestResultQuery): AxiosPromise<TestResultVO[]> => {
+  return request({
+    url: '/qms/testResult/list',
+    method: 'get',
+    params: query
+  });
+};
+
+
+/**
+ * 鏌ヨ娴嬭瘯缁撴灉鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listTestResultAll = (query?: TestResultQuery): AxiosPromise<TestResultVO[]> => {
+  return request({
+    url: '/qms/testResult/listAll',
+    method: 'get',
+    params: query
+  });
+};
+/**
+ * 鏌ヨ娴嬭瘯椤圭洰
+ * @param query
+ * @returns {*}
+ */
+export const listTestItem = () => {
+  return request({
+    url: '/qms/testResult/distinctTestItems',
+    method: 'get'
+  });
+};
+
+/**
+ * 鏌ヨ娴嬭瘯缁撴灉璇︾粏
+ * @param id
+ */
+export const getTestResult = (id: string | number): AxiosPromise<TestResultVO> => {
+  return request({
+    url: '/qms/testResult/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板娴嬭瘯缁撴灉
+ * @param data
+ */
+export const addTestResult = (data: TestResultForm) => {
+  return request({
+    url: '/qms/testResult',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼娴嬭瘯缁撴灉
+ * @param data
+ */
+export const updateTestResult = (data: TestResultForm) => {
+  return request({
+    url: '/qms/testResult',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎娴嬭瘯缁撴灉
+ * @param id
+ */
+export const delTestResult = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/qms/testResult/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/qms/testResult/types.ts b/src/api/qms/testResult/types.ts
new file mode 100644
index 0000000..8acb305
--- /dev/null
+++ b/src/api/qms/testResult/types.ts
@@ -0,0 +1,201 @@
+export interface TestResultVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum: string;
+
+  /**
+   * 娴嬭瘯椤圭洰
+   */
+  testItem: string;
+
+  /**
+   * 瀹為檯鐢靛帇
+   */
+  voltage: number;
+
+  /**
+   * 瀹為檯鐢垫祦
+   */
+  loadCurrent: number;
+
+  /**
+   * 鏍囧噯璺濈
+   */
+  stdDistance: number;
+
+  /**
+   * 鎰熷簲鐗�
+   */
+  inductor: string;
+
+  /**
+   * 杈撳嚭寮曡剼锛堥粦锛涚櫧锛�
+   */
+  output: string;
+
+  /**
+   * 娴嬭瘯鏁版嵁
+   */
+  testValue: number;
+
+  /**
+   * 鍒ゆ柇鏉′欢锛堟渶灏忓��<=鐪熷疄鍊�<=鏈�澶у�� 锛�
+   */
+  judgeDetail: string;
+
+  /**
+   * 娴嬭瘯缁撴灉锛圤K; NG锛�
+   */
+  testResult: string;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+}
+
+export interface TestResultForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 娴嬭瘯椤圭洰
+   */
+  testItem?: string;
+
+  /**
+   * 瀹為檯鐢靛帇
+   */
+  voltage?: number;
+
+  /**
+   * 瀹為檯鐢垫祦
+   */
+  loadCurrent?: number;
+
+  /**
+   * 鏍囧噯璺濈
+   */
+  stdDistance?: number;
+
+  /**
+   * 鎰熷簲鐗�
+   */
+  inductor?: string;
+
+  /**
+   * 杈撳嚭寮曡剼锛堥粦锛涚櫧锛�
+   */
+  output?: string;
+
+  /**
+   * 娴嬭瘯鏁版嵁
+   */
+  testValue?: number;
+
+  /**
+   * 鍒ゆ柇鏉′欢锛堟渶灏忓��<=鐪熷疄鍊�<=鏈�澶у�� 锛�
+   */
+  judgeDetail?: string;
+
+  /**
+   * 娴嬭瘯缁撴灉锛圤K; NG锛�
+   */
+  testResult?: string;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+}
+
+export interface TestResultQuery extends PageQuery {
+
+  /**
+   * 鎵规鍙�
+   */
+  batchCode?: string;
+
+  /**
+   * 娴嬭瘯搴忓彿
+   */
+  testNum?: string;
+
+  /**
+   * 娴嬭瘯椤圭洰
+   */
+  testItem?: string;
+
+  /**
+   * 瀹為檯鐢靛帇
+   */
+  voltage?: number;
+
+  /**
+   * 瀹為檯鐢垫祦
+   */
+  loadCurrent?: number;
+
+  /**
+   * 鏍囧噯璺濈
+   */
+  stdDistance?: number;
+
+  /**
+   * 鎰熷簲鐗�
+   */
+  inductor?: string;
+
+  /**
+   * 杈撳嚭寮曡剼锛堥粦锛涚櫧锛�
+   */
+  output?: string;
+
+  /**
+   * 娴嬭瘯鏁版嵁
+   */
+  testValue?: number;
+
+  /**
+   * 鍒ゆ柇鏉′欢锛堟渶灏忓��<=鐪熷疄鍊�<=鏈�澶у�� 锛�
+   */
+  judgeDetail?: string;
+
+  /**
+   * 娴嬭瘯缁撴灉锛圤K; NG锛�
+   */
+  testResult?: string;
+
+  /**
+   * 鏃ユ湡鑼冨洿鍙傛暟
+   */
+  params?: any;
+}
+
+
+
diff --git a/src/assets/icons/svg/batch-detail.svg b/src/assets/icons/svg/batch-detail.svg
new file mode 100644
index 0000000..35be712
--- /dev/null
+++ b/src/assets/icons/svg/batch-detail.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733988825570" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1518" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M902.386347 299.738453c-0.02048-0.02048 0-0.054613-0.02048-0.085333-0.4096-1.93536-1.1264-3.754667-2.048-5.471573-0.211627-0.395947-0.426667-0.771413-0.648533-1.153707-1.04448-1.723733-2.22208-3.34848-3.67616-4.73088l0-0.01024L618.038613 24.139093c-0.006827 0-0.034133-0.01024-0.034133-0.03072l-0.037547-0.027307c-0.01024-0.013653-0.044373-0.013653-0.064853-0.03072-1.419947-1.355093-3.085653-2.450773-4.840107-3.42016-0.433493-0.23552-0.853333-0.457387-1.314133-0.662187-1.764693-0.836267-3.631787-1.52576-5.618347-1.921707-0.170667-0.01024-0.324267-0.01024-0.47104-0.017067-1.344853-0.24576-2.69312-0.402773-4.12672-0.402773L201.8816 17.626453c-36.939093 0-67.003733 28.699307-67.003733 63.993173l0 864.576853c0 35.283627 30.06464 64.01024 67.003733 64.01024l633.965227 0c36.94592 0 66.993493-28.726613 66.993493-64.01024L902.84032 304.049493C902.84032 302.578347 902.66624 301.134507 902.386347 299.738453M823.197013 281.82528l-198.362453 0L624.83456 93.313707 823.197013 281.82528zM856.306347 946.213547c0 10.769067-9.17504 19.554987-20.45952 19.554987L201.8816 965.768533c-11.30496 0-20.476587-8.779093-20.476587-19.554987L181.405013 81.629867c0-10.789547 9.17504-19.551573 20.476587-19.551573l376.425813 0 0 241.96096c0 12.274347 10.43456 22.224213 23.261867 22.224213l254.743893 0L856.306347 946.213547 856.306347 946.213547z" fill="#050101" p-id="1519"></path><path d="M711.3216 738.78528 313.50784 738.78528c-12.20608 0-22.09792 10.417493-22.09792 23.27552 0 12.87168 9.888427 23.27552 22.09792 23.27552l397.810347 0c12.19584 0 22.09792-10.407253 22.09792-23.27552C733.412693 749.202773 723.51744 738.78528 711.3216 738.78528" fill="#050101" p-id="1520"></path><path d="M711.3216 585.63584 313.50784 585.63584c-12.20608 0-22.09792 10.41408-22.09792 23.261867 0 12.868267 9.888427 23.272107 22.09792 23.272107l397.810347 0c12.19584 0 22.09792-10.386773 22.09792-23.272107C733.412693 596.04992 723.51744 585.63584 711.3216 585.63584" fill="#050101" p-id="1521"></path><path d="M291.396267 455.758507c0 12.8512 9.89184 23.261867 22.09792 23.261867l397.810347 0c12.209493 0 22.104747-10.410667 22.104747-23.261867 0-12.864853-9.888427-23.268693-22.104747-23.268693L313.494187 432.489813C301.29152 432.493227 291.396267 442.893653 291.396267 455.758507" fill="#050101" p-id="1522"></path></svg>
\ No newline at end of file
diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png
index 3f919d8..fd0c6ba 100644
--- a/src/assets/logo/logo.png
+++ b/src/assets/logo/logo.png
Binary files differ
diff --git a/src/assets/logo/logo_bak.png b/src/assets/logo/logo_bak.png
new file mode 100644
index 0000000..3f919d8
--- /dev/null
+++ b/src/assets/logo/logo_bak.png
Binary files differ
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index 631c261..fefa671 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -34,7 +34,7 @@
   }
 });
 
-const title = ref('RuoYi-Vue-Plus');
+const title = ref('LB-QMS');
 const settingsStore = useSettingsStore();
 const sideTheme = computed(() => settingsStore.sideTheme);
 </script>
diff --git a/src/views/index.vue b/src/views/index.vue
index 9168123..85a21e8 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -2,92 +2,12 @@
   <div class="app-container home">
     <el-row :gutter="20">
       <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h2>
-        <p>
-          RuoYi-Vue-Plus 鏄熀浜� RuoYi-Vue 閽堝 鍒嗗竷寮忛泦缇� 鍦烘櫙鍗囩骇(涓嶅吋瀹瑰師妗嗘灦)
-          <br />
-          * 鍓嶇寮�鍙戞鏋� Vue3銆乀S銆丒lement Plus<br />
-          * 鍚庣寮�鍙戞鏋� Spring Boot<br />
-          * 瀹瑰櫒妗嗘灦 Undertow 鍩轰簬 Netty 鐨勯珮鎬ц兘瀹瑰櫒<br />
-          * 鏉冮檺璁よ瘉妗嗘灦 Sa-Token 鏀寔澶氱粓绔璇佺郴缁�<br />
-          * 鍏崇郴鏁版嵁搴� MySQL 閫傞厤 8.X 鏈�浣� 5.7<br />
-          * 缂撳瓨鏁版嵁搴� Redis 閫傞厤 6.X 鏈�浣� 4.X<br />
-          * 鏁版嵁搴撴鏋� Mybatis-Plus 蹇�� CRUD 澧炲姞寮�鍙戞晥鐜�<br />
-          * 鏁版嵁搴撴鏋� p6spy 鏇村己鍔茬殑 SQL 鍒嗘瀽<br />
-          * 澶氭暟鎹簮妗嗘灦 dynamic-datasource 鏀寔涓讳粠涓庡绉嶇被鏁版嵁搴撳紓鏋�<br />
-          * 搴忓垪鍖栨鏋� Jackson 缁熶竴浣跨敤 jackson 楂樻晥鍙潬<br />
-          * Redis瀹㈡埛绔� Redisson 鎬ц兘寮哄姴銆丄PI涓板瘜<br />
-          * 鍒嗗竷寮忛檺娴� Redisson 鍏ㄥ眬銆佽姹侷P銆侀泦缇D 澶氱闄愭祦<br />
-          * 鍒嗗竷寮忛攣 Lock4j 娉ㄨВ閿併�佸伐鍏烽攣 澶氱澶氭牱<br />
-          * 鍒嗗竷寮忓箓绛� Lock4j 鍩轰簬鍒嗗竷寮忛攣瀹炵幇<br />
-          * 鍒嗗竷寮忛摼璺拷韪� SkyWalking 鏀寔閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br />
-          * 鍒嗗竷寮忎换鍔¤皟搴� SnailJob 楂樻�ц兘 楂樺彲闈� 鏄撴墿灞�<br />
-          * 鏂囦欢瀛樺偍 Minio 鏈湴瀛樺偍<br />
-          * 鏂囦欢瀛樺偍 涓冪墰銆侀樋閲屻�佽吘璁� 浜戝瓨鍌�<br />
-          * 鐩戞帶妗嗘灦 SpringBoot-Admin 鍏ㄦ柟浣嶆湇鍔$洃鎺�<br />
-          * 鏍¢獙妗嗘灦 Validation 澧炲己鎺ュ彛瀹夊叏鎬� 涓ヨ皑鎬�<br />
-          * Excel妗嗘灦 Alibaba EasyExcel 鎬ц兘浼樺紓 鎵╁睍鎬у己<br />
-          * 鏂囨。妗嗘灦 SpringDoc銆乯avadoc 鏃犳敞瑙i浂鍏ヤ镜鍩轰簬java娉ㄩ噴<br />
-          * 宸ュ叿绫绘鏋� Hutool銆丩ombok 鍑忓皯浠g爜鍐椾綑 澧炲姞瀹夊叏鎬�<br />
-          * 浠g爜鐢熸垚鍣� 閫傞厤MP銆丼pringDoc瑙勮寖鍖栦唬鐮� 涓�閿敓鎴愬墠鍚庣浠g爜<br />
-          * 閮ㄧ讲鏂瑰紡 Docker 瀹瑰櫒缂栨帓 涓�閿儴缃蹭笟鍔¢泦缇�<br />
-          * 鍥介檯鍖� SpringMessage Spring鏍囧噯鍥介檯鍖栨柟妗�<br />
-        </p>
-        <p><b>褰撳墠鐗堟湰:</b> <span>v5.3.0</span></p>
-        <p>
-          <el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
-        </p>
-        <p>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">璁块棶鐮佷簯</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">璁块棶GitHub</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
-            >鏇存柊鏃ュ織</el-button
-          >
-        </p>
+        <h2>鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺</h2>
+
       </el-col>
 
       <el-col :sm="24" :lg="12" style="padding-left: 20px">
-        <h2>RuoYi-Cloud-Plus澶氱鎴峰井鏈嶅姟绠$悊绯荤粺</h2>
-        <p>
-          RuoYi-Cloud-Plus 寰湇鍔¢�氱敤鏉冮檺绠$悊绯荤粺 閲嶅啓 RuoYi-Cloud 鍏ㄦ柟浣嶅崌绾�(涓嶅吋瀹瑰師妗嗘灦)
-          <br />
-          * 鍓嶇寮�鍙戞鏋� Vue3銆乀S銆丒lement UI<br />
-          * 鍚庣寮�鍙戞鏋� Spring Boot<br />
-          * 寰湇鍔″紑鍙戞鏋� Spring Cloud銆丼pring Cloud Alibaba<br />
-          * 瀹瑰櫒妗嗘灦 Undertow 鍩轰簬 XNIO 鐨勯珮鎬ц兘瀹瑰櫒<br />
-          * 鏉冮檺璁よ瘉妗嗘灦 Sa-Token銆丣wt 鏀寔澶氱粓绔璇佺郴缁�<br />
-          * 鍏崇郴鏁版嵁搴� MySQL 閫傞厤 8.X 鏈�浣� 5.7<br />
-          * 鍏崇郴鏁版嵁搴� Oracle 閫傞厤 11g 12c<br />
-          * 鍏崇郴鏁版嵁搴� PostgreSQL 閫傞厤 13 14<br />
-          * 鍏崇郴鏁版嵁搴� SQLServer 閫傞厤 2017 2019<br />
-          * 缂撳瓨鏁版嵁搴� Redis 閫傞厤 6.X 鏈�浣� 5.X<br />
-          * 鍒嗗竷寮忔敞鍐屼腑蹇� Alibaba Nacos 閲囩敤2.X 鍩轰簬GRPC閫氫俊楂樻�ц兘<br />
-          * 鍒嗗竷寮忛厤缃腑蹇� Alibaba Nacos 閲囩敤2.X 鍩轰簬GRPC閫氫俊楂樻�ц兘<br />
-          * 鏈嶅姟缃戝叧 Spring Cloud Gateway 鍝嶅簲寮忛珮鎬ц兘缃戝叧<br />
-          * 璐熻浇鍧囪  Spring Cloud Loadbalancer 璐熻浇鍧囪 澶勭悊<br />
-          * RPC杩滅▼璋冪敤 Apache Dubbo 鍘熺敓鎬佷娇鐢ㄤ綋楠屻�侀珮鎬ц兘<br />
-          * 鍒嗗竷寮忛檺娴佺啍鏂� Alibaba Sentinel 鏃犱镜鍏ャ�侀珮鎵╁睍<br />
-          * 鍒嗗竷寮忎簨鍔� Alibaba Seata 鏃犱镜鍏ャ�侀珮鎵╁睍 鏀寔 鍥涚妯″紡<br />
-          * 鍒嗗竷寮忔秷鎭槦鍒� Apache Kafka 楂樻�ц兘楂橀�熷害<br />
-          * 鍒嗗竷寮忔秷鎭槦鍒� Apache RocketMQ 楂樺彲鐢ㄥ姛鑳藉鏍�<br />
-          * 鍒嗗竷寮忔秷鎭槦鍒� RabbitMQ 鏀寔鍚勭鎵╁睍鎻掍欢鍔熻兘澶氭牱鎬�<br />
-          * 鍒嗗竷寮忔悳绱㈠紩鎿� ElasticSearch 涓氱晫鐭ュ悕<br />
-          * 鍒嗗竷寮忛摼璺拷韪� Apache SkyWalking 閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br />
-          * 鍒嗗竷寮忔棩蹇椾腑蹇� ELK 涓氱晫鎴愮啛瑙e喅鏂规<br />
-          * 鍒嗗竷寮忕洃鎺� Prometheus銆丟rafana 鍏ㄦ柟浣嶆�ц兘鐩戞帶<br />
-          * 鍏朵綑涓� Vue 鐗堟湰涓�鑷�<br />
-        </p>
-        <p><b>褰撳墠鐗堟湰:</b> <span>v2.3.0</span></p>
-        <p>
-          <el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
-        </p>
-        <p>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">璁块棶鐮佷簯</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">璁块棶GitHub</el-button>
-          <el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
-            >鏇存柊鏃ュ織</el-button
-          >
-        </p>
+
       </el-col>
     </el-row>
     <el-divider />
diff --git a/src/views/login.vue b/src/views/login.vue
index cbc980c..e5d7c23 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -2,7 +2,7 @@
   <div class="login">
     <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
       <div class="title-box">
-        <h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
+        <h3 class="title">鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺</h3>
         <lang-select />
       </div>
       <el-form-item v-if="tenantEnabled" prop="tenantId">
@@ -73,7 +73,7 @@
     </el-form>
     <!--  搴曢儴  -->
     <div class="el-login-footer">
-      <span>Copyright 漏 2018-2024 鐤媯鐨勭嫯瀛怢i All Rights Reserved.</span>
+      <span>Copyright 漏 2018-2024 涓婃捣鍏版郸鏅鸿兘绉戞妧鏈夐檺鍏徃 All Rights Reserved.</span>
     </div>
   </div>
 </template>
@@ -95,8 +95,8 @@
 
 const loginForm = ref<LoginData>({
   tenantId: '000000',
-  username: 'admin',
-  password: 'admin123',
+  username: '',
+  password: '',
   rememberMe: false,
   code: '',
   uuid: ''
diff --git a/src/views/qms/batch/index.vue b/src/views/qms/batch/index.vue
new file mode 100644
index 0000000..ac5733e
--- /dev/null
+++ b/src/views/qms/batch/index.vue
@@ -0,0 +1,299 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="鎵规鍙�" prop="batchCode">
+              <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="浜у搧鍨嬪彿" prop="prodModel">
+              <el-input v-model="queryParams.prodModel" placeholder="璇疯緭鍏ヤ骇鍝佸瀷鍙�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <div style="display: inline" v-if="showMore">
+              <el-form-item label="鐢ㄦ埛缂栧彿" prop="userId">
+                <el-input v-model="queryParams.userId" placeholder="璇疯緭鍏ョ敤鎴风紪鍙�" clearable @keyup.enter="handleQuery" />
+              </el-form-item>
+              <el-form-item label="璁惧鍙�" prop="deviceCode">
+                <el-input v-model="queryParams.deviceCode" placeholder="璇疯緭鍏ヨ澶囧彿" clearable @keyup.enter="handleQuery" />
+              </el-form-item>
+              <el-form-item label="鏁伴噺" prop="num">
+                <el-input v-model="queryParams.num" placeholder="璇疯緭鍏ユ暟閲�" clearable @keyup.enter="handleQuery" />
+              </el-form-item>
+            </div>
+
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+              <el-button v-if="!showMore" type="text" @click="showMore = !showMore">灞曞紑</el-button>
+              <el-button v-if="showMore" type="text" @click="showMore = !showMore">鏀惰捣</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:batch:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:batch:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:batch:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:batch:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" :columns="columns" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table
+        v-loading="loading"
+        :data="batchList"
+        @selection-change="handleSelectionChange"
+        stripe
+        highlight-current-row
+      >
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="ID" align="center" prop="id" v-if="columns[0].visible"/>
+        <el-table-column label="鎵规鍙�" align="center" prop="batchCode" v-if="columns[1].visible" />
+        <el-table-column label="浜у搧鍨嬪彿" align="center" prop="prodModel" v-if="columns[2].visible" />
+        <el-table-column label="鐢ㄦ埛缂栧彿" align="center" prop="userId" v-if="columns[3].visible" />
+        <el-table-column label="璁惧鍙�" align="center" prop="deviceCode" v-if="columns[4].visible" />
+        <el-table-column label="鏁伴噺" align="center" prop="num" v-if="columns[5].visible" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" v-if="columns[6].visible" />
+        <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[7].visible" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="鏄庣粏" placement="top">
+              <el-button link type="primary" icon="Document" @click="handleDetail(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:batch:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:batch:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规壒娆$鐞嗗璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="batchFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鎵规鍙�" prop="batchCode">
+          <el-input v-model="form.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </el-form-item>
+        <el-form-item label="浜у搧鍨嬪彿" prop="prodModel">
+          <el-input v-model="form.prodModel" placeholder="璇疯緭鍏ヤ骇鍝佸瀷鍙�" />
+        </el-form-item>
+        <el-form-item label="鐢ㄦ埛缂栧彿" prop="userId">
+          <el-input v-model="form.userId" placeholder="璇疯緭鍏ョ敤鎴风紪鍙�" />
+        </el-form-item>
+        <el-form-item label="璁惧鍙�" prop="deviceCode">
+          <el-input v-model="form.deviceCode" placeholder="璇疯緭鍏ヨ澶囧彿" />
+        </el-form-item>
+        <el-form-item label="鏁伴噺" prop="num">
+          <el-input v-model="form.num" placeholder="璇疯緭鍏ユ暟閲�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Batch" lang="ts">
+import { listBatch, getBatch, delBatch, addBatch, updateBatch } from '@/api/qms/batch';
+import { BatchVO, BatchQuery, BatchForm } from '@/api/qms/batch/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const batchList = ref<BatchVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const showMore = ref(false);
+const queryFormRef = ref<ElFormInstance>();
+const batchFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: BatchForm = {
+  id: undefined,
+  batchCode: undefined,
+  prodModel: undefined,
+  userId: undefined,
+  deviceCode: undefined,
+  num: undefined,
+  remark: undefined,
+  createTime: undefined, // 娣诲姞 createTime 瀛楁
+}
+const data = reactive<PageData<BatchForm, BatchQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    batchCode: undefined,
+    prodModel: undefined,
+    userId: undefined,
+    deviceCode: undefined,
+    num: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    prodModel: [
+      { required: true, message: "浜у搧鍨嬪彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    userId: [
+      { required: true, message: "鐢ㄦ埛缂栧彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    deviceCode: [
+      { required: true, message: "璁惧鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    num: [
+      { required: true, message: "鏁伴噺涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 娣诲姞 columns 鏁扮粍
+const columns = ref<FieldOption[]>([
+  { key: 0, label: 'ID', align: 'center', visible: false },
+  { key: 1, label: '鎵规鍙�', align: 'center', visible: true },
+  { key: 2, label: '浜у搧鍨嬪彿', align: 'center', visible: true },
+  { key: 3, label: '鐢ㄦ埛缂栧彿', align: 'center', visible: true },
+  { key: 4, label: '璁惧鍙�', align: 'center', visible: true },
+  { key: 5, label: '鏁伴噺', align: 'center', visible: true },
+  { key: 6, label: '澶囨敞', align: 'center', visible: true },
+  { key: 7, label: '鍒涘缓鏃堕棿', align: 'center', visible: true },
+]);
+
+/** 鏌ヨ鎵规绠$悊鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listBatch(queryParams.value);
+  batchList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  batchFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: BatchVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞鎵规绠$悊";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: BatchVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getBatch(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼鎵规绠$悊";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  batchFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateBatch(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addBatch(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: BatchVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鎵规绠$悊缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delBatch(_ids);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/batch/export', {
+    ...queryParams.value
+  }, `batch_${new Date().getTime()}.xlsx`)
+}
+
+const handleDetail = (batch) => {
+  proxy.$router.push({ path: 'sensorResult', query: {batchCode: batch.batchCode} });
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/device/index.vue b/src/views/qms/device/index.vue
new file mode 100644
index 0000000..4632479
--- /dev/null
+++ b/src/views/qms/device/index.vue
@@ -0,0 +1,280 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="璁惧绫诲瀷" prop="deviceType">
+              <el-select v-model="queryParams.deviceType" placeholder="璇烽�夋嫨璁惧绫诲瀷" clearable >
+                <el-option v-for="dict in lb_device_type" :key="dict.value" :label="dict.label" :value="dict.value"/>
+              </el-select>
+            </el-form-item>
+            <el-form-item label="璁惧缂栧彿" prop="deviceCode">
+              <el-input v-model="queryParams.deviceCode" placeholder="璇疯緭鍏ヨ澶囩紪鍙�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鎵�鍦ㄧ粍" prop="deviceGroup">
+              <el-input v-model="queryParams.deviceGroup" placeholder="璇疯緭鍏ユ墍鍦ㄧ粍" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鎵�鍦ㄩ儴闂�" prop="deviceDept">
+              <el-input v-model="queryParams.deviceDept" placeholder="璇疯緭鍏ユ墍鍦ㄩ儴闂�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鎵�鍦ㄥ伐浣�" prop="deviceStation">
+              <el-input v-model="queryParams.deviceStation" placeholder="璇疯緭鍏ユ墍鍦ㄥ伐浣�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:device:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:device:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:device:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:device:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" stripe highlight-current-row :data="deviceList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="false" />
+        <el-table-column label="璁惧绫诲瀷" align="center" prop="deviceType">
+          <template #default="scope">
+            <dict-tag :options="lb_device_type" :value="scope.row.deviceType"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="璁惧缂栧彿" align="center" prop="deviceCode" />
+        <el-table-column label="鎵�鍦ㄧ粍" align="center" prop="deviceGroup" />
+        <el-table-column label="鎵�鍦ㄩ儴闂�" align="center" prop="deviceDept" />
+        <el-table-column label="鎵�鍦ㄥ伐浣�" align="center" prop="deviceStation" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:device:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:device:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祴璇曚华瀵硅瘽妗� -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="deviceFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="璁惧绫诲瀷" prop="deviceType">
+          <el-radio-group v-model="form.deviceType">
+            <el-radio
+              v-for="dict in lb_device_type"
+              :key="dict.value"
+              :value="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="璁惧缂栧彿" prop="deviceCode">
+          <el-input v-model="form.deviceCode" placeholder="璇疯緭鍏ヨ澶囩紪鍙�" />
+        </el-form-item>
+        <el-form-item label="鎵�鍦ㄧ粍" prop="deviceGroup">
+          <el-input v-model="form.deviceGroup" placeholder="璇疯緭鍏ユ墍鍦ㄧ粍" />
+        </el-form-item>
+        <el-form-item label="鎵�鍦ㄩ儴闂�" prop="deviceDept">
+          <el-input v-model="form.deviceDept" placeholder="璇疯緭鍏ユ墍鍦ㄩ儴闂�" />
+        </el-form-item>
+        <el-form-item label="鎵�鍦ㄥ伐浣�" prop="deviceStation">
+          <el-input v-model="form.deviceStation" placeholder="璇疯緭鍏ユ墍鍦ㄥ伐浣�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Device" lang="ts">
+import { listDevice, getDevice, delDevice, addDevice, updateDevice } from '@/api/qms/device';
+import { DeviceVO, DeviceQuery, DeviceForm } from '@/api/qms/device/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { lb_device_type } = toRefs<any>(proxy?.useDict('lb_device_type'));
+
+const deviceList = ref<DeviceVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const deviceFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: DeviceForm = {
+  id: undefined,
+  deviceType: undefined,
+  deviceCode: undefined,
+  deviceGroup: undefined,
+  deviceDept: undefined,
+  deviceStation: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<DeviceForm, DeviceQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    deviceType: undefined,
+    deviceCode: undefined,
+    deviceGroup: undefined,
+    deviceDept: undefined,
+    deviceStation: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    deviceType: [
+      { required: true, message: "璁惧绫诲瀷涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    deviceCode: [
+      { required: true, message: "璁惧缂栧彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    deviceGroup: [
+      { required: true, message: "鎵�鍦ㄧ粍涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    deviceDept: [
+      { required: true, message: "鎵�鍦ㄩ儴闂ㄤ笉鑳戒负绌�", trigger: "blur" }
+    ],
+    deviceStation: [
+      { required: true, message: "鎵�鍦ㄥ伐浣嶄笉鑳戒负绌�", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ娴嬭瘯浠垪琛� */
+const getList = async () => {
+  loading.value = true;
+  const res = await listDevice(queryParams.value);
+  deviceList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  deviceFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: DeviceVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞娴嬭瘯浠�";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: DeviceVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getDevice(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼娴嬭瘯浠�";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  deviceFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateDevice(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addDevice(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: DeviceVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯浠紪鍙蜂负"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delDevice(_ids);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/device/export', {
+    ...queryParams.value
+  }, `device_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/retestResult/index.vue b/src/views/qms/retestResult/index.vue
new file mode 100644
index 0000000..2699e87
--- /dev/null
+++ b/src/views/qms/retestResult/index.vue
@@ -0,0 +1,461 @@
+<template>
+  <div class="p-2">
+<!--    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">-->
+<!--      <div v-show="showSearch" class="mb-[10px]">-->
+<!--        <el-card shadow="hover">-->
+<!--          <el-form ref="queryFormRef" :model="queryParams" :inline="true">-->
+<!--            <el-form-item label="鎵规鍙�" prop="batchCode">-->
+<!--              <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable @keyup.enter="handleQuery" />-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">-->
+<!--              <el-input v-model="queryParams.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" clearable @keyup.enter="handleQuery" />-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">-->
+<!--              <el-input v-model="queryParams.testItem" placeholder="璇疯緭鍏ユ祴璇曢」鐩�" clearable @keyup.enter="handleQuery" />-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="澶嶆祴娆℃暟" prop="retestNum">-->
+<!--              <el-input v-model="queryParams.retestNum" placeholder="璇疯緭鍏ュ娴嬫鏁�" clearable @keyup.enter="handleQuery" />-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="璁惧鍙�" prop="deviceCode">-->
+<!--              <el-input v-model="queryParams.deviceCode" placeholder="璇疯緭鍏ヨ澶囧彿" clearable @keyup.enter="handleQuery" />-->
+<!--            </el-form-item>-->
+<!--            <el-form-item label="鐢ㄦ埛甯愬彿" prop="userName">-->
+<!--              <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰笎鍙�" clearable @keyup.enter="handleQuery" />-->
+<!--            </el-form-item>-->
+<!--            <el-form-item>-->
+<!--              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>-->
+<!--              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>-->
+<!--            </el-form-item>-->
+<!--          </el-form>-->
+<!--        </el-card>-->
+<!--      </div>-->
+<!--    </transition>-->
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+<!--          <el-col :span="1.5">-->
+<!--            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:retestResult:add']">鏂板</el-button>-->
+<!--          </el-col>-->
+<!--          <el-col :span="1.5">-->
+<!--            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:retestResult:edit']">淇敼</el-button>-->
+<!--          </el-col>-->
+<!--          <el-col :span="1.5">-->
+<!--            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:retestResult:remove']">鍒犻櫎</el-button>-->
+<!--          </el-col>-->
+<!--          <el-col :span="1.5">-->
+<!--            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:retestResult:export']">瀵煎嚭</el-button>-->
+<!--          </el-col>-->
+          <right-toolbar v-model:showSearch="showSearch" :columns="columns" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" stripe highlight-current-row :data="retestResultList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="false"/>
+        <!-- 鎵规鍙� -->
+        <el-table-column
+          v-if="columns[0].visible"
+          label="鎵规鍙�"
+          align="center"
+          prop="batchCode"
+        />
+
+        <!-- 娴嬭瘯搴忓彿 -->
+        <el-table-column
+          v-if="columns[1].visible"
+          label="娴嬭瘯搴忓彿"
+          align="center"
+          prop="testNum"
+        />
+
+        <!-- 娴嬭瘯椤圭洰 -->
+        <el-table-column
+          v-if="columns[2].visible"
+          label="娴嬭瘯椤圭洰"
+          align="center"
+          prop="testItem"
+        />
+
+        <!-- 澶嶆祴娆℃暟 -->
+        <el-table-column
+          v-if="columns[3].visible"
+          label="澶嶆祴娆℃暟"
+          align="center"
+          prop="retestNum"
+        />
+
+        <!-- 璁惧鍙� -->
+        <el-table-column
+          v-if="columns[4].visible"
+          label="璁惧鍙�"
+          align="center"
+          prop="deviceCode"
+        />
+
+        <!-- 鐢ㄦ埛甯愬彿 -->
+        <el-table-column
+          v-if="columns[5].visible"
+          label="鐢ㄦ埛甯愬彿"
+          align="center"
+          prop="userName"
+        />
+
+        <!-- 鐜娓╁害 -->
+        <el-table-column
+          v-if="columns[6].visible"
+          label="鐜娓╁害"
+          align="center"
+          prop="temp"
+        />
+
+        <!-- 瀹為檯鐢靛帇 -->
+        <el-table-column
+          v-if="columns[7].visible"
+          label="瀹為檯鐢靛帇"
+          align="center"
+          prop="voltage"
+        />
+
+        <!-- 瀹為檯鐢垫祦 -->
+        <el-table-column
+          v-if="columns[8].visible"
+          label="瀹為檯鐢垫祦"
+          align="center"
+          prop="loadCurrent"
+        />
+
+        <!-- 鏍囧噯璺濈 -->
+        <el-table-column
+          v-if="columns[9].visible"
+          label="鏍囧噯璺濈"
+          align="center"
+          prop="stdDistance"
+        />
+
+        <!-- 鎰熷簲鐗� -->
+        <el-table-column
+          v-if="columns[10].visible"
+          label="鎰熷簲鐗�"
+          align="center"
+          prop="inductor"
+        />
+
+        <!-- 杈撳嚭寮曡剼 -->
+        <el-table-column
+          v-if="columns[11].visible"
+          label="杈撳嚭寮曡剼"
+          align="center"
+          prop="output"
+        />
+
+        <!-- 娴嬭瘯鏁版嵁 -->
+        <el-table-column
+          v-if="columns[12].visible"
+          label="娴嬭瘯鏁版嵁"
+          align="center"
+          prop="testValue"
+        />
+
+        <!-- 鍒ゆ柇鏉′欢 -->
+        <el-table-column
+          v-if="columns[13].visible"
+          label="鍒ゆ柇鏉′欢"
+          align="center"
+          prop="judgeDetail"
+        />
+
+        <!-- 娴嬭瘯缁撴灉 -->
+        <el-table-column
+          v-if="columns[14].visible"
+          label="娴嬭瘯缁撴灉"
+          align="center"
+          prop="testResult"
+        />
+
+        <!-- 澶囨敞 -->
+        <el-table-column
+          v-if="columns[15].visible"
+          label="澶囨敞"
+          align="center"
+          prop="remark"
+        />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['lb-qms:retestResult:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['lb-qms:retestResult:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀瑰娴嬭褰曞璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="retestResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="" prop="id">
+          <el-input v-model="form.id" placeholder="璇疯緭鍏�" />
+        </el-form-item>
+        <el-form-item label="澶嶆祴娆℃暟" prop="retestNum">
+          <el-input v-model="form.retestNum" placeholder="璇疯緭鍏ュ娴嬫鏁�" />
+        </el-form-item>
+        <el-form-item label="璁惧鍙�" prop="deviceCode">
+          <el-input v-model="form.deviceCode" placeholder="璇疯緭鍏ヨ澶囧彿" />
+        </el-form-item>
+        <el-form-item label="鐢ㄦ埛甯愬彿" prop="userName">
+          <el-input v-model="form.userName" placeholder="璇疯緭鍏ョ敤鎴峰笎鍙�" />
+        </el-form-item>
+        <el-form-item label="鐜娓╁害" prop="temp">
+          <el-input v-model="form.temp" placeholder="璇疯緭鍏ョ幆澧冩俯搴�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+          <el-input v-model="form.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+          <el-input v-model="form.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+          <el-input v-model="form.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" />
+        </el-form-item>
+        <el-form-item label="鎰熷簲鐗�" prop="inductor">
+          <el-input v-model="form.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" />
+        </el-form-item>
+        <el-form-item label="杈撳嚭寮曡剼" prop="output">
+          <el-input v-model="form.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+          <el-input v-model="form.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" />
+        </el-form-item>
+        <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+          <el-input v-model="form.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+          <el-input v-model="form.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="RetestResult" lang="ts">
+import { listRetestResult, getRetestResult, delRetestResult, addRetestResult, updateRetestResult } from '@/api/qms/retestResult';
+import { RetestResultVO, RetestResultQuery, RetestResultForm } from '@/api/qms/retestResult/types';
+import { watch } from 'vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const retestResultList = ref<RetestResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const retestResultFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: RetestResultForm = {
+  id: undefined,
+  batchCode: undefined,
+  testNum: undefined,
+  testItem: undefined,
+  retestNum: undefined,
+  deviceCode: undefined,
+  userName: undefined,
+  temp: undefined,
+  voltage: undefined,
+  loadCurrent: undefined,
+  stdDistance: undefined,
+  inductor: undefined,
+  output: undefined,
+  testValue: undefined,
+  judgeDetail: undefined,
+  testResult: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<RetestResultForm, RetestResultQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    batchCode: undefined,
+    testNum: undefined,
+    testItem: undefined,
+    retestNum: undefined,
+    deviceCode: undefined,
+    userName: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testItem: [
+      { required: true, message: "娴嬭瘯椤圭洰涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    retestNum: [
+      { required: true, message: "澶嶆祴娆℃暟涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 瀹氫箟琛ㄦ牸鍒楃殑鏄剧ず/闅愯棌閰嶇疆
+const columns = ref<FieldOption[]>([
+  { key: 0, label: '鎵规鍙�', align: 'center', visible: true },
+  { key: 1, label: '娴嬭瘯搴忓彿', align: 'center', visible: true },
+  { key: 2, label: '娴嬭瘯椤圭洰', align: 'center', visible: true },
+  { key: 3, label: '澶嶆祴娆℃暟', align: 'center', visible: true },
+  { key: 4, label: '璁惧鍙�', align: 'center', visible: true },
+  { key: 5, label: '鐢ㄦ埛甯愬彿', align: 'center', visible: true },
+  { key: 6, label: '鐜娓╁害', align: 'center', visible: true },
+  { key: 7, label: '瀹為檯鐢靛帇', align: 'center', visible: true },
+  { key: 8, label: '瀹為檯鐢垫祦', align: 'center', visible: true },
+  { key: 9, label: '鏍囧噯璺濈', align: 'center', visible: true },
+  { key: 10, label: '鎰熷簲鐗�', align: 'center', visible: true },
+  { key: 11, label: '杈撳嚭寮曡剼', align: 'center', visible: true },
+  { key: 12, label: '娴嬭瘯鏁版嵁', align: 'center', visible: true },
+  { key: 13, label: '鍒ゆ柇鏉′欢', align: 'center', visible: true },
+  { key: 14, label: '娴嬭瘯缁撴灉', align: 'center', visible: true },
+  { key: 15, label: '澶囨敞', align: 'center', visible: true },
+]);
+
+/** 鏌ヨ澶嶆祴璁板綍鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  queryParams.value.batchCode = proxy.$route.query.batchCode;
+  queryParams.value.testNum = proxy.$route.query.testNum;
+  if (proxy.$route.query.retestNum) {
+    queryParams.value.retestNum = proxy.$route.query.retestNum;
+  }
+
+  const res = await listRetestResult(queryParams.value);
+  retestResultList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  retestResultFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: RetestResultVO[]) => {
+  ids.value = selection.map(item => item.batchCode);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞澶嶆祴璁板綍";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: RetestResultVO) => {
+  reset();
+  const _batchCode = row?.batchCode || ids.value[0]
+  const res = await getRetestResult(_batchCode);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼澶嶆祴璁板綍";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  retestResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.batchCode) {
+        await updateRetestResult(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addRetestResult(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: RetestResultVO) => {
+  const _batchCodes = row?.batchCode || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎澶嶆祴璁板綍缂栧彿涓�"' + _batchCodes + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delRetestResult(_batchCodes);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/retestResult/export', {
+    ...queryParams.value
+  }, `retestResult_${new Date().getTime()}.xlsx`)
+}
+
+watch(
+  () => proxy.$route.query.testNum,
+  (testNum) => {
+    console.log('path', proxy.$route.path);
+    console.log('testNum', testNum);
+    if (proxy.$route.path === '/qms/retestResult' && testNum) {
+      queryParams.value.pageNum = 1;
+      getList();
+    }
+  }
+);
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/sensor/index.vue b/src/views/qms/sensor/index.vue
new file mode 100644
index 0000000..c2a6750
--- /dev/null
+++ b/src/views/qms/sensor/index.vue
@@ -0,0 +1,451 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="浜у搧鍨嬪彿" prop="prodModel">
+              <el-input v-model="queryParams.prodModel" placeholder="璇疯緭鍏ヤ骇鍝佸瀷鍙�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:sensor:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:sensor:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:sensor:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:sensor:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table
+        v-loading="loading"
+        :data="sensorList"
+        @selection-change="handleSelectionChange"
+        stripe
+        highlight-current-row
+      >
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="prodId" v-if="false" />
+        <el-table-column label="浜у搧鍨嬪彿" align="center" prop="prodModel" />
+        <el-table-column label="浜у搧鏉$爜" align="center" prop="prodBarcode" />
+        <el-table-column label="浜у搧澶х被" align="center" prop="prodClass">
+          <template #default="scope">
+            <dict-tag :options="lb_sensor_class" :value="scope.row.prodClass"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="浜у搧灏忕被" align="center" prop="prodSubclass">
+          <template #default="scope">
+            <dict-tag :options="lb_sensor_l_subclass" :value="scope.row.prodSubclass"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="渚涚數鐢垫簮" align="center" prop="supply">
+          <template #default="scope">
+            <dict-tag :options="lb_power_supply" :value="scope.row.supply"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐢靛帇楂樺��" align="center" prop="supplyHigh" />
+        <el-table-column label="鐢靛帇涓��" align="center" prop="supplyMiddle" />
+        <el-table-column label="鐢靛帇浣庡��" align="center" prop="supplyLow" />
+        <el-table-column label="璐熻浇鐢垫祦" align="center" prop="loadCurrent" />
+        <el-table-column label="寮曠嚎鏁伴噺" align="center" prop="wire">
+          <template #default="scope">
+            <dict-tag :options="lb_sensor_wire" :value="scope.row.wire"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="杈撳嚭淇″彿" align="center" prop="outputSignal">
+          <template #default="scope">
+            <dict-tag :options="lp_output_signal" :value="scope.row.outputSignal"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="杈撳嚭绫诲瀷" align="center" prop="outputType">
+          <template #default="scope">
+            <dict-tag :options="lb_output_type" :value="scope.row.outputType"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="杈撳嚭鏋佹��" align="center" prop="outputPolarity">
+          <template #default="scope">
+            <dict-tag :options="lb_output_polarity" :value="scope.row.outputPolarity"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎰熷簲璺濈" align="center" prop="distance" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:sensor:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:sensor:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀逛紶鎰熷櫒瀵硅瘽妗� -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
+      <el-form ref="sensorFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-row>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="浜у搧鍨嬪彿" prop="prodModel">
+              <el-input v-model="form.prodModel" placeholder="璇疯緭鍏ヤ骇鍝佸瀷鍙�" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="浜у搧鏉$爜" prop="prodBarcode">
+              <el-input v-model="form.prodBarcode" placeholder="璇疯緭鍏ヤ骇鍝佹潯鐮�" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :xs="24" :sm="24">
+            <el-form-item label="浜у搧澶х被" prop="prodClass">
+              <el-radio-group v-model="form.prodClass">
+                <el-radio
+                  v-for="dict in lb_sensor_class"
+                  :key="dict.value"
+                  :value="dict.value"
+                >{{dict.label}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+
+        </el-row>
+
+        <el-row>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="浜у搧灏忕被" prop="prodSubclass">
+              <el-select v-model="form.prodSubclass" placeholder="璇烽�夋嫨浜у搧灏忕被">
+                <el-option
+                  v-for="dict in (form.prodClass == '鍏夌數鍨�'?lb_sensor_p_subclass:form.prodClass=='鐢靛鍨�'?lb_sensor_c_subclass:lb_sensor_l_subclass)"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="dict.value"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="渚涚數鐢垫簮" prop="supply">
+              <el-radio-group v-model="form.supply">
+                <el-radio
+                  v-for="dict in lb_power_supply"
+                  :key="dict.value"
+                  :value="dict.value"
+                >{{dict.label}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="鐢靛帇楂樺��" prop="supplyHigh">
+              <el-input type="number" v-model="form.supplyHigh" placeholder="璇疯緭鍏ョ數鍘嬮珮鍊�">
+                <template #append>V</template>
+              </el-input>
+            </el-form-item>
+        </el-col>
+        <el-col :xs="24" :sm="12">
+          <el-form-item label="鐢靛帇涓��" prop="supplyMiddle">
+            <el-input type="number" v-model="form.supplyMiddle" placeholder="璇疯緭鍏ョ數鍘嬩腑鍊�">
+              <template #append>V</template>
+            </el-input>
+          </el-form-item>
+        </el-row>
+
+        <el-row>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="鐢靛帇浣庡��" prop="supplyLow">
+              <el-input type="number" v-model="form.supplyLow" placeholder="璇疯緭鍏ョ數鍘嬩綆鍊�">
+                <template #append>V</template>
+              </el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="璐熻浇鐢垫祦" prop="loadCurrent">
+              <el-input type="number" v-model="form.loadCurrent" placeholder="璇疯緭鍏ヨ礋杞界數娴�">
+                <template #append>A</template>
+              </el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+
+        <el-row>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="寮曠嚎鏁伴噺" prop="wire">
+              <el-select v-model="form.wire" placeholder="璇烽�夋嫨寮曠嚎鏁伴噺">
+                <el-option
+                  v-for="dict in lb_sensor_wire"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="parseInt(dict.value)"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="杈撳嚭淇″彿" prop="outputSignal">
+              <el-radio-group v-model="form.outputSignal">
+                <el-radio
+                  v-for="dict in lp_output_signal"
+                  :key="dict.value"
+                  :value="dict.value"
+                >{{dict.label}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+
+
+        <el-form-item label="杈撳嚭绫诲瀷" prop="outputType">
+          <el-radio-group v-model="form.outputType">
+            <el-radio
+              v-for="dict in lb_output_type"
+              :key="dict.value"
+              :value="dict.value"
+            >{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-row>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="杈撳嚭鏋佹��" prop="outputPolarity">
+              <el-radio-group v-model="form.outputPolarity">
+                <el-radio
+                  v-for="dict in lb_output_polarity"
+                  :key="dict.value"
+                  :value="dict.value"
+                >{{dict.label}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item label="鎰熷簲璺濈" prop="distance">
+              <el-input type="number" v-model="form.distance" placeholder="璇疯緭鍏ユ劅搴旇窛绂�">
+                <template #append>m</template>
+              </el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Sensor" lang="ts">
+import { listSensor, getSensor, delSensor, addSensor, updateSensor } from '@/api/qms/sensor';
+import { SensorVO, SensorQuery, SensorForm } from '@/api/qms/sensor/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { lp_output_signal, lb_output_polarity, lb_sensor_wire, lb_power_supply, lb_output_type, lb_sensor_class, lb_sensor_l_subclass, lb_sensor_c_subclass, lb_sensor_p_subclass } = toRefs<any>(proxy?.useDict('lp_output_signal', 'lb_output_polarity', 'lb_sensor_wire', 'lb_power_supply', 'lb_output_type', 'lb_sensor_class', 'lb_sensor_l_subclass', 'lb_sensor_c_subclass', 'lb_sensor_p_subclass'));
+
+const sensorList = ref<SensorVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const sensorFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: SensorForm = {
+  prodId: undefined,
+  prodModel: undefined,
+  prodBarcode: undefined,
+  prodClass: undefined,
+  prodSubclass: undefined,
+  supply: undefined,
+  supplyHigh: undefined,
+  supplyMiddle: undefined,
+  supplyLow: undefined,
+  loadCurrent: undefined,
+  wire: undefined,
+  outputSignal: undefined,
+  outputType: undefined,
+  outputPolarity: undefined,
+  distance: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<SensorForm, SensorQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    prodModel: undefined,
+    params: {
+    }
+  },
+  rules: {
+    prodId: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    prodModel: [
+      { required: true, message: "浜у搧鍨嬪彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    prodBarcode: [
+      { required: true, message: "浜у搧鏉$爜涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    prodClass: [
+      { required: true, message: "浜у搧澶х被涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    prodSubclass: [
+      { required: true, message: "浜у搧灏忕被涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    supply: [
+      { required: true, message: "渚涚數鐢垫簮涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    loadCurrent: [
+      { required: true, message: "璐熻浇鐢垫祦涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    wire: [
+      { required: true, message: "寮曠嚎鏁伴噺涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    outputSignal: [
+      { required: true, message: "杈撳嚭淇″彿涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    outputType: [
+      { required: true, message: "杈撳嚭绫诲瀷涓嶈兘涓虹┖", trigger: "change" }
+    ],
+    outputPolarity: [
+      { required: true, message: "杈撳嚭鏋佹�т笉鑳戒负绌�", trigger: "change" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ浼犳劅鍣ㄥ垪琛� */
+const getList = async () => {
+  loading.value = true;
+  const res = await listSensor(queryParams.value);
+  sensorList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  sensorFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: SensorVO[]) => {
+  ids.value = selection.map(item => item.prodId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞浼犳劅鍣�";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: SensorVO) => {
+  reset();
+  const _prodId = row?.prodId || ids.value[0]
+  const res = await getSensor(_prodId);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼浼犳劅鍣�";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  sensorFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.prodId) {
+        await updateSensor(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addSensor(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: SensorVO) => {
+  const _prodIds = row?.prodId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎浼犳劅鍣ㄧ紪鍙蜂负"' + _prodIds + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delSensor(_prodIds);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/sensor/export', {
+    ...queryParams.value
+  }, `sensor_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/sensorResult/index.vue b/src/views/qms/sensorResult/index.vue
new file mode 100644
index 0000000..4e64a99
--- /dev/null
+++ b/src/views/qms/sensorResult/index.vue
@@ -0,0 +1,274 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:sensorResult:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:sensorResult:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:sensorResult:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:sensorResult:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" :columns="columns" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table
+        v-loading="loading"
+        :data="sensorResultList"
+        :row-key="(row) => `${row.batchCode}-${row.testNum}`"
+
+        stripe
+        highlight-current-row
+        indent="50"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="鎵规鍙�" align="center" prop="batchCode" v-if="columns[0].visible" />
+        <el-table-column label="娴嬭瘯搴忓彿" align="center" prop="testNum" v-if="columns[1].visible" />
+        <el-table-column label="鍒ゅ畾缁撴灉" align="center" prop="judgeResult" v-if="columns[2].visible" />
+        <el-table-column label="鎬绘祴璇曢」鏁�" align="center" prop="totalCount" v-if="columns[3].visible" />
+        <el-table-column label="OK椤规暟" align="center" prop="okCount" v-if="columns[4].visible" />
+        <el-table-column label="NG椤规暟" align="center" prop="ngCount" v-if="columns[5].visible" />
+        <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="鏄庣粏" placement="top">
+              <el-button link type="primary" icon="Document" @click="handleDetail(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:sensorResult:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:sensorResult:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规壒娆℃槑缁嗗璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="sensorResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鍒ゅ畾缁撴灉" prop="judgeResult">
+          <el-input v-model="form.judgeResult" placeholder="璇疯緭鍏ュ垽瀹氱粨鏋�" />
+        </el-form-item>
+        <el-form-item label="鎬绘祴璇曢」鏁�" prop="totalCount">
+          <el-input v-model="form.totalCount" placeholder="璇疯緭鍏ユ�绘祴璇曢」鏁�" />
+        </el-form-item>
+        <el-form-item label="OK椤规暟" prop="okCount">
+          <el-input v-model="form.okCount" placeholder="璇疯緭鍏K椤规暟" />
+        </el-form-item>
+        <el-form-item label="NG椤规暟" prop="ngCount">
+          <el-input v-model="form.ngCount" placeholder="璇疯緭鍏G椤规暟" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="SensorResult" lang="ts">
+import { listSensorResult, getSensorResult, delSensorResult, addSensorResult, updateSensorResult } from '@/api/qms/sensorResult';
+import { SensorResultVO, SensorResultQuery, SensorResultForm } from '@/api/qms/sensorResult/types';
+import { ref, reactive, watch, onMounted } from 'vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const sensorResultList = ref<SensorResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const sensorResultFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: SensorResultForm = {
+  batchCode: undefined,
+  testNum: undefined,
+  judgeResult: undefined,
+  totalCount: undefined,
+  okCount: undefined,
+  ngCount: undefined,
+  createTime: undefined, // 娣诲姞 createTime 瀛楁
+}
+const data = reactive<PageData<SensorResultForm, SensorResultQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    judgeResult: undefined,
+    totalCount: undefined,
+    okCount: undefined,
+    ngCount: undefined,
+    params: {
+    }
+  },
+  rules: {
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    judgeResult: [
+      { required: true, message: "鍒ゅ畾缁撴灉涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    totalCount: [
+      { required: true, message: "鎬绘祴璇曢」鏁颁笉鑳戒负绌�", trigger: "blur" }
+    ],
+    okCount: [
+      { required: true, message: "OK椤规暟涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    ngCount: [
+      { required: true, message: "NG椤规暟涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 娣诲姞 columns 鏁扮粍
+const columns = ref<FieldOption[]>([
+  { key: 0, label: '鎵规鍙�', align: 'center', visible: true },
+  { key: 1, label: '娴嬭瘯搴忓彿', align: 'center', visible: true },
+  { key: 2, label: '鍒ゅ畾缁撴灉', align: 'center', visible: true },
+  { key: 3, label: '鎬绘祴璇曢」鏁�', align: 'center', visible: true },
+  { key: 4, label: 'OK椤规暟', align: 'center', visible: true },
+  { key: 5, label: 'NG椤规暟', align: 'center', visible: true },
+  { key: 6, label: '鍒涘缓鏃堕棿', align: 'center', visible: true },
+]);
+
+/** 鏌ヨ鎵规鏄庣粏鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  queryParams.value.batchCode = proxy.$route.query.batchCode;
+  const res = await listSensorResult(queryParams.value);
+  sensorResultList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  sensorResultFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: SensorResultVO[]) => {
+  ids.value = selection.map(item => item.batchCode);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞鎵规鏄庣粏";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: SensorResultVO) => {
+  reset();
+  const _batchCode = row?.batchCode || ids.value[0]
+  const res = await getSensorResult(_batchCode);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼鎵规鏄庣粏";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  sensorResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.batchCode) {
+        await updateSensorResult(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addSensorResult(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: SensorResultVO) => {
+  const _batchCodes = row?.batchCode || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鎵规鏄庣粏缂栧彿涓�"' + _batchCodes + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delSensorResult(_batchCodes);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/sensorResult/export', {
+    ...queryParams.value
+  }, `sensorResult_${new Date().getTime()}.xlsx`)
+}
+
+const handleDetail = (sensor) => {
+  console.log("sensor", sensor)
+  if (!sensor.retestNum) {
+    proxy.$router.push({ path: 'testResult', query: {batchCode: sensor.batchCode, testNum: sensor.testNum} });
+  } else {
+    proxy.$router.push({ path: 'retestResult', query: {batchCode: sensor.batchCode.split("-")[0], testNum: sensor.testNum, retestNum: sensor.retestNum} });
+  }
+
+}
+
+watch(()=>proxy.$route.query.batchCode,(batchCode)=>{
+  console.log("batchCode", batchCode)
+  if(batchCode) {
+    getList();
+  }
+})
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/sensorRetest/index.vue b/src/views/qms/sensorRetest/index.vue
new file mode 100644
index 0000000..eeaf1e7
--- /dev/null
+++ b/src/views/qms/sensorRetest/index.vue
@@ -0,0 +1,273 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="鎵规鍙�" prop="batchCode">
+              <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+              <el-input v-model="queryParams.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="澶嶆祴娆℃暟" prop="retestNum">
+              <el-input v-model="queryParams.retestNum" placeholder="璇疯緭鍏ュ娴嬫鏁�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="璁惧鍙�" prop="deviceCode">
+              <el-input v-model="queryParams.deviceCode" placeholder="璇疯緭鍏ヨ澶囧彿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鐢ㄦ埛甯愬彿" prop="userName">
+              <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰笎鍙�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['lb-qms:sensorRetest:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['lb-qms:sensorRetest:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['lb-qms:sensorRetest:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['lb-qms:sensorRetest:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table
+        v-loading="loading"
+        :data="sensorRetestList"
+        @selection-change="handleSelectionChange"
+        stripe
+        highlight-current-row
+      >
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="鎵规鍙�" align="center" prop="batchCode" v-if="true" />
+        <el-table-column label="娴嬭瘯搴忓彿" align="center" prop="testNum" v-if="true" />
+        <el-table-column label="澶嶆祴娆℃暟" align="center" prop="retestNum" v-if="true" />
+        <el-table-column label="璁惧鍙�" align="center" prop="deviceCode" />
+        <el-table-column label="鐢ㄦ埛甯愬彿" align="center" prop="userName" />
+        <el-table-column label="鍒ゅ畾缁撴灉" align="center" prop="judgeResult" />
+        <el-table-column label="鎬绘祴璇曢」鏁�" align="center" prop="totalCount" />
+        <el-table-column label="OK椤规暟" align="center" prop="okCount" />
+        <el-table-column label="NG椤规暟" align="center" prop="ngCount" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['lb-qms:sensorRetest:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['lb-qms:sensorRetest:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀瑰娴嬭褰曞璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="sensorRetestFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="璁惧鍙�" prop="deviceCode">
+          <el-input v-model="form.deviceCode" placeholder="璇疯緭鍏ヨ澶囧彿" />
+        </el-form-item>
+        <el-form-item label="鐢ㄦ埛甯愬彿" prop="userName">
+          <el-input v-model="form.userName" placeholder="璇疯緭鍏ョ敤鎴峰笎鍙�" />
+        </el-form-item>
+        <el-form-item label="鍒ゅ畾缁撴灉" prop="judgeResult">
+          <el-input v-model="form.judgeResult" placeholder="璇疯緭鍏ュ垽瀹氱粨鏋�" />
+        </el-form-item>
+        <el-form-item label="鎬绘祴璇曢」鏁�" prop="totalCount">
+          <el-input v-model="form.totalCount" placeholder="璇疯緭鍏ユ�绘祴璇曢」鏁�" />
+        </el-form-item>
+        <el-form-item label="OK椤规暟" prop="okCount">
+          <el-input v-model="form.okCount" placeholder="璇疯緭鍏K椤规暟" />
+        </el-form-item>
+        <el-form-item label="NG椤规暟" prop="ngCount">
+          <el-input v-model="form.ngCount" placeholder="璇疯緭鍏G椤规暟" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="SensorRetest" lang="ts">
+import { listSensorRetest, getSensorRetest, delSensorRetest, addSensorRetest, updateSensorRetest } from 'src/api/qms/sensorRetest';
+import { SensorRetestVO, SensorRetestQuery, SensorRetestForm } from '@/api/qms/sensorRetest/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const sensorRetestList = ref<SensorRetestVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const sensorRetestFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: SensorRetestForm = {
+  batchCode: undefined,
+  testNum: undefined,
+  retestNum: undefined,
+  deviceCode: undefined,
+  userName: undefined,
+  judgeResult: undefined,
+  totalCount: undefined,
+  okCount: undefined,
+  ngCount: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<SensorRetestForm, SensorRetestQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    batchCode: undefined,
+    testNum: undefined,
+    retestNum: undefined,
+    deviceCode: undefined,
+    userName: undefined,
+    params: {
+    }
+  },
+  rules: {
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    retestNum: [
+      { required: true, message: "澶嶆祴娆℃暟涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ澶嶆祴璁板綍鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listSensorRetest(queryParams.value);
+  sensorRetestList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  sensorRetestFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: SensorRetestVO[]) => {
+  ids.value = selection.map(item => item.batchCode);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞澶嶆祴璁板綍";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: SensorRetestVO) => {
+  reset();
+  const _batchCode = row?.batchCode || ids.value[0]
+  const res = await getSensorRetest(_batchCode);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼澶嶆祴璁板綍";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  sensorRetestFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.batchCode) {
+        await updateSensorRetest(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addSensorRetest(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: SensorRetestVO) => {
+  const _batchCodes = row?.batchCode || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎澶嶆祴璁板綍缂栧彿涓�"' + _batchCodes + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delSensorRetest(_batchCodes);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/sensorRetest/export', {
+    ...queryParams.value
+  }, `sensorRetest_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/testResult/index.vue b/src/views/qms/testResult/index.vue
new file mode 100644
index 0000000..f2cc9fe
--- /dev/null
+++ b/src/views/qms/testResult/index.vue
@@ -0,0 +1,313 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+<!--          <el-col :span="1.5">-->
+<!--            <el-button v-hasPermi="['qms:testResult:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>-->
+<!--          </el-col>-->
+<!--          <el-col :span="1.5">-->
+<!--            <el-button v-hasPermi="['qms:testResult:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"-->
+<!--              >淇敼</el-button-->
+<!--            >-->
+<!--          </el-col>-->
+<!--          <el-col :span="1.5">-->
+<!--            <el-button v-hasPermi="['qms:testResult:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"-->
+<!--              >鍒犻櫎</el-button-->
+<!--            >-->
+<!--          </el-col>-->
+<!--          <el-col :span="1.5">-->
+<!--            <el-button v-hasPermi="['qms:testResult:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>-->
+<!--          </el-col>-->
+
+          <right-toolbar v-model:showSearch="showSearch" :columns="columns" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" stripe highlight-current-row :data="testResultList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column v-if="false" label="" align="center" prop="id" />
+        <el-table-column v-if="columns[0].visible" label="鎵规鍙�" align="center" prop="batchCode" />
+        <el-table-column v-if="columns[1].visible" label="娴嬭瘯搴忓彿" align="center" prop="testNum" />
+        <el-table-column v-if="columns[2].visible" label="娴嬭瘯椤圭洰" align="center" prop="testItem" />
+        <el-table-column v-if="columns[3].visible" label="瀹為檯鐢靛帇" align="center" prop="voltage" />
+        <el-table-column v-if="columns[4].visible" label="瀹為檯鐢垫祦" align="center" prop="loadCurrent" />
+        <el-table-column v-if="columns[5].visible" label="鏍囧噯璺濈" align="center" prop="stdDistance" />
+        <el-table-column v-if="columns[6].visible" label="鎰熷簲鐗�" align="center" prop="inductor" />
+        <el-table-column v-if="columns[7].visible" label="杈撳嚭寮曡剼" align="center" prop="output" />
+        <el-table-column v-if="columns[8].visible" label="娴嬭瘯鏁版嵁" align="center" prop="testValue" />
+        <el-table-column v-if="columns[9].visible" label="鍒ゆ柇鏉′欢" align="center" prop="judgeDetail" />
+        <el-table-column v-if="columns[10].visible" label="娴嬭瘯缁撴灉" align="center" prop="testResult" />
+        <el-table-column v-if="columns[11].visible" label="澶囨敞" align="center" prop="remark" />
+        <el-table-column v-if="columns[12].visible" label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="160">
+          <template #default="scope">
+            <span>{{ scope.row.createTime }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button v-hasPermi="['qms:testResult:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button v-hasPermi="['qms:testResult:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祴璇曠粨鏋滃璇濇 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="testResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鎵规鍙�" prop="batchCode">
+          <el-input v-model="form.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+          <el-input v-model="form.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+          <el-input v-model="form.testItem" placeholder="璇疯緭鍏ユ祴璇曢」鐩�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+          <el-input v-model="form.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+          <el-input v-model="form.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+          <el-input v-model="form.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" />
+        </el-form-item>
+        <el-form-item label="鎰熷簲鐗�" prop="inductor">
+          <el-input v-model="form.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" />
+        </el-form-item>
+        <el-form-item label="杈撳嚭寮曡剼" prop="output">
+          <el-input v-model="form.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+          <el-input v-model="form.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" />
+        </el-form-item>
+        <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+          <el-input v-model="form.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+          <el-input v-model="form.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="TestResult" lang="ts">
+import { listTestResult, getTestResult, delTestResult, addTestResult, updateTestResult } from '@/api/qms/testResult';
+import { TestResultVO, TestResultQuery, TestResultForm } from '@/api/qms/testResult/types';
+import { ref, reactive, watch, onMounted, computed } from 'vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const testResultList = ref<TestResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const testResultFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: TestResultForm = {
+  id: undefined,
+  batchCode: undefined,
+  testNum: undefined,
+  testItem: undefined,
+  voltage: undefined,
+  loadCurrent: undefined,
+  stdDistance: undefined,
+  inductor: undefined,
+  output: undefined,
+  testValue: undefined,
+  judgeDetail: undefined,
+  testResult: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<TestResultForm, TestResultQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 20,
+    batchCode: undefined,
+    testNum: undefined,
+    testItem: undefined,
+    voltage: undefined,
+    loadCurrent: undefined,
+    stdDistance: undefined,
+    inductor: undefined,
+    output: undefined,
+    testValue: undefined,
+    judgeDetail: undefined,
+    testResult: undefined,
+    params: {}
+  },
+  rules: {
+    id: [{ required: true, message: '涓嶈兘涓虹┖', trigger: 'blur' }],
+    batchCode: [{ required: true, message: '鎵规鍙蜂笉鑳戒负绌�', trigger: 'blur' }],
+    testNum: [{ required: true, message: '娴嬭瘯搴忓彿涓嶈兘涓虹┖', trigger: 'blur' }],
+    testItem: [{ required: true, message: '娴嬭瘯椤圭洰涓嶈兘涓虹┖', trigger: 'blur' }],
+    voltage: [{ required: true, message: '瀹為檯鐢靛帇涓嶈兘涓虹┖', trigger: 'blur' }],
+    loadCurrent: [{ required: true, message: '瀹為檯鐢垫祦涓嶈兘涓虹┖', trigger: 'blur' }],
+    stdDistance: [{ required: true, message: '鏍囧噯璺濈涓嶈兘涓虹┖', trigger: 'blur' }],
+    inductor: [{ required: true, message: '鎰熷簲鐗╀笉鑳戒负绌�', trigger: 'blur' }],
+    output: [{ required: true, message: '杈撳嚭寮曡剼涓嶈兘涓虹┖', trigger: 'blur' }],
+    testValue: [{ required: true, message: '娴嬭瘯鏁版嵁涓嶈兘涓虹┖', trigger: 'blur' }],
+    judgeDetail: [{ required: true, message: '鍒ゆ柇鏉′欢涓嶈兘涓虹┖', trigger: 'blur' }],
+    testResult: [{ required: true, message: '娴嬭瘯缁撴灉涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ娴嬭瘯缁撴灉鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  queryParams.value.batchCode = proxy.$route.query.batchCode;
+  queryParams.value.testNum = proxy.$route.query.testNum;
+
+  const res = await listTestResult(queryParams.value);
+  testResultList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = { ...initFormData };
+  testResultFormRef.value?.resetFields();
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: TestResultVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '娣诲姞娴嬭瘯缁撴灉';
+};
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: TestResultVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getTestResult(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '淇敼娴嬭瘯缁撴灉';
+};
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  testResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateTestResult(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addTestResult(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: TestResultVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯缁撴灉缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => (loading.value = false));
+  await delTestResult(_ids);
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+  await getList();
+};
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download(
+    'qms/testResult/export',
+    {
+      ...queryParams.value
+    },
+    `testResult_${new Date().getTime()}.xlsx`
+  );
+};
+
+watch(
+  () => proxy.$route.query.testNum,
+  (testNum) => {
+    console.log('path', proxy.$route.path);
+    console.log('testNum', testNum);
+    if (proxy.$route.path === '/qms/testResult' && testNum) {
+      queryParams.value.pageNum = 1;
+      getList();
+    }
+  }
+);
+const columns = ref<FieldOption[]>([
+  { key: 0, label: '鎵规鍙�', align: 'center', visible: true },
+  { key: 1, label: '娴嬭瘯搴忓彿', align: 'center', visible: true },
+  { key: 2, label: '娴嬭瘯椤圭洰', align: 'center', visible: true },
+  { key: 3, label: '瀹為檯鐢靛帇', align: 'center', visible: true },
+  { key: 4, label: '瀹為檯鐢垫祦', align: 'center', visible: true },
+  { key: 5, label: '鏍囧噯璺濈', align: 'center', visible: true },
+  { key: 6, label: '鎰熷簲鐗�', align: 'center', visible: true },
+  { key: 7, label: '杈撳嚭寮曡剼', align: 'center', visible: true },
+  { key: 8, label: '娴嬭瘯鏁版嵁', align: 'center', visible: true },
+  { key: 9, label: '鍒ゆ柇鏉′欢', align: 'center', visible: true },
+  { key: 10, label: '娴嬭瘯缁撴灉', align: 'center', visible: true },
+  { key: 11, label: '澶囨敞', align: 'center', visible: true },
+  { key: 12, label: `鍒涘缓鏃堕棿`, visible: true, children: [] }
+]);
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/testResult/index1.vue b/src/views/qms/testResult/index1.vue
new file mode 100644
index 0000000..678bccd
--- /dev/null
+++ b/src/views/qms/testResult/index1.vue
@@ -0,0 +1,339 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="鎵规鍙�" prop="batchCode">
+              <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+              <el-input v-model="queryParams.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+              <el-input v-model="queryParams.testItem" placeholder="璇疯緭鍏ユ祴璇曢」鐩�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+              <el-input v-model="queryParams.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+              <el-input v-model="queryParams.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+              <el-input v-model="queryParams.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鎰熷簲鐗�" prop="inductor">
+              <el-input v-model="queryParams.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="杈撳嚭寮曡剼" prop="output">
+              <el-input v-model="queryParams.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+              <el-input v-model="queryParams.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+              <el-input v-model="queryParams.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+              <el-input v-model="queryParams.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:testResult:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:testResult:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:testResult:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:testResult:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="testResultList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="false" />
+        <el-table-column label="鎵规鍙�" align="center" prop="batchCode" />
+        <el-table-column label="娴嬭瘯搴忓彿" align="center" prop="testNum" />
+        <el-table-column label="娴嬭瘯椤圭洰" align="center" prop="testItem" />
+        <el-table-column label="瀹為檯鐢靛帇" align="center" prop="voltage" />
+        <el-table-column label="瀹為檯鐢垫祦" align="center" prop="loadCurrent" />
+        <el-table-column label="鏍囧噯璺濈" align="center" prop="stdDistance" />
+        <el-table-column label="鎰熷簲鐗�" align="center" prop="inductor" />
+        <el-table-column label="杈撳嚭寮曡剼" align="center" prop="output" />
+        <el-table-column label="娴嬭瘯鏁版嵁" align="center" prop="testValue" />
+        <el-table-column label="鍒ゆ柇鏉′欢" align="center" prop="judgeDetail" />
+        <el-table-column label="娴嬭瘯缁撴灉" align="center" prop="testResult" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:testResult:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:testResult:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祴璇曠粨鏋滃璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="testResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鎵规鍙�" prop="batchCode">
+          <el-input v-model="form.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+          <el-input v-model="form.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+          <el-input v-model="form.testItem" placeholder="璇疯緭鍏ユ祴璇曢」鐩�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+          <el-input v-model="form.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+          <el-input v-model="form.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+          <el-input v-model="form.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" />
+        </el-form-item>
+        <el-form-item label="鎰熷簲鐗�" prop="inductor">
+          <el-input v-model="form.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" />
+        </el-form-item>
+        <el-form-item label="杈撳嚭寮曡剼" prop="output">
+          <el-input v-model="form.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+          <el-input v-model="form.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" />
+        </el-form-item>
+        <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+          <el-input v-model="form.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+          <el-input v-model="form.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="TestResult" lang="ts">
+import { listTestResult, getTestResult, delTestResult, addTestResult, updateTestResult } from '@/api/qms/testResult';
+import { TestResultVO, TestResultQuery, TestResultForm } from '@/api/qms/testResult/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const testResultList = ref<TestResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const testResultFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: TestResultForm = {
+  id: undefined,
+  batchCode: undefined,
+  testNum: undefined,
+  testItem: undefined,
+  voltage: undefined,
+  loadCurrent: undefined,
+  stdDistance: undefined,
+  inductor: undefined,
+  output: undefined,
+  testValue: undefined,
+  judgeDetail: undefined,
+  testResult: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<TestResultForm, TestResultQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    batchCode: undefined,
+    testNum: undefined,
+    testItem: undefined,
+    voltage: undefined,
+    loadCurrent: undefined,
+    stdDistance: undefined,
+    inductor: undefined,
+    output: undefined,
+    testValue: undefined,
+    judgeDetail: undefined,
+    testResult: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testItem: [
+      { required: true, message: "娴嬭瘯椤圭洰涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    voltage: [
+      { required: true, message: "瀹為檯鐢靛帇涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    loadCurrent: [
+      { required: true, message: "瀹為檯鐢垫祦涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    stdDistance: [
+      { required: true, message: "鏍囧噯璺濈涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    inductor: [
+      { required: true, message: "鎰熷簲鐗╀笉鑳戒负绌�", trigger: "blur" }
+    ],
+    output: [
+      { required: true, message: "杈撳嚭寮曡剼涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testValue: [
+      { required: true, message: "娴嬭瘯鏁版嵁涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    judgeDetail: [
+      { required: true, message: "鍒ゆ柇鏉′欢涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testResult: [
+      { required: true, message: "娴嬭瘯缁撴灉涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ娴嬭瘯缁撴灉鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listTestResult(queryParams.value);
+  testResultList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  testResultFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: TestResultVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞娴嬭瘯缁撴灉";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: TestResultVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getTestResult(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼娴嬭瘯缁撴灉";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  testResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: TestResultVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯缁撴灉缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delTestResult(_ids);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/testResult/export', {
+    ...queryParams.value
+  }, `testResult_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/testResult/index2.vue b/src/views/qms/testResult/index2.vue
new file mode 100644
index 0000000..596f4ae
--- /dev/null
+++ b/src/views/qms/testResult/index2.vue
@@ -0,0 +1,372 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:testResult:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:testResult:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:testResult:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:testResult:export']">瀵煎嚭</el-button>
+          </el-col>
+
+          <right-toolbar :columns="columns" v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="testResultList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <template v-for="col in visibleColumns" :key="col.prop">
+          <el-table-column
+            :label="col.label"
+            :align="col.align"
+            :prop="col.prop"
+            v-if="col.visible"
+          >
+            <template #header="{ column }">
+              <div @dblclick="handleColumnDoubleClick(column.property)">
+                {{ column.label }}
+              </div>
+            </template>
+          </el-table-column>
+        </template>
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:testResult:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:testResult:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祴璇曠粨鏋滃璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="testResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鎵规鍙�" prop="batchCode">
+          <el-input v-model="form.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+          <el-input v-model="form.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+          <el-input v-model="form.testItem" placeholder="璇疯緭鍏ユ祴璇曢」鐩�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+          <el-input v-model="form.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+          <el-input v-model="form.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+          <el-input v-model="form.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" />
+        </el-form-item>
+        <el-form-item label="鎰熷簲鐗�" prop="inductor">
+          <el-input v-model="form.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" />
+        </el-form-item>
+        <el-form-item label="杈撳嚭寮曡剼" prop="output">
+          <el-input v-model="form.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+          <el-input v-model="form.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" />
+        </el-form-item>
+        <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+          <el-input v-model="form.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+          <el-input v-model="form.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 鍒楅�夋嫨瀵硅瘽妗� -->
+    <el-dialog title="閫夋嫨鍒�" v-model="showColumnSelector" width="300px" append-to-body @close="showColumnSelector = false">
+      <el-checkbox-group v-model="selectedColumns">
+        <el-checkbox v-for="column in columns" :key="column.prop" :label="column.prop">{{ column.label }}</el-checkbox>
+      </el-checkbox-group>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="saveColumnSelection">纭� 瀹�</el-button>
+          <el-button @click="showColumnSelector = false">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="TestResult" lang="ts">
+import { listTestResult, getTestResult, delTestResult, addTestResult, updateTestResult } from '@/api/qms/testResult';
+import { TestResultVO, TestResultQuery, TestResultForm } from '@/api/qms/testResult/types';
+import { ref, reactive, watch, onMounted, computed } from 'vue';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const testResultList = ref<TestResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const testResultFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: TestResultForm = {
+  id: undefined,
+  batchCode: undefined,
+  testNum: undefined,
+  testItem: undefined,
+  voltage: undefined,
+  loadCurrent: undefined,
+  stdDistance: undefined,
+  inductor: undefined,
+  output: undefined,
+  testValue: undefined,
+  judgeDetail: undefined,
+  testResult: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<TestResultForm, TestResultQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 20,
+    batchCode: undefined,
+    testNum: undefined,
+    testItem: undefined,
+    voltage: undefined,
+    loadCurrent: undefined,
+    stdDistance: undefined,
+    inductor: undefined,
+    output: undefined,
+    testValue: undefined,
+    judgeDetail: undefined,
+    testResult: undefined,
+    params: {
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testItem: [
+      { required: true, message: "娴嬭瘯椤圭洰涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    voltage: [
+      { required: true, message: "瀹為檯鐢靛帇涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    loadCurrent: [
+      { required: true, message: "瀹為檯鐢垫祦涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    stdDistance: [
+      { required: true, message: "鏍囧噯璺濈涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    inductor: [
+      { required: true, message: "鎰熷簲鐗╀笉鑳戒负绌�", trigger: "blur" }
+    ],
+    output: [
+      { required: true, message: "杈撳嚭寮曡剼涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testValue: [
+      { required: true, message: "娴嬭瘯鏁版嵁涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    judgeDetail: [
+      { required: true, message: "鍒ゆ柇鏉′欢涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testResult: [
+      { required: true, message: "娴嬭瘯缁撴灉涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ娴嬭瘯缁撴灉鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  queryParams.value.batchCode = proxy.$route.query.batchCode;
+  queryParams.value.testNum = proxy.$route.query.testNum;
+
+  const res = await listTestResult(queryParams.value);
+  testResultList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  testResultFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: TestResultVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞娴嬭瘯缁撴灉";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: TestResultVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getTestResult(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼娴嬭瘯缁撴灉";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  testResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: TestResultVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯缁撴灉缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delTestResult(_ids);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/testResult/export', {
+    ...queryParams.value
+  }, `testResult_${new Date().getTime()}.xlsx`)
+}
+
+const columns = [
+  { prop: 'id', label: 'ID', align: 'center', visible: false },
+  { prop: 'batchCode', label: '鎵规鍙�', align: 'center', visible: true },
+  { prop: 'testNum', label: '娴嬭瘯搴忓彿', align: 'center', visible: true },
+  { prop: 'testItem', label: '娴嬭瘯椤圭洰', align: 'center', visible: true },
+  { prop: 'voltage', label: '瀹為檯鐢靛帇', align: 'center', visible: true },
+  { prop: 'loadCurrent', label: '瀹為檯鐢垫祦', align: 'center', visible: true },
+  { prop: 'stdDistance', label: '鏍囧噯璺濈', align: 'center', visible: true },
+  { prop: 'inductor', label: '鎰熷簲鐗�', align: 'center', visible: true },
+  { prop: 'output', label: '杈撳嚭寮曡剼', align: 'center', visible: true },
+  { prop: 'testValue', label: '娴嬭瘯鏁版嵁', align: 'center', visible: true },
+  { prop: 'judgeDetail', label: '鍒ゆ柇鏉′欢', align: 'center', visible: true },
+  { prop: 'testResult', label: '娴嬭瘯缁撴灉', align: 'center', visible: true },
+  { prop: 'remark', label: '澶囨敞', align: 'center', visible: true }
+];
+
+const selectedColumns = ref(columns.map(column => column.prop));
+
+const visibleColumns = computed(() => {
+  // 娣诲姞渚濊禆椤癸紝纭繚褰� selectedColumns 鍙樺寲鏃讹紝visibleColumns 涔熶細閲嶆柊璁$畻
+  return columns.filter(column => selectedColumns.value.includes(column.prop));
+});
+
+// 鏂板 watch 鐩戝惉鍣紝鐩戝惉 selectedColumns 鐨勫彉鍖�
+watch(()=> selectedColumns.value, (newSelectedColumns) => {
+  console.log("selectedColumns", selectedColumns.value)
+  columns.forEach(column => {
+    column.visible = newSelectedColumns.includes(column.prop);
+  });
+});
+
+const handleColumnDoubleClick = (prop: string) => {
+  // 鏂板鍙屽嚮浜嬩欢澶勭悊鍑芥暟
+  console.log('鍙屽嚮鍒楋細', prop);
+  const column = columns.find(col => col.prop === prop);
+  console.log(column)
+  if (column) {
+    column.visible = !column.visible;
+  }
+  selectedColumns.value = columns.filter(col => col.visible).map(col => col.prop);
+  // // 纭繚 columns 鐨� visible 灞炴�у悓姝ユ洿鏂�
+  // columns.forEach(col => {
+  //   col.visible = selectedColumns.value.includes(col.prop);
+  // });
+};
+
+const saveColumnSelection = () => {
+  columns.forEach(column => {
+    column.visible = selectedColumns.value.includes(column.prop);
+  });
+};
+
+watch(() => proxy.$route.query.testNum, (testNum) => {
+  console.log("testNum", testNum)
+  if(testNum) {
+    queryParams.value.pageNum = 1;
+    getList();
+  }
+
+})
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/qms/trend/index.vue b/src/views/qms/trend/index.vue
new file mode 100644
index 0000000..f9687f7
--- /dev/null
+++ b/src/views/qms/trend/index.vue
@@ -0,0 +1,467 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="浜у搧鍨嬪彿" prop="prodModel">
+              <el-select v-model="queryParams.prodModel" placeholder="璇烽�夋嫨浜у搧鍨嬪彿" clearable @keyup.enter="handleQuery">
+                <el-option v-for="item in prodModelOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+              <el-select v-model="selectedTestItems" :multiple="true" placeholder="璇烽�夋嫨娴嬭瘯椤圭洰" clearable @keyup.enter="handleQuery">
+                <el-option v-for="item in testItemsOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="鎵规鍙�" prop="batchCode">
+              <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
+              <el-date-picker
+                v-model="dateRangeCreateTime"
+                value-format="YYYY-MM-DD"
+                type="daterange"
+                range-separator="-"
+                start-placeholder="寮�濮嬫棩鏈�"
+                end-placeholder="缁撴潫鏃ユ湡"
+                unlink-panels
+                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:testResult:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:testResult:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:testResult:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:testResult:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="handleQuery"></right-toolbar>
+        </el-row>
+      </template>
+
+
+      <div ref="chartRef" style="width: 100%; height: 660px;"></div>
+
+      <!--      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />-->
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祴璇曠粨鏋滃璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="testResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鎵规鍙�" prop="batchCode">
+          <el-input v-model="form.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+          <el-input v-model="form.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+          <el-select v-model="form.testItem" placeholder="璇烽�夋嫨娴嬭瘯椤圭洰">
+            <el-option v-for="item in testItemsOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+          <el-input v-model="form.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+          <el-input v-model="form.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+          <el-input v-model="form.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" />
+        </el-form-item>
+        <el-form-item label="鎰熷簲鐗�" prop="inductor">
+          <el-input v-model="form.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" />
+        </el-form-item>
+        <el-form-item label="杈撳嚭寮曡剼" prop="output">
+          <el-input v-model="form.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+          <el-input v-model="form.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" />
+        </el-form-item>
+        <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+          <el-input v-model="form.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+          <el-input v-model="form.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="TestResult" lang="ts">
+import { listTestResultAll, getTestResult, delTestResult, addTestResult, updateTestResult, listTestItem } from '@/api/qms/testResult';
+import { TestResultVO, TestResultQuery, TestResultForm } from '@/api/qms/testResult/types';
+import * as echarts from 'echarts';
+import { listProdModels} from '@/api/qms/batch';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const testResultList = ref<TestResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+// 璁剧疆榛樿鏃堕棿涓烘渶杩戜竴涓湀
+const now = new Date();
+const dateRangeCreateTime = ref<[DateModelType, DateModelType]>([]);
+const oneMonthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate());
+dateRangeCreateTime.value = [oneMonthAgo.toISOString().split('T')[0], now.toISOString().split('T')[0]] as [DateModelType, DateModelType];
+
+const queryFormRef = ref<ElFormInstance>();
+const testResultFormRef = ref<ElFormInstance>();
+const chartRef = ref<HTMLDivElement | null>(null); // 鏂板 chartRef 寮曠敤
+
+const testItemsOptions = ref<{ label: string, value: string }[]>([]); // 鏂板娴嬭瘯椤圭洰閫夐」
+const selectedTestItems = ref<string | string[]>([]); // 淇敼涓哄吋瀹瑰崟閫夌殑绫诲瀷
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: TestResultForm = {
+  id: undefined,
+  batchCode: undefined,
+  testNum: undefined,
+  testItem: undefined,
+  voltage: undefined,
+  loadCurrent: undefined,
+  stdDistance: undefined,
+  inductor: undefined,
+  output: undefined,
+  testValue: undefined,
+  judgeDetail: undefined,
+  testResult: undefined,
+  remark: undefined
+}
+const prodModelOptions = ref<{ label: string, value: string }[]>([]);
+
+const data = reactive<PageData<TestResultForm, TestResultQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    batchCode: undefined,
+    testNum: undefined,
+    testItem: undefined,
+    voltage: undefined,
+    loadCurrent: undefined,
+    stdDistance: undefined,
+    inductor: undefined,
+    output: undefined,
+    testValue: undefined,
+    judgeDetail: undefined,
+    testResult: undefined,
+    startTime: undefined, // 鏂板 startTime 瀛楁
+    endTime: undefined, // 鏂板 endTime 瀛楁
+    prodModel: undefined, // 鏂板浜у搧鍨嬪彿瀛楁
+    params: {
+      createTime: undefined,
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testItem: [
+      { required: true, message: "娴嬭瘯椤圭洰涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    voltage: [
+      { required: true, message: "瀹為檯鐢靛帇涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    loadCurrent: [
+      { required: true, message: "瀹為檯鐢垫祦涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    stdDistance: [
+      { required: true, message: "鏍囧噯璺濈涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    inductor: [
+      { required: true, message: "鎰熷簲鐗╀笉鑳戒负绌�", trigger: "blur" }
+    ],
+    output: [
+      { required: true, message: "杈撳嚭寮曡剼涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testValue: [
+      { required: true, message: "娴嬭瘯鏁版嵁涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    judgeDetail: [
+      { required: true, message: "鍒ゆ柇鏉′欢涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testResult: [
+      { required: true, message: "娴嬭瘯缁撴灉涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    prodModel: [
+      { required: true, message: "浜у搧鍨嬪彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 鑾峰彇鎵�鏈夋祴璇曢」鐩�
+const fetchTestItems = async () => {
+  const res = await listTestItem();
+  testItemsOptions.value = res.data.map(item => ({ label: item, value: item }));
+}
+// 鑾峰彇鎵�鏈変骇鍝佸瀷鍙�
+const fetchProdModels = async () => {
+  const res = await listProdModels();
+  prodModelOptions.value = res.data.map(item => ({ label: item, value: item }));
+}
+
+/** 鏌ヨ娴嬭瘯缁撴灉鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  queryParams.value.params = {};
+  // 澶勭悊 selectedTestItems 鐨勫�硷紝鍏煎鍗曢�夊拰澶氶��
+  if (Array.isArray(selectedTestItems.value)) {
+    queryParams.value.testItem = selectedTestItems.value.join(','); // 澶氶�夋ā寮�
+  } else {
+    queryParams.value.testItem = selectedTestItems.value; // 鍗曢�夋ā寮�
+  }
+  proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, 'CreateTime');
+  const res = await listTestResultAll(queryParams.value);
+  testResultList.value = res.data;
+  loading.value = false;
+  updateChart(); // 鏇存柊鍥捐〃
+}
+
+// 鐩戝惉 testResultList 鐨勫彉鍖栵紝鍔ㄦ�佹洿鏂板浘琛�
+watch(testResultList, () => {
+  updateChart();
+});
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+
+  testResultFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  // 鏍¢獙鏃堕棿鑼冨洿鏄惁瓒呰繃涓変釜鏈�
+  if (dateRangeCreateTime.value[0] && dateRangeCreateTime.value[1]) {
+    const startDate = new Date(dateRangeCreateTime.value[0]);
+    const endDate = new Date(dateRangeCreateTime.value[1]);
+    const timeDiff = endDate.getTime() - startDate.getTime();
+    const maxDuration = 90 * 24 * 60 * 60 * 1000; // 涓変釜鏈堢殑姣鏁�
+
+    if (timeDiff > maxDuration) {
+      proxy?.$modal.msgWarning("鏃堕棿鑼冨洿涓嶈兘瓒呰繃涓変釜鏈堬紝璇烽噸鏂伴�夋嫨锛�");
+      return;
+    }
+  }
+
+  // 鏍¢獙浜у搧鍨嬪彿鏄惁閫夋嫨
+  if (!queryParams.value.prodModel) {
+    proxy?.$modal.msgWarning("璇烽�夋嫨浜у搧鍨嬪彿锛�");
+    return;
+  }
+
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  dateRangeCreateTime.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  selectedTestItems.value = []; // 閲嶇疆閫変腑鐨勬祴璇曢」鐩�
+  reset(); // 璋冪敤閲嶇疆鏂规硶浠ユ竻绌烘椂闂存瀛楁
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: TestResultVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞娴嬭瘯缁撴灉";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: TestResultVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getTestResult(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼娴嬭瘯缁撴灉";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  testResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await handleQuery();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: TestResultVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯缁撴灉缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delTestResult(_ids);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await handleQuery();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/testResult/export', {
+    ...queryParams.value
+  }, `testResult_${new Date().getTime()}.xlsx`)
+}
+
+// 鏇存柊鍥捐〃
+const updateChart = () => {
+  if (!chartRef.value) return;
+  const chart = echarts.init(chartRef.value);
+
+  // 瀹氫箟20涓笉閲嶅鐨勯鑹叉睜
+  const colorPool = [
+    '#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de',
+    '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc', '#5e7ce0',
+    '#f0c757', '#ff9f7f', '#d48265', '#7fb80e', '#b37feb',
+    '#4dc9b0', '#f47920', '#65b581', '#f08585', '#4a90e2',
+    // 鏂板棰滆壊浠ユ彁楂樺姣斿害
+    '#ff6f61', '#ffd700', '#00ced1', '#da70d6', '#adff2f'
+  ];
+
+  // 浣跨敤Map鏇夸唬Object鏇撮珮鏁�
+  const seriesMap = new Map<string, [string, number][]>();
+  testResultList.value.forEach(item => {
+    const key = item.testItem;
+    if (!seriesMap.has(key)) {
+      seriesMap.set(key, []);
+    }
+    seriesMap.get(key)?.push([item.createTime, item.testValue]);
+  });
+
+  const option = {
+    grid: {
+      top: '15%',
+      left: '5%',
+      right: '5%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'time',
+      name: '鏃堕棿'
+    },
+    yAxis: {
+      type: 'value',
+      name: '娴嬭瘯鏁版嵁'
+    },
+    legend: {
+      data: Array.from(seriesMap.keys())
+    },
+    series: Array.from(seriesMap).map(([name, data], index) => ({
+      name: name,
+      data: data,
+      type: 'line',
+      animationDuration: 1000,
+      lineStyle: {
+        color: colorPool[index % colorPool.length],
+        width: 3 // 澧炲姞绾挎潯瀹藉害
+      },
+      itemStyle: {
+        color: colorPool[index % colorPool.length],
+        opacity: 0.8 // 娣诲姞閫忔槑搴�
+      }
+    })),
+    // 鏂板 dataZoom 閰嶇疆
+    dataZoom: [
+      {
+        type: 'slider', // X杞存粦鍔ㄦ潯
+        xAxisIndex: 0,
+        start: 0, // 鍒濆鑼冨洿璧风偣鐧惧垎姣�
+        end: 100 // 鍒濆鑼冨洿缁堢偣鐧惧垎姣�
+      },
+      {
+        type: 'slider', // Y杞存粦鍔ㄦ潯
+        yAxisIndex: 0,
+        start: 0, // 鍒濆鑼冨洿璧风偣鐧惧垎姣�
+        end: 100 // 鍒濆鑼冨洿缁堢偣鐧惧垎姣�
+      },
+      {
+        type: 'inside', // X杞村唴缃嫋鍔ㄥ拰妗嗛��
+        xAxisIndex: 0,
+        start: 0,
+        end: 100
+      },
+      {
+        type: 'inside', // Y杞村唴缃嫋鍔ㄥ拰妗嗛��
+        yAxisIndex: 0,
+        start: 0,
+        end: 100
+      }
+    ]
+  };
+
+  chart.setOption(option, true);
+  window.addEventListener('resize', () => {
+    chart.resize();
+  });
+}
+
+onMounted(() => {
+  fetchTestItems(); // 璋冪敤鑾峰彇娴嬭瘯椤圭洰鎺ュ彛
+  fetchProdModels(); // 璋冪敤鑾峰彇浜у搧鍨嬪彿鎺ュ彛
+});
+</script>
+
diff --git a/src/views/qms/trend/index1.vue b/src/views/qms/trend/index1.vue
new file mode 100644
index 0000000..e3aa980
--- /dev/null
+++ b/src/views/qms/trend/index1.vue
@@ -0,0 +1,349 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="鎵规鍙�" prop="batchCode">
+              <el-input v-model="queryParams.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+              <el-select v-model="selectedTestItems" :multiple="true" placeholder="璇烽�夋嫨娴嬭瘯椤圭洰" clearable @keyup.enter="handleQuery">
+                <el-option v-for="item in testItemsOptions" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
+              <el-date-picker
+                v-model="dateRangeCreateTime"
+                value-format="YYYY-MM-DD"
+                type="daterange"
+                range-separator="-"
+                start-placeholder="寮�濮嬫棩鏈�"
+                end-placeholder="缁撴潫鏃ユ湡"
+                unlink-panels
+                :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['qms:testResult:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['qms:testResult:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['qms:testResult:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['qms:testResult:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="testResultList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="" align="center" prop="id" v-if="false" />
+        <el-table-column label="鎵规鍙�" align="center" prop="batchCode" />
+        <el-table-column label="娴嬭瘯搴忓彿" align="center" prop="testNum" />
+        <el-table-column label="娴嬭瘯椤圭洰" align="center" prop="testItem" />
+        <el-table-column label="瀹為檯鐢靛帇" align="center" prop="voltage" />
+        <el-table-column label="瀹為檯鐢垫祦" align="center" prop="loadCurrent" />
+        <el-table-column label="鏍囧噯璺濈" align="center" prop="stdDistance" />
+        <el-table-column label="鎰熷簲鐗�" align="center" prop="inductor" />
+        <el-table-column label="杈撳嚭寮曡剼" align="center" prop="output" />
+        <el-table-column label="娴嬭瘯鏁版嵁" align="center" prop="testValue" />
+        <el-table-column label="鍒ゆ柇鏉′欢" align="center" prop="judgeDetail" />
+        <el-table-column label="娴嬭瘯缁撴灉" align="center" prop="testResult" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['qms:testResult:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['qms:testResult:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+<!--      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />-->
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祴璇曠粨鏋滃璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="testResultFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鎵规鍙�" prop="batchCode">
+          <el-input v-model="form.batchCode" placeholder="璇疯緭鍏ユ壒娆″彿" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯搴忓彿" prop="testNum">
+          <el-input v-model="form.testNum" placeholder="璇疯緭鍏ユ祴璇曞簭鍙�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯椤圭洰" prop="testItem">
+          <el-select v-model="form.testItem" placeholder="璇烽�夋嫨娴嬭瘯椤圭洰">
+            <el-option v-for="item in testItemsOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢靛帇" prop="voltage">
+          <el-input v-model="form.voltage" placeholder="璇疯緭鍏ュ疄闄呯數鍘�" />
+        </el-form-item>
+        <el-form-item label="瀹為檯鐢垫祦" prop="loadCurrent">
+          <el-input v-model="form.loadCurrent" placeholder="璇疯緭鍏ュ疄闄呯數娴�" />
+        </el-form-item>
+        <el-form-item label="鏍囧噯璺濈" prop="stdDistance">
+          <el-input v-model="form.stdDistance" placeholder="璇疯緭鍏ユ爣鍑嗚窛绂�" />
+        </el-form-item>
+        <el-form-item label="鎰熷簲鐗�" prop="inductor">
+          <el-input v-model="form.inductor" placeholder="璇疯緭鍏ユ劅搴旂墿" />
+        </el-form-item>
+        <el-form-item label="杈撳嚭寮曡剼" prop="output">
+          <el-input v-model="form.output" placeholder="璇疯緭鍏ヨ緭鍑哄紩鑴�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯鏁版嵁" prop="testValue">
+          <el-input v-model="form.testValue" placeholder="璇疯緭鍏ユ祴璇曟暟鎹�" />
+        </el-form-item>
+        <el-form-item label="鍒ゆ柇鏉′欢" prop="judgeDetail">
+          <el-input v-model="form.judgeDetail" placeholder="璇疯緭鍏ュ垽鏂潯浠�" />
+        </el-form-item>
+        <el-form-item label="娴嬭瘯缁撴灉" prop="testResult">
+          <el-input v-model="form.testResult" placeholder="璇疯緭鍏ユ祴璇曠粨鏋�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="TestResult" lang="ts">
+import { listTestResultAll, getTestResult, delTestResult, addTestResult, updateTestResult, listTestItem } from '@/api/qms/testResult';
+import { TestResultVO, TestResultQuery, TestResultForm } from '@/api/qms/testResult/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const testResultList = ref<TestResultVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const dateRangeCreateTime = ref<[DateModelType, DateModelType]>(['', '']);
+const queryFormRef = ref<ElFormInstance>();
+const testResultFormRef = ref<ElFormInstance>();
+
+const testItemsOptions = ref<{ label: string, value: string }[]>([]); // 鏂板娴嬭瘯椤圭洰閫夐」
+const selectedTestItems = ref<string | string[]>([]); // 淇敼涓哄吋瀹瑰崟閫夌殑绫诲瀷
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: TestResultForm = {
+  id: undefined,
+  batchCode: undefined,
+  testNum: undefined,
+  testItem: undefined,
+  voltage: undefined,
+  loadCurrent: undefined,
+  stdDistance: undefined,
+  inductor: undefined,
+  output: undefined,
+  testValue: undefined,
+  judgeDetail: undefined,
+  testResult: undefined,
+  remark: undefined
+}
+const data = reactive<PageData<TestResultForm, TestResultQuery>>({
+  form: {...initFormData},
+  queryParams: {
+    batchCode: undefined,
+    testNum: undefined,
+    testItem: undefined,
+    voltage: undefined,
+    loadCurrent: undefined,
+    stdDistance: undefined,
+    inductor: undefined,
+    output: undefined,
+    testValue: undefined,
+    judgeDetail: undefined,
+    testResult: undefined,
+    startTime: undefined, // 鏂板 startTime 瀛楁
+    endTime: undefined, // 鏂板 endTime 瀛楁
+    params: {
+      createTime: undefined,
+    }
+  },
+  rules: {
+    id: [
+      { required: true, message: "涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    batchCode: [
+      { required: true, message: "鎵规鍙蜂笉鑳戒负绌�", trigger: "blur" }
+    ],
+    testNum: [
+      { required: true, message: "娴嬭瘯搴忓彿涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testItem: [
+      { required: true, message: "娴嬭瘯椤圭洰涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    voltage: [
+      { required: true, message: "瀹為檯鐢靛帇涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    loadCurrent: [
+      { required: true, message: "瀹為檯鐢垫祦涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    stdDistance: [
+      { required: true, message: "鏍囧噯璺濈涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    inductor: [
+      { required: true, message: "鎰熷簲鐗╀笉鑳戒负绌�", trigger: "blur" }
+    ],
+    output: [
+      { required: true, message: "杈撳嚭寮曡剼涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testValue: [
+      { required: true, message: "娴嬭瘯鏁版嵁涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    judgeDetail: [
+      { required: true, message: "鍒ゆ柇鏉′欢涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+    testResult: [
+      { required: true, message: "娴嬭瘯缁撴灉涓嶈兘涓虹┖", trigger: "blur" }
+    ],
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+// 鑾峰彇鎵�鏈夋祴璇曢」鐩�
+const fetchTestItems = async () => {
+  const res = await listTestItem();
+  testItemsOptions.value = res.data.map(item => ({ label: item, value: item }));
+}
+
+/** 鏌ヨ娴嬭瘯缁撴灉鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  queryParams.value.params = {};
+  // 澶勭悊 selectedTestItems 鐨勫�硷紝鍏煎鍗曢�夊拰澶氶��
+  if (Array.isArray(selectedTestItems.value)) {
+    queryParams.value.testItem = selectedTestItems.value.join(','); // 澶氶�夋ā寮�
+  } else {
+    queryParams.value.testItem = selectedTestItems.value; // 鍗曢�夋ā寮�
+  }
+  proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, 'CreateTime');
+  const res = await listTestResultAll(queryParams.value);
+  testResultList.value = res.data;
+  loading.value = false;
+}
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+
+  testResultFormRef.value?.resetFields();
+}
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  console.log("queryParams::",queryParams.value)
+  getList();
+}
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  dateRangeCreateTime.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  selectedTestItems.value = []; // 閲嶇疆閫変腑鐨勬祴璇曢」鐩�
+  reset(); // 璋冪敤閲嶇疆鏂规硶浠ユ竻绌烘椂闂存瀛楁
+}
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: TestResultVO[]) => {
+  ids.value = selection.map(item => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = "娣诲姞娴嬭瘯缁撴灉";
+}
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: TestResultVO) => {
+  reset();
+  const _id = row?.id || ids.value[0]
+  const res = await getTestResult(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = "淇敼娴嬭瘯缁撴灉";
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  testResultFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      } else {
+        await addTestResult(form.value).finally(() =>  buttonLoading.value = false);
+      }
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      await getList();
+    }
+  });
+}
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: TestResultVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯缁撴灉缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await delTestResult(_ids);
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  await getList();
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download('qms/testResult/export', {
+    ...queryParams.value
+  }, `testResult_${new Date().getTime()}.xlsx`)
+}
+
+onMounted(() => {
+  getList();
+  fetchTestItems(); // 璋冪敤鑾峰彇娴嬭瘯椤圭洰鎺ュ彛
+});
+</script>
diff --git a/src/views/register.vue b/src/views/register.vue
index f1d1e5a..3f4054d 100644
--- a/src/views/register.vue
+++ b/src/views/register.vue
@@ -2,7 +2,7 @@
   <div class="register">
     <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
       <div class="title-box">
-        <h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
+        <h3 class="title">鍏板疂杞﹂棿璐ㄩ噺绠$悊绯荤粺</h3>
         <lang-select />
       </div>
       <el-form-item v-if="tenantEnabled" prop="tenantId">
diff --git a/tsconfig.json b/tsconfig.json
index 6315891..bb287e9 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,7 @@
   "compilerOptions": {
     "target": "esnext",
     "module": "esnext",
-    //    "useDefineForClassFields": true,
+//    "useDefineForClassFields": true,
     "moduleResolution": "bundler",
     "strict": true,
     "jsx": "preserve",
diff --git a/vite.config.ts b/vite.config.ts
index e125e36..97c8d9d 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -64,6 +64,16 @@
         'echarts',
         'vue-i18n',
         '@vueup/vue-quill',
+        'bpmn-js/lib/Viewer',
+        'bpmn-js/lib/Modeler.js',
+        'bpmn-js-properties-panel',
+        'min-dash',
+        'diagram-js/lib/navigation/movecanvas',
+        'diagram-js/lib/navigation/zoomscroll',
+        'bpmn-js/lib/features/palette/PaletteProvider',
+        'bpmn-js/lib/features/context-pad/ContextPadProvider',
+        'diagram-js/lib/draw/BaseRenderer',
+        'tiny-svg',
         'image-conversion',
         'element-plus/es/components/**/css'
       ]
diff --git a/vite/plugins/i18n.ts b/vite/plugins/i18n.ts
new file mode 100644
index 0000000..8777d1a
--- /dev/null
+++ b/vite/plugins/i18n.ts
@@ -0,0 +1,6 @@
+import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
+export default (path: any) => {
+  return VueI18nPlugin({
+    include: [path.resolve(__dirname, '../../src/lang/**.json')]
+  });
+};

--
Gitblit v1.9.3