diff --git a/.env.development b/.env.development
index 11728ea..695ffb3 100644
--- a/.env.development
+++ b/.env.development
@@ -1,6 +1,8 @@
VITE_APP_API_BASE_URL='http://10.97.245.96'
+VITE_APP_API_INT_BASE_URL='http://127.0.0.1'
+VITE_APP_API_EXT_BASE_URL= 'http://8.134.12.216:3005'
# 32字节密钥(AES-256)
-VITE_APP_KEY='cms.SBU4.PE$20@2aXz3kL9pR7sQ4fT1vB'
+VITE_APP_KEY='cms.SBU4.PE$20@2aXz3kL9pR7sQ4fT1'
# 16字节向量
VITE_APP_IV='m8Zp2x7cK9bF3dS5'
\ No newline at end of file
diff --git a/.env.production b/.env.production
index eb8add6..341d4f0 100644
--- a/.env.production
+++ b/.env.production
@@ -1,7 +1,8 @@
-VITE_APP_AUTH_API='http://192.168.8.8:9000'
-VITE_APP_API_BASE_URL='http://192.168.8.8:9002'
+VITE_APP_API_BASE_URL='http://10.97.245.96'
+VITE_APP_API_INT_BASE_URL='http://10.97.245.96'
+VITE_APP_API_EXT_BASE_URL= 'http://8.134.12.216:3005'
# 32字节密钥(AES-256)
-VITE_APP_KEY='cms.SBU4.PE$20@2aXz3kL9pR7sQ4fT1vB'
+VITE_APP_KEY='cms.SBU4.PE$20@2aXz3kL9pR7sQ4fT1'
# 16字节向量
VITE_APP_IV='m8Zp2x7cK9bF3dS5'
\ No newline at end of file
diff --git a/auto-imports.d.ts b/auto-imports.d.ts
index 9d24007..76e1ee6 100644
--- a/auto-imports.d.ts
+++ b/auto-imports.d.ts
@@ -6,5 +6,5 @@
// biome-ignore lint: disable
export {}
declare global {
-
+ const ElMessage: typeof import('element-plus/es')['ElMessage']
}
diff --git a/components.d.ts b/components.d.ts
index 17f34fd..6ab28a9 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -9,35 +9,27 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AuthPage: typeof import('./src/components/authentication/AuthPage.vue')['default']
- ElAlert: typeof import('element-plus/es')['ElAlert']
- ElAside: typeof import('element-plus/es')['ElAside']
- ElAvatar: typeof import('element-plus/es')['ElAvatar']
- ElBadge: typeof import('element-plus/es')['ElBadge']
- ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
- ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
- ElContainer: typeof import('element-plus/es')['ElContainer']
- ElDropdown: typeof import('element-plus/es')['ElDropdown']
- ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
- ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
- ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
- ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
- ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
+ ElPagination: typeof import('element-plus/es')['ElPagination']
ElSelect: typeof import('element-plus/es')['ElSelect']
- ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
LoginForm: typeof import('./src/components/authentication/LoginForm.vue')['default']
RegisterForm: typeof import('./src/components/authentication/RegisterForm.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
+ Sidebar: typeof import('./src/components/menu/Sidebar.vue')['default']
+ SidebarItem: typeof import('./src/components/menu/SidebarItem.vue')['default']
+ }
+ export interface GlobalDirectives {
+ vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}
diff --git a/index.html b/index.html
index b19040a..3df5019 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
Vite App
+ SBU4 I4.0
diff --git a/public/favicon.ico b/public/favicon.ico
index df36fcf..6640bfe 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/src/components/authentication/AuthPage.vue b/src/components/authentication/AuthPage.vue
index fc7da43..6130076 100644
--- a/src/components/authentication/AuthPage.vue
+++ b/src/components/authentication/AuthPage.vue
@@ -80,7 +80,6 @@
-
diff --git a/src/components/menu/SidebarItem.vue b/src/components/menu/SidebarItem.vue
new file mode 100644
index 0000000..6d70a3f
--- /dev/null
+++ b/src/components/menu/SidebarItem.vue
@@ -0,0 +1,72 @@
+
+
+
+ {{ t('menu.menuTooDeep') }}
+
+
+
+
+
+ {{ t(item.menu_name) }}
+
+
+
+
+
+
+
+ {{ t(item.menu_name) }}
+
+
+
+
diff --git a/src/i18n/lang/en-US.js b/src/i18n/lang/en-US.js
index 3e5d34d..8c13ef6 100644
--- a/src/i18n/lang/en-US.js
+++ b/src/i18n/lang/en-US.js
@@ -10,8 +10,8 @@ export default {
passwordMinLength: 'Password must be at least 6 characters',
confirmPassword: 'Confirm Password',
passwordMismatch: 'Passwords do not match',
- usernamePlaceholder: 'User name',
- passwordPlaceholder: 'Password',
+ usernamePlaceholder: 'Enter username',
+ passwordPlaceholder: 'Enter password',
loginBtn: 'Login',
confirmPasswordPlaceholder: 'Please confirm your password',
registerBtn: 'Register',
@@ -21,16 +21,95 @@ export default {
invalidCredentials: 'Invalid username or password',
serverError: 'Server error, please try again later',
networkError: 'Network error, please check your connection',
- unknownError: 'An unknown error occurred, please try again'
+ unknownError: 'An unknown error occurred, please try again',
+ requestTimeout: 'Request timed out, please try again later',
+ unauthorized: 'Unauthorized access, please login again',
+ forbidden: 'Access forbidden, insufficient permissions',
+ notFound: 'Requested resource not found',
+ requestFailed: 'Request processing failed',
+ usernameExists: 'Username already exists',
+ emailExists: 'Email already registered',
+ invalidUsername: 'Invalid username format',
+ invalidPassword: 'Password format does not meet requirements',
+ registerSuccess: 'Registration successful',
+ registerFailed: 'Registration failed, please try again'
},
common: {
login: 'Login',
register: 'Register',
- switchLang: 'Switch Language'
+ switchLang: 'Switch Language',
+ languageChanged: 'Language switched successfully',
+ close: 'Close',
+ confirm: 'Confirm',
+ cancel: 'Cancel',
+ refresh: 'Refresh',
+ collapseMenu: 'Collapse Menu',
+ expandMenu: 'Expand Menu',
+ operationSuccess: 'Operation successful',
+ operationFailed: 'Operation failed',
+ pleaseWait: 'Please wait...',
+ back: 'Back',
+ save: 'Save',
+ delete: 'Delete',
+ edit: 'Edit',
+ add: 'Add',
+ search: 'Search',
+ reset: 'Reset'
},
el: {
form: {
required: 'Please fill in the required field'
}
+ },
+ menu: {
+ settings: 'Settings',
+ manage: 'Management',
+ equipment: 'Equipment',
+ purchase: 'Purchase',
+ spare: 'Spare Parts',
+ maintenance: 'Maintenance',
+ report: 'Reports',
+ capacity: 'Capacity',
+ applet: 'Mini Program',
+ useringroup: 'User Groups',
+ userindepartment: 'User Departments',
+ passwordreset: 'Password Reset',
+ users: 'Users',
+ groups: 'Groups',
+ departments: 'Departments',
+ instruments: 'Instruments',
+ fixtures: 'Fixtures',
+ racks: 'Aging Racks',
+ testers: 'Test Stations',
+ purlist: 'Purchase List',
+ splist: 'Spare Parts List',
+ dashboard: 'Dashboard',
+ home: 'Home',
+ help: 'Help Center',
+ dailyReport: 'Daily Report',
+ monthlyReport: 'Monthly Report',
+ annualReport: 'Annual Report',
+ faultReport: 'Fault Report',
+ systemBasic: 'Basic Settings',
+ security: 'Security Settings',
+ notifications: 'Notification Settings',
+ logs: 'System Logs',
+ fetchFailed: 'Failed to fetch menu',
+ noMenuData: 'No menu data available',
+ loadFailed: 'Failed to load menu information',
+ noPermission: 'No access permission',
+ menuTooDeep: "Menu level too deep"
+ },
+ main: {
+ welcome: 'Welcome to the system',
+ contentDescription: 'Please select a menu item on the left to proceed'
+ },
+ user: {
+ profile: "Profile",
+ logout: "Logout",
+ notifications: "Notifications",
+ defaultName: "User",
+ defaultRole: "Regular User"
}
}
+
\ No newline at end of file
diff --git a/src/i18n/lang/zh-CN.js b/src/i18n/lang/zh-CN.js
index 4f91c2a..de3ceff 100644
--- a/src/i18n/lang/zh-CN.js
+++ b/src/i18n/lang/zh-CN.js
@@ -1,4 +1,10 @@
export default {
+ theme: {
+ light: '亮色主题',
+ dark: '暗色主题',
+ custom: '自定义主题',
+ themeSwitched: '已切换到{theme}主题'
+ },
auth: {
loginTitle: '用户登录',
registerTitle: '用户注册',
@@ -10,7 +16,7 @@ export default {
passwordMinLength: '密码长度不能少于6个字符',
confirmPassword: '确认密码',
passwordMismatch: '两次输入的密码不一致',
- usernamePlaceholder: '请输入用户名',
+ usernamePlaceholder: '请输入用户名',
passwordPlaceholder: '请输入密码',
loginBtn: '登录',
confirmPasswordPlaceholder: '请再次输入密码',
@@ -24,17 +30,20 @@ export default {
serverError: '服务器错误,请稍后再试',
networkError: '网络连接错误,请检查网络',
unknownError: '发生未知错误,请重试',
- // 新增错误类型翻译
- requestTimeout: '请求超时,请稍后重试',
- unauthorized: '未授权访问,请重新登录',
- forbidden: '禁止访问,没有权限',
+ requestTimeout: '请求超时超时,请稍后重试',
+ unauthorized: '未授权访问,请,请重新登录',
+ forbidden: '禁止访问访问,没有权限',
notFound: '请求的资源不存在',
requestFailed: '请求处理失败',
- // 可能需要的其他注册相关提示
usernameExists: '用户名已存在',
emailExists: '邮箱已被注册',
- invalidUsername: '用户名格式不正确',
- invalidPassword: '密码格式不符合要求'
+ invalidUsername: '用户名格式名格式不正确',
+ invalidPassword: '密码格式不符合要求',
+ sessionExpired: '登录状态已失效,请重新登录',
+ loginSuccess: '登录成功',
+ welcomeBack: '欢迎回来',
+ logoutSuccess: '退出登录成功',
+ logoutFailed: '退出登录失败'
},
common: {
login: '登录',
@@ -43,12 +52,63 @@ export default {
languageChanged: '语言已切换',
close: '关闭',
confirm: '确认',
- cancel: '取消'
+ cancel: '取消',
+ refresh: '刷新',
+ collapseMenu: '折叠菜单',
+ expandMenu: '展开菜单',
+ unknownError: '未知错误',
+ networkError: '网络异常',
+ languageChanged: '语言已切换'
},
el: {
form: {
required: '请输入必填项'
}
+ },
+ menu: {
+ settings: '设置',
+ manage: '管理',
+ equipment: '设备',
+ purchase: '采购',
+ spare: '备件',
+ maintenance: '维护',
+ report: '报告',
+ capacity: '容量',
+ applet: '小程序',
+ useringroup: '用户组别',
+ userindepartment: '用户部门',
+ passwordreset: '密码重置',
+ users: '用户',
+ groups: '组别',
+ departments: '部门',
+ instruments: '仪器',
+ fixtures: '夹具',
+ racks: '老化架',
+ testers: '测试位',
+ purlist: '采购清单',
+ splist: '备件清单',
+ fetchFailed: '获取菜单失败',
+ menuTooDeep: "菜单层级过深"
+ },
+ user: {
+ profile: "个人信息",
+ logout: "退出登录",
+ notifications: "通知",
+ defaultName: "用户",
+ defaultRole: "普通用户",
+ unknown: '未知用户',
+ unauthorized: '未授权',
+ defaultRole: '普通用户'
+ },
+ error: {
+ noUserInfo: '未找到用户信息',
+ invalidBase64: '加密数据格式无效',
+ decryptFailed: '用户信息解密失败',
+ jsonParseFailed: 'JSON解析失败',
+ invalidUserInfo: '用户信息格式无效',
+ loadUserFailed: '加载用户信息失败'
+ },
+ notification: {
+ openCenter: '打开通知中心'
}
}
-
\ No newline at end of file
diff --git a/src/router/index.js b/src/router/index.js
index 1e85329..cfc9038 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,58 +1,84 @@
import { createRouter, createWebHistory } from 'vue-router';
+import Cookies from 'js-cookie';
import AuthPage from '../components/authentication/AuthPage.vue';
import MainPage from '../views/mainpage/index.vue';
+import UutReport from '../views/uut/Report.vue';
+import UutHistory from '../views/uut/History.vue';
+import DashboardTest from '../views/dashboard/test.vue';
+import Profile from '../views/user/Profile.vue';
+import Testers from '../views/equipment/Testers.vue';
-// 路由守卫:检查是否登录 - 优化版
-const requireAuth = (to, from, next) => {
- // 更可靠的登录状态检查
+// 登录状态检查工具函数
+const checkLoginStatus = () => {
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
const hasUserInfo = !!localStorage.getItem('userInfo');
-
- if (isLoggedIn && hasUserInfo) {
+ const hasToken = !!Cookies.get('access_token');
+ return isLoggedIn && hasUserInfo && hasToken;
+};
+
+// 清除登录状态方法
+const clearLoginState = () => {
+ localStorage.removeItem('isLoggedIn');
+ localStorage.removeItem('userInfo');
+ localStorage.removeItem('rememberedUser');
+ Cookies.remove('access_token');
+};
+
+// 路由守卫:需要登录的路由
+const requireAuth = (to, from, next) => {
+ if (checkLoginStatus()) {
next();
} else {
- // 清除无效的登录状态
- localStorage.removeItem('isLoggedIn');
- next('/');
+ clearLoginState();
+ next('/auth');
}
};
+// 路由配置
const routes = [
{
- path: '/',
+ path: '/auth',
name: 'Auth',
component: AuthPage,
beforeEnter: (to, from, next) => {
- const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
- const hasUserInfo = !!localStorage.getItem('userInfo');
-
- if (isLoggedIn && hasUserInfo) {
- next('/main');
+ if (checkLoginStatus()) {
+ next('/');
} else {
next();
}
}
},
{
- path: '/main',
+ path: '/',
name: 'Main',
component: MainPage,
- beforeEnter: requireAuth
+ beforeEnter: requireAuth,
+ children: [
+ { path: '', redirect: '/dashboard' },
+ { path: 'dashboard', name: 'Dashboard', component: DashboardTest },
+ { path: 'uut/report', name: 'UutReport', component: UutReport },
+ { path: 'uut/history', name: 'UutHistory', component: UutHistory },
+ { path: 'profile', component: Profile },
+ { path: 'testers', component: Testers }
+ ]
},
{
path: '/:pathMatch(.*)*',
- redirect: '/'
+ name: 'NotFound',
+ redirect: (to) => checkLoginStatus() ? '/' : '/auth'
}
];
const router = createRouter({
- history: createWebHistory(),
- routes
+ history: createWebHistory('/sbu4i4/'),
+ routes,
+ scrollBehavior(to, from, savedPosition) {
+ return savedPosition || { top: 0 };
+ }
});
-// 全局路由守卫,处理可能的登录状态异常
+// 全局路由守卫
router.beforeEach((to, from, next) => {
- // 对所有需要授权的路由进行检查
if (to.matched.some(record => record.beforeEnter === requireAuth)) {
requireAuth(to, from, next);
} else {
@@ -60,4 +86,5 @@ router.beforeEach((to, from, next) => {
}
});
-export default router;
+// 导出路由和工具函数
+export { router as default, clearLoginState, checkLoginStatus };
diff --git a/src/utils/api/api.js b/src/utils/api/api.js
new file mode 100644
index 0000000..5e9d887
--- /dev/null
+++ b/src/utils/api/api.js
@@ -0,0 +1,53 @@
+// 所有API接口路径集中管理
+export const ApiPaths = {
+ // 仪器相关接口
+ machine: {
+ list: '/api/i4/v1/get/machines.php', // 获取仪器列表
+ update: '/api/i4/v1/update/machine.php', // 更新仪器信息
+ create: '/api/i4/v1/create/machine.php', // 创建仪器
+ export: '/download.php?x=Machine List' // 导出仪器列表
+ },
+
+ // 用户相关接口
+ user: {
+ list: '/api/i4/v1/get/users.php', // 获取用户列表
+ listWithDisabled: '/api/i4/v1/get/users.php?include_disabled=1' // 包含禁用用户
+ },
+
+ // 品牌相关接口
+ brand: {
+ list: '/api/i4/v1/get/brands.php' // 获取品牌列表
+ },
+
+ // 客户相关接口
+ customer: {
+ list: '/api/i4/v1/get/customers.php' // 获取客户列表
+ },
+
+ // 来源类型相关接口
+ sourceType: {
+ list: '/api/i4/v1/get/eq_sources.php' // 获取来源类型列表
+ },
+
+ // 其他来源类型相关接口
+ otherSource: {
+ list: '/api/i4/v1/get/eq_source_other.php' // 获取其他来源类型列表
+ },
+
+ // 状态相关接口
+ status: {
+ list: '/api/i4/v1/get/eq_status.php' // 获取状态列表
+ },
+
+ // 位置相关接口
+ location: {
+ list: '/api/i4/v1/get/locations.php' // 获取位置列表
+ },
+
+ // 部门相关接口
+ department: {
+ list: '/api/i4/v1/get/departments.php' // 获取部门列表
+ }
+};
+
+export default ApiPaths;
diff --git a/src/utils/api/index.js b/src/utils/api/index.js
index 9430696..f712338 100644
--- a/src/utils/api/index.js
+++ b/src/utils/api/index.js
@@ -2,17 +2,37 @@ import axios from "axios";
import crypto from "@/utils/crypto";
import Cookies from 'js-cookie';
import { ElMessage, ElLoading } from 'element-plus';
+import router, { clearLoginState } from '@/router'; // 修正路由导入路径
// 获取基础 API 地址
const API_BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
-if (!API_BASE_URL) {
+// 获取内网 API 地址
+const VITE_APP_API_INT_BASE_URL = import.meta.env.VITE_APP_API_INT_BASE_URL;
+
+// 获取外网 API 地址
+const VITE_APP_API_EXT_BASE_URL = import.meta.env.VITE_APP_API_EXT_BASE_URL;
+
+
+if (!API_BASE_URL || !VITE_APP_API_INT_BASE_URL || !VITE_APP_API_EXT_BASE_URL) {
console.warn('API基础地址未配置,请在环境变量中设置VITE_APP_API_BASE_URL');
}
+// 设置基础 API 地址
+const hostname = `${window.location.hostname}`;
+var auth_api_url = ''
+if(hostname == '192.168.6.104') {
+ auth_api_url = API_BASE_URL
+}else if (hostname == '8.134.12.216') {
+ auth_api_url = VITE_APP_API_EXT_BASE_URL
+}else if (hostname == '10.97.245.96') {
+ auth_api_url = VITE_APP_API_INT_BASE_URL
+}
+
+
// 创建axios实例
const api = axios.create({
- baseURL: API_BASE_URL,
+ baseURL: auth_api_url,
timeout: 15000, // 请求超时时间
headers: {
'Content-Type': 'application/json'
@@ -59,16 +79,16 @@ api.interceptors.request.use(
config.headers['Authorization'] = `Bearer ${accessToken}`;
}
- // 处理POST请求加密
+ // 处理POST请求加密(不加密请求会标记isEncrypted: true)
if (config.method?.toLowerCase() === 'post' && !config.isEncrypted) {
try {
// 添加用户信息
let requestData = { ...config.data };
- const storedUserInfo = localStorage.getItem('info');
+ const storedUserInfo = localStorage.getItem('userInfo'); // 修正存储键名
if (storedUserInfo) {
- const info = JSON.parse(crypto.decrypt(storedUserInfo));
- requestData['user_id'] = info.id;
+ const userInfo = JSON.parse(crypto.decrypt(storedUserInfo));
+ requestData['user_id'] = userInfo.userId; // 匹配后端返回的userId字段
}
// 加密数据
@@ -99,13 +119,12 @@ api.interceptors.response.use(
// 处理业务逻辑错误
const { data } = response;
-
- // 根据实际后端规范调整
- if (data.code && data.code !== 200) {
- ElMessage.error(data.message || '操作失败');
- return Promise.reject(new Error(data.message || '请求失败'));
+ // 根据实际后端规范调整(确保与后端code一致)
+ if (data.code == null || ![0, 200].includes(data.code)) {
+ ElMessage.error(data.message || '操作失败');
+ return Promise.reject(new Error(data.message || '请求失败'));
}
-
+
return data;
},
async (error) => {
@@ -123,13 +142,10 @@ api.interceptors.response.use(
switch (status) {
case 401:
// 未授权,处理token过期逻辑
- ElMessage.error('登录已过期,请重新登录');
- // 清除登录状态
- Cookies.remove('access_token');
- localStorage.removeItem('info');
- // 跳转到登录页(假设使用vue-router)
- if (window.router) {
- window.router.push('/');
+ if (!router.currentRoute.value.path.includes('/auth')) {
+ ElMessage.error('登录已过期,请重新登录');
+ clearLoginState(); // 使用路由文件中的清除方法
+ router.push('/auth');
}
break;
case 403:
@@ -216,7 +232,7 @@ const request = {
},
/**
- * 不加密的POST请求
+ * 不加密的POST请求(用于登录等不需要加密的场景)
* @param {string} url 请求地址
* @param {object} data 请求数据
* @param {object} config 额外配置
@@ -228,6 +244,7 @@ const request = {
method: 'post',
data,
isEncrypted: true, // 标记为已加密,跳过加密处理
+ showLoading: config.showLoading !== false, // 默认为显示加载状态
...config
});
}
diff --git a/src/utils/crypto/index.js b/src/utils/crypto/index.js
index 3afd7a1..aa98ccc 100644
--- a/src/utils/crypto/index.js
+++ b/src/utils/crypto/index.js
@@ -1,30 +1,103 @@
-import CryptoJS from 'crypto-js';
+import CryptoJS from 'crypto-js'; // 假设使用crypto-js库
-const key = CryptoJS.enc.Utf8.parse(import.meta.env.VITE_APP_KEY);
-const iv = CryptoJS.enc.Utf8.parse(import.meta.env.VITE_APP_IV);
+// 密钥必须与加密时完全一致(建议从环境变量读取,避免硬编码)
+const SECRET_KEY = import.meta.env.VITE_APP_KEY || 'your-secret-key-32bytes'; // 32字节密钥(AES-256)
+const IV = import.meta.env.VITE_APP_IV || '16bytesIvValue!'; // 16字节IV
-const crypto = {
- encrypt: (str) => {
- const encrypted = CryptoJS.AES.encrypt(str, key, {
+// 验证密钥和IV长度(关键修复)
+if (SECRET_KEY.length !== 32) {
+ console.error(`加密密钥长度错误:需要32字节,实际${SECRET_KEY.length}字节`);
+}
+if (IV.length !== 16) {
+ console.error(`IV长度错误:需要16字节,实际${IV.length}字节`);
+}
+
+/**
+ * 加密函数
+ * @param {string} data - 要加密的字符串(需确保是有效的UTF-8)
+ * @returns {string} 加密后的Base64字符串
+ */
+export const encrypt = (data) => {
+ try {
+ // 确保输入是字符串,且是有效的UTF-8
+ if (typeof data !== 'string') {
+ throw new Error('加密数据必须是字符串');
+ }
+
+ // 验证UTF-8有效性
+ new TextDecoder().decode(new TextEncoder().encode(data));
+
+ const key = CryptoJS.enc.Utf8.parse(SECRET_KEY);
+ const iv = CryptoJS.enc.Utf8.parse(IV);
+
+ const encrypted = CryptoJS.AES.encrypt(
+ data,
+ key,
+ {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
- });
- // 将加密结果转换为字节数组,然后转换为十六进制字符串
- const hexString = CryptoJS.enc.Hex.stringify(encrypted.ciphertext);
- return hexString;
- },
- decrypt: (hexString) => {
- // 将十六进制字符串转换回字节数组
- const ciphertext = CryptoJS.enc.Hex.parse(hexString);
- const encryptedParams = CryptoJS.lib.CipherParams.create({ ciphertext });
- const decrypted = CryptoJS.AES.decrypt(encryptedParams, key, {
- iv: iv,
- mode: CryptoJS.mode.CBC,
- padding: CryptoJS.pad.Pkcs7
- });
- return decrypted.toString(CryptoJS.enc.Utf8);
+ }
+ );
+
+ // 返回Base64格式的密文(避免特殊字符问题)
+ return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
+ } catch (error) {
+ console.error('加密失败:', error);
+ throw new Error(`加密错误: ${error.message}`);
}
};
-export default crypto;
+/**
+ * 解密函数
+ * @param {string} encryptedData - 加密后的Base64字符串
+ * @returns {string} 解密后的UTF-8字符串
+ */
+export const decrypt = (encryptedData) => {
+ try {
+ // 验证输入是否为字符串
+ if (typeof encryptedData !== 'string' || encryptedData.trim() === '') {
+ throw new Error('解密数据必须是非空字符串');
+ }
+
+ // 验证Base64有效性(关键修复)
+ if (!/^[A-Za-z0-9+/=]+$/.test(encryptedData)) {
+ throw new Error('解密数据不是有效的Base64格式');
+ }
+
+ const key = CryptoJS.enc.Utf8.parse(SECRET_KEY);
+ const iv = CryptoJS.enc.Utf8.parse(IV);
+
+ // 先将Base64转换为CipherParams对象
+ const cipherParams = CryptoJS.lib.CipherParams.create({
+ ciphertext: CryptoJS.enc.Base64.parse(encryptedData)
+ });
+
+ const decrypted = CryptoJS.AES.decrypt(
+ cipherParams,
+ key,
+ {
+ iv: iv,
+ mode: CryptoJS.mode.CBC,
+ padding: CryptoJS.pad.Pkcs7
+ }
+ );
+
+ // 转换为UTF-8字符串(处理可能的编码错误)
+ const result = decrypted.toString(CryptoJS.enc.Utf8);
+
+ if (!result) {
+ throw new Error('解密结果为空,可能是密钥不匹配或数据损坏');
+ }
+
+ // 再次验证UTF-8有效性
+ new TextDecoder().decode(new TextEncoder().encode(result));
+
+ return result;
+ } catch (error) {
+ console.error('解密失败:', error);
+ throw new Error(`解密错误: ${error.message}`);
+ }
+};
+
+export default { encrypt, decrypt };
diff --git a/src/views/dashboard/test.vue b/src/views/dashboard/test.vue
new file mode 100644
index 0000000..d9eb155
--- /dev/null
+++ b/src/views/dashboard/test.vue
@@ -0,0 +1,6 @@
+
+ test
+
+
diff --git a/src/views/equipment/Testers.vue b/src/views/equipment/Testers.vue
new file mode 100644
index 0000000..60a0292
--- /dev/null
+++ b/src/views/equipment/Testers.vue
@@ -0,0 +1,1000 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/mainpage/index.vue b/src/views/mainpage/index.vue
index 8f60e71..5c1599a 100644
--- a/src/views/mainpage/index.vue
+++ b/src/views/mainpage/index.vue
@@ -1,17 +1,43 @@
-
-
-