Commit dcb685b2 by Neo Turing

本地

parents 0b0fa780 37f5db10
......@@ -6,6 +6,10 @@
"eslint.useFlatConfig": true,
"editor.formatOnSave": false,
"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.enabledParsers": ["ts"],
"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 @@
"dev:prod": "vite --mode prod",
"gen-route": "sa gen-route",
"lint": "eslint . --fix",
"lint:chinese": "eslint . --fix --format=./eslint-chinese-formatter.js",
"prepare": "simple-git-hooks",
"preview": "vite preview",
"release": "sa release",
......@@ -98,6 +99,7 @@
"@vitejs/plugin-vue": "5.1.1",
"@vitejs/plugin-vue-jsx": "4.0.0",
"eslint": "9.8.0",
"eslint-plugin-chinese": "^0.1.0",
"eslint-plugin-vue": "9.27.0",
"lint-staged": "15.2.7",
"pnpm": "^9.7.1",
......
......@@ -156,6 +156,9 @@ importers:
eslint:
specifier: 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:
specifier: 9.27.0
version: 9.27.0(eslint@9.8.0)
......@@ -2971,6 +2974,12 @@ packages:
eslint-parser-plain@0.1.0:
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:
resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==}
engines: {node: ^14.18.0 || >=16.0.0}
......@@ -4680,6 +4689,10 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
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:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
......@@ -8510,6 +8523,14 @@ snapshots:
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):
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.8.0)
......@@ -10373,6 +10394,8 @@ snapshots:
require-directory@2.1.1: {}
requireindex@1.2.0: {}
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
......
......@@ -4,9 +4,9 @@ import '@wangeditor/editor/dist/css/style.css';
import VueGridLayout from 'vue-grid-layout';
import MateChat from '@matechat/core';
import '@devui-design/icons/icomoon/devui-icon.css';
import axios from 'axios';
import * as echarts from 'echarts/core';
import WangEditor from 'wangeditor';
import axiosInstance from '@/service/request/axios';
import './plugins/assets';
import { localStg } from '@/utils/storage';
// main.js or main.ts
......@@ -64,7 +64,7 @@ async function setupApp() {
setupDayjs();
// 全局注册工具库到 window 对象,供外部组件使用
(window as any).$axios = axios;
(window as any).$axios = axiosInstance;
(window as any).$echarts = echarts;
(window as any).$WangEditor = WangEditor;
......
/* 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 { getRootMenu } from '@/service/api';
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;
......@@ -15,10 +15,10 @@ interface ItemProps {
// 定义props
const props = withDefaults(
defineProps<{
Item?: ItemProps;
item?: ItemProps;
}>(),
{
Item: () => ({})
item: () => ({})
}
);
......@@ -55,32 +55,20 @@ const cssVars = computed(() => ({
}));
// 当前步骤
const currentStep = ref(3);
const currentStep = ref(0);
// 加载状态
const loading = ref(false);
// 表单数据
const formModel = reactive({
labName: '',
labAddress: '',
contactName: '',
contactPhone: '',
contactEmail: '',
applyDate: null as number | null,
applyTime: null as number | null,
remarks: '',
Kvid: ''
});
// 表单数据 - 直接使用props.item作为formModel
const formModel = reactive<ItemProps>({});
// 监听传入的Item变化
// 监听props.item变化,同步到formModel
watch(
() => props.Item,
() => props.item,
newItem => {
if (newItem && newItem.Kvid) {
// 将传入的Item中的Kvid设置到formModel中
formModel.Kvid = newItem.Kvid;
}
// 保持formModel为props.item的响应式副本
Object.assign(formModel, newItem || {});
},
{ immediate: true, deep: true }
);
......@@ -108,7 +96,7 @@ const testItems = ref([
// 验证规则
const rules = {
labName: {
SampleName: {
required: true,
trigger: ['blur', 'input'],
message: '请输入实验室名称'
......@@ -132,12 +120,12 @@ function nextStep() {
if (currentStep.value < 4) {
// 如果当前是第一步(申请信息填写),则调用子组件的创建接口
if (currentStep.value === 0) {
// 如果已经存在Kvid(从props的Item或formModel中),则直接进入下一步
if ((props.Item && props.Item.Kvid) || formModel.Kvid) {
console.log('已存在Kvid,直接进入下一步');
currentStep.value += 1;
return;
}
// // 如果已经存在Kvid(从props的Item或formModel中),则直接进入下一步
// if ((props.item && props.item.Kvid) || formModel.Kvid) {
// console.log('已存在Kvid,直接进入下一步');
// currentStep.value += 1;
// return;
// }
// 使用ref获取到的组件实例
if (appInfoRef.value && typeof appInfoRef.value.validateAndSave === 'function') {
......@@ -145,7 +133,9 @@ function nextStep() {
appInfoRef.value.validateAndSave().then((success: boolean) => {
if (success) {
// 保存成功后进入下一步,并获取返回的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;
}
});
......@@ -225,19 +215,13 @@ function saveApplication(submit = true) {
<NForm
ref="formRef"
:model="formModel"
:rules="rules"
label-placement="left"
label-width="120px"
require-mark-placement="right-hanging"
size="medium"
>
<!-- 步骤1: 申请信息填写 -->
<ApplicationInformation
v-if="currentStep === 0"
ref="appInfoRef"
:Kvid="formModel.Kvid"
:item="props.Item"
></ApplicationInformation>
<ApplicationInformation v-if="currentStep === 0" ref="appInfoRef" :item="formModel"></ApplicationInformation>
<!-- 步骤2: 审核文件提交 -->
<FileReview
......
......@@ -290,7 +290,7 @@ const handleCloseAndRefresh = () => {
<template>
<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">
<!-- 统计卡片区域 -->
<NGrid cols="4" x-gap="20" responsive="screen" :collapsed-rows="1">
......
......@@ -9,7 +9,7 @@ import SearchFieldEditDrawer from './codet/components/SearchFieldEditDrawer.vue'
import AddFieldDrawer from './codet/components/AddFieldDrawer.vue';
import DataTransform from './codet/components/DataTransform.vue';
import CreateDialog from './codet/components/CreateDialog.vue';
// 通过window对象使用axios - 已在main.ts中全局注册
// 使用封装的axios实例 - 已在main.ts中全局注册,支持代理
const axios = (window as any).$axios;
// 定义组件名称,避免与HTML元素冲突
......@@ -494,7 +494,7 @@ async function fetchData() {
// 自定义axios请求配置,使用自定义参数序列化函数
console.log('📡 发送请求到 /table.json');
const response = await axios.get('/table.json', {
const response = await axios.get('/codet/test.json', {
params: searchParams,
paramsSerializer: (params: any) => {
// 自定义参数序列化,避免数组被转换为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