Commit dcb685b2 by Neo Turing

本地

parents 0b0fa780 37f5db10
...@@ -6,6 +6,10 @@ ...@@ -6,6 +6,10 @@
"eslint.useFlatConfig": true, "eslint.useFlatConfig": true,
"editor.formatOnSave": false, "editor.formatOnSave": false,
"eslint.validate": ["html", "css", "scss", "json", "jsonc"], "eslint.validate": ["html", "css", "scss", "json", "jsonc"],
"eslint.format.enable": true,
"eslint.options": {
"format": "./eslint-chinese-formatter.js"
},
"i18n-ally.displayLanguage": "zh-cn", "i18n-ally.displayLanguage": "zh-cn",
"i18n-ally.enabledParsers": ["ts"], "i18n-ally.enabledParsers": ["ts"],
"i18n-ally.enabledFrameworks": ["vue"], "i18n-ally.enabledFrameworks": ["vue"],
......
# ESLint 中文设置说明
本项目已配置了 ESLint 中文错误消息显示功能。
## 🚀 功能特性
- ✅ 中文错误和警告消息
- ✅ 支持 Vue、TypeScript、JavaScript 规则
- ✅ 自定义中文格式化器
- ✅ VS Code 编辑器集成
## 📦 安装的依赖
```bash
pnpm add eslint-plugin-chinese -D
```
## 🔧 配置文件说明
### 1. eslint.config.js
- 主要的 ESLint 配置文件
- 已添加中文插件支持
### 2. eslint-chinese-messages.js
- 中文错误消息映射表
- 包含常用的 Vue、TypeScript、JavaScript 规则翻译
### 3. eslint-chinese-formatter.js
- 自定义中文格式化器
- 将 ESLint 输出格式化为中文
### 4. .vscode/settings.json
- VS Code 编辑器配置
- 启用中文格式化器显示
## 🎯 使用方法
### 方式一:使用中文格式化器(推荐)
```bash
# 运行 ESLint 检查并显示中文消息
pnpm lint:chinese
```
### 方式二:使用默认 lint 命令
```bash
# 常规 ESLint 检查(英文消息)
pnpm lint
```
### 方式三:在 VS Code 中查看
1. 安装 ESLint 扩展
2. 打开任意代码文件
3. 错误信息将以中文显示在编辑器中
## 📋 中文消息示例
| 规则 | 英文消息 | 中文消息 |
|------|----------|----------|
| `vue/multi-word-component-names` | Component name should always be multi-word | 组件名应该由多个单词组成 |
| `no-unused-vars` | 'xxx' is defined but never used | 存在未使用的变量 |
| `no-console` | Unexpected console statement | 不应使用console语句 |
| `@typescript-eslint/no-explicit-any` | Unexpected any. Specify a different type | 不应使用any类型 |
## 🛠️ 自定义配置
### 添加新的中文消息
编辑 `eslint-chinese-messages.js` 文件:
```javascript
export const chineseMessages = {
// 添加新的规则翻译
'your-rule-id': '你的中文消息',
// ...
};
```
### 修改格式化器样式
编辑 `eslint-chinese-formatter.js` 文件来自定义输出格式。
## 🐛 故障排除
### 问题:中文消息不显示
1. 确保已安装 `eslint-plugin-chinese`
2. 检查 ESLint 配置文件是否正确
3. 重启 VS Code 编辑器
### 问题:VS Code 中显示英文
1. 确保 ESLint 扩展已启用
2. 检查 `.vscode/settings.json` 配置
3. 重新加载窗口 (Ctrl+Shift+P -> Reload Window)
## 💡 提示
- 中文消息仅影响显示,不影响 ESLint 的实际功能
- 可以根据需要添加更多规则的中文翻译
- 建议在团队中统一使用中文消息格式化器
## 📚 相关链接
- [ESLint 官方文档](https://eslint.org/)
- [Vue ESLint 插件](https://eslint.vuejs.org/)
- [TypeScript ESLint](https://typescript-eslint.io/)
# Axios 封装使用说明
本项目已经封装了一个支持代理的 axios 实例,你可以通过以下方式使用:
## 方式一:使用全局注册的 axios 实例(推荐)
```typescript
// 直接使用 - 已在 main.ts 中全局注册
const axios = (window as any).$axios;
// 发送请求
const response = await axios.get('/codet/table.json', {
params: searchParams
});
```
## 方式二:导入封装的 http 工具
```typescript
import { axios, http, request } from '@/utils/http';
// 使用 axios 实例
const response1 = await axios.get('/api/data');
// 使用便捷的 http 方法
const response2 = await http.get('/api/data');
// 使用项目封装的 request 实例
const { data, error } = await request({ url: '/api/data', method: 'get' });
```
## 方式三:直接导入封装的 axios 实例
```typescript
import axiosInstance from '@/service/request/axios';
const response = await axiosInstance.get('/api/data');
```
## 特性
**自动代理支持**:开发环境下自动使用 Vite 代理配置
**Token 自动注入**:自动添加 Authorization header
**错误处理**:401 错误时自动清除登录状态
**请求/响应拦截**:可以方便地添加全局处理逻辑
## 代理配置
封装的 axios 实例会根据环境变量自动决定是否使用代理:
- 开发环境且 `VITE_HTTP_PROXY=Y` 时:使用代理路径(如 `/proxy-default`
- 其他情况:直接请求配置的 baseURL
## 配置说明
```typescript
// src/service/request/axios.ts
const axiosInstance = axios.create({
baseURL, // 根据代理配置自动设置
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
```
## 迁移建议
如果你之前使用原生 axios,现在可以直接替换:
```typescript
// 之前
import axios from 'axios';
const response = await axios.get('/api/data');
// 现在(方式一)
const axios = (window as any).$axios;
const response = await axios.get('/api/data');
// 现在(方式二)
import { axios } from '@/utils/http';
const response = await axios.get('/api/data');
```
现在所有的 axios 请求都会自动走代理配置,无需每个页面单独配置!
import { chineseMessages } from './eslint-chinese-messages.js';
// ESLint 中文格式化器
export default function chineseFormatter(results) {
let output = '';
let errorCount = 0;
let warningCount = 0;
let fixableErrorCount = 0;
let fixableWarningCount = 0;
results.forEach(result => {
const messages = result.messages;
if (messages.length === 0) {
return;
}
errorCount += result.errorCount;
warningCount += result.warningCount;
fixableErrorCount += result.fixableErrorCount;
fixableWarningCount += result.fixableWarningCount;
output += `\n${result.filePath}\n`;
messages.forEach(message => {
const severity = message.severity === 2 ? '错误' : '警告';
const ruleId = message.ruleId || '';
const line = message.line || 0;
const column = message.column || 0;
// 使用中文消息映射
const chineseMessage = chineseMessages[ruleId] || message.message;
output += ` ${line}:${column} ${severity} ${chineseMessage}`;
if (ruleId) {
output += ` ${ruleId}`;
}
output += '\n';
});
});
if (errorCount || warningCount) {
output += '\n';
if (errorCount > 0) {
output += `✖ 发现 ${errorCount} 个错误`;
if (fixableErrorCount > 0) {
output += `,其中 ${fixableErrorCount} 个可自动修复`;
}
output += '\n';
}
if (warningCount > 0) {
output += `⚠ 发现 ${warningCount} 个警告`;
if (fixableWarningCount > 0) {
output += `,其中 ${fixableWarningCount} 个可自动修复`;
}
output += '\n';
}
if (fixableErrorCount > 0 || fixableWarningCount > 0) {
output += '\n运行 `pnpm lint` 来自动修复这些问题。\n';
}
} else {
output = '✅ 代码检查通过!没有发现问题。\n';
}
return output;
}
// ESLint 中文错误消息映射
export const chineseMessages = {
// Vue 相关规则
'vue/multi-word-component-names': '组件名应该由多个单词组成',
'vue/component-name-in-template-casing': '模板中的组件名应使用PascalCase格式',
'vue/no-unused-vars': '存在未使用的变量',
'vue/no-unused-components': '存在未使用的组件',
'vue/require-v-for-key': 'v-for指令必须包含key属性',
'vue/no-multiple-template-root': '模板不能有多个根元素',
'vue/html-self-closing': 'HTML标签应该自闭合',
// JavaScript 基础规则
'no-unused-vars': '存在未使用的变量',
'no-console': '不应使用console语句',
'no-debugger': '不应使用debugger语句',
'no-alert': '不应使用alert语句',
'no-empty': '代码块不应为空',
'no-var': '应使用let或const代替var',
'prefer-const': '应使用const声明不会重新赋值的变量',
'no-undef': '使用了未定义的变量',
'no-duplicate-keys': '对象中存在重复的键',
'no-unreachable': '存在不可达的代码',
// TypeScript 相关规则
'@typescript-eslint/no-unused-vars': '存在未使用的TypeScript变量',
'@typescript-eslint/no-explicit-any': '不应使用any类型',
'@typescript-eslint/no-empty-function': '函数不应为空',
'@typescript-eslint/prefer-nullish-coalescing': '应使用空值合并运算符',
// 代码风格规则
'indent': '缩进不正确',
'quotes': '引号使用不一致',
'semi': '缺少分号',
'comma-dangle': '尾随逗号使用不正确',
'eol-last': '文件末尾应有换行符',
'no-trailing-spaces': '行尾存在多余空格',
// UnoCSS 相关规则
'unocss/order-attributify': 'UnoCSS属性顺序不正确'
};
// 创建中文消息处理器
export function createChineseMessageProcessor() {
return {
messages: chineseMessages,
// 转换错误消息为中文
translateMessage(ruleId, originalMessage) {
return this.messages[ruleId] || originalMessage;
},
// 处理 ESLint 结果
processResults(results) {
return results.map(result => ({
...result,
messages: result.messages.map(message => ({
...message,
message: this.translateMessage(message.ruleId, message.message)
}))
}));
}
};
}
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
"dev:prod": "vite --mode prod", "dev:prod": "vite --mode prod",
"gen-route": "sa gen-route", "gen-route": "sa gen-route",
"lint": "eslint . --fix", "lint": "eslint . --fix",
"lint:chinese": "eslint . --fix --format=./eslint-chinese-formatter.js",
"prepare": "simple-git-hooks", "prepare": "simple-git-hooks",
"preview": "vite preview", "preview": "vite preview",
"release": "sa release", "release": "sa release",
...@@ -98,6 +99,7 @@ ...@@ -98,6 +99,7 @@
"@vitejs/plugin-vue": "5.1.1", "@vitejs/plugin-vue": "5.1.1",
"@vitejs/plugin-vue-jsx": "4.0.0", "@vitejs/plugin-vue-jsx": "4.0.0",
"eslint": "9.8.0", "eslint": "9.8.0",
"eslint-plugin-chinese": "^0.1.0",
"eslint-plugin-vue": "9.27.0", "eslint-plugin-vue": "9.27.0",
"lint-staged": "15.2.7", "lint-staged": "15.2.7",
"pnpm": "^9.7.1", "pnpm": "^9.7.1",
......
...@@ -156,6 +156,9 @@ importers: ...@@ -156,6 +156,9 @@ importers:
eslint: eslint:
specifier: 9.8.0 specifier: 9.8.0
version: 9.8.0 version: 9.8.0
eslint-plugin-chinese:
specifier: ^0.1.0
version: 0.1.0(eslint@9.8.0)
eslint-plugin-vue: eslint-plugin-vue:
specifier: 9.27.0 specifier: 9.27.0
version: 9.27.0(eslint@9.8.0) version: 9.27.0(eslint@9.8.0)
...@@ -2971,6 +2974,12 @@ packages: ...@@ -2971,6 +2974,12 @@ packages:
eslint-parser-plain@0.1.0: eslint-parser-plain@0.1.0:
resolution: {integrity: sha512-oOeA6FWU0UJT/Rxc3XF5Cq0nbIZbylm7j8+plqq0CZoE6m4u32OXJrR+9iy4srGMmF6v6pmgvP1zPxSRIGh3sg==} resolution: {integrity: sha512-oOeA6FWU0UJT/Rxc3XF5Cq0nbIZbylm7j8+plqq0CZoE6m4u32OXJrR+9iy4srGMmF6v6pmgvP1zPxSRIGh3sg==}
eslint-plugin-chinese@0.1.0:
resolution: {integrity: sha512-pAwMAZ15Pn8+VuXad0eOattKLwymQt6OfZ+UYMBiPAVM6LzlsnadcoToIu1bNizxVOxkGe7I7s7kIcX6KCT2bA==}
engines: {node: ^14.17.0 || ^16.0.0 || >= 18.0.0}
peerDependencies:
eslint: '>=7'
eslint-plugin-es-x@7.8.0: eslint-plugin-es-x@7.8.0:
resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
...@@ -4680,6 +4689,10 @@ packages: ...@@ -4680,6 +4689,10 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
requireindex@1.2.0:
resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==}
engines: {node: '>=0.10.5'}
resolve-from@4.0.0: resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'} engines: {node: '>=4'}
...@@ -8510,6 +8523,14 @@ snapshots: ...@@ -8510,6 +8523,14 @@ snapshots:
eslint-parser-plain@0.1.0: {} eslint-parser-plain@0.1.0: {}
eslint-plugin-chinese@0.1.0(eslint@9.8.0):
dependencies:
eslint: 9.8.0
requireindex: 1.2.0
vue-eslint-parser: 9.4.3(eslint@9.8.0)
transitivePeerDependencies:
- supports-color
eslint-plugin-es-x@7.8.0(eslint@9.8.0): eslint-plugin-es-x@7.8.0(eslint@9.8.0):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0) '@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
...@@ -10373,6 +10394,8 @@ snapshots: ...@@ -10373,6 +10394,8 @@ snapshots:
require-directory@2.1.1: {} require-directory@2.1.1: {}
requireindex@1.2.0: {}
resolve-from@4.0.0: {} resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {} resolve-pkg-maps@1.0.0: {}
......
...@@ -4,9 +4,9 @@ import '@wangeditor/editor/dist/css/style.css'; ...@@ -4,9 +4,9 @@ import '@wangeditor/editor/dist/css/style.css';
import VueGridLayout from 'vue-grid-layout'; import VueGridLayout from 'vue-grid-layout';
import MateChat from '@matechat/core'; import MateChat from '@matechat/core';
import '@devui-design/icons/icomoon/devui-icon.css'; import '@devui-design/icons/icomoon/devui-icon.css';
import axios from 'axios';
import * as echarts from 'echarts/core'; import * as echarts from 'echarts/core';
import WangEditor from 'wangeditor'; import WangEditor from 'wangeditor';
import axiosInstance from '@/service/request/axios';
import './plugins/assets'; import './plugins/assets';
import { localStg } from '@/utils/storage'; import { localStg } from '@/utils/storage';
// main.js or main.ts // main.js or main.ts
...@@ -64,7 +64,7 @@ async function setupApp() { ...@@ -64,7 +64,7 @@ async function setupApp() {
setupDayjs(); setupDayjs();
// 全局注册工具库到 window 对象,供外部组件使用 // 全局注册工具库到 window 对象,供外部组件使用
(window as any).$axios = axios; (window as any).$axios = axiosInstance;
(window as any).$echarts = echarts; (window as any).$echarts = echarts;
(window as any).$WangEditor = WangEditor; (window as any).$WangEditor = WangEditor;
......
/* eslint-disable no-plusplus */ /* eslint-disable no-plusplus */
import { truncate } from 'node:fs/promises'; // import { truncate } from 'node:fs/promises'; // 移除未使用的导入
import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types'; import type { CustomRoute, ElegantConstRoute, ElegantRoute } from '@elegant-router/types';
import { getRootMenu } from '@/service/api'; import { getRootMenu } from '@/service/api';
import { useRouteStore } from '@/store/modules/route'; import { useRouteStore } from '@/store/modules/route';
......
import axios from 'axios';
import type { AxiosInstance, AxiosRequestConfig } from 'axios';
import { localStg } from '@/utils/storage';
import { getServiceBaseURL } from '@/utils/service';
// 创建支持代理的 axios 实例
const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y';
const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy);
// 创建 axios 实例
const axiosInstance: AxiosInstance = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// 请求拦截器
axiosInstance.interceptors.request.use(
(config: AxiosRequestConfig) => {
// 添加 token
const token = localStg.get('token');
if (token && config.headers) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
axiosInstance.interceptors.response.use(
response => {
return response;
},
error => {
// 处理错误
if (error.response?.status === 401) {
// Token 过期,清除本地存储
localStg.remove('token');
localStg.remove('refreshToken');
localStg.remove('userInfo');
// 可以在这里添加跳转到登录页的逻辑
}
return Promise.reject(error);
}
);
export default axiosInstance;
import axiosInstance from '@/service/request/axios';
import { request } from '@/service/request';
// 导出封装的axios实例(支持代理)
export const axios = axiosInstance;
// 导出项目封装的request实例
export { request };
// 便捷的HTTP方法
export const http = {
get: axiosInstance.get.bind(axiosInstance),
post: axiosInstance.post.bind(axiosInstance),
put: axiosInstance.put.bind(axiosInstance),
delete: axiosInstance.delete.bind(axiosInstance),
patch: axiosInstance.patch.bind(axiosInstance),
request: axiosInstance.request.bind(axiosInstance)
};
// 默认导出axios实例
export default axiosInstance;
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, defineProps, onMounted, watch } from 'vue'; import { computed, defineProps, onMounted, reactive, ref, watch, withDefaults } from 'vue';
// 只保留kvid的prop,移除formModel和availabilityData // 定义Item对象的接口
const props = defineProps({ interface ItemProps {
kvid: { Kvid?: string;
type: String, [key: string]: any;
default: '' }
}
});
// 定义props
const props = withDefaults(
defineProps<{
item?: ItemProps;
}>(),
{
item: () => ({})
}
);
// 主体框架内置的第三方方法通过window对象暴露、供外部组件使用 // 主体框架内置的第三方方法通过window对象暴露、供外部组件使用
const axios = (window as any).$axios; const axios = (window as any).$axios;
// 获取主题状态 // 获取主题状态
...@@ -23,97 +30,122 @@ const legendTextColor = computed(() => (isDarkMode.value ? 'rgba(255, 255, 255, ...@@ -23,97 +30,122 @@ const legendTextColor = computed(() => (isDarkMode.value ? 'rgba(255, 255, 255,
const borderColor = computed(() => (isDarkMode.value ? '#303030' : '#ffffff')); const borderColor = computed(() => (isDarkMode.value ? '#303030' : '#ffffff'));
// CSS变量 // CSS变量
const cssVars = computed(() => ({ const cssVars = computed(() => ({
'--text-color': textColor.value, '--text-color': textColor.value,
'--bg-color': isDarkMode.value ? '#1f1f1f' : '#f5f5f5', '--bg-color': isDarkMode.value ? '#1f1f1f' : '#f5f5f5',
'--panel-bg': isDarkMode.value ? '#262626' : '#ffffff', '--panel-bg': isDarkMode.value ? '#262626' : '#ffffff',
'--card-bg': isDarkMode.value ? '#303030' : '#ffffff', '--card-bg': isDarkMode.value ? '#303030' : '#ffffff',
'--border-color': isDarkMode.value ? '#434343' : '#e8e8e8' '--border-color': isDarkMode.value ? '#434343' : '#e8e8e8'
})); }));
// 实验室名称相关状态
const labNameOptions = ref<Array<{ label: string; value: string }>>([]);
// 存储 API 获取的实验室完整数据(包含地址等信息)
const apiLabOptions = ref<Array<{
label: string;
value: string;
address?: string;
kvid?: string;
}>>([]);
const searchInputValue = ref('');
const loading = ref(false); const loading = ref(false);
// 后缀列表,用于生成模糊匹配选项
const suffixList = ['实验室', '研究中心', '技术研究所'];
// 存储API返回的详细数据 // 存储API返回的详细数据
const detailedData = ref<any>({}); const detailedData = reactive<ItemProps>({});
// 存储原始数据,用于比较是否有变化
const originalData = ref<ItemProps>({});
// 记录当前已加载的kvid,避免重复请求
const loadedKvid = ref('');
// 创建一个通用的日期处理函数 // 创建一个通用的日期处理函数
const createDateComputed = (getter: () => string | null, setter: (val: string | null) => void) => { const createDateComputed = (getter: () => string | null, setter: (val: string | null) => void) => {
return computed({ return computed({
get: () => { get: () => {
const value = getter() const value = getter();
return value ? new Date(value) : null return value ? new Date(value).getTime() : null;
}, },
set: (val: any) => { set: (val: any) => {
if (!val) { if (!val) {
setter(null) setter(null);
return return;
} }
try { try {
// 确保val是Date对象 // 确保val是数字时间戳或Date对象
const dateVal = val instanceof Date ? val : new Date(val) let dateVal: Date;
if (typeof val === 'number') {
dateVal = new Date(val);
} else if (val instanceof Date) {
dateVal = val;
} else {
dateVal = new Date(val);
}
// 检查是否是有效日期 // 检查是否是有效日期
if (!isNaN(dateVal.getTime())) { if (!isNaN(dateVal.getTime())) {
// 格式化为ISO字符串 // 格式化为ISO字符串
const formattedDate = dateVal.toISOString() const formattedDate = dateVal.toISOString();
setter(formattedDate) setter(formattedDate);
} else { } else {
console.warn('无效的日期值:', val) console.warn('无效的日期值:', val);
setter(null) setter(null);
} }
} catch (err) { } catch (err) {
console.error('日期处理错误:', err) console.error('日期处理错误:', err);
setter(null) setter(null);
} }
} }
}) });
} };
// 创建处理后的DealDate计算属性
const formattedDealDate = createDateComputed(
() => detailedData.DealDate,
val => (detailedData.DealDate = val)
);
// 创建处理后的CreateTime计算属性 // 比较数据是否有变化的辅助函数
const formattedCreateTime = createDateComputed( const hasDataChanged = (): { changed: boolean; changedFields: Partial<ItemProps> } => {
() => detailedData.value.CreateTime, const changedFields: Partial<ItemProps> = {};
(val) => detailedData.value.CreateTime = val let changed = false;
)
// 比较关键字段
const fieldsToCheck = [
'SampleName',
'SampleManufacturerAddress',
'SampleManufacturerContactName',
'SampleManufacturerContactNumber',
'DealDate',
'Remark'
];
fieldsToCheck.forEach(field => {
const currentValue = detailedData[field];
const originalValue = originalData.value[field];
if (currentValue !== originalValue) {
changedFields[field] = currentValue;
changed = true;
}
});
return { changed, changedFields };
};
// 获取详情数据 // 获取详情数据
const fetchDetail = async (kvid: string) => { const fetchDetail = async (kvid: string) => {
if (!kvid) return; if (!kvid) return;
console.log(`准备获取详情数据,Kvid: ${kvid},当前loadedKvid: ${loadedKvid.value}`); console.log(`准备获取详情数据,Kvid: ${kvid},当前loadedKvid: ${detailedData.Kvid}`);
if (kvid === loadedKvid.value && Object.keys(detailedData.value).length > 0) { if (kvid === detailedData.Kvid && Object.keys(detailedData).length > 0) {
console.log('跳过重复请求,使用已缓存的数据'); console.log('跳过重复请求,使用已缓存的数据');
return; return;
} }
loading.value = true; loading.value = true;
try { try {
console.log('开始获取详情数据,Kvid:', kvid); console.log('开始获取详情数据,Kvid:', kvid);
// 调用接口获取详情数据 // 调用接口获取详情数据
const response = await axios.get(`/Restful/Kivii.Lims.Entities.Report/Read.json?Kvid=${kvid}`); const response = await axios.get(`/Restful/Kivii.Lims.Entities.Report/Read.json?Kvid=${kvid}`);
if (response.data) { if (response.data) {
// 直接赋值给detailedData // 直接赋值给detailedData
detailedData.value = response.data.Result || {}; Object.assign(detailedData, response.data.Result || {});
// 同时更新原始数据副本
originalData.value = JSON.parse(JSON.stringify(response.data.Result || {}));
console.log('获取到详情数据:', response.data.Result); console.log('获取到详情数据:', response.data.Result);
// 更新已加载的kvid
loadedKvid.value = kvid;
message.success('详情数据加载成功'); message.success('详情数据加载成功');
} else { } else {
message.error('获取详情数据失败'); message.error('获取详情数据失败');
...@@ -126,276 +158,222 @@ const fetchDetail = async (kvid: string) => { ...@@ -126,276 +158,222 @@ const fetchDetail = async (kvid: string) => {
} }
}; };
// 监听输入值变化,实时更新选项
watch(searchInputValue, (newVal) => {
if (newVal) {
updateOptions(newVal);
}
});
// 监听kvid的变化,如果有值则获取详情数据
watch(() => props.kvid, (newKvid) => {
if (newKvid && newKvid !== loadedKvid.value) {
loadedKvid.value = newKvid;
fetchDetail(newKvid);
}
}, { immediate: true });
// 更新选项列表,包括API数据和模糊匹配
const updateOptions = (query: any) => {
console.log('更新选项:', query);
// 确保 query 是字符串类型
const queryStr = typeof query === 'string' ? query : '';
// 首先基于API获取的数据进行过滤
const filteredApiOptions = apiLabOptions.value.filter(item =>
!queryStr || item.label.toLowerCase().includes(queryStr.toLowerCase())
);
// 创建带后缀的选项列表
const combinedOptions: Array<{ label: string; value: string }> = [];
// 对于每个过滤后的API选项,生成带有不同后缀的选项
filteredApiOptions.forEach(item => {
// 添加原始选项
combinedOptions.push({
label: item.label,
value: item.value
});
// 为每个选项添加后缀变体
suffixList.forEach(suffix => {
const newLabel = `${item.label}${suffix}`;
combinedOptions.push({
label: newLabel,
value: newLabel
});
});
});
console.log('更新后的选项:', combinedOptions);
labNameOptions.value = combinedOptions;
};
// 获取实验室数据 // 监听props.item变化,同步到detailedData
const fetchLabData = async () => { watch(
loading.value = true; () => props.item,
try { newItem => {
console.log('开始获取检测机构数据'); if (newItem && newItem.Kvid && newItem.Kvid !== detailedData.Kvid) {
// 第一步:获取检测机构数据 fetchDetail(newItem.Kvid);
const orgResponse = await axios.post(
'/Restful/Kivii.Organizations.Entities.Organization/Query.json',
{ InternalCode: "检测机构" }
);
console.log('检测机构数据响应:', orgResponse.data);
if (!orgResponse.data?.Results || orgResponse.data.Results.length === 0) {
message.error('未找到检测机构数据');
loading.value = false;
return;
} }
// 保持detailedData为props.item的响应式副本
Object.assign(detailedData, newItem || {});
// 同时更新原始数据副本
originalData.value = JSON.parse(JSON.stringify(newItem || {}));
},
{ immediate: true, deep: true }
);
const orgKvid = orgResponse.data.Results[0].Kvid;
console.log('获取到检测机构Kvid:', orgKvid);
// 第二步:根据ParentKvid获取实验室列表
const labResponse = await axios.post(
'/Restful/Kivii.Organizations.Entities.Organization/Query.json',
{ ParentKvid: orgKvid }
);
console.log('实验室数据响应:', labResponse.data);
if (!labResponse.data?.Results) {
message.error('获取实验室列表失败');
loading.value = false;
return;
}
// 转换为下拉框数据格式,同时保存更多信息用于自动填充
const results = labResponse.data.Results || [];
apiLabOptions.value = results.map((item: any) => ({
label: item.Name,
value: item.Name,
address: item.Address || '',
kvid: item.Kvid
}));
// 初始化选项列表
updateOptions('');
// 如果没有数据,显示提示
if (apiLabOptions.value.length === 0) {
message.info('暂无实验室数据');
} else {
message.success(`成功获取${apiLabOptions.value.length}个实验室数据`);
}
} catch (error) {
console.error('获取实验室数据失败', error);
message.error('获取实验室数据失败');
} finally {
loading.value = false;
}
};
// 实验室名称搜索处理
const handleLabSearch = (query: any) => {
console.log('搜索实验室:', query);
// 无论输入是什么,都更新searchInputValue
searchInputValue.value = typeof query === 'string' ? query : '';
// 即使输入为空也更新选项
updateOptions(searchInputValue.value);
};
// 实验室名称选择处理
const handleLabSelect = (value: string) => {
console.log('选择实验室:', value);
if (!value) {
// 如果清空选择,则清空地址
detailedData.value.SampleManufacturerAddress = '';
// 清空时重置选项列表,显示所有可用选项
updateOptions('');
return;
}
// 确保通过多种方式设置实验室名称
detailedData.value.SampleName = value;
// 使用nextTick确保值更新
(window as any).$nextTick && (window as any).$nextTick(() => {
// 再次确认值已设置
console.log('nextTick后检查实验室名称:', detailedData.value.SampleName);
});
// 首先尝试查找完全匹配的实验室
let selected = apiLabOptions.value.find(item => item.value === value);
// 如果没有找到完全匹配的,可能是带后缀的选项
if (!selected) {
// 检查是否是带后缀的选项
for (const item of apiLabOptions.value) {
for (const suffix of suffixList) {
if (value === `${item.label}${suffix}`) {
selected = item;
break;
}
}
if (selected) break;
}
}
// 如果找到匹配的实验室数据且有地址信息,则自动填充地址
if (selected && selected.address) {
console.log('自动填充地址:', selected.address);
detailedData.value.SampleManufacturerAddress = selected.address;
message.success('已自动填充实验室地址');
}
};
// 组件加载时获取实验室数据 // 组件加载时初始化
onMounted(() => { onMounted(() => {
console.log('applicationInformation组件已挂载,开始获取实验室数据'); console.log('applicationInformation组件已挂载');
// 确保detailedData对象已正确初始化 // 确保detailedData对象已正确初始化
if (!detailedData.value) { if (Object.keys(detailedData).length === 0) {
detailedData.value = {}; Object.assign(detailedData, {});
} }
// 如果已有选择的实验室名称,确保它被设置 // 如果已有选择的实验室名称,确保它被设置
if (detailedData.value.SampleName) { if (detailedData.SampleName) {
console.log('已有实验室名称:', detailedData.value.SampleName); console.log('已有实验室名称:', detailedData.SampleName);
} }
fetchLabData().then(() => {
// 初始化时更新一次选项列表,显示所有可用选项
updateOptions('');
});
}); });
// 创建接口验证和提交方法 // 创建实体方法
const validateAndSave = async () => { const createEntity = async () => {
// 验证必填字段 // 验证:Kvid不存在时才创建
console.log('验证实验室名称:', detailedData.value.SampleName); if (detailedData.Kvid) {
console.log('实体已存在,无需创建');
// 尝试从DOM中获取实验室名称的值 return { success: true, data: detailedData };
const selectElement = document.querySelector('input[placeholder="请输入或选择实验室名称"]');
if (selectElement && (selectElement as HTMLInputElement).value && !detailedData.value.SampleName) {
// 如果DOM中有值但detailedData中没有,则手动设置
detailedData.value.SampleName = (selectElement as HTMLInputElement).value;
console.log('从DOM获取并设置实验室名称:', detailedData.value.SampleName);
} }
if (!detailedData.value.SampleName) { try {
message.error('请输入实验室名称'); loading.value = true;
return false;
// 构建全量数据的请求参数
// 先用detailedData的数据作为基础
const Item = { ...detailedData };
// 然后检查特定字段,如果为空则使用默认值
if (!Item.SupplierName) {
Item.SupplierName = '江苏省纺织产品质量监督检验研究院';
}
if (!Item.PayeeName) {
Item.PayeeName = '江苏省纺织产品质量监督检验研究院';
}
if (!Item.DemanderName) {
Item.DemanderName = (window as any).$KiviiContext?.CurrentMember?.OrganizationName || '默认组织名称';
}
if (!Item.DemanderKvid) {
Item.DemanderKvid = (window as any).$KiviiContext?.CurrentMember?.OrganizationKvid || '';
}
if (!Item.OperatorName) {
Item.OperatorName = (window as any).$KiviiContext?.CurrentMember?.FullName || '默认用户';
}
if (!Item.OperatorKvid) {
Item.OperatorKvid = (window as any).$KiviiContext?.CurrentMember?.Kvid || '';
}
if (Item.TestNeedJudge === undefined || Item.TestNeedJudge === null) {
Item.TestNeedJudge = true;
}
if (!Item.Quantity) {
Item.Quantity = '1';
}
if (Item.Status === undefined || Item.Status === null) {
Item.Status = 0;
}
if (!Item.SampleManufacturerName) {
Item.SampleManufacturerName = (window as any).$KiviiContext?.CurrentMember?.DepartmentName || '默认部门';
}
if (!Item.SampleManufacturerKvid) {
Item.SampleManufacturerKvid = (window as any).$KiviiContext?.CurrentMember?.DepartmentKvid || '';
}
if (!Item.TemplateName) {
Item.TemplateName = '默认模板';
}
const requestData = { Item };
console.log('创建实体请求数据:', requestData);
// 调用创建接口
const response = await axios.post('/Restful/Kivii.Lims.Entities.Report/Create.json', requestData);
if (response.data.Results.length > 0) {
// 保存返回的Kvid
if (response.data.Results[0].Kvid) {
Object.assign(detailedData, response.data.Results[0]);
}
console.log('实体创建成功:', response.data);
message.success('实体创建成功');
return { success: true, data: response.data };
}
// 处理错误情况
console.error('创建失败:', response.data);
message.error(response.data?.Message || '创建失败,请重试');
return { success: false, error: response.data?.Message || '创建失败' };
} catch (error) {
console.error('创建实体失败:', error);
message.error('系统错误,请稍后重试');
return { success: false, error: '系统错误' };
} finally {
loading.value = false;
} }
};
// 如果已经有Kvid,直接返回成功,不需要调用Create接口
if (detailedData.value.Kvid || props.kvid) { // 更新实体方法
console.log('已存在Kvid,无需创建新记录'); const updateEntity = async (changedFields: Partial<ItemProps> = {}) => {
return true; // 验证:Kvid存在时才更新
if (!detailedData.Kvid) {
console.log('实体不存在,无法更新');
message.error('实体不存在,请先创建');
return { success: false, error: '实体不存在' };
} }
try { try {
// 显示加载状态
loading.value = true; loading.value = true;
// 构建请求参数 // 构建只包含更改字段和Kvid的请求参数
const requestData = { const requestData = {
// 这里是Item参数内容,直接使用detailedData中的数据
Item: { Item: {
"SupplierName": "江苏省纺织产品质量监督检验研究院", Kvid: detailedData.Kvid,
"PayeeName": "江苏省纺织产品质量监督检验研究院", ...changedFields
"DemanderName": (window as any).$KiviiContext?.CurrentMember?.OrganizationName || "默认组织名称",
"DemanderKvid": (window as any).$KiviiContext?.CurrentMember?.OrganizationKvid || "",
"OperatorName": (window as any).$KiviiContext?.CurrentMember?.FullName || "默认用户",
"OperatorKvid": (window as any).$KiviiContext?.CurrentMember?.Kvid || "",
"TestNeedJudge": true,
"Quantity": "1",
"DealDate": detailedData.value.CreateTime,
"Status": 0,
"SampleManufacturerName": (window as any).$KiviiContext?.CurrentMember?.DepartmentName || "默认部门",
"SampleManufacturerKvid": (window as any).$KiviiContext?.CurrentMember?.DepartmentKvid || "",
"SampleManufacturerContactName": detailedData.value.SampleManufacturerContactName,
"SampleManufacturerContactNumber": detailedData.value.SampleManufacturerContactNumber,
"SampleManufacturerAddress": detailedData.value.SampleManufacturerAddress,
"SampleName": detailedData.value.SampleName,
"TemplateName": "默认模板",
"Remark": detailedData.value.Remark
} }
}; };
// 调用创建接口 console.log('更新实体请求数据:', requestData);
const response = await axios.post('/Restful/Kivii.Lims.Entities.Report/Create.json', requestData);
// 调用更新接口
if (response.data) { const response = await axios.post('/Restful/Kivii.Lims.Entities.Report/Update.json', requestData);
if (response.data.Results.length > 0) {
// 保存返回的Kvid // 保存返回的Kvid
if (response.data.Kvid) { if (response.data.Results[0].Kvid) {
detailedData.value.Kvid = response.data.Kvid; // 更新本地数据
// 更新已加载的kvid,避免重复请求 Object.assign(detailedData, response.data.Results[0]);
loadedKvid.value = response.data.Kvid;
} }
message.success('申请信息保存成功'); console.log('实体更新成功:', response.data);
return true; message.success('实体更新成功');
} else { return { success: true, data: response.data };
// 处理错误情况
message.error(response.data?.Message || '保存失败,请重试');
return false;
} }
// 处理错误情况
console.error('更新失败:', response.data);
message.error(response.data?.Message || '更新失败,请重试');
return { success: false, error: response.data?.Message || '更新失败' };
} catch (error) { } catch (error) {
console.error('保存申请信息失败:', error); console.error('更新实体失败:', error);
message.error('系统错误,请稍后重试'); message.error('系统错误,请稍后重试');
return false; return { success: false, error: '系统错误' };
} finally { } finally {
// 隐藏加载状态
loading.value = false; loading.value = false;
} }
}; };
// 智能验证和保存方法(统一入口)
const validateAndSave = async () => {
console.log('开始验证和保存数据...');
// 1. 验证必填字段
console.log('验证实验室名称:', detailedData.SampleName);
if (!detailedData.SampleName) {
message.error('请输入实验室名称');
return false;
}
// 2. 判断操作类型
const hasKvid = Boolean(detailedData.Kvid);
if (!hasKvid) {
// 没有Kvid,需要创建
console.log('检测到无Kvid,执行创建操作');
const result = await createEntity();
if (result.success) {
// 创建成功后更新原始数据副本
originalData.value = JSON.parse(JSON.stringify(detailedData));
}
return result.success;
} else {
// 有Kvid,检查是否有数据变化
const { changed, changedFields } = hasDataChanged();
if (!changed) {
// 没有变化,直接返回成功
console.log('数据没有变化,无需更新');
message.success('数据已是最新,无需保存');
return true;
} else {
// 有变化,需要更新
console.log('检测到数据变化,执行更新操作,变更字段:', changedFields);
const result = await updateEntity(changedFields);
if (result.success) {
// 更新成功后更新原始数据副本
originalData.value = JSON.parse(JSON.stringify(detailedData));
}
return result.success;
}
}
};
// 导出方法供父组件调用 // 导出方法供父组件调用
defineExpose({ defineExpose({
validateAndSave, validateAndSave,
...@@ -408,95 +386,81 @@ defineExpose({ ...@@ -408,95 +386,81 @@ defineExpose({
<!-- 实验室信息 --> <!-- 实验室信息 -->
<div class="section-title"> <div class="section-title">
<svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="currentColor" d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z"></path> <path
fill="currentColor"
d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z"
></path>
</svg> </svg>
<span>实验室信息</span> <span>实验室信息</span>
</div> </div>
<div style="display: flex; width: 100%;"> <div style="display: flex; width: 100%">
<n-form-item path="labName" label="实验室名称" style="width: 50%;" > <NFormItem path="SampleName" label="实验室名称" style="width: 50%">
<n-select <NInput
v-model:value="detailedData.SampleName" v-model:value="detailedData.SampleName"
:options="labNameOptions" placeholder="请输入实验室名称"
:loading="loading" clearable
placeholder="请输入或选择实验室名称" size="medium"
clearable />
filterable </NFormItem>
@update:value="handleLabSelect"
@search="handleLabSearch" <NFormItem path="labAddress" label="实验室地址" style="width: 50%">
@clear="() => updateOptions('')" <NInput v-model:value="detailedData.SampleManufacturerAddress" placeholder="请输入实验室详细地址"></NInput>
remote </NFormItem>
:remote-props="{ loading }" </div>
size="medium" <NDivider></NDivider>
>
<template #empty>
<n-empty description="暂无实验室数据" size="small" ></n-empty>
</template>
</n-select>
<!-- 添加一个隐藏的输入框,确保数据绑定 -->
<input type="hidden" :value="detailedData.SampleName" />
</n-form-item>
<n-form-item path="labAddress" label="实验室地址" style="width: 50%;">
<n-input
v-model:value="detailedData.SampleManufacturerAddress"
placeholder="请输入实验室详细地址"
></n-input>
</n-form-item>
</div>
<n-divider ></n-divider>
<!-- 联系人信息 --> <!-- 联系人信息 -->
<div class="section-title"> <div class="section-title">
<svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="currentColor" d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"></path> <path
fill="currentColor"
d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"
></path>
</svg> </svg>
<span>联系人信息</span> <span>联系人信息</span>
</div> </div>
<div style="display: flex; width: 100%;"> <div style="display: flex; width: 100%">
<n-form-item path="contactName" label="联系人姓名" style="width: 50%;"> <NFormItem path="contactName" label="联系人姓名" style="width: 50%">
<n-input <NInput v-model:value="detailedData.SampleManufacturerContactName" placeholder="请输入联系人姓名"></NInput>
v-model:value="detailedData.SampleManufacturerContactName" </NFormItem>
placeholder="请输入联系人姓名"
></n-input> <NFormItem path="contactPhone" label="联系电话" style="width: 50%">
</n-form-item> <NInput v-model:value="detailedData.SampleManufacturerContactNumber" placeholder="请输入联系电话"></NInput>
</NFormItem>
<n-form-item path="contactPhone" label="联系电话" style="width: 50%;">
<n-input
v-model:value="detailedData.SampleManufacturerContactNumber"
placeholder="请输入联系电话"
></n-input>
</n-form-item>
</div> </div>
<n-divider ></n-divider> <NDivider></NDivider>
<!-- 申请时间 --> <!-- 申请时间 -->
<div class="section-title"> <div class="section-title">
<svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> <svg class="section-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="currentColor" d="M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M17,12H12V17H17V12Z"></path> <path
fill="currentColor"
d="M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M17,12H12V17H17V12Z"
></path>
</svg> </svg>
<span>申请时间设置</span> <span>申请时间设置</span>
</div> </div>
<n-form-item path="applyDate" label="申请时间"> <NFormItem path="applyDate" label="申请时间">
<n-date-picker <NDatePicker
v-model:value="formattedCreateTime" v-model:value="formattedDealDate"
type="datetime" type="datetime"
clearable clearable
style="width: 100%" style="width: 100%"
format="yyyy-MM-dd HH:mm" format="yyyy-MM-dd HH:mm"
:disabled-date="(ts: number) => ts < Date.now() - 86400000" :disabled-date="(ts: number) => ts < Date.now() - 86400000"
></n-date-picker> ></NDatePicker>
</n-form-item> </NFormItem>
<n-form-item path="remarks" label="备注说明" class="last-form-item"> <NFormItem path="remarks" label="备注说明" class="last-form-item">
<n-input <NInput
v-model:value="detailedData.Remark" v-model:value="detailedData.Remark"
type="textarea" type="textarea"
placeholder="如有特殊需求,请在此说明" placeholder="如有特殊需求,请在此说明"
:autosize="{ minRows: 3, maxRows: 5 }" :autosize="{ minRows: 3, maxRows: 5 }"
></n-input> ></NInput>
</n-form-item> </NFormItem>
</div> </div>
</template> </template>
......
...@@ -15,10 +15,10 @@ interface ItemProps { ...@@ -15,10 +15,10 @@ interface ItemProps {
// 定义props // 定义props
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
Item?: ItemProps; item?: ItemProps;
}>(), }>(),
{ {
Item: () => ({}) item: () => ({})
} }
); );
...@@ -55,32 +55,20 @@ const cssVars = computed(() => ({ ...@@ -55,32 +55,20 @@ const cssVars = computed(() => ({
})); }));
// 当前步骤 // 当前步骤
const currentStep = ref(3); const currentStep = ref(0);
// 加载状态 // 加载状态
const loading = ref(false); const loading = ref(false);
// 表单数据 // 表单数据 - 直接使用props.item作为formModel
const formModel = reactive({ const formModel = reactive<ItemProps>({});
labName: '',
labAddress: '',
contactName: '',
contactPhone: '',
contactEmail: '',
applyDate: null as number | null,
applyTime: null as number | null,
remarks: '',
Kvid: ''
});
// 监听传入的Item变化 // 监听props.item变化,同步到formModel
watch( watch(
() => props.Item, () => props.item,
newItem => { newItem => {
if (newItem && newItem.Kvid) { // 保持formModel为props.item的响应式副本
// 将传入的Item中的Kvid设置到formModel中 Object.assign(formModel, newItem || {});
formModel.Kvid = newItem.Kvid;
}
}, },
{ immediate: true, deep: true } { immediate: true, deep: true }
); );
...@@ -108,7 +96,7 @@ const testItems = ref([ ...@@ -108,7 +96,7 @@ const testItems = ref([
// 验证规则 // 验证规则
const rules = { const rules = {
labName: { SampleName: {
required: true, required: true,
trigger: ['blur', 'input'], trigger: ['blur', 'input'],
message: '请输入实验室名称' message: '请输入实验室名称'
...@@ -132,12 +120,12 @@ function nextStep() { ...@@ -132,12 +120,12 @@ function nextStep() {
if (currentStep.value < 4) { if (currentStep.value < 4) {
// 如果当前是第一步(申请信息填写),则调用子组件的创建接口 // 如果当前是第一步(申请信息填写),则调用子组件的创建接口
if (currentStep.value === 0) { if (currentStep.value === 0) {
// 如果已经存在Kvid(从props的Item或formModel中),则直接进入下一步 // // 如果已经存在Kvid(从props的Item或formModel中),则直接进入下一步
if ((props.Item && props.Item.Kvid) || formModel.Kvid) { // if ((props.item && props.item.Kvid) || formModel.Kvid) {
console.log('已存在Kvid,直接进入下一步'); // console.log('已存在Kvid,直接进入下一步');
currentStep.value += 1; // currentStep.value += 1;
return; // return;
} // }
// 使用ref获取到的组件实例 // 使用ref获取到的组件实例
if (appInfoRef.value && typeof appInfoRef.value.validateAndSave === 'function') { if (appInfoRef.value && typeof appInfoRef.value.validateAndSave === 'function') {
...@@ -145,7 +133,9 @@ function nextStep() { ...@@ -145,7 +133,9 @@ function nextStep() {
appInfoRef.value.validateAndSave().then((success: boolean) => { appInfoRef.value.validateAndSave().then((success: boolean) => {
if (success) { if (success) {
// 保存成功后进入下一步,并获取返回的Kvid // 保存成功后进入下一步,并获取返回的Kvid
formModel.Kvid = appInfoRef.value.detailedData.value.Kvid || ''; Object.assign(formModel, appInfoRef.value.detailedData);
// formModel = { ...appInfoRef.value.detailedData };
// formModel.Kvid = appInfoRef.value.detailedData.Kvid || '';
currentStep.value += 1; currentStep.value += 1;
} }
}); });
...@@ -225,19 +215,13 @@ function saveApplication(submit = true) { ...@@ -225,19 +215,13 @@ function saveApplication(submit = true) {
<NForm <NForm
ref="formRef" ref="formRef"
:model="formModel" :model="formModel"
:rules="rules"
label-placement="left" label-placement="left"
label-width="120px" label-width="120px"
require-mark-placement="right-hanging" require-mark-placement="right-hanging"
size="medium" size="medium"
> >
<!-- 步骤1: 申请信息填写 --> <!-- 步骤1: 申请信息填写 -->
<ApplicationInformation <ApplicationInformation v-if="currentStep === 0" ref="appInfoRef" :item="formModel"></ApplicationInformation>
v-if="currentStep === 0"
ref="appInfoRef"
:Kvid="formModel.Kvid"
:item="props.Item"
></ApplicationInformation>
<!-- 步骤2: 审核文件提交 --> <!-- 步骤2: 审核文件提交 -->
<FileReview <FileReview
......
...@@ -290,7 +290,7 @@ const handleCloseAndRefresh = () => { ...@@ -290,7 +290,7 @@ const handleCloseAndRefresh = () => {
<template> <template>
<div> <div>
<MainSupplier v-if="showMainSupplier" :Item="currentItem" @close-and-refresh="handleCloseAndRefresh"></MainSupplier> <MainSupplier v-if="showMainSupplier" :item="currentItem" @close-and-refresh="handleCloseAndRefresh"></MainSupplier>
<NSpace v-if="!showMainSupplier" vertical size="large" style="padding: 20px"> <NSpace v-if="!showMainSupplier" vertical size="large" style="padding: 20px">
<!-- 统计卡片区域 --> <!-- 统计卡片区域 -->
<NGrid cols="4" x-gap="20" responsive="screen" :collapsed-rows="1"> <NGrid cols="4" x-gap="20" responsive="screen" :collapsed-rows="1">
......
...@@ -9,7 +9,7 @@ import SearchFieldEditDrawer from './codet/components/SearchFieldEditDrawer.vue' ...@@ -9,7 +9,7 @@ import SearchFieldEditDrawer from './codet/components/SearchFieldEditDrawer.vue'
import AddFieldDrawer from './codet/components/AddFieldDrawer.vue'; import AddFieldDrawer from './codet/components/AddFieldDrawer.vue';
import DataTransform from './codet/components/DataTransform.vue'; import DataTransform from './codet/components/DataTransform.vue';
import CreateDialog from './codet/components/CreateDialog.vue'; import CreateDialog from './codet/components/CreateDialog.vue';
// 通过window对象使用axios - 已在main.ts中全局注册 // 使用封装的axios实例 - 已在main.ts中全局注册,支持代理
const axios = (window as any).$axios; const axios = (window as any).$axios;
// 定义组件名称,避免与HTML元素冲突 // 定义组件名称,避免与HTML元素冲突
...@@ -494,7 +494,7 @@ async function fetchData() { ...@@ -494,7 +494,7 @@ async function fetchData() {
// 自定义axios请求配置,使用自定义参数序列化函数 // 自定义axios请求配置,使用自定义参数序列化函数
console.log('📡 发送请求到 /table.json'); console.log('📡 发送请求到 /table.json');
const response = await axios.get('/table.json', { const response = await axios.get('/codet/test.json', {
params: searchParams, params: searchParams,
paramsSerializer: (params: any) => { paramsSerializer: (params: any) => {
// 自定义参数序列化,避免数组被转换为key[]=value格式 // 自定义参数序列化,避免数组被转换为key[]=value格式
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment