Compare commits
27 Commits
b724969912
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f546564cc | ||
| b6c362d906 | |||
| 76b480ece5 | |||
|
|
76f9657fe9 | ||
| e0d2c7c886 | |||
|
|
df11ca0db6 | ||
|
|
8b587163df | ||
|
|
f1e73f3319 | ||
|
|
ddc715e17c | ||
| 87edf1c76f | |||
| 9100f65325 | |||
|
|
a0192d9567 | ||
|
|
99b9bc8a5b | ||
|
|
c9498c444b | ||
|
|
20a821ba32 | ||
|
|
a61036e5dc | ||
|
|
ba43de8f4b | ||
|
|
96fe85b3be | ||
|
|
3149ffb932 | ||
|
|
05bfa95bfe | ||
|
|
4539bec3a4 | ||
|
|
48cfebd008 | ||
|
|
2cc8329695 | ||
|
|
0f3b5d4371 | ||
|
|
000b87eb92 | ||
|
|
8a4511dc36 | ||
|
|
3eaea3116d |
2
.env
2
.env
@@ -4,7 +4,7 @@
|
||||
VUE_APP_TITLE=D2Admin
|
||||
|
||||
# 网络请求公用地址
|
||||
VUE_APP_API=/api/
|
||||
VUE_APP_API=/background/v1/
|
||||
|
||||
# 仓库地址
|
||||
VUE_APP_REPO=https://github.com/d2-projects/d2-admin
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
|
||||
# 页面 title 前缀
|
||||
VUE_APP_TITLE=D2Admin Dev
|
||||
|
||||
# 后台接口地址(代理目标)
|
||||
VUE_APP_BASE_URL=http://127.0.0.1:8787/background/
|
||||
|
||||
1
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
18.16.0
|
||||
8
.npmrc
Normal file
8
.npmrc
Normal file
@@ -0,0 +1,8 @@
|
||||
# pnpm 配置
|
||||
# 强制通过 pnpm 安装,禁止使用 npm / yarn
|
||||
engine-strict=true
|
||||
# 使用 lockfile 锁定依赖版本
|
||||
lockfile=true
|
||||
# 依赖提升模式(兼容旧项目)
|
||||
shamefully-hoist=true
|
||||
# 允许在 package.json 中通过 packageManager 字段指定包管理器版本
|
||||
493
README.md
493
README.md
@@ -1,192 +1,357 @@
|
||||

|
||||
# MES-UI
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/d2-projects/d2-admin/stargazers" target="_blank"><img src="https://img.shields.io/github/stars/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/network/members" target="_blank"><img src="https://img.shields.io/github/forks/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/issues" target="_blank"><img src="https://img.shields.io/github/issues/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/issues?q=is%3Aissue+is%3Aclosed" target="_blank"><img src="https://img.shields.io/github/issues-closed/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/pulls" target="_blank"><img src="https://img.shields.io/github/issues-pr/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/pulls?q=is%3Apr+is%3Aclosed" target="_blank"><img src="https://img.shields.io/github/issues-pr-closed/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank"><img src="https://img.shields.io/github/last-commit/d2-projects/d2-admin.svg"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank"><img src="https://visitor-badge.glitch.me/badge?page_id=d2-projects.d2-admin"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/releases" target="_blank"><img src="https://img.shields.io/github/release/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://deepscan.io/dashboard#view=project&tid=8014&pid=10161&bid=136697"><img src="https://deepscan.io/api/teams/8014/projects/10161/branches/136697/badge/grade.svg" alt="DeepScan grade"></a>
|
||||
</p>
|
||||
基于 [D2 Admin](https://github.com/d2-projects/d2-admin)(Vue 2.7 + Element UI)的企业级 MES 中后台前端项目。
|
||||
|
||||
[D2Admin](https://github.com/d2-projects/d2-admin) is a fully open source and free enterprise back-end product front-end integration solution, using the latest front-end technology stack, javascript files loading of local first screen less than 60kb, has prepared most of the project preparations, and with a lot of sample code to help the management system agile development.
|
||||
---
|
||||
|
||||
[](https://open.vscode.dev/d2-projects/d2-admin)
|
||||
## 环境要求
|
||||
|
||||
[中文](https://github.com/d2-projects/d2-admin/blob/master/README.zh.md) | **English**
|
||||
本项目通过 **[Volta](https://volta.sh/)** 和 **[pnpm](https://pnpm.io/)** 锁定开发环境版本,确保团队成员使用完全一致的 Node.js 和包管理器版本。
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||
[](https://app.netlify.com/sites/d2-admin/deploys)
|
||||
|
||||
The following access addresses are built and deployed by the latest master branch code at the same time. The access effect is completely consistent. Please select the appropriate access link according to your own network situation.
|
||||
|
||||
| server | link | server |
|
||||
| 工具 | 锁定版本 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| d2.pub | [Link](https://d2.pub/d2-admin/preview) | China server |
|
||||
| github | [Link](https://d2-projects.github.io/d2-admin) | GitHub pages |
|
||||
| netlify | [Link](https://d2-admin.netlify.com) | Netlify CDN |
|
||||
| **Node.js** | `18.16.0` | JavaScript 运行时(`.node-version` 文件) |
|
||||
| **pnpm** | `10.33.0` | 包管理器(`package.json` → `packageManager` 字段) |
|
||||
|
||||
## Document
|
||||
> 配置文件说明:
|
||||
> - `.node-version` → Volta 读取,自动切换 Node.js 版本
|
||||
> - `package.json` → `engines` 字段限制版本范围,`packageManager` 字段锁定 pnpm 版本
|
||||
> - `.npmrc` → `engine-strict=true` 强制使用锁定版本,不满足则安装失败
|
||||
|
||||
[document on https://d2.pub](https://d2.pub/doc/d2-admin/)
|
||||
---
|
||||
|
||||
## Features
|
||||
## 从零开始安装到启动
|
||||
|
||||
* Build with vue-cli3
|
||||
* First screen loading waiting animation
|
||||
* Five themes
|
||||
* Built-in UEditor rich text editor
|
||||
* Detailed documentation
|
||||
* Login and logout
|
||||
* Separate routing and menu settings
|
||||
* Foldable sidebar
|
||||
* Multi-national language
|
||||
* Rich text editor
|
||||
* Markdown editor
|
||||
* full screen
|
||||
* Fontawesome icon library
|
||||
* Icon selector
|
||||
* Automatically register SVG icon
|
||||
* Simulation data
|
||||
* Clipboard package
|
||||
* Chart library
|
||||
* Time and date calculation tool
|
||||
* Import Excel ( xlsx + csv )
|
||||
* Data export Excel ( xlsx + csv )
|
||||
* Data export text
|
||||
* Digital animation
|
||||
* Drag and drop the size of the block layout
|
||||
* Grid layout for drag and resize and position
|
||||
* Out-of-the-box page layout components
|
||||
* Load and parse markdown files
|
||||
* GitHub style markdown display component
|
||||
* markdown internal code highlighting
|
||||
* Expanded Baidu cloud link resolution and optimized display for markdown
|
||||
* Right click menu component
|
||||
* Custom scrollbars and scrolling controls
|
||||
* Common style extraction, convenient theme customization
|
||||
* Support temporary menu configuration
|
||||
* System function display module `1.1.4 +`
|
||||
* Multi-tab mode `1.1.4 +`
|
||||
* Beautify the scroll bar `1.1.4 +`
|
||||
* json view `1.1.4 +`
|
||||
* cookie wrapper `1.1.5 +`
|
||||
* Multi-tab global control API `1.1.5 +`
|
||||
* Menu Global Control API `1.1.5 +`
|
||||
* Multi-tab page close control support right-click menu `1.1.10 +`
|
||||
* Modular global state management `1.2.0 +`
|
||||
* Multiple data persistence methods: distinguish users, distinguish routes, page data snapshot function `1.2.0 +`
|
||||
* Support for menu system that jumps out of external links `1.2.0 +`
|
||||
* Support menu svg icon `1.3.0 +`
|
||||
* Logging and error catching `1.3.0 +`
|
||||
* Global menu search `1.3.0 +`
|
||||
* Custom login redirect `1.3.0 +`
|
||||
* Switch global base component size `1.4.0 +`
|
||||
* Page loading progress bar `1.4.1 +`
|
||||
* Adaptive top menu bar `1.4.7 +`
|
||||
* Support for merging cells when exporting xslx `1.5.4 +`
|
||||
* Multiple tabs support drag and drop sorting `1.8.0 +`
|
||||
* load only local JavaScript code less than 60kb on the homepage `1.8.0 +`
|
||||
* Built in build file volume checking tool `1.8.0 +`
|
||||
* Example of multi page `1.23.0 +`
|
||||
* Split chunks `1.23.0 +`
|
||||
### 第一步:安装 Volta(如果尚未安装)
|
||||
|
||||
## Other synchronous repositories
|
||||
Volta 会根据 `.node-version` 自动安装并切换到项目所需的 Node.js 版本。
|
||||
|
||||
| type | link |
|
||||
| --- | --- |
|
||||
| gitee | [https://gitee.com/d2-projects/d2-admin](https://gitee.com/d2-projects/d2-admin) |
|
||||
| coding | [https://d2-projects.coding.net/p/d2-projects/d/d2-admin/git](https://d2-projects.coding.net/p/d2-projects/d/d2-admin/git) |
|
||||
**Windows(PowerShell):**
|
||||
|
||||
## Other versions
|
||||
|
||||
| Name | HomePage | Preview | Introduction |
|
||||
| --- | --- | --- | --- |
|
||||
| Starter template | [Link](https://github.com/d2-projects/d2-admin-start-kit) | [Link](https://d2.pub/d2-admin-start-kit/preview) | The simplest version |
|
||||
|
||||
## Open source backend implementation
|
||||
|
||||
> The backend is contributed by the open source community. The latest version of D2Admin is not guaranteed. Please contact its open source author for related usage issues.
|
||||
|
||||
| Name | technology | HomePage | Preview | Introduction |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| django-vue-admin-pro | Django | [Link](https://github.com/dvadmin-pro/django-vue-admin-pro) | [Link](http://demo.pro.django-vue-admin.com) | Django + Jwt + D2Admin |
|
||||
| boot-admin | SpringBoot | [Link](https://github.com/hb0730/boot-admin) | [Link](http://admin.hb0730.com/) | Management system based on SpringBoot |
|
||||
| FlaskPermission | Flask | [Link](https://github.com/huguodong/flask-permission) | [Link](http://47.97.218.139:9999) | Permission management based on Flask |
|
||||
| CareyShop | ThinkPHP5 | [Link](https://github.com/dnyz520/careyshop-admin) | [Link](https://demo.careyshop.cn/admin/) | High Performance Mall Framework System for CareyShop |
|
||||
| jiiiiiin-security | Spring Boot | [Link](https://github.com/Jiiiiiin/jiiiiiin-security) | [Link](https://github.com/Jiiiiiin/jiiiiiin-security) | Content management infrastructure projects |
|
||||
| Taroco | Spring Cloud | [Link](https://github.com/liuht777/Taroco) | [Link](http://111.231.192.110/) | Complete microservice enterprise solution |
|
||||
| Aooms | Spring Cloud | [Link](https://gitee.com/cyb-javaer/Aooms) | [Link](https://www.yuboon.com/Aooms) | Extremely fast microservice development, not just as simple as JFinal |
|
||||
| GOA | Beego | [Link](https://github.com/Qsnh/goa) | [Link](http://goaio.vip/) | Online question answering system based on Beego + Vue |
|
||||
| CMDB | Django | [Link](https://github.com/CJFJack/django_vue_cmdb) | [Link](https://mp.weixin.qq.com/s?__biz=MzU1OTYzODA4Mw==&mid=2247484250&idx=1&sn=981024ac0580d8a3eba95742bd32b268) | authority system with dynamic menu |
|
||||
|
||||
## Community projects
|
||||
|
||||
> These projects are contributed by the open source community and are not guaranteed to use the latest version of D2Admin. Please contact their open source authors for related usage questions.
|
||||
|
||||
| Name | HomePage | Preview | Introduction |
|
||||
| --- | --- | --- | --- |
|
||||
| d2-admin-xiya-go-cms | [Link](https://github.com/d2-projects/d2-admin-xiya-go-cms) | [Link](https://d2.pub/d2-admin-xiya-go-cms/preview) | D2Admin + authority system + dynamic router |
|
||||
| d2-advance | [Link](https://github.com/d2-projects/d2-advance) | [Link](https://d2.pub/d2-advance/preview) | Technical exploration inspired by D2Admin |
|
||||
| d2-crud-plus | [Link](https://github.com/greper/d2-crud-plus) | [Link](http://qiniu.veryreader.com/D2CrudPlusExample/index.html) | Easy development of crud function |
|
||||
| d2-crud | [Link](https://github.com/d2-projects/d2-crud) | [Link]() | Encapsulation of common operations in tables |
|
||||
| d2-admin-pm | [Link](https://github.com/wjkang/d2-admin-pm) | [Link](http://jaycewu.coding.me/d2-admin-pm) | RBAC privilege management solution based on D2Admin |
|
||||
| LanBlog | [Link](https://github.com/sinksmell/LanBlog) | [Link](http://47.101.222.133/) | Vue + Beego restful api personal blog system |
|
||||
| d2-admin-start-kit-plus | [Link](https://github.com/hank-cp/d2-admin-start-kit-plus) | [Link](https://github.com/hank-cp/d2-admin-start-kit-plus) | D2Admin Start kit modular version |
|
||||
| d2-ribbons | [Link](https://github.com/d2-projects/d2-ribbons) | [Link](https://github.com/d2-projects/d2-ribbons) | Open source project logo Library |
|
||||
|
||||
## Badge
|
||||
|
||||
If your open source project is based on D2Admin development, please add the following badge to your README:
|
||||
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/d2-projects/d2-admin/master/docs/image/d2-admin@2x.png" width="200">
|
||||
</a>
|
||||
|
||||
Copy the following code into the README to:
|
||||
|
||||
``` html
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank"><img src="https://raw.githubusercontent.com/d2-projects/d2-admin/master/docs/image/d2-admin@2x.png" width="200"></a>
|
||||
```powershell
|
||||
winget install Volta.Volta
|
||||
```
|
||||
|
||||
At the same time, you can report your project to us. We will place the excellent project in D2Admin and help you publicize it.
|
||||
> `winget` 安装完成后,**当前终端窗口不会自动加载 Volta**。选择以下任一方式生效:
|
||||
>
|
||||
> **方式一(关闭重开):** 关掉当前终端,重新打开一个 PowerShell / VS Code 终端。
|
||||
>
|
||||
> **方式二(不重启立即生效):** 在当前终端执行:
|
||||
> ```powershell
|
||||
> $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
|
||||
> ```
|
||||
>
|
||||
> 生效后验证:
|
||||
> ```powershell
|
||||
> volta --version # 应输出版本号(如 2.0.2)
|
||||
> ```
|
||||
|
||||
## Contributor
|
||||
**macOS / Linux:**
|
||||
|
||||
* [@FairyEver](https://github.com/FairyEver)
|
||||
* [@sunhaoxiang](https://github.com/sunhaoxiang)
|
||||
* [@Aysnine](https://github.com/Aysnine)
|
||||
* [@luchaohai](https://github.com/luchaohai)
|
||||
* [@han-feng](https://github.com/han-feng)
|
||||
* [@rongxingsun](https://github.com/rongxingsun)
|
||||
* [@dnyz520](https://github.com/dnyz520)
|
||||
```bash
|
||||
curl https://get.volta.sh | bash
|
||||
# 重新加载 shell 配置
|
||||
source ~/.bashrc # 或 source ~/.zshrc
|
||||
volta --version
|
||||
```
|
||||
|
||||
## Become a sponsor
|
||||
> 更多安装方式请参考 [Volta 官方文档](https://docs.volta.sh/guide/getting-started)。
|
||||
|
||||
[Sponsor me on afdian.net](https://afdian.net/@fairyever)
|
||||
### 第二步:启用 pnpm(Volta 内置支持,无需额外安装)
|
||||
|
||||
## Sponsor
|
||||
```bash
|
||||
# Volta 会根据 package.json 中的 packageManager 字段
|
||||
# 自动安装并使用 pnpm@10.33.0
|
||||
volta install pnpm@10.33.0
|
||||
|
||||
**cochlea** | **Baron** | **苦行僧** | **吴地安宁** | **KingDong** | **sunyongmofang**
|
||||
# 验证版本
|
||||
pnpm --version # 应输出 10.33.0
|
||||
```
|
||||
|
||||
## Visitor
|
||||
> 如果没有 Volta,也可以手动安装 pnpm:
|
||||
> ```bash
|
||||
> npm install -g pnpm@10.33.0
|
||||
> ```
|
||||
|
||||

|
||||
### 第三步:克隆项目并安装依赖
|
||||
|
||||
> Total visitor since 2019.08.27
|
||||
```bash
|
||||
# 克隆仓库
|
||||
git clone <仓库地址>
|
||||
cd mes-ui
|
||||
|
||||
## Star history
|
||||
# Volta 检测到 .node-version 后,自动下载并安装 Node.js 18.16.0
|
||||
# 如果你本机没有这个版本,Volta 会提示 "Fetching node@18.16.0" —— 等它完成即可
|
||||
node --version # 应输出 v18.16.0
|
||||
|
||||
[](https://starchart.cc/d2-projects/d2-admin)
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## License
|
||||
> **没有安装 Node 18.16.0 怎么办?**
|
||||
> **Volta 用户**:无需手动操作。进入项目目录时 Volta 会自动检测 `.node-version`,如果本机缺这个版本,它会**自动下载安装**,你只需等待几秒终端提示完成即可。
|
||||
> **nvm 用户**:执行 `nvm install 18.16.0 && nvm use 18.16.0`。
|
||||
> **手动安装**:去 [nodejs.org](https://nodejs.org/) 下载 Node.js 18.16.0 安装包。
|
||||
>
|
||||
> 装完后建议运行 `node --version` 确认输出 `v18.16.0`,否则 `pnpm install` 会因为 `engine-strict=true` 直接报错拒绝安装。
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fd2-projects%2Fd2-admin?ref=badge_large)
|
||||
### 第四步:配置环境变量
|
||||
|
||||
在项目根目录创建 `.env.development.local` 文件(此文件已在 `.gitignore` 中,不会提交):
|
||||
|
||||
```bash
|
||||
# 后台 API 地址
|
||||
VUE_APP_API=http://你的后台服务器地址/
|
||||
```
|
||||
|
||||
### 第五步:启动开发服务器
|
||||
|
||||
```bash
|
||||
pnpm serve
|
||||
```
|
||||
|
||||
浏览器会自动打开 `http://localhost:8080`(或终端提示的实际端口)。
|
||||
|
||||
---
|
||||
|
||||
## 常用命令速查
|
||||
|
||||
| 命令 | 说明 |
|
||||
| --- | --- |
|
||||
| `pnpm serve` | 启动开发服务器(自动打开浏览器) |
|
||||
| `pnpm build` | 生产环境构建 |
|
||||
| `pnpm build:preview` | 预发布环境构建 |
|
||||
| `pnpm lint` | ESLint 代码检查并自动修复 |
|
||||
| `pnpm test:unit` | 运行单元测试 |
|
||||
| `pnpm install` | 安装依赖 |
|
||||
| `pnpm add <包名>` | 添加新依赖(**禁止使用 npm install**) |
|
||||
| `pnpm remove <包名>` | 移除依赖 |
|
||||
|
||||
---
|
||||
|
||||
## 版本锁定机制
|
||||
|
||||
### Node.js 版本锁定(Volta)
|
||||
|
||||
```
|
||||
项目根目录
|
||||
├── .node-version → 18.16.0(Volta 自动读取切换)
|
||||
```
|
||||
|
||||
- Volta 进入项目目录时自动将 Node.js 切换为 `18.16.0`
|
||||
- 如果该版本未安装,Volta 会自动下载并安装
|
||||
- 传统方案(nvm)也兼容:`nvm use` 读取 `.nvmrc`,本项目同时提供 `.node-version` 供 Volta 使用
|
||||
|
||||
### pnpm 版本锁定
|
||||
|
||||
```
|
||||
package.json
|
||||
├── packageManager → pnpm@10.33.0(Corepack / Volta 识别)
|
||||
├── engines.pnpm → >=10.33.0 <11(npm/pnpm install 时校验)
|
||||
```
|
||||
|
||||
```ini
|
||||
# .npmrc
|
||||
engine-strict=true # 版本不满足 → 安装直接报错
|
||||
```
|
||||
|
||||
### 依赖版本锁定
|
||||
|
||||
```
|
||||
pnpm-lock.yaml → 锁定所有依赖的精确版本(等价于 npm 的 package-lock.json)
|
||||
```
|
||||
|
||||
> `pnpm-lock.yaml` **必须提交到 Git**,保证所有环境安装的依赖版本完全一致。
|
||||
|
||||
---
|
||||
|
||||
## 项目目录结构
|
||||
|
||||
```
|
||||
mes-ui/
|
||||
├── .github/ # GitHub 相关配置
|
||||
├── docs/ # 项目文档
|
||||
├── public/ # 静态资源(不经过 webpack 处理)
|
||||
├── src/ # PC 端业务源码
|
||||
│ ├── api/ # 接口请求层(按业务模块拆分)
|
||||
│ │ ├── _service.js # axios 请求服务实例
|
||||
│ │ └── production-master-data/ # 生产主数据模块 API
|
||||
│ ├── assets/ # 静态资源(经 webpack 处理,含 SCSS / SVG)
|
||||
│ ├── components/ # 公共组件
|
||||
│ │ ├── page-table/ # CRUD 表格便捷组合体
|
||||
│ │ └── page-dialog-form/ # 新增/编辑弹框组件
|
||||
│ ├── composables/ # 可复用逻辑函数
|
||||
│ │ ├── useTableColumns.js # 列定义工厂
|
||||
│ │ ├── useTableButtons.js # 按钮定义工厂
|
||||
│ │ └── useI18n.js # i18n Mixin 工厂
|
||||
│ ├── layout/ # 页面布局(header-aside)
|
||||
│ ├── libs/ # 工具函数
|
||||
│ ├── locales/ # 国际化语言包(zh-chs / en)
|
||||
│ ├── router/ # 路由配置(modules/ 按业务模块拆分)
|
||||
│ ├── store/ # Vuex 状态管理
|
||||
│ ├── utils/ # 通用工具(文件下载、Excel 读取等)
|
||||
│ └── views/ # 业务页面(按模块三级目录组织)
|
||||
│ ├── production-master-data/ # 生产主数据
|
||||
│ ├── system-administration/ # 系统管理
|
||||
│ ├── equipment-management/ # 设备管理
|
||||
│ ├── quality-management/ # 质量管控
|
||||
│ ├── planning-production/ # 计划生产
|
||||
│ └── data-platform/ # 数据平台
|
||||
├── src.mobile/ # 移动端业务源码
|
||||
├── .browserslistrc # 浏览器兼容配置
|
||||
├── .editorconfig # 编辑器统一配置
|
||||
├── .env / .env.development / .env.preview # 环境变量
|
||||
├── .eslintignore / .eslintrc.js # ESLint 配置
|
||||
├── .gitignore # Git 忽略配置
|
||||
├── .node-version # Volta Node.js 版本锁定(18.16.0)
|
||||
├── .npmrc # pnpm 配置(版本强校验)
|
||||
├── .postcssrc.js # PostCSS 配置
|
||||
├── babel.config.js # Babel 配置
|
||||
├── jest.config.js # Jest 单元测试配置
|
||||
├── jsconfig.json # VS Code 路径别名提示
|
||||
├── package.json # 项目依赖与脚本
|
||||
├── pnpm-lock.yaml # pnpm 依赖锁定文件(必须提交)
|
||||
├── vue.config.js # Vue CLI 构建配置
|
||||
└── README.md # 项目说明(本文件)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 项目文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
| --- | --- |
|
||||
| [表格组件使用说明](./docs/表格组件使用说明.md) | `page-table` + `page-dialog-form` 完整使用手册 |
|
||||
| [国际化规则](./docs/国际化规则.md) | i18n 使用规范与迁移指南 |
|
||||
| [迁移提示词](./docs/迁移提示词.md) | 旧页面迁移到新方案的规则文档 |
|
||||
| [SCT基础表格重构设计](./docs/SCT基础表格重构设计.md) | 旧 `sct-base-table` → 新架构设计文档 |
|
||||
| [变更日志](./docs/变更日志.md) | 项目变更日志 |
|
||||
|
||||
---
|
||||
|
||||
## 关键目录说明
|
||||
|
||||
### `src/components/` — 公共组件
|
||||
|
||||
| 组件 | 作用 |
|
||||
| --- | --- |
|
||||
| `page-table` | CRUD 表格组合体(按钮栏 + 表格 + 分页 + 高度自适应 + 帮助按钮) |
|
||||
| `page-dialog-form` | 新增/编辑弹框(表单渲染 + 校验 + i18n 自动翻译) |
|
||||
| `d2-container` | 页面容器(卡片、全屏、透明等多种布局变体) |
|
||||
| `d2-container-frame` | iframe 嵌套外部页面容器 |
|
||||
|
||||
> `page-table` 和 `page-dialog-form` 是本项目核心组件,详细用法见 [表格组件使用说明](./docs/表格组件使用说明.md)。
|
||||
|
||||
### `src/composables/` — 逻辑复用层
|
||||
|
||||
| 文件 | 作用 |
|
||||
| --- | --- |
|
||||
| `useTableColumns.js` | 列定义工厂,消除手动分配 `idx`,约定 `prop: '_actions'` 为操作列 |
|
||||
| `useTableButtons.js` | 按钮工厂,一键生成工具栏 + 行内按钮,自动注入权限过滤 |
|
||||
| `useI18n.js` | i18n Mixin 工厂,通过 `i18nMixin(prefix)` 注入 `key()` / `ckey()` |
|
||||
| `useConfirmHandle.js` | 确认操作 Mixin,封装 `$confirm` + API 调用 |
|
||||
|
||||
### `src/router/modules/` — 路由模块
|
||||
|
||||
按业务一级模块拆分,examples:
|
||||
|
||||
| 文件 | 路由前缀 | 对应模块 |
|
||||
| --- | --- | --- |
|
||||
| `production-master-data.js` | `/production_configuration` | 生产主数据 |
|
||||
| `system-administration.js` | `/system_settings` | 系统管理 |
|
||||
| `equipment-management.js` | `/equipment_management` | 设备管理 |
|
||||
| `quality-management.js` | `/quality_management` | 质量管控 |
|
||||
| `planning-production.js` | `/planning_production` | 计划生产 |
|
||||
| `data-platform.js` | `/data_platform` | 数据平台 |
|
||||
|
||||
### `src/api/` — 接口请求层
|
||||
|
||||
按业务模块父目录存放,命名规则:`{模块名}.js`。例如:
|
||||
- `production-master-data/device-category.js` → 设备类型
|
||||
- `production-master-data/factory-area.js` → 工厂区域
|
||||
|
||||
---
|
||||
|
||||
## 构建配置文件
|
||||
|
||||
| 文件 | 作用 |
|
||||
| --- | --- |
|
||||
| `vue.config.js` | Vue CLI 构建配置(多页入口、CDN、webpack 插件、主题色替换、SVG sprites、路径别名) |
|
||||
| `babel.config.js` | Babel 转译配置 |
|
||||
| `.postcssrc.js` | PostCSS 自动前缀等配置 |
|
||||
| `.eslintrc.js` | ESLint 代码规范 |
|
||||
| `jest.config.js` | 单元测试框架配置 |
|
||||
| `jsconfig.json` | VS Code 路径别名智能提示(`@` → `src/`) |
|
||||
| `.browserslistrc` | 目标浏览器范围 |
|
||||
| `.editorconfig` | 编辑器缩进/换行风格统一 |
|
||||
|
||||
---
|
||||
|
||||
## 环境变量
|
||||
|
||||
| 文件 | 说明 |
|
||||
| --- | --- |
|
||||
| `.env` | 所有环境通用变量 |
|
||||
| `.env.development` | 开发环境变量 |
|
||||
| `.env.development.local` | 本地开发环境变量(不提交 Git,需自行创建) |
|
||||
| `.env.preview` | 预发布环境变量 |
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1:`pnpm install` 报错 "Unsupported engine"
|
||||
|
||||
> 意思:本机 Node.js 或 pnpm 版本不在项目规定范围内,install 被阻断。
|
||||
|
||||
**先排查版本:**
|
||||
|
||||
```bash
|
||||
node --version # 项目要求 >=18.16.0 <19
|
||||
pnpm --version # 项目要求 >=10.33.0 <11
|
||||
```
|
||||
|
||||
**谁把版本不对:**
|
||||
|
||||
| 情景 | 解决办法 |
|
||||
| --- | --- |
|
||||
| **用 Volta**(推荐) | `volta install node@18.16.0` + `volta install pnpm@10.33.0`,进入项目目录自动生效 |
|
||||
| **用 nvm** | `nvm install 18.16.0 && nvm use 18.16.0`,再 `npm install -g pnpm@10.33.0` |
|
||||
| **手动安装** | 去 [nodejs.org](https://nodejs.org/) 下载 18.16.0,再 `npm install -g pnpm@10.33.0` |
|
||||
| **已有正确版本但切换不生效** | 重新打开终端,确保 `volta` / `nvm` 已正确加载 |
|
||||
|
||||
> 版本验证通过后,重新执行 `pnpm install`。
|
||||
|
||||
### Q2:启动后页面报跨域错误
|
||||
|
||||
在 `.env.development.local` 中确认 `VUE_APP_API` 指向正确的后台地址,且后台已开启 CORS。
|
||||
|
||||
### Q3:端口被占用
|
||||
|
||||
默认端口为 `8080`。如需修改,在 `.env.development.local` 中添加:
|
||||
|
||||
```bash
|
||||
PORT=8081
|
||||
```
|
||||
|
||||
### Q4:`volta` 命令无法识别 / "无法将 volta 项识别为 cmdlet"
|
||||
|
||||
> 原因:`winget install Volta.Volta` 不会自动刷新当前终端的 PATH 缓存。
|
||||
|
||||
**解决办法二选一:**
|
||||
|
||||
```powershell
|
||||
# 方案 A:关掉当前终端,重新打开一个(推荐,最简单)
|
||||
# 方案 B:在当前终端执行以下命令立即生效,免重启
|
||||
$env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
|
||||
```
|
||||
|
||||
> 生效后 `volta --version` 应输出版本号。
|
||||
|
||||
### Q5:依赖安装慢
|
||||
|
||||
pnpm 默认使用 npm registry。如需加速,可配置镜像:
|
||||
|
||||
```bash
|
||||
pnpm config set registry https://registry.npmmirror.com
|
||||
```
|
||||
|
||||
205
README.zh.md
205
README.zh.md
@@ -1,205 +0,0 @@
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/d2-projects/d2-admin/stargazers" target="_blank"><img src="https://img.shields.io/github/stars/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/network/members" target="_blank"><img src="https://img.shields.io/github/forks/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/issues" target="_blank"><img src="https://img.shields.io/github/issues/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/issues?q=is%3Aissue+is%3Aclosed" target="_blank"><img src="https://img.shields.io/github/issues-closed/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/pulls" target="_blank"><img src="https://img.shields.io/github/issues-pr/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/pulls?q=is%3Apr+is%3Aclosed" target="_blank"><img src="https://img.shields.io/github/issues-pr-closed/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank"><img src="https://img.shields.io/github/last-commit/d2-projects/d2-admin.svg"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank"><img src="https://visitor-badge.glitch.me/badge?page_id=d2-projects.d2-admin"></a>
|
||||
<a href="https://github.com/d2-projects/d2-admin/releases" target="_blank"><img src="https://img.shields.io/github/release/d2-projects/d2-admin.svg"></a>
|
||||
<a href="https://deepscan.io/dashboard#view=project&tid=8014&pid=10161&bid=136697"><img src="https://deepscan.io/api/teams/8014/projects/10161/branches/136697/badge/grade.svg" alt="DeepScan grade"></a>
|
||||
</p>
|
||||
|
||||
[D2Admin](https://github.com/d2-projects/d2-admin) 是一个完全 **开源免费** 的企业中后台产品前端集成方案,使用最新的前端技术栈,小于 60kb 的本地首屏 js 加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助力管理系统敏捷开发。
|
||||
|
||||
[](https://open.vscode.dev/d2-projects/d2-admin)
|
||||
|
||||
**中文** | [English](https://github.com/d2-projects/d2-admin)
|
||||
|
||||
## 预览
|
||||
|
||||

|
||||
[](https://app.netlify.com/sites/d2-admin/deploys)
|
||||
|
||||
下列访问地址均由最新的 master 分支代码同时构建部署,访问效果完全一致,请根据自身网络情况选择合适的访问链接。
|
||||
|
||||
| 位置 | 链接 | 部署位置 |
|
||||
| --- | --- | --- |
|
||||
| d2.pub | [preview](https://d2.pub/d2-admin/preview) | 中国服务器 |
|
||||
| github | [preview](https://d2-projects.github.io/d2-admin) | GitHub pages |
|
||||
| netlify | [preview](https://d2-admin.netlify.com) | Netlify CDN |
|
||||
|
||||
## 文档
|
||||
|
||||
[在 https://d2.pub 上的本项目文档](https://d2.pub/doc/d2-admin/)
|
||||
|
||||
## 功能
|
||||
|
||||
* 使用 vue-cli3 构建
|
||||
* 首屏加载等待动画
|
||||
* 五款主题
|
||||
* 内置 UEditor 富文本编辑器
|
||||
* 详细的文档
|
||||
* 登录和注销
|
||||
* 分离的路由和菜单设置
|
||||
* 可折叠侧边栏
|
||||
* 多国语
|
||||
* 富文本编辑器
|
||||
* Markdown 编辑器
|
||||
* 全屏
|
||||
* Fontawesome 图标库
|
||||
* 图标选择器
|
||||
* 自动注册 SVG 图标
|
||||
* 模拟数据
|
||||
* 剪贴板封装
|
||||
* 图表库
|
||||
* 时间日期计算工具
|
||||
* 导入 Excel ( xlsx + csv )
|
||||
* 数据导出 Excel ( xlsx + csv )
|
||||
* 数据导出文本
|
||||
* 数字动画
|
||||
* 可拖拽调整大小的区块布局
|
||||
* 可拖拽调整大小和位置的网格布局
|
||||
* 开箱即用的页面布局组件
|
||||
* 加载并解析 markdown 文件
|
||||
* GitHub 样式的 markdown 显示组件
|
||||
* markdown 内代码高亮
|
||||
* 为 markdown 扩展了百度云链接解析和优化显示
|
||||
* 右键菜单组件
|
||||
* 自定义滚动条和滚动控制
|
||||
* 公用样式抽离,方便的主题定制
|
||||
* 支持临时菜单配置
|
||||
* 系统功能展示模块 `1.1.4 +`
|
||||
* 多标签页模式 `1.1.4 +`
|
||||
* 美化滚动条 `1.1.4 +`
|
||||
* json view `1.1.4 +`
|
||||
* cookie 封装 `1.1.5 +`
|
||||
* 多标签页全局控制 API `1.1.5 +`
|
||||
* 菜单全局控制 API `1.1.5 +`
|
||||
* 多标签页关闭控制支持右键菜单 `1.1.10 +`
|
||||
* 模块化全局状态管理 `1.2.0 +`
|
||||
* 多种数据持久化方式:区分用户,区分路由,页面数据快照功能 `1.2.0 +`
|
||||
* 支持跳出外部链接的菜单系统 `1.2.0 +`
|
||||
* 支持菜单 svg 图标 `1.3.0 +`
|
||||
* 日志记录和错误捕捉 `1.3.0 +`
|
||||
* 全局菜单搜索 `1.3.0 +`
|
||||
* 自定义登录重定向 `1.3.0 +`
|
||||
* 切换全局基础组件尺寸 `1.4.0 +`
|
||||
* 页面载入进度条 `1.4.1 +`
|
||||
* 自适应的顶部菜单栏 `1.4.7 +`
|
||||
* 数据导出 xslx 时支持合并单元格 `1.5.4 +`
|
||||
* 多标签页支持拖拽排序 `1.8.0 +`
|
||||
* 优化生产环境构建,首页只加载小于 60kb 的本地 js 代码 `1.8.0 +`
|
||||
* 内置了构建文件体积检查工具 `1.8.0 +`
|
||||
* 构建多页面示例 `1.23.0 +`
|
||||
* 分包优化 `1.23.0 +`
|
||||
|
||||
## 其它同步仓库
|
||||
|
||||
| 位置 | 链接 |
|
||||
| --- | --- |
|
||||
| 码云 | [https://gitee.com/d2-projects/d2-admin](https://gitee.com/d2-projects/d2-admin) |
|
||||
| coding | [https://d2-projects.coding.net/p/d2-projects/d/d2-admin/git](https://d2-projects.coding.net/p/d2-projects/d/d2-admin/git) |
|
||||
|
||||
> 如果您在 github 仓库下载很慢,可以尝试使用我们的码云仓库克隆代码
|
||||
|
||||
## 其它版本
|
||||
|
||||
| 名称 | 主页 | 预览 | 介绍 |
|
||||
| --- | --- | --- | --- |
|
||||
| 简化版模板 | [Github](https://github.com/d2-projects/d2-admin-start-kit) | [预览](https://d2.pub/d2-admin-start-kit/preview) | 无 |
|
||||
|
||||
## 开源后端实现
|
||||
|
||||
> 后端由开源社区贡献,不保证使用 D2Admin 最新版本,相关使用问题请联系其开源作者。
|
||||
|
||||
| 名称 | 技术 | 主页 | 预览 | 介绍 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| django-vue-admin-pro | Django | [Github](https://github.com/dvadmin-pro/django-vue-admin-pro) | [预览](http://demo.pro.django-vue-admin.com) | Django + Jwt + D2Admin |
|
||||
| boot-admin | SpringBoot | [Github](https://github.com/hb0730/boot-admin) | [预览](http://admin.hb0730.com/) | 基于 SpringBoot 前后端分离的后台管理系统 |
|
||||
| FlaskPermission | Flask | [Github](https://github.com/huguodong/flask-permission) | [预览](http://47.97.218.139:9999) | 基于 Python Flask 的权限管理 |
|
||||
| CareyShop | ThinkPHP5 | [Github](https://github.com/dnyz520/careyshop-admin) | [预览](https://demo.careyshop.cn/admin/#/index) | 适用于 CareyShop 的高性能商城框架系统 |
|
||||
| jiiiiiin-security | Spring Boot | [Github](https://github.com/Jiiiiiin/jiiiiiin-security) | [预览](https://github.com/Jiiiiiin/jiiiiiin-security) | 前后端分离的内容管理基础项目,注重用户权限管理功能 |
|
||||
| Taroco | Spring Cloud | [Github](https://github.com/liuht777/Taroco) | [预览](http://111.231.192.110/) | 整套微服务企业级解决方案 |
|
||||
| Aooms | Spring Cloud | [码云](https://gitee.com/cyb-javaer/Aooms) | [预览](https://www.yuboon.com/Aooms) | 极速微服务开发,不止像JFinal一样简单 |
|
||||
| GOA | Beego | [Github](https://github.com/Qsnh/goa) | [预览](http://goaio.vip/) | 基于 Beego + Vue 开发的在线问答系统 |
|
||||
| CMDB | Django | [Github](https://github.com/CJFJack/django_vue_cmdb) | [预览](https://mp.weixin.qq.com/s?__biz=MzU1OTYzODA4Mw==&mid=2247484250&idx=1&sn=981024ac0580d8a3eba95742bd32b268) | 分用户加载不同菜单和权限 |
|
||||
|
||||
## 社区项目
|
||||
|
||||
> 这些项目由开源社区贡献,不保证使用 D2Admin 最新版本,相关使用问题请联系其开源作者。
|
||||
|
||||
| 名称 | 主页 | 预览 | 介绍 |
|
||||
| --- | --- | --- | --- |
|
||||
| d2-admin-xiya-go-cms | [Github](https://github.com/d2-projects/d2-admin-xiya-go-cms) | [预览](https://d2.pub/d2-admin-xiya-go-cms/preview) | D2Admin + 权限系统 + 动态路由 |
|
||||
| d2-advance | [Github](https://github.com/d2-projects/d2-advance) | [预览](https://d2.pub/d2-advance/preview) | 由 D2Admin 启发的技术探索 |
|
||||
| d2-crud-plus | [Github](https://github.com/greper/d2-crud-plus) | [预览](http://qiniu.veryreader.com/D2CrudPlusExample/index.html) | 简化d2-crud配置,快速开发crud功能 |
|
||||
| d2-crud | [Github](https://github.com/d2-projects/d2-crud) | [预览]() | 表格常用操作封装 |
|
||||
| d2-admin-pm | [Github](https://github.com/wjkang/d2-admin-pm) | [预览](http://jaycewu.coding.me/d2-admin-pm) | 基于 D2Admin 的 RBAC 权限管理解决方案 |
|
||||
| LanBlog | [Github](https://github.com/sinksmell/LanBlog) | [预览](http://47.101.222.133/) | Vue + Beego restful api 开发的懒人博客 |
|
||||
| d2-admin-start-kit-plus | [Github](https://github.com/hank-cp/d2-admin-start-kit-plus) | [预览](https://github.com/hank-cp/d2-admin-start-kit-plus) | D2Admin 简化版模块化版本 |
|
||||
| d2-ribbons | [Github](https://github.com/d2-projects/d2-ribbons) | [预览](https://github.com/d2-projects/d2-ribbons) | 开源项目徽标库 |
|
||||
|
||||
## 加入我们
|
||||
|
||||
D2Admin 是完全开源免费的项目,旨在帮助开发者更方便地进行管理系统开发,同时也提供 QQ 交流群和微信群,前后端的朋友可以相互答疑,项目组成员全部在内,所有 D2 相关项目使用问题欢迎在群内提问。
|
||||
|
||||
* QQ 1 群 `806395827`
|
||||
* QQ 2 群 `592981556`
|
||||
|
||||

|
||||
|
||||
## 徽章
|
||||
|
||||
如果您的开源项目基于 D2Admin 开发,请在您的 README 添加下面的徽章:
|
||||
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/FairyEver/d2-admin/master/docs/image/d2-admin@2x.png" width="200">
|
||||
</a>
|
||||
|
||||
复制下面代码加入到 README 中即可:
|
||||
|
||||
``` html
|
||||
<a href="https://github.com/d2-projects/d2-admin" target="_blank"><img src="https://raw.githubusercontent.com/FairyEver/d2-admin/master/docs/image/d2-admin@2x.png" width="200"></a>
|
||||
```
|
||||
|
||||
同时您可以将您的项目汇报给我们,优秀项目我们会放置在 D2Admin 相关位置并帮助您宣传。
|
||||
|
||||
## 贡献
|
||||
|
||||
* [@FairyEver](https://github.com/FairyEver)
|
||||
* [@sunhaoxiang](https://github.com/sunhaoxiang)
|
||||
* [@Aysnine](https://github.com/Aysnine)
|
||||
* [@luchaohai](https://github.com/luchaohai)
|
||||
* [@han-feng](https://github.com/han-feng)
|
||||
* [@rongxingsun](https://github.com/rongxingsun)
|
||||
* [@dnyz520](https://github.com/dnyz520)
|
||||
|
||||
## 成为赞助者
|
||||
|
||||
[在 "爱发电" 上赞助我](https://afdian.net/@fairyever)
|
||||
|
||||
## 赞助
|
||||
|
||||
**cochlea** | **Baron** | **苦行僧** | **吴地安宁** | **KingDong** | **sunyongmofang**
|
||||
|
||||
## 访问统计
|
||||
|
||||

|
||||
|
||||
> 自 2019-08-27 起
|
||||
|
||||
## Star 历史
|
||||
|
||||
[](https://starchart.cc/d2-projects/d2-admin)
|
||||
|
||||
## License
|
||||
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fd2-projects%2Fd2-admin?ref=badge_large)
|
||||
|
||||

|
||||
1241
docs/SCT基础表格重构设计.md
Normal file
1241
docs/SCT基础表格重构设计.md
Normal file
File diff suppressed because it is too large
Load Diff
146
docs/Sass废弃修复.md
Normal file
146
docs/Sass废弃修复.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Sass 废弃警告修复记录
|
||||
|
||||
> 项目运行 `pnpm serve` 时,Dart Sass 输出了多类废弃 API 警告。本文档记录问题现象、根因及修复方案。
|
||||
|
||||
---
|
||||
|
||||
## 1. `legacy-js-api` — Sass Loader 旧 JS API 警告
|
||||
|
||||
### 问题现象
|
||||
|
||||
```
|
||||
Deprecation Warning [legacy-js-api]: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0.
|
||||
```
|
||||
|
||||
### 根因
|
||||
|
||||
`sass-loader` 旧版本默认通过旧版 JS API 调用 Dart Sass。Dart Sass 计划在 2.0 中移除该 API。
|
||||
|
||||
### 修复
|
||||
|
||||
**文件:`package.json`**
|
||||
|
||||
将 `sass-loader` 升级到 `10.5.2`(v10 最后一个稳定版,兼容 Webpack 4),该版本支持 `silenceDeprecations` 选项。
|
||||
|
||||
```diff
|
||||
- "sass-loader": "^10.3.1",
|
||||
+ "sass-loader": "~10.5.2",
|
||||
```
|
||||
|
||||
**文件:`vue.config.js`**
|
||||
|
||||
在 `css.loaderOptions.sass` 中添加 `silenceDeprecations` 配置。
|
||||
|
||||
```js
|
||||
sass: {
|
||||
additionalData: '@use "@/assets/style/public.scss" as *;',
|
||||
sassOptions: {
|
||||
silenceDeprecations: ['legacy-js-api', 'import']
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. `import` — Sass `@import` 废弃警告
|
||||
|
||||
### 问题现象
|
||||
|
||||
```
|
||||
DEPRECATION WARNING [import]: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
|
||||
|
||||
╷
|
||||
1 │ @import '~@/assets/style/unit/color.scss';
|
||||
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
╵
|
||||
src\assets\style\public.scss 1:9 @use
|
||||
```
|
||||
|
||||
### 根因
|
||||
|
||||
`public.scss` 通过 `@import '~@/assets/style/unit/color.scss'` 引入颜色变量文件。Dart Sass 正在推动从 `@import` 迁移到 `@use` / `@forward` 模块系统。
|
||||
|
||||
### 为何不改成 `@use`
|
||||
|
||||
该 `@import` 使用了 Webpack 专有的 `~` 前缀做模块路径解析(`~@/assets/...`),`@use` 对此机制支持不稳定,直接替换可能导致构建失败。
|
||||
|
||||
### 修复
|
||||
|
||||
在上述 `silenceDeprecations` 数组中追加 `'import'`,静默该警告。
|
||||
|
||||
---
|
||||
|
||||
## 3. `global-builtin` — 全局内置函数 `nth()` 废弃警告
|
||||
|
||||
### 问题现象
|
||||
|
||||
```
|
||||
Deprecation Warning [global-builtin]: Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
|
||||
Use list.nth instead.
|
||||
|
||||
╷
|
||||
42 │ .#{$prefix}-m-#{nth($sizes, $index)} { margin: #{nth($sizes, $index)}px !important; }
|
||||
│ ^^^^^^^^^^^^^^^^^^^
|
||||
╵
|
||||
src\assets\style\public-class.scss 42:19 @import
|
||||
src\App.vue 3:9 root stylesheet
|
||||
```
|
||||
|
||||
### 根因
|
||||
|
||||
Dart Sass 正在将全局内置函数迁移到命名空间模块。`nth()` 属于 `sass:list` 模块,不应再作为全局函数直接调用。
|
||||
|
||||
### 修复(从源码根治)
|
||||
|
||||
**文件:`src/assets/style/public-class.scss`**
|
||||
|
||||
**步骤 1** — 文件顶部引入 `sass:list` 模块:
|
||||
|
||||
```diff
|
||||
+ @use 'sass:list';
|
||||
@import 'public';
|
||||
```
|
||||
|
||||
**步骤 2** — 将 `@for` 循环中所有 `nth()` 替换为 `list.nth()`:
|
||||
|
||||
```diff
|
||||
@for $index from 1 to 6 {
|
||||
- .#{$prefix}-m-#{nth($sizes, $index)} { margin: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-mt-#{nth($sizes, $index)} { margin-top: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-mr-#{nth($sizes, $index)} { margin-right: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-mb-#{nth($sizes, $index)} { margin-bottom: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-ml-#{nth($sizes, $index)} { margin-left: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-p-#{nth($sizes, $index)} { padding: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-pt-#{nth($sizes, $index)} { padding-top: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-pr-#{nth($sizes, $index)} { padding-right: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-pb-#{nth($sizes, $index)} { padding-bottom: #{nth($sizes, $index)}px !important; }
|
||||
- .#{$prefix}-pl-#{nth($sizes, $index)} { padding-left: #{nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-m-#{list.nth($sizes, $index)} { margin: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-mt-#{list.nth($sizes, $index)} { margin-top: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-mr-#{list.nth($sizes, $index)} { margin-right: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-mb-#{list.nth($sizes, $index)} { margin-bottom: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-ml-#{list.nth($sizes, $index)} { margin-left: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-p-#{list.nth($sizes, $index)} { padding: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-pt-#{list.nth($sizes, $index)} { padding-top: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-pr-#{list.nth($sizes, $index)} { padding-right: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-pb-#{list.nth($sizes, $index)} { padding-bottom: #{list.nth($sizes, $index)}px !important; }
|
||||
+ .#{$prefix}-pl-#{list.nth($sizes, $index)} { padding-left: #{list.nth($sizes, $index)}px !important; }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 涉及文件汇总
|
||||
|
||||
| 文件 | 修改内容 |
|
||||
|------|---------|
|
||||
| `package.json` | `sass-loader` 版本 `^10.3.1` → `~10.5.2` |
|
||||
| `vue.config.js` | 添加 `sassOptions.silenceDeprecations` 配置 |
|
||||
| `src/assets/style/public-class.scss` | 引入 `sass:list` 模块,`nth()` → `list.nth()` |
|
||||
|
||||
---
|
||||
|
||||
## 后续建议
|
||||
|
||||
- 当项目未来升级到 Vue CLI 5 / Webpack 5 时,可考虑将 `public.scss` 中的 `@import` 改为 `@use`(届时 `~` 前缀路径的兼容方案需要重新评估)。
|
||||
- `silenceDeprecations` 中的 `'import'` 为临时静默方案,待 `@import` 改造完成后移除。
|
||||
648
docs/功能测试-SPC采集配置.md
Normal file
648
docs/功能测试-SPC采集配置.md
Normal file
@@ -0,0 +1,648 @@
|
||||
# SPC采集配置功能测试流程文档
|
||||
|
||||
> **测试对象**:生产配置 → SPC采集模型 → SPC采集配置(Data Collection Configuration)
|
||||
> **迁移日期**:2026-06-02
|
||||
> **对应页面**:[`src/views/production-master-data/spc-configuration/data-collection-configuration/index.vue`](file:///c:/code/compony/mes-ui-d2/src/views/production-master-data/spc-configuration/data-collection-configuration/index.vue)
|
||||
> **API 文件**:[`src/api/production-master-data/data-collection-configuration.js`](file:///c:/code/compony/mes-ui-d2/src/api/production-master-data/data-collection-configuration.js)
|
||||
> **路由路径**:`/production_configuration/spc_configuration/binding_scada_node`(旧值暂留,后续批量替换)
|
||||
> **i18n 前缀**:`page.production_master_data.spc_configuration.data_collection_configuration`
|
||||
|
||||
---
|
||||
|
||||
## 一、测试环境配置
|
||||
|
||||
| 配置项 | 要求 |
|
||||
|--------|------|
|
||||
| 浏览器 | Chrome 最新版、Edge 最新版(兼容 IE 11+) |
|
||||
| 屏幕分辨率 | ≥ 1920×1080 |
|
||||
| Node.js | ≥ 16.x |
|
||||
| 包管理器 | pnpm ≥ 7.x |
|
||||
| 后端服务 | Webman 已启动,且 SPC采集配置相关 API 可正常访问(`production_configuration/spc_configuration/binding_scada_node/*`) |
|
||||
| 测试账号 | 具有「生产配置 → SPC采集模型 → SPC采集配置」菜单权限的管理员账号(含 create / edit / delete 权限) |
|
||||
| 测试数据 | 准备至少 5 条测试用 SPC采集配置(采集编码唯一),覆盖不同采集类型(实时、周期、触发)、不同状态(启用、禁用) |
|
||||
| SCADA节点依赖 | 系统中至少配置 1 个可用的 SCADA 节点,便于绑定 |
|
||||
| 语言切换 | 先测中文(简体中文),再切到英文验证 i18n 是否正常 |
|
||||
| 权限分配 | 系统管理 → 菜单管理中确认 SPC采集配置按钮权限(新增、编辑、删除)已正确配置 |
|
||||
|
||||
---
|
||||
|
||||
## 二、测试前置条件
|
||||
|
||||
1. 后端服务(Webman)已启动,`production_configuration/spc_configuration/binding_scada_node/` 模块的 API 可正常访问
|
||||
2. 前端项目已执行 `pnpm install` 且 `pnpm dev` 正常启动,可访问登录页
|
||||
3. 使用具有「生产配置 → SPC采集模型 → SPC采集配置」菜单权限的管理员账号登录
|
||||
4. 侧边栏菜单 **生产配置 → SPC采集模型** 下可见 **SPC采集配置** 子菜单
|
||||
5. 浏览器控制台无 Vue 警告或 i18n key 缺失报错
|
||||
6. 确认 `src/locales/zh-chs.json` 和 `src/locales/en.json` 中存在 `page.production_master_data.spc_configuration.data_collection_configuration` 配置
|
||||
7. 数据库中 SPC采集配置表为空或仅有少量测试数据,便于验证
|
||||
8. 系统中至少存在 1 个已配置的 SCADA 节点
|
||||
|
||||
---
|
||||
|
||||
## 三、功能测试用例
|
||||
|
||||
### 3.1 页面加载与数据展示
|
||||
|
||||
#### TC-SCC-001:菜单导航与页面加载
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录系统 2. 点击侧边栏菜单「生产配置 → SPC采集模型 → SPC采集配置」 |
|
||||
| **预期结果** | 页面正常加载,URL 显示 `/production_configuration/spc_configuration/binding_scada_node`;页面顶部显示搜索栏(采集编码、采集名称、SCADA节点、状态、查询、重置按钮);中部显示 SPC采集配置 列表表格;底部分页组件显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-002:列表表格列头与数据展示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看 SPC采集配置 列表的表头列与各行数据 |
|
||||
| **预期结果** | 表格列依次为:序号、采集编码、采集名称、SCADA节点、采集类型、采集间隔(秒)、状态(tag 标签展示)、备注、操作;操作列包含「编辑」「删除」两个按钮(带图标);当数据为空时显示空数据占位提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-003:状态列 Tag 展示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看列表中状态列的不同取值 |
|
||||
| **预期结果** | 启用状态显示绿色 tag(success 类型),禁用状态显示红色 tag(danger 类型) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-004:工具栏按钮显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看表格上方的工具栏 |
|
||||
| **预期结果** | 显示蓝色「新增」按钮(含加号图标),右上角显示问号「帮助」按钮(点击跳转 `/help/data-collection-configuration`) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-005:表格自适应高度
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 加载页面后查看表格高度 2. 缩小/放大浏览器窗口 3. 折叠/展开侧边栏 |
|
||||
| **预期结果** | 表格高度自动填满 d2-container 剩余空间,窗口变化时无空白或滚动条异常 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.2 搜索功能
|
||||
|
||||
#### TC-SCC-006:按采集编码精确搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「采集编码」输入框输入完整的采集编码(如 `SCC_001`) 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示编码匹配的数据行;分页总数更新为匹配条数;输入框右侧 X 按钮可清空内容 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-007:按采集编码模糊搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「采集编码」输入框输入编码的片段(如 `SCC_`) 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格显示所有编码包含 `SCC_` 的数据行 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-008:按采集名称搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「采集名称」输入框输入关键字(如「温度」) 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示名称包含关键字的 SPC采集配置 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-009:按 SCADA 节点搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「SCADA节点」输入框输入节点关键字 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示 SCADA 节点字段匹配的数据行 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-010:按状态筛选
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「状态」下拉中选择「启用」 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示状态为启用的数据行;下拉中可选择「启用」「禁用」或留空(全部) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-011:组合条件搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 同时输入「采集编码」「采集名称」「SCADA节点」「状态」搜索条件 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格显示同时满足所有条件的数据行(AND 关系) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-012:回车键触发搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在搜索输入框中输入关键字 2. 按下键盘回车键 |
|
||||
| **预期结果** | 等同于点击「查询」按钮,表格按搜索条件刷新 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-013:重置搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 输入搜索条件并点击「查询」 2. 点击「重置」按钮 |
|
||||
| **预期结果** | 搜索输入框和状态下拉清空,表格恢复显示全部 SPC采集配置 数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-014:搜索无结果
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 输入不存在的编码或名称 2. 点击「查询」 |
|
||||
| **预期结果** | 表格显示空数据占位提示,分页总数显示 0 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.3 分页功能
|
||||
|
||||
#### TC-SCC-015:分页默认显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 加载页面后查看分页组件 |
|
||||
| **预期结果** | 分页默认显示第 1 页,每页 10 条记录,总条数显示当前数据库 SPC采集配置 总数 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-016:切换每页条数
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在分页组件的「每页显示」下拉中切换为 20、50、100 2. 观察表格变化 |
|
||||
| **预期结果** | 表格按新条数加载数据,分页组件正确显示总页数 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-017:翻页功能
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 准备至少 25 条测试数据 2. 点击「下一页」「上一页」「首页」「末页」按钮 3. 输入页码跳转 |
|
||||
| **预期结果** | 表格按页加载数据,翻页流畅无报错 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.4 新增 SPC采集配置
|
||||
|
||||
#### TC-SCC-018:打开新增弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击表格上方「新增」按钮 |
|
||||
| **预期结果** | 弹出新增 SPC采集配置 对话框,标题显示"新增SPC采集配置",包含采集编码、采集名称、SCADA节点、采集类型、采集间隔(秒)、状态、备注共 7 个表单项;「确定」「取消」按钮位于底部;弹框宽度约 50% |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-019:新增表单必填校验 - 采集编码
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 不填写「采集编码」直接点击「确定」 |
|
||||
| **预期结果** | 「采集编码」输入框下方显示红色校验提示"请输入采集编码" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-020:新增表单必填校验 - 采集名称
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 不填写「采集名称」直接点击「确定」 |
|
||||
| **预期结果** | 「采集名称」输入框下方显示红色校验提示"请输入采集名称" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-021:新增表单必填校验 - SCADA 节点
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 不填写「SCADA节点」直接点击「确定」 |
|
||||
| **预期结果** | 「SCADA节点」输入框下方显示红色校验提示"请输入SCADA节点" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-022:新增表单必填校验 - 采集类型
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 不选择「采集类型」直接点击「确定」 |
|
||||
| **预期结果** | 「采集类型」下拉框下方显示红色校验提示"请选择采集类型" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-023:新增表单必填校验 - 状态
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 清空「状态」下拉框 3. 点击「确定」 |
|
||||
| **预期结果** | 「状态」下拉框下方显示红色校验提示"请选择状态" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-024:新增成功 - 实时采集类型
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 采集编码输入 `SCC_TEST_001` 3. 采集名称输入「温度实时采集」 4. SCADA节点输入 `SCADA_NODE_01` 5. 采集类型选择「实时」 6. 采集间隔输入 `1` 7. 状态选择「启用」 8. 备注输入「测试用」 9. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,页面右上角显示绿色成功提示"操作成功";表格自动刷新并显示新增的 SPC采集配置 行 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-025:新增成功 - 周期采集类型
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 采集编码输入 `SCC_TEST_002` 3. 采集名称输入「压力周期采集」 4. SCADA节点输入 `SCADA_NODE_02` 5. 采集类型选择「周期」 6. 采集间隔输入 `60` 7. 状态选择「启用」 8. 备注留空 9. 点击「确定」 |
|
||||
| **预期结果** | 新增成功,备注字段允许为空,表格中可见新增行(采集类型显示为"周期",采集间隔 60) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-026:新增成功 - 触发采集类型
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 采集编码输入 `SCC_TEST_003` 3. 采集名称输入「流量触发采集」 4. SCADA节点输入 `SCADA_NODE_03` 5. 采集类型选择「触发」 6. 状态选择「禁用」 7. 点击「确定」 |
|
||||
| **预期结果** | 新增成功,表格中状态列 tag 显示为红色(禁用) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-027:新增 - 编码长度校验
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在采集编码输入框输入超过 100 个字符 3. 失焦后观察 |
|
||||
| **预期结果** | 输入框下方显示校验提示"长度在 1 到 100 个字符" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-028:新增 - 编码重复
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 输入已存在的 SPC采集配置 编码 3. 点击「确定」 |
|
||||
| **预期结果** | 提示后端返回的"编码已存在"等错误信息,新增失败,弹框不关闭 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-029:取消新增
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写部分内容 3. 点击「取消」按钮 |
|
||||
| **预期结果** | 弹框关闭,表格数据无变化,无任何提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-030:新增 - 备注多行输入
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在备注框中输入多行文字(超过 2 行) 3. 观察文本框是否自动扩展 |
|
||||
| **预期结果** | 备注为 textarea 类型,输入多行内容后自动扩展(最少 2 行,最多 6 行) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.5 编辑 SPC采集配置
|
||||
|
||||
#### TC-SCC-031:打开编辑弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击某一 SPC采集配置 行操作列的「编辑」按钮 |
|
||||
| **预期结果** | 弹出编辑 SPC采集配置 对话框,标题显示"编辑SPC采集配置",采集编码、采集名称、SCADA节点、采集类型、采集间隔(秒)、状态、备注共 7 个字段正确回填该行的数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-032:编辑成功 - 修改名称
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框(选择名称为"温度实时采集"的行) 2. 修改采集名称为"温度实时采集-改" 3. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格对应行的名称列更新为新值 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-033:编辑成功 - 修改状态
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框(选择状态为启用的行) 2. 修改状态为「禁用」 3. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格对应行的状态 tag 由绿色变为红色 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-034:编辑成功 - 修改采集间隔
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 修改采集间隔为 `120` 3. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格对应行的采集间隔列更新为 `120` |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-035:编辑后清空必填项 - 采集名称
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 清空「采集名称」输入框 3. 点击「确定」 |
|
||||
| **预期结果** | 「采集名称」下方显示红色校验提示,无法提交 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-036:编辑后清空必填项 - SCADA 节点
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 清空「SCADA节点」输入框 3. 点击「确定」 |
|
||||
| **预期结果** | 「SCADA节点」下方显示红色校验提示,无法提交 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-037:取消编辑
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 修改部分字段 3. 点击「取消」按钮 |
|
||||
| **预期结果** | 弹框关闭,表格数据无变化,无任何提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.6 删除 SPC采集配置
|
||||
|
||||
#### TC-SCC-038:删除确认弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击某一 SPC采集配置 行操作列的「删除」按钮 |
|
||||
| **预期结果** | 弹出确认对话框,标题显示"提示",内容为"确定要删除该SPC采集配置吗?",按钮为"确定""取消" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-039:取消删除
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 点击「删除」按钮 2. 在确认弹框中点击「取消」 |
|
||||
| **预期结果** | 确认弹框关闭,表格数据无变化,无任何提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-040:删除成功
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 点击「删除」按钮 2. 在确认弹框中点击「确定」 |
|
||||
| **预期结果** | 确认弹框关闭,提示"操作成功",表格对应行被移除;分页组件总数减 1;如删除的是当前页最后一行则自动跳到上一页 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-041:删除最后一行后页码修正
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 确保当前页只有 1 条数据 2. 删除该行 |
|
||||
| **预期结果** | 删除成功后自动跳转到上一页(如当前已是第 1 页则停留在第 1 页),不会出现空页或无数据错误 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.7 国际化(i18n)
|
||||
|
||||
#### TC-SCC-042:中文显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 系统语言切换为简体中文,查看 SPC采集配置 页面 |
|
||||
| **预期结果** | 所有列头、表单 label、按钮、提示、状态枚举均显示中文(采集编码、采集名称、SCADA节点、采集类型、采集间隔(秒)、状态、备注、新增、编辑、删除、确定、取消、提示、操作成功、实时、周期、触发、启用、禁用 等) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-043:英文显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 系统语言切换为英文,查看 SPC采集配置 页面 |
|
||||
| **预期结果** | 所有列头、表单 label、按钮、提示、状态枚举均显示英文(Collection Code、Collection Name、SCADA Node、Collection Type、Interval (s)、Status、Remark、Add、Edit、Delete、Confirm、Cancel、Tip、Operation succeeded、Real-time、Periodic、Trigger、Enable、Disable 等) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-044:弹框 i18n 自动响应
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在中文状态下打开新增弹框 2. 不关闭弹框,将语言切换为英文 3. 观察弹框标题、按钮文字、表单 label 是否自动翻译为英文 |
|
||||
| **预期结果** | 弹框中的所有文案(包括 title、label、placeholder、按钮文字、校验提示)跟随语言切换自动更新为对应英文,无需关闭重开 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-045:缺失 key 校验
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 打开浏览器开发者工具,切换页面、打开弹框、切换语言 |
|
||||
| **预期结果** | 控制台无 i18n key 缺失警告(如 `[vue-i18n] Value of key 'xxx' is not a string`) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.8 权限控制
|
||||
|
||||
#### TC-SCC-046:无 create 权限时隐藏新增按钮
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 使用没有 `create` 权限的账号登录,访问 SPC采集配置 页面 |
|
||||
| **预期结果** | 工具栏不显示「新增」按钮 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-047:无 edit 权限时隐藏编辑按钮
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 使用没有 `edit` 权限的账号登录,访问 SPC采集配置 页面 |
|
||||
| **预期结果** | 行内操作列不显示「编辑」按钮 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-048:无 delete 权限时隐藏删除按钮
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 使用没有 `delete` 权限的账号登录,访问 SPC采集配置 页面 |
|
||||
| **预期结果** | 行内操作列不显示「删除」按钮 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.9 错误处理
|
||||
|
||||
#### TC-SCC-049:网络异常 - 列表加载失败
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 关闭后端服务或断开网络 2. 刷新 SPC采集配置 页面 |
|
||||
| **预期结果** | 表格 loading 状态结束,显示空数据占位;页面顶部弹出红色错误提示(来自全局拦截器);表格不卡死 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-SCC-050:网络异常 - 提交失败
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写完整数据 3. 在点击「确定」之前断开网络 4. 点击「确定」 |
|
||||
| **预期结果** | 弹框不关闭,loading 状态结束;弹出红色错误提示;用户可修改后重试 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
---
|
||||
|
||||
## 四、测试结果汇总
|
||||
|
||||
| 用例编号 | 测试项 | 结果 | 问题摘要 |
|
||||
|:--------:|--------|:----:|---------|
|
||||
| TC-SCC-001 | 菜单导航与页面加载 | ⬜ | |
|
||||
| TC-SCC-002 | 列表表格列头与数据展示 | ⬜ | |
|
||||
| TC-SCC-003 | 状态列 Tag 展示 | ⬜ | |
|
||||
| TC-SCC-004 | 工具栏按钮显示 | ⬜ | |
|
||||
| TC-SCC-005 | 表格自适应高度 | ⬜ | |
|
||||
| TC-SCC-006 | 按采集编码精确搜索 | ⬜ | |
|
||||
| TC-SCC-007 | 按采集编码模糊搜索 | ⬜ | |
|
||||
| TC-SCC-008 | 按采集名称搜索 | ⬜ | |
|
||||
| TC-SCC-009 | 按 SCADA 节点搜索 | ⬜ | |
|
||||
| TC-SCC-010 | 按状态筛选 | ⬜ | |
|
||||
| TC-SCC-011 | 组合条件搜索 | ⬜ | |
|
||||
| TC-SCC-012 | 回车键触发搜索 | ⬜ | |
|
||||
| TC-SCC-013 | 重置搜索 | ⬜ | |
|
||||
| TC-SCC-014 | 搜索无结果 | ⬜ | |
|
||||
| TC-SCC-015 | 分页默认显示 | ⬜ | |
|
||||
| TC-SCC-016 | 切换每页条数 | ⬜ | |
|
||||
| TC-SCC-017 | 翻页功能 | ⬜ | |
|
||||
| TC-SCC-018 | 打开新增弹框 | ⬜ | |
|
||||
| TC-SCC-019 | 新增表单必填校验 - 采集编码 | ⬜ | |
|
||||
| TC-SCC-020 | 新增表单必填校验 - 采集名称 | ⬜ | |
|
||||
| TC-SCC-021 | 新增表单必填校验 - SCADA 节点 | ⬜ | |
|
||||
| TC-SCC-022 | 新增表单必填校验 - 采集类型 | ⬜ | |
|
||||
| TC-SCC-023 | 新增表单必填校验 - 状态 | ⬜ | |
|
||||
| TC-SCC-024 | 新增成功 - 实时采集类型 | ⬜ | |
|
||||
| TC-SCC-025 | 新增成功 - 周期采集类型 | ⬜ | |
|
||||
| TC-SCC-026 | 新增成功 - 触发采集类型 | ⬜ | |
|
||||
| TC-SCC-027 | 新增 - 编码长度校验 | ⬜ | |
|
||||
| TC-SCC-028 | 新增 - 编码重复 | ⬜ | |
|
||||
| TC-SCC-029 | 取消新增 | ⬜ | |
|
||||
| TC-SCC-030 | 新增 - 备注多行输入 | ⬜ | |
|
||||
| TC-SCC-031 | 打开编辑弹框 | ⬜ | |
|
||||
| TC-SCC-032 | 编辑成功 - 修改名称 | ⬜ | |
|
||||
| TC-SCC-033 | 编辑成功 - 修改状态 | ⬜ | |
|
||||
| TC-SCC-034 | 编辑成功 - 修改采集间隔 | ⬜ | |
|
||||
| TC-SCC-035 | 编辑后清空必填项 - 采集名称 | ⬜ | |
|
||||
| TC-SCC-036 | 编辑后清空必填项 - SCADA 节点 | ⬜ | |
|
||||
| TC-SCC-037 | 取消编辑 | ⬜ | |
|
||||
| TC-SCC-038 | 删除确认弹框 | ⬜ | |
|
||||
| TC-SCC-039 | 取消删除 | ⬜ | |
|
||||
| TC-SCC-040 | 删除成功 | ⬜ | |
|
||||
| TC-SCC-041 | 删除最后一行后页码修正 | ⬜ | |
|
||||
| TC-SCC-042 | 中文显示 | ⬜ | |
|
||||
| TC-SCC-043 | 英文显示 | ⬜ | |
|
||||
| TC-SCC-044 | 弹框 i18n 自动响应 | ⬜ | |
|
||||
| TC-SCC-045 | 缺失 key 校验 | ⬜ | |
|
||||
| TC-SCC-046 | 无 create 权限时隐藏新增按钮 | ⬜ | |
|
||||
| TC-SCC-047 | 无 edit 权限时隐藏编辑按钮 | ⬜ | |
|
||||
| TC-SCC-048 | 无 delete 权限时隐藏删除按钮 | ⬜ | |
|
||||
| TC-SCC-049 | 网络异常 - 列表加载失败 | ⬜ | |
|
||||
| TC-SCC-050 | 网络异常 - 提交失败 | ⬜ | |
|
||||
|
||||
---
|
||||
|
||||
## 五、问题记录
|
||||
|
||||
| 序号 | 用例编号 | 问题描述 | 复现步骤 | 严重程度 | 截图 | 处理状态 | 责任人 |
|
||||
|:----:|:--------:|---------|---------|:--------:|:----:|:--------:|--------|
|
||||
| 1 | | | | | | | |
|
||||
| 2 | | | | | | | |
|
||||
| 3 | | | | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 六、测试结论
|
||||
|
||||
| 项目 | 描述 |
|
||||
|------|------|
|
||||
| 测试用例总数 | 50 |
|
||||
| 通过用例数 | |
|
||||
| 未通过用例数 | |
|
||||
| 通过率 | |
|
||||
| 是否达到上线标准 | ⬜ 是 / ⬜ 否 |
|
||||
| 备注 | |
|
||||
|
||||
**测试人员**:________________
|
||||
|
||||
**测试日期**:________________
|
||||
|
||||
**审核人员**:________________
|
||||
|
||||
**审核日期**:________________
|
||||
698
docs/功能测试-设备类别.md
Normal file
698
docs/功能测试-设备类别.md
Normal file
@@ -0,0 +1,698 @@
|
||||
# 设备类别功能测试流程文档
|
||||
|
||||
> **测试对象**:设备管理 → 设备模型 → 设备类别(Equipment Category)
|
||||
> **迁移日期**:2026-06-02
|
||||
> **对应页面**:`src/views/equipment-management/equipment-model/equipment-category/index.vue`
|
||||
> **API 文件**:`src/api/equipment-management/equipment-category.js`
|
||||
> **路由路径**:`/device_management/equipment_model/equipment_category`(旧值暂留)
|
||||
> **i18n 前缀**:`page.equipment_management.equipment_model.equipment_category`
|
||||
|
||||
---
|
||||
|
||||
## 一、测试环境配置
|
||||
|
||||
| 配置项 | 要求 |
|
||||
|--------|------|
|
||||
| 浏览器 | Chrome 最新版、Edge 最新版(兼容 IE 11+) |
|
||||
| 屏幕分辨率 | ≥ 1920×1080 |
|
||||
| Node.js | ≥ 16.x |
|
||||
| 包管理器 | pnpm ≥ 7.x |
|
||||
| 后端服务 | 设备类别相关 API 正常运行(`device_management/equipment_model/equipment_category/*`) |
|
||||
| 测试账号 | 具有设备管理 → 设备类别菜单权限的账号(含 create/edit/delete 权限) |
|
||||
| 测试数据 | 准备至少 5 条测试用设备类别(编码唯一) |
|
||||
| 语言切换 | 先测中文(简体中文),再切到英文验证 i18n |
|
||||
| 权限分配 | 系统管理 → 菜单管理中确认设备类别按钮权限(新增、编辑、删除)已正确配置 |
|
||||
|
||||
---
|
||||
|
||||
## 二、测试前置条件
|
||||
|
||||
1. 后端服务(Webman)已启动,`device_management` 模块的 API 可正常访问
|
||||
2. 前端项目已执行 `pnpm install` 且 `pnpm dev` 正常启动,可访问登录页
|
||||
3. 使用具有「设备管理 → 设备类别」菜单权限的管理员账号登录
|
||||
4. 侧边栏菜单 **设备管理 → 设备模型** 下可见 **设备类别** 子菜单
|
||||
5. 浏览器控制台无 Vue 警告或 i18n key 缺失报错
|
||||
6. 确认 `src/locales/zh-chs.json` 和 `src/locales/en.json` 中存在 `page.equipment_management.equipment_model.equipment_category` 配置
|
||||
7. 数据库中设备类别表为空或仅有少量测试数据,便于验证
|
||||
|
||||
---
|
||||
|
||||
## 三、功能测试用例
|
||||
|
||||
### 3.1 页面加载与数据展示
|
||||
|
||||
#### TC-EC-001:菜单导航与页面加载
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录系统 2. 点击侧边栏菜单「设备管理 → 设备模型 → 设备类别」 |
|
||||
| **预期结果** | 页面正常加载,URL 显示 `/device_management/equipment_model/equipment_category`;页面顶部显示搜索栏(设备类别编码、设备类别名称、查询、重置按钮);中部显示设备类别列表表格;底部分页组件显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-002:列表表格列头与数据展示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看设备类别列表的表头列与各行数据 |
|
||||
| **预期结果** | 表格列依次为:序号、设备类别编码、设备类别名称、备注、操作;操作列包含「编辑」「删除」两个按钮(带图标);当数据为空时显示空数据占位提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-003:工具栏按钮显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看表格上方的工具栏 |
|
||||
| **预期结果** | 显示蓝色「新增」按钮(含加号图标),右上角显示问号「帮助」按钮(点击跳转 `/help/equipment-category`) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-004:表格自适应高度
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 加载页面后查看表格高度 2. 缩小/放大浏览器窗口 3. 折叠/展开侧边栏 |
|
||||
| **预期结果** | 表格高度自动填满 d2-container 剩余空间,窗口变化时无空白或滚动条异常 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.2 搜索功能
|
||||
|
||||
#### TC-EC-005:按编码精确搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「设备类别编码」输入框输入完整的类别编码(如 `EQ_CAT_001`) 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示编码匹配的数据行;分页总数更新为匹配条数;输入框右侧 X 按钮可清空内容 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-006:按编码模糊搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「设备类别编码」输入框输入编码的片段(如 `EQ_`) 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格显示所有编码包含 `EQ_` 的数据行 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-007:按名称搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「设备类别名称」输入框输入关键字(如「自动化」) 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格仅显示名称包含关键字的设备类别 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-008:组合条件搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 同时输入「设备类别编码」和「设备类别名称」搜索条件 2. 点击「查询」按钮 |
|
||||
| **预期结果** | 表格显示同时满足两个条件的数据行(AND 关系) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-009:回车键触发搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在搜索输入框中输入关键字 2. 按下键盘回车键 |
|
||||
| **预期结果** | 等同于点击「查询」按钮,表格按搜索条件刷新 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-010:重置搜索
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 输入搜索条件并点击「查询」 2. 点击「重置」按钮 |
|
||||
| **预期结果** | 搜索输入框清空,表格恢复显示全部设备类别数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-011:搜索无结果
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 输入不存在的编码或名称 2. 点击「查询」 |
|
||||
| **预期结果** | 表格显示空数据占位提示,分页总数显示 0 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.3 分页功能
|
||||
|
||||
#### TC-EC-012:分页默认显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 加载页面后查看分页组件 |
|
||||
| **预期结果** | 分页默认显示第 1 页,每页 10 条记录,总条数显示当前数据库设备类别总数 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-013:切换每页条数
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在分页组件的「每页显示」下拉中切换为 20、50、100 2. 观察表格变化 |
|
||||
| **预期结果** | 表格按新条数加载数据,分页组件正确显示总页数 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-014:翻页功能
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 准备至少 25 条测试数据 2. 点击「下一页」「上一页」「首页」「末页」按钮 3. 输入页码跳转 |
|
||||
| **预期结果** | 表格按页加载数据,翻页流畅无报错 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.4 新增设备类别
|
||||
|
||||
#### TC-EC-015:打开新增弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击表格上方「新增」按钮 |
|
||||
| **预期结果** | 弹出新增设备类别对话框,标题显示"新增设备类别",包含设备类别编码、设备类别名称、备注三个表单项;「确定」「取消」按钮位于底部 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-016:新增表单必填校验
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 不填写任何内容直接点击「确定」 |
|
||||
| **预期结果** | 设备类别编码、设备类别名称输入框下方显示红色校验提示"请输入设备类别编码"、"请输入设备类别名称" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-017:新增成功 - 正常数据
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 输入编码 `EQ_CAT_TEST_001` 3. 输入名称「测试设备类别001」 4. 输入备注「测试用」 5. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,页面右上角显示绿色成功提示"操作成功";表格自动刷新并显示新增的设备类别行 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-018:新增成功 - 仅必填
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 仅填写编码和名称,备注留空 3. 点击「确定」 |
|
||||
| **预期结果** | 新增成功,备注字段允许为空 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-019:新增 - 编码长度校验
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在编码输入框输入超过 100 个字符 3. 失焦后观察 |
|
||||
| **预期结果** | 输入框下方显示校验提示"长度在 1 到 100 个字符" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-020:新增 - 编码重复
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 输入已存在的设备类别编码 3. 点击「确定」 |
|
||||
| **预期结果** | 提示后端返回的"编码已存在"等错误信息,新增失败 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-021:取消新增
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写部分内容 3. 点击「取消」按钮 |
|
||||
| **预期结果** | 弹框关闭,表格数据无变化,无任何提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-022:新增 - 备注多行输入
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在备注框中输入多行文字(超过 2 行) 3. 观察文本框是否自动扩展 |
|
||||
| **预期结果** | 备注为 textarea 类型,输入多行内容后自动扩展(最少 2 行,最多 6 行) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.5 编辑设备类别
|
||||
|
||||
#### TC-EC-023:打开编辑弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击某一设备类别行操作列的「编辑」按钮 |
|
||||
| **预期结果** | 弹出编辑设备类别对话框,标题显示"编辑设备类别",设备类别编码、设备类别名称、备注三个字段正确回填该行的数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-024:编辑成功
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 修改设备类别名称为"测试设备类别001-改" 3. 点击「确定」 |
|
||||
| **预期结果** | 弹框关闭,提示"操作成功",表格对应行的名称列更新为新值 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-025:编辑后清空必填项
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 清空设备类别名称输入框 3. 点击「确定」 |
|
||||
| **预期结果** | 设备类别名称下方显示红色校验提示,无法提交 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-026:取消编辑
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开编辑弹框 2. 修改部分字段 3. 点击「取消」按钮 |
|
||||
| **预期结果** | 弹框关闭,表格数据不变 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.6 删除设备类别
|
||||
|
||||
#### TC-EC-027:删除确认弹框
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 点击某一设备类别行操作列的「删除」按钮 |
|
||||
| **预期结果** | 弹出 Element UI 确认提示框,内容为"确定要删除该设备类别吗?",包含「确定」和「取消」按钮,图标为警告三角 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-028:删除成功
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 点击「删除」按钮 2. 在确认框中点击「确定」 |
|
||||
| **预期结果** | 确认框关闭,提示"操作成功",该设备类别从列表中消失,分页总数减 1 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-029:取消删除
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 点击「删除」按钮 2. 在确认框中点击「取消」 |
|
||||
| **预期结果** | 确认框关闭,设备类别数据不变 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-030:删除最后一页最后一条
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 翻到最后一页且只剩 1 条数据 2. 删除该数据 3. 观察分页跳转 |
|
||||
| **预期结果** | 删除成功后自动跳转到新的最后一页,无空白页 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.7 权限控制
|
||||
|
||||
#### TC-EC-031:无新增权限 - 按钮隐藏
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录无新增权限(`/device_management/equipment_model/equipment_category/create`)的账号 2. 进入设备类别页面 |
|
||||
| **预期结果** | 工具栏「新增」按钮不显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-032:无编辑权限 - 按钮隐藏
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录无编辑权限(`/device_management/equipment_model/equipment_category/edit`)的账号 2. 进入设备类别页面 |
|
||||
| **预期结果** | 行操作列「编辑」按钮不显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-033:无删除权限 - 按钮隐藏
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录无删除权限(`/device_management/equipment_model/equipment_category/delete`)的账号 2. 进入设备类别页面 |
|
||||
| **预期结果** | 行操作列「删除」按钮不显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-034:完全无权限 - 仅可查看
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 登录仅有查看权限的账号 2. 进入设备类别页面 |
|
||||
| **预期结果** | 表格正常加载数据;工具栏无「新增」按钮;行操作列无「编辑」「删除」按钮 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.8 国际化(i18n)
|
||||
|
||||
#### TC-EC-035:简体中文 - 页面显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换系统语言为简体中文 2. 进入设备类别页面 |
|
||||
| **预期结果** | 所有列头、按钮、表单标签、提示信息均显示中文("设备类别编码"、"设备类别名称"、"备注"、"操作"、"新增"、"编辑"、"删除"等) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-036:英文 - 页面显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换系统语言为 English 2. 进入设备类别页面 |
|
||||
| **预期结果** | 所有列头、按钮、表单标签、提示信息均显示英文("Equipment Category Code"、"Equipment Category Name"、"Remark"、"Actions"、"Add"、"Edit"、"Delete"等) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-037:英文 - 弹框显示
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 点击「新增」按钮 |
|
||||
| **预期结果** | 弹框标题显示"Add Equipment Category",表单标签显示"Equipment Category Code"、"Equipment Category Name"、"Remark";底部按钮显示"Confirm"、"Cancel" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-038:英文 - 删除确认
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换到英文 2. 点击某行的「Delete」按钮 |
|
||||
| **预期结果** | 确认框内容显示"Are you sure to delete this equipment category?",按钮显示"Confirm"、"Cancel" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-039:语言切换实时响应
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在中文下打开新增弹框 2. 不关闭弹框,直接切换系统语言到英文 |
|
||||
| **预期结果** | 弹框内所有文字立即切换为英文,无需刷新页面 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.9 异常与边界场景
|
||||
|
||||
#### TC-EC-040:网络异常 - 列表加载失败
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 停止后端服务 2. 刷新设备类别页面 |
|
||||
| **预期结果** | 页面显示 loading 结束后,弹出错误提示(如"请求失败"或"数据请求异常"),表格显示空数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-041:网络异常 - 新增失败
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 停止后端服务 3. 填写表单并点击「确定」 |
|
||||
| **预期结果** | 提示网络错误信息,弹框不关闭,确定按钮恢复可点击 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-042:特殊字符输入
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在设备类别名称中输入特殊字符(如 `!@#$%^&*()_+-=[]{}|;':",./<>?`) 3. 点击「确定」 |
|
||||
| **预期结果** | 后端允许特殊字符时新增成功;不允许时显示后端错误提示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-043:超长备注输入
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 在备注中输入超过 500 字符的文本 3. 点击「确定」 |
|
||||
| **预期结果** | textarea 自动扩展(最多 6 行出现滚动条),新增/编辑成功 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-044:备注包含换行符
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增/编辑弹框 2. 在备注中输入多行文字(含回车换行) 3. 提交后再次编辑查看 |
|
||||
| **预期结果** | 备注正确保存换行符,再次打开编辑时能正确显示多行内容 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-045:浏览器刷新页面
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在搜索栏输入条件后查询 2. 按 F5 或点击浏览器刷新按钮 |
|
||||
| **预期结果** | 页面重新加载,搜索条件清空,表格显示全部数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.10 UI 与交互细节
|
||||
|
||||
#### TC-EC-047:搜索区 - 标签文本正确
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看搜索区的标签(label) |
|
||||
| **预期结果** | 显示"设备类别编码"、"设备类别名称";输入框 placeholder 显示"请输入设备类别编码"、"请输入设备类别名称" |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-048:搜索区 - 按钮图标正确
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看搜索区的按钮 |
|
||||
| **预期结果** | 「查询」按钮为蓝色 primary 样式,左侧带放大镜图标;「重置」按钮为默认样式,左侧带刷新图标 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-049:操作列 - 按钮样式
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 查看行操作列 |
|
||||
| **预期结果** | 「编辑」按钮为蓝色文字带编辑图标;「删除」按钮为红色文字带删除图标;按钮间有一定间距;操作列固定在右侧 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-050:弹框 - 表单交互
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 观察表单布局 |
|
||||
| **预期结果** | 表单 label 宽度 110px;输入框占 90% 宽度;备注为 textarea,可自适应高度(2-6 行);所有表单项垂直排列;弹框宽度为 35% |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-051:弹框 - 确定按钮 loading
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开新增弹框 2. 填写表单 3. 在网络较慢情况下点击「确定」 |
|
||||
| **预期结果** | 「确定」按钮显示 loading 状态,无法重复点击;接口返回后自动关闭弹框 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-052:空数据占位
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 清空设备类别表(或筛选不存在的条件) 2. 查看表格 |
|
||||
| **预期结果** | 表格区域显示 Element UI 默认的空数据占位("暂无数据" 或类似提示) |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### 3.11 数据一致性
|
||||
|
||||
#### TC-EC-053:新增后分页正确
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在有 10 条数据时(每页 10 条) 2. 新增 1 条数据 |
|
||||
| **预期结果** | 新增后分页总数变为 11,自动跳转到第 1 页并显示新数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-054:编辑后数据刷新
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 编辑某行数据并保存 2. 重新搜索该数据 |
|
||||
| **预期结果** | 搜索结果显示编辑后的最新数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
#### TC-EC-055:删除后数据不残留
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 删除某条数据 2. 重新搜索该编码或刷新页面 |
|
||||
| **预期结果** | 搜索结果显示无此数据 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
---
|
||||
|
||||
## 四、兼容性测试
|
||||
|
||||
### TC-EC-056:Chrome 浏览器
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 使用 Chrome 最新版执行 TC-EC-001 ~ TC-EC-055 全部用例 |
|
||||
| **预期结果** | 全部通过 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-EC-057:Edge 浏览器
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 使用 Edge 最新版执行核心用例 TC-EC-001/015/023/027/035/036 |
|
||||
| **预期结果** | 全部通过 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-EC-058:不同分辨率适配
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 切换浏览器窗口分辨率为 1920×1080、1366×768 2. 验证页面布局 |
|
||||
| **预期结果** | 表格、分页、弹框等元素无错位,按钮可正常点击 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
---
|
||||
|
||||
## 五、回归测试
|
||||
|
||||
### TC-EC-059:菜单切换不卡顿
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 在「设备类别」页面停留 5 秒 2. 切换到「设备管理」其他子页面 3. 再切回「设备类别」 |
|
||||
| **预期结果** | 页面切换流畅,无报错,数据正确加载 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
### TC-EC-060:浏览器标签页切换
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试步骤** | 1. 打开「设备类别」页面 2. 切换到其他浏览器标签页 3. 30 秒后切回 |
|
||||
| **预期结果** | 页面数据无丢失,Token 未过期情况下正常显示 |
|
||||
| **实际结果** | |
|
||||
| **测试状态** | ⬜ 通过 / ⬜ 未通过 |
|
||||
| **问题描述** | |
|
||||
|
||||
---
|
||||
|
||||
## 六、测试通过标准
|
||||
|
||||
| 类别 | 描述 |
|
||||
|------|------|
|
||||
| ✅ 全部通过 | 全部测试用例均通过 |
|
||||
| ⚠️ 部分通过 | 个别非核心用例未通过,但不影响主要功能使用 |
|
||||
| ❌ 未通过 | 存在阻塞性缺陷(页面无法加载、核心 CRUD 失败、权限绕过等) |
|
||||
|
||||
---
|
||||
|
||||
## 七、问题记录汇总
|
||||
|
||||
| 问题编号 | 关联测试用例 | 问题描述 | 严重程度 | 处理状态 | 处理人 |
|
||||
|---------|------------|---------|---------|---------|-------|
|
||||
| | | | | | |
|
||||
| | | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 八、测试结论
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| **测试人员** | |
|
||||
| **测试日期** | |
|
||||
| **测试用例总数** | 60 |
|
||||
| **通过用例数** | |
|
||||
| **未通过用例数** | |
|
||||
| **遗留问题数** | |
|
||||
| **整体结论** | ⬜ 通过 ⬜ 有条件通过 ⬜ 未通过 |
|
||||
| **备注说明** | |
|
||||
|
||||
---
|
||||
|
||||
> **文档维护说明**:本测试文档为设备类别功能独立测试文档。后续该功能代码变更(Bug 修复、新增功能、需求变更)时,需同步更新本测试文档并重新执行相关测试用例。
|
||||
210
docs/功能测试流程-异常不良管理.md
Normal file
210
docs/功能测试流程-异常不良管理.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# 功能测试流程文档 ——【异常不良管理】
|
||||
|
||||
> 本文档为【异常不良管理】功能迁移后的独立测试文档。测试人员请按以下流程逐步执行,对通过的用例在"通过"列打勾(`[x]`),未通过的用例在"问题记录"列描述具体现象(包括错误截图编号、操作步骤、实际/预期差异等)。
|
||||
|
||||
| 项目名称 | MES-UI(生产主数据 → 产品模型 → 异常不良管理) |
|
||||
| --- | --- |
|
||||
| 文档版本 | v1.0 |
|
||||
| 适用版本 | mes-ui 本次迁移版本 |
|
||||
| 编写日期 | 2026-06-02 |
|
||||
| 测试入口 | 菜单:生产主数据 → 产品模型 → 异常不良管理 |
|
||||
| 关联文件 | [主页面](file:///d:/code/mes/mes-ui/src/views/production-master-data/product-model/product-ng-info/index.vue) · [导入组件](file:///d:/code/mes/mes-ui/src/views/production-master-data/product-model/product-ng-info/components/ImportDialog/index.vue) · [API](file:///d:/code/mes/mes-ui/src/api/production-master-data/product-ng-info.js) · [路由](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js) · [文件工具](file:///d:/code/mes/mes-ui/src/utils/file.js) |
|
||||
|
||||
---
|
||||
|
||||
## 一、测试环境配置要求
|
||||
|
||||
| 项 | 要求 |
|
||||
| --- | --- |
|
||||
| 后台环境 | Webman + 已部署 `production_configuration/product_model/product_ng_info/` 路由接口;数据库中已创建 `product_ng_info` 与 `device_category` 数据表 |
|
||||
| 前端环境 | `npm run serve` / `pnpm run serve` 启动 mes-ui 工程;浏览器推荐 Chrome 110+ |
|
||||
| 登录账号 | 拥有「生产主数据 / 异常不良管理」菜单访问权限,且分配以下权限点:<br>· `…/product_ng_info/create`(新增 / 导入)<br>· `…/product_ng_info/edit`(编辑)<br>· `…/product_ng_info/delete`(删除 / 批量删除)<br>· `…/product_ng_info/export`(导出) |
|
||||
| 网络要求 | 前后端网络互通;浏览器可访问 `VUE_APP_API` 环境变量对应域名 |
|
||||
| 数据准备 | · 至少 3 条设备类别(device_category)数据<br>· 至少 5 条异常不良类别记录<br>· 准备一份合法格式的 Excel 导入文件(含 3 行导入数据)和一份缺少列的非法文件 |
|
||||
| 浏览器工具 | 打开 DevTools(F12)→ Network 与 Console,便于抓取接口与异常 |
|
||||
| 第三方库 | `xlsx` 已安装(Excel 读写支持),确认 `node_modules/xlsx/` 目录存在 |
|
||||
|
||||
---
|
||||
|
||||
## 二、测试前置条件
|
||||
|
||||
1. 已成功登录系统,登录账号具备上述所有权限点。
|
||||
2. 侧边栏显示 **生产主数据 → 产品模型 → 异常不良管理** 菜单可点击进入。
|
||||
3. 浏览器语言切换为「简体中文」,界面文字全部为中文,无 `key is not defined` 或英文缺失。
|
||||
4. 列表默认展示至少 5 条记录(通过 `product_ng_info/list` 接口返回)。
|
||||
5. 浏览器缩放比例 100%;分辨率建议 1440×900 及以上。
|
||||
6. DevTools 关闭缓存(Network → Disable cache),确保加载最新前端资源。
|
||||
|
||||
---
|
||||
|
||||
## 三、测试用例
|
||||
|
||||
> **字段说明**:
|
||||
> - 设备类别 `device_category_id`:必选,从设备类别 API 动态加载
|
||||
> - 类别 `type`:必选,下拉值「异常(ERR)/不良(NG)」
|
||||
> - 异常不良编码 `number`:必填,长度 1~100
|
||||
> - 异常不良名称 `explain`:必填,长度 1~100
|
||||
> - 备注 `note`:选填,多行文本
|
||||
> - 列表展示字段:复选框 / 设备类别 / 异常不良类别 / 异常不良编码 / 异常不良名称 / 备注 / 操作
|
||||
|
||||
### 3.1 列表展示与加载
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.1.1 | 进入「异常不良管理」菜单 | 列表正常加载,无 JS 报错;表头依次为:复选框、设备类别、异常不良类别、异常不良编码、异常不良名称、备注、操作 | | [ ] | |
|
||||
| 3.1.2 | 观察列表加载过程 | 列表加载期间显示 loading 遮罩动画,加载完成后消失 | | [ ] | |
|
||||
| 3.1.3 | 列表为空时(搜索条件无匹配结果) | 表格区域显示空状态占位(如"暂无数据"),无 JavaScript 报错 | | [ ] | |
|
||||
| 3.1.4 | 分页组件:跳转到第 2 页 | 请求参数 `page_no=2` 正确,数据刷新 | | [ ] | |
|
||||
| 3.1.5 | 修改每页条数(如从 10 改为 20) | `page_size=20`,列表重载 | | [ ] | |
|
||||
| 3.1.6 | 列表行内操作列固定在右侧,横向滚动查看 | 操作列不随滚动条消失,固定可见 | | [ ] | |
|
||||
|
||||
### 3.2 搜索功能
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.2.1 | 点击「设备类别」下拉框 → 从列表中选择一项 → 点击「查询」 | 列表仅展示所选设备类别的记录 | | [ ] | |
|
||||
| 3.2.2 | 在「查询类型」下拉框中选择「异常」→ 点击「查询」 | 列表仅展示 `type=ERR` 的记录 | | [ ] | |
|
||||
| 3.2.3 | 在「查询类型」下拉框中选择「不良」→ 点击「查询」 | 列表仅展示 `type=NG` 的记录 | | [ ] | |
|
||||
| 3.2.4 | 在「异常不良编码」输入框输入关键字 → 点击「查询」 | 列表仅展示编码包含该关键字的记录 | | [ ] | |
|
||||
| 3.2.5 | 在「异常不良名称」输入框输入关键字 → 点击「查询」 | 列表仅展示名称包含该关键字的记录 | | [ ] | |
|
||||
| 3.2.6 | 设备类别 + 类型 + 编码同时填写 → 点击「查询」 | 列表为三条件 AND 过滤结果 | | [ ] | |
|
||||
| 3.2.7 | 输入查询条件后点击「重置」 | 所有输入框、下拉框恢复初始状态,列表恢复为全量数据 | | [ ] | |
|
||||
| 3.2.8 | 在搜索输入框中按回车键 | 等同于点击「查询」,触发列表刷新 | | [ ] | |
|
||||
| 3.2.9 | 设备类别下拉框首次展开时(即 focus 事件) | 自动调用 `/device_category/all` 加载选项;未展开时不再重复请求 | | [ ] | |
|
||||
|
||||
### 3.3 新增
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.3.1 | 点击工具栏「新增」按钮 | 弹出标题为「新增异常不良类别」的对话框,表单为空 | | [ ] | |
|
||||
| 3.3.2 | 新增弹框打开时,设备类别下拉框自动加载可选项 | 设备类别下拉列表有数据,与搜索时的下拉来源一致 | | [ ] | |
|
||||
| 3.3.3 | 不填任何字段,点击「确定」 | 表单校验:设备类别、类别、编码、名称四个字段下方均红字提示必填 | | [ ] | |
|
||||
| 3.3.4 | 仅选择设备类别与类别,不填编码 → 点击「确定」 | 仅编码字段提示必填;名称字段提示必填 | | [ ] | |
|
||||
| 3.3.5 | 编码输入 101 字符 → 点击「确定」 | 编码字段下方提示「长度在1到100个字符」 | | [ ] | |
|
||||
| 3.3.6 | 名称输入 101 字符 → 点击「确定」 | 名称字段下方提示「长度在1到100个字符」 | | [ ] | |
|
||||
| 3.3.7 | 正常填写:设备类别=任意、类别=异常、编码=ERR-01、名称=设备宕机、备注=生产设备意外停机 → 点击「确定」 | 弹出"操作成功"提示,对话框自动关闭,列表自动刷新并出现该条记录 | | [ ] | |
|
||||
| 3.3.8 | 新增弹框点击「取消」或右上角 × | 对话框关闭,列表未新增任何记录 | | [ ] | |
|
||||
| 3.3.9 | 使用无 `product_ng_info/create` 权限的账号访问 | 工具栏不显示「新增」按钮 | | [ ] | |
|
||||
| 3.3.10 | 后端返回「编码已存在」业务错误 | 页面友好提示错误原因,弹框不关闭(可修正后重试) | | [ ] | |
|
||||
|
||||
### 3.4 编辑
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.4.1 | 列表行点击「编辑」 | 弹出标题为「编辑异常不良类别」的对话框,表单字段回显该行当前数据 | | [ ] | |
|
||||
| 3.4.2 | 清空编码后「确定」 | 提示编码必填 | | [ ] | |
|
||||
| 3.4.3 | 修改名称为新值,备注保留为空 → 「确定」 | 操作成功,列表对应行名称更新,备注为空 | | [ ] | |
|
||||
| 3.4.4 | 修改编码(与已有数据重复)→ 「确定」 | 后端返回重复提示,页面友好显示不崩溃 | | [ ] | |
|
||||
| 3.4.5 | 编辑对话中点击「取消」 | 对话框关闭,列表数据未变化 | | [ ] | |
|
||||
| 3.4.6 | 无 `product_ng_info/edit` 权限的账号 | 行内不显示「编辑」按钮 | | [ ] | |
|
||||
| 3.4.7 | 两个标签页同时打开同一条编辑 → A 保存后 B 再次保存 | 后端返回最新数据的验证结果,不产生脏写 | | [ ] | |
|
||||
|
||||
### 3.5 删除(行内删除)
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.5.1 | 列表行点击「删除」 | 弹出确认框,提示「确定要执行该操作吗?」 | | [ ] | |
|
||||
| 3.5.2 | 确认框点击「取消」 | 关闭确认框,数据未删除 | | [ ] | |
|
||||
| 3.5.3 | 确认框点击「确定」 | 操作成功,列表自动移除该记录 | | [ ] | |
|
||||
| 3.5.4 | 删除最后一页唯一一条数据 | 列表自动回退到上一页(或空状态),不出现空白页 | | [ ] | |
|
||||
| 3.5.5 | 删除已被其他业务引用的记录 | 后端返回业务级错误提示,数据保留,页面不崩溃 | | [ ] | |
|
||||
| 3.5.6 | 无 `product_ng_info/delete` 权限的账号 | 行内不显示「删除」按钮 | | [ ] | |
|
||||
| 3.5.7 | 网络断开时点击「删除」 | 提示网络异常,数据未被误删 | | [ ] | |
|
||||
|
||||
### 3.6 批量删除
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.6.1 | 不勾选任何复选框,点击「批量删除」 | 提示"请先选择数据" | | [ ] | |
|
||||
| 3.6.2 | 勾选 2~3 行记录 → 点击「批量删除」 | 弹出确认框「确定要删除所选异常不良类别吗?」 | | [ ] | |
|
||||
| 3.6.3 | 确认框点击「确定」 | 选中的记录全部从列表消失,操作成功提示 | | [ ] | |
|
||||
| 3.6.4 | 确认框点击「取消」 | 数据保留,勾选状态保留 | | [ ] | |
|
||||
| 3.6.5 | 选中的行中包含已被引用的数据 | 后端返回针对性的业务错误提示,未删除任何数据 | | [ ] | |
|
||||
| 3.6.6 | 勾选全选(表头复选框)→ 批量删除 | 所有可见页数据被选中并删除 | | [ ] | |
|
||||
|
||||
### 3.7 Excel 导入
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.7.1 | 点击工具栏「导入」按钮 | 弹出标题为「导入异常不良数据」的对话框,上方显示黄色警告提示 | | [ ] | |
|
||||
| 3.7.2 | 点击「下载模板」按钮 | 浏览器下载 Excel 文件(文件名为「异常不良数据导入模版.xlsx」),内容包含表头 | | [ ] | |
|
||||
| 3.7.3 | 点击「选择文件」→ 选择非 Excel 文件(如 .txt) | 提示"上传文件格式错误" | | [ ] | |
|
||||
| 3.7.4 | 点击「选择文件」→ 选择列缺失的 Excel(缺少"异常不良编码"列) | 提示"文件列缺失: 异常不良编码" | | [ ] | |
|
||||
| 3.7.5 | 选择合法 Excel 文件(含 3 行数据) | 预览表格展示 3 行数据,列与 Excel 内容一致 | | [ ] | |
|
||||
| 3.7.6 | 选择合法 Excel 后,再次选择另一个文件 | 上一个文件被替换,预览表格刷新为新文件数据 | | [ ] | |
|
||||
| 3.7.7 | 预览数据无误后点击「确定」 | 提示"操作成功",弹框关闭,列表刷新,新数据出现在列表中 | | [ ] | |
|
||||
| 3.7.8 | 未选择文件(预览表格为空)点击「确定」 | 提示"请先导入数据" | | [ ] | |
|
||||
| 3.7.9 | 导入过程中点击「取消」或关闭弹框 | 操作中断,已读取的预览数据被清空 | | [ ] | |
|
||||
| 3.7.10 | 无 `product_ng_info/create` 权限的账号 | 工具栏不显示「导入」按钮 | | [ ] | |
|
||||
| 3.7.11 | 导入的 Excel 中设备类别名称在后端无法匹配 | 后端返回相应错误提示,列表不新增 | | [ ] | |
|
||||
|
||||
### 3.8 Excel 导出
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.8.1 | 点击工具栏「导出」按钮 | 弹出确认框「确定要导出当前查询结果吗?」 | | [ ] | |
|
||||
| 3.8.2 | 确认框点击「取消」 | 提示"操作已取消",未触发任何请求 | | [ ] | |
|
||||
| 3.8.3 | 确认框点击「确定」 | 提示"创建导出任务成功",自动跳转至任务管理页面 | | [ ] | |
|
||||
| 3.8.4 | 在查询条件中筛选后再点击「导出」 | 后端收到的请求包含当前搜索条件 `device_category_id` / `type` / `number` / `explain` | | [ ] | |
|
||||
| 3.8.5 | 无 `product_ng_info/export` 权限的账号 | 工具栏不显示「导出」按钮 | | [ ] | |
|
||||
|
||||
### 3.9 权限与国际化
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.9.1 | 切换为仅有「查询」权限的账号 | 仅能查看列表与查询,无新增/编辑/删除/导入/导出按钮 | | [ ] | |
|
||||
| 3.9.2 | 切换为中文环境 → 英文环境 → 刷新页面 | 表头、按钮、弹框标题、验证提示语全部为英文 | | [ ] | |
|
||||
| 3.9.3 | 中文下打开新增弹框 → 切换至英文 | 弹框标题、表单 label 立即切换为英文(无需关闭弹框) | | [ ] | |
|
||||
| 3.9.4 | 英文环境下切换回中文 | 所有文案恢复中文 | | [ ] | |
|
||||
| 3.9.5 | 英文下执行新增/编辑/导入功能,成功/失败提示 | 提示语为英文,含义正确 | | [ ] | |
|
||||
|
||||
### 3.10 异常与边界
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.10.1 | 后端 `/list` 接口返回 500 错误 | 列表显示空状态或错误提示用 Message 弹窗展示,页面不白屏 | | [ ] | |
|
||||
| 3.10.2 | 后端返回字段缺失(如某行缺少 `device_category`) | 缺失字段列显示为空,不抛 JS 异常 | | [ ] | |
|
||||
| 3.10.3 | 备注字段输入 5000 字符并保存 | 提交成功,列表备注列可滚动展示(建议使用省略号截断) | | [ ] | |
|
||||
| 3.10.4 | 新增/编辑弹框打开后按 ESC 键 | 弹框关闭,表单状态被重置 | | [ ] | |
|
||||
| 3.10.5 | 列表横向宽度超过浏览器视口 | 操作列固定在右侧,表格支持横向滚动 | | [ ] | |
|
||||
| 3.10.6 | DevTools Console 全程监控 | 无 `Vue warn`、无未捕获 Promise 异常、无 i18n key 缺失警告 | | [ ] | |
|
||||
| 3.10.7 | 快速连续点击「新增」→ 「确定」 | 提交按钮 loading 状态阻止重复提交 | | [ ] | |
|
||||
| 3.10.8 | 设备类别 API 请求失败时展开搜索下拉 | 下拉框为空,页面不崩溃;重试正常 | | [ ] | |
|
||||
|
||||
---
|
||||
|
||||
## 四、测试结果汇总
|
||||
|
||||
| 用例总数 | 通过 | 失败 | 阻塞 | 通过率 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 五、问题记录区
|
||||
|
||||
| 编号 | 用例编号 | 复现步骤 | 实际结果 | 严重程度 | 处理人 | 状态 | 备注 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| 1 | | | | | | | |
|
||||
| 2 | | | | | | | |
|
||||
| 3 | | | | | | | |
|
||||
| 4 | | | | | | | |
|
||||
| 5 | | | | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 六、测试结论
|
||||
|
||||
| 项目 | 结论 |
|
||||
| --- | --- |
|
||||
| 功能完整性 | ☐ 满足 ☐ 部分缺失 ☐ 不满足 |
|
||||
| 性能表现 | ☐ 良好 ☐ 一般 ☐ 差 |
|
||||
| 权限控制 | ☐ 正确 ☐ 存在漏洞 |
|
||||
| 国际化 | ☐ 完整 ☐ 部分缺失 ☐ 缺失 |
|
||||
| 导入导出 | ☐ 正常 ☐ 部分异常 ☐ 不可用 |
|
||||
| 是否可发布 | ☐ 是 ☐ 否(请说明阻塞问题) |
|
||||
|
||||
测试人员签字:__________________ 日期:__________
|
||||
|
||||
---
|
||||
*本测试流程文档为【异常不良管理】功能迁移版本专用,请独立归档保存。*
|
||||
153
docs/功能测试流程-计量单位.md
Normal file
153
docs/功能测试流程-计量单位.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# 功能测试流程文档 ——【计量单位】
|
||||
|
||||
> 本文档为【计量单位】功能迁移后的独立测试文档。测试人员请按以下流程逐步执行,对通过的用例在"通过"列打勾(`[x]`),未通过的用例在"问题记录"列描述具体现象(包括错误截图编号、操作步骤、实际/预期差异等)。
|
||||
|
||||
| 项目名称 | MES-UI(生产主数据 → 物料模型 → 计量单位) |
|
||||
| --- | --- |
|
||||
| 文档版本 | v1.0 |
|
||||
| 适用版本 | mes-ui 本次迁移版本 |
|
||||
| 编写日期 | 2026-06-02 |
|
||||
| 测试入口 | 菜单:生产主数据 → 物料模型 → 计量单位 |
|
||||
| 关联文件 | [`src/views/production-master-data/material-model/material-unit/index.vue`](file:///d:/code/mes/mes-ui/src/views/production-master-data/material-model/material-unit/index.vue) [`src/api/production-master-data/material-unit.js`](file:///d:/code/mes/mes-ui/src/api/production-master-data/material-unit.js) [`src/router/modules/production-master-data.js`](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js) |
|
||||
|
||||
---
|
||||
|
||||
## 一、测试环境配置要求
|
||||
|
||||
| 项 | 要求 |
|
||||
| --- | --- |
|
||||
| 后台环境 | Webman + 已部署 `production_configuration/matetial_model/unit` 路由接口;数据库中存在 `unit` 数据表 |
|
||||
| 前端环境 | `npm run serve` 启动 mes-ui 工程;浏览器推荐 Chrome 110+ |
|
||||
| 登录账号 | 拥有 `生产主数据 / 计量单位` 菜单访问权限,且分配以下权限点:<br>· `…/unit/create`(新增)<br>· `…/unit/edit`(编辑)<br>· `…/unit/delete`(删除) |
|
||||
| 网络要求 | 前后端网络互通;浏览器可访问 `VUE_APP_API` 域名 |
|
||||
| 数据准备 | 至少 1 条已存在的单位数据;至少 1 个物料已引用本表(用于验证删除联动提示) |
|
||||
| 浏览器工具 | 打开 DevTools(F12)→ Network 与 Console,方便抓取接口与异常 |
|
||||
|
||||
---
|
||||
|
||||
## 二、测试前置条件
|
||||
|
||||
1. 已成功登录系统,且侧边栏显示 **生产主数据 → 物料模型 → 计量单位** 菜单。
|
||||
2. 浏览器语言分别切换为「简体中文 / English」时,界面文字均能正确翻译,无 `key is not defined` / 英文缺失等情况。
|
||||
3. 列表默认展示接口 `production_configuration/matetial_model/unit/list` 返回的记录。
|
||||
4. 浏览器缩放比例为 100%;分辨率建议 1440×900 及以上。
|
||||
5. DevTools 关闭缓存(Network → Disable cache),避免旧 JS 资源影响测试。
|
||||
|
||||
---
|
||||
|
||||
## 三、测试用例
|
||||
|
||||
> **字段说明(数据库与表单共用)**:
|
||||
> - 编码 `code`:必填,长度 1~100,文本
|
||||
> - 名称 `name`:必填,长度 1~100,文本
|
||||
> - 备注 `remark`:选填,文本域(多行)
|
||||
> - 列表展示字段:序号 / 编码 / 名称 / 备注 / 创建时间 / 操作
|
||||
|
||||
### 3.1 列表与搜索
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.1.1 | 进入"计量单位"菜单 | 列表正常加载,无报错;表头依次为:序号、单位编码、单位名称、备注、创建时间、操作 | | [ ] | |
|
||||
| 3.1.2 | 在"单位编码"输入框输入关键字 → 点击「查询」 | 列表仅展示编码包含该关键字的记录 | | [ ] | |
|
||||
| 3.1.3 | 在"单位名称"输入框输入关键字 → 点击「查询」 | 列表仅展示名称包含该关键字的记录 | | [ ] | |
|
||||
| 3.1.4 | 编码 + 名称同时输入关键字 → 点击「查询」 | 列表为两条件 AND 过滤结果 | | [ ] | |
|
||||
| 3.1.5 | 输入查询条件后点击「重置」 | 输入框清空,列表恢复为全量数据 | | [ ] | |
|
||||
| 3.1.6 | 在搜索输入框按回车键 | 等同于点击「查询」,触发列表刷新 | | [ ] | |
|
||||
| 3.1.7 | 列表分页:跳转到第 2 页 / 修改每页条数 | 列表请求参数 `page_no` / `page_size` 正确变化,数据正常刷新 | | [ ] | |
|
||||
| 3.1.8 | 列表为空时(无数据) | 显示空状态占位(如"暂无数据"),不报错 | | [ ] | |
|
||||
| 3.1.9 | 列表加载中 | 表格区域显示 loading 遮罩 | | [ ] | |
|
||||
| 3.1.10 | 切换浏览器语言为 English 后刷新 | 表头、操作按钮、占位文字均显示英文 | | [ ] | |
|
||||
|
||||
### 3.2 新增
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.2.1 | 点击工具栏「新增」按钮 | 弹出标题为「新增单位」的对话框,显示空表单 | | [ ] | |
|
||||
| 3.2.2 | 不填任何字段,点击「确定」 | 触发表单校验:「单位编码」「单位名称」下方红字提示必填 | | [ ] | |
|
||||
| 3.2.3 | 仅填写编码(不填名称),点击「确定」 | 仅名称字段提示必填 | | [ ] | |
|
||||
| 3.2.4 | 输入 101 字符的编码,点击「确定」 | 提示「长度在 1 到 100 个字符」 | | [ ] | |
|
||||
| 3.2.5 | 正常填写:编码 `PCS`、名称 `个`、备注 `基本计数单位` → 点击「确定」 | 弹出"操作成功"提示,对话框自动关闭,列表自动刷新并出现该条记录 | | [ ] | |
|
||||
| 3.2.6 | 必填项后存在前后空格时点击「确定」 | 提交数据带空格(视业务策略决定是否 trim),列表正常展示 | | [ ] | |
|
||||
| 3.2.7 | 新增弹框点击「取消」 | 对话框关闭,未提交任何数据 | | [ ] | |
|
||||
| 3.2.8 | 新增弹框点击右上角 × | 对话框关闭,未提交任何数据 | | [ ] | |
|
||||
| 3.2.9 | 在无 `unit/create` 权限的账号下访问 | 工具栏不显示「新增」按钮 | | [ ] | |
|
||||
| 3.2.10 | 后端返回 `code 已存在` 错误 | 页面提示后端错误信息(如 `code 重复`),不崩溃 | | [ ] | |
|
||||
|
||||
### 3.3 编辑
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.3.1 | 在列表行点击「编辑」 | 弹出标题为「编辑单位」的对话框,且表单字段回显当前行数据 | | [ ] | |
|
||||
| 3.3.2 | 清空编码,点击「确定」 | 提示编码必填 | | [ ] | |
|
||||
| 3.3.3 | 修改名称为新值,备注保留为空,点击「确定」 | 提示操作成功,列表对应行的名称更新 | | [ ] | |
|
||||
| 3.3.4 | 修改编码(与已有数据重复),点击「确定」 | 后端返回重复提示,页面友好显示 | | [ ] | |
|
||||
| 3.3.5 | 编辑过程中点击「取消」 | 对话框关闭,原数据未发生变化 | | [ ] | |
|
||||
| 3.3.6 | 无 `unit/edit` 权限的账号 | 行内不显示「编辑」按钮 | | [ ] | |
|
||||
| 3.3.7 | 同时打开两个浏览器标签 A、B,均进入编辑 | B 保存后 A 再次保存时数据为 B 已提交后的最新值(避免脏写) | | [ ] | |
|
||||
|
||||
### 3.4 删除
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.4.1 | 在列表行点击「删除」 | 弹出确认框,提示「确定要执行该操作吗?」 | | [ ] | |
|
||||
| 3.4.2 | 确认框点击「取消」 | 关闭确认框,未删除 | | [ ] | |
|
||||
| 3.4.3 | 确认框点击「确定」 | 提示操作成功,列表自动移除该记录 | | [ ] | |
|
||||
| 3.4.4 | 删除被物料引用的单位 | 后端返回业务级提示,页面正确展示且不崩溃 | | [ ] | |
|
||||
| 3.4.5 | 删除最后一页唯一一条记录后 | 列表自动回退到上一页(或显示空状态),无空白页 | | [ ] | |
|
||||
| 3.4.6 | 无 `unit/delete` 权限的账号 | 行内不显示「删除」按钮 | | [ ] | |
|
||||
| 3.4.7 | 网络断开时点击「删除」 | 提示网络异常,未误删数据 | | [ ] | |
|
||||
|
||||
### 3.5 权限与国际化
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.5.1 | 切换为仅有「查询」权限的账号 | 仅能查看列表与查询,无新增/编辑/删除按钮 | | [ ] | |
|
||||
| 3.5.2 | 切换语言为 English,刷新页面 | 表头、按钮、弹框标题、提示语全部为英文 | | [ ] | |
|
||||
| 3.5.3 | 在中文下打开新增 → 切换为英文 | 弹框标题、表单 label 立即切换为英文(无需关闭弹框) | | [ ] | |
|
||||
| 3.5.4 | 切换语言后,校验提示(如"长度在 1 到 100 个字符") | 同步显示对应语言 | | [ ] | |
|
||||
|
||||
### 3.6 异常与边界
|
||||
|
||||
| 用例编号 | 操作步骤 | 预期结果 | 实际结果 | 通过 | 问题记录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 3.6.1 | 后端返回 500 错误 | 列表显示空状态或错误提示,不白屏 | | [ ] | |
|
||||
| 3.6.2 | 后端返回字段缺失 | 缺失字段列显示为空,不抛 JS 异常 | | [ ] | |
|
||||
| 3.6.3 | 备注字段输入 5000 字符 | 提交成功,列表中可滚动展示(可截断) | | [ ] | |
|
||||
| 3.6.4 | 在表单输入过程中按 ESC | 弹框关闭,表单状态重置 | | [ ] | |
|
||||
| 3.6.5 | 列表行内操作按钮溢出 | 操作列固定在右侧且可滚动查看 | | [ ] | |
|
||||
| 3.6.6 | DevTools Console 检查 | 全程无 `Vue warn`、未捕获 Promise 异常、key 缺失警告 | | [ ] | |
|
||||
|
||||
---
|
||||
|
||||
## 四、测试结果汇总
|
||||
|
||||
| 用例总数 | 通过 | 失败 | 阻塞 | 通过率 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 五、问题记录区
|
||||
|
||||
| 编号 | 用例编号 | 复现步骤 | 实际结果 | 严重程度 | 处理人 | 状态 | 备注 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| 1 | | | | | | | |
|
||||
| 2 | | | | | | | |
|
||||
| 3 | | | | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 六、测试结论
|
||||
|
||||
| 项目 | 结论 |
|
||||
| --- | --- |
|
||||
| 功能完整性 | ☐ 满足 ☐ 部分缺失 ☐ 不满足 |
|
||||
| 性能表现 | ☐ 良好 ☐ 一般 ☐ 差 |
|
||||
| 权限控制 | ☐ 正确 ☐ 存在漏洞 |
|
||||
| 国际化 | ☐ 完整 ☐ 部分缺失 ☐ 缺失 |
|
||||
| 是否可发布 | ☐ 是 ☐ 否(请说明阻塞问题) |
|
||||
|
||||
测试人员签字:__________________ 日期:__________
|
||||
|
||||
---
|
||||
*本测试流程文档为【计量单位】功能迁移版本专用,请独立归档保存。*
|
||||
3410
docs/功能测试流程文档.md
Normal file
3410
docs/功能测试流程文档.md
Normal file
File diff suppressed because it is too large
Load Diff
443
docs/国际化规则.md
Normal file
443
docs/国际化规则.md
Normal file
@@ -0,0 +1,443 @@
|
||||
# i18n 国际化规范文档
|
||||
|
||||
> **版本**:v1.0
|
||||
> **适用项目**:`mes-ui`
|
||||
> **语言包文件**:`src/locales/zh-chs.json` / `src/locales/en.json` / `src/locales/zh-cht.json` / `src/locales/ja.json`
|
||||
> **配合使用**:`src/composables/useI18n.js`(`i18nMixin`)
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [语言包文件规范](#1-语言包文件规范)
|
||||
2. [Key 命名规范(三层层级)](#2-key-命名规范三层层级)
|
||||
3. [公共 Key 规范(page.common)](#3-公共-key-规范pagecommon)
|
||||
4. [页面中如何使用(i18nMixin)](#4-页面中如何使用i18nmixin)
|
||||
5. [常见场景 Key 模板](#5-常见场景-key-模板)
|
||||
6. [旧项目 Key 迁移对照](#6-旧项目-key-迁移对照)
|
||||
7. [翻译格式规范](#7-翻译格式规范)
|
||||
8. [新增页面 Checklist](#8-新增页面-checklist)
|
||||
|
||||
---
|
||||
|
||||
## 1. 语言包文件规范
|
||||
|
||||
### 1.1 文件清单
|
||||
|
||||
| 文件 | 语言 | `_element` 值 |
|
||||
|------|------|--------------|
|
||||
| `src/locales/zh-chs.json` | 简体中文 | `"zh-CN"` |
|
||||
| `src/locales/zh-cht.json` | 繁体中文 | `"zh-TW"` |
|
||||
| `src/locales/en.json` | 英文 | `"en"` |
|
||||
| `src/locales/ja.json` | 日文 | `"ja"` |
|
||||
|
||||
### 1.2 文件格式
|
||||
|
||||
- **编码**:UTF-8
|
||||
- **格式**:标准 JSON(不允许尾逗号,不允许注释)
|
||||
- **顶层结构**:
|
||||
|
||||
```json
|
||||
{
|
||||
"_element": "zh-CN",
|
||||
"_name": "简体中文",
|
||||
"page": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
- `_element`:对应 `element-ui/lib/locale/lang/` 的语言包文件名
|
||||
- `_name`:语言切换下拉菜单中的显示名
|
||||
- `page`:所有业务翻译的根节点,之下按模块层级嵌套
|
||||
|
||||
### 1.3 维护原则
|
||||
|
||||
- 中文和英文文件**必须保持 key 结构完全一致**,不允许一边有 key 而另一边没有
|
||||
- 新增页面时**中英文同步添加**
|
||||
- 每次新增后用 `node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))"` 验证 JSON 合法性
|
||||
|
||||
---
|
||||
|
||||
## 2. Key 命名规范(三层层级)
|
||||
|
||||
### 2.1 核心规则
|
||||
|
||||
```
|
||||
page.{一级模块英文} .{二级模块英文} .{三级模块英文} .{具体key}
|
||||
└─ snake_case ─┘└─ snake_case ─┘└─ snake_case ─┘
|
||||
```
|
||||
|
||||
**英文来源**:[后台Webman界面截图对照表](./后台Webman界面截图对照表.md) 中的英文列。
|
||||
|
||||
**格式转换**:对照表英文 → 全小写 + 空格替换为下划线 + `&` 替换为下划线。
|
||||
|
||||
### 2.2 对照表 → i18n Key 映射表
|
||||
|
||||
| 对照表英文名 | snake_case(用于 i18n key) | kebab-case(用于目录名) |
|
||||
|-------------|---------------------------|------------------------|
|
||||
| System Administration | `system_administration` | `system-administration` |
|
||||
| Production Master Data | `production_master_data` | `production-master-data` |
|
||||
| Equipment Management | `equipment_management` | `equipment-management` |
|
||||
| Planning & Production | `planning_production` | `planning-production` |
|
||||
| Quality Management | `quality_management` | `quality-management` |
|
||||
| Data Platform | `data_platform` | `data-platform` |
|
||||
| Factory Model | `factory_model` | `factory-model` |
|
||||
| Process Model | `process_model` | `process-model` |
|
||||
| Product Management | `product_management` | `product-management` |
|
||||
| Material Model | `material_model` | `material-model` |
|
||||
| SPC Configuration | `spc_configuration` | `spc-configuration` |
|
||||
| Team Model | `team_model` | `team-model` |
|
||||
| User Management | `user_management` | `user-management` |
|
||||
| Menu Management | `menu_management` | `menu-management` |
|
||||
| System Utilities | `system_utilities` | `system-utilities` |
|
||||
| System Monitoring | `system_monitoring` | `system-monitoring` |
|
||||
| Batch Management | `batch_management` | `batch-management` |
|
||||
| Production Monitoring | `production_monitoring` | `production-monitoring` |
|
||||
| Process Control | `process_control` | `process-control` |
|
||||
| Inspection Management | `inspection_management` | `inspection-management` |
|
||||
| Traceability | `traceability` | `traceability` |
|
||||
| Production Reports | `production_reports` | `production-reports` |
|
||||
| Correlation Analysis | `correlation_analysis` | `correlation-analysis` |
|
||||
| Factory Area | `factory_area` | `factory-area` |
|
||||
| Production Line | `production_line` | `production-line` |
|
||||
|
||||
> **完整映射表**见本文档末尾附录 A。
|
||||
|
||||
### 2.3 完整示例
|
||||
|
||||
以「生产配置 → 工厂模型 → 工厂区域」页面为例:
|
||||
|
||||
```json
|
||||
{
|
||||
"page": {
|
||||
"production_master_data": {
|
||||
"factory_model": {
|
||||
"factory_area": {
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"code": "所区编码",
|
||||
"name": "所区名称",
|
||||
"add": "新 增",
|
||||
"edit": "编 辑",
|
||||
"delete": "删 除"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
对应的 `i18nMixin` 前缀:
|
||||
|
||||
```js
|
||||
mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 公共 Key 规范(page.common)
|
||||
|
||||
### 3.1 什么应该放公共
|
||||
|
||||
跨页面、跨模块重复出现的文案,提取到 `page.common` 下:
|
||||
|
||||
```json
|
||||
{
|
||||
"page": {
|
||||
"common": {
|
||||
"help": "帮 助",
|
||||
"confirm": "确定",
|
||||
"cancel": "取消",
|
||||
"save": "保存",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"add": "新增",
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"operation": "操作",
|
||||
"tip": "提示",
|
||||
"confirm_delete": "确定要执行该操作吗?",
|
||||
"operation_success": "操作成功",
|
||||
"no_data": "暂无数据",
|
||||
"loading": "加载中..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 已有公共 Key 清单
|
||||
|
||||
| key | 中文 | 英文 | 用途 |
|
||||
|-----|------|------|------|
|
||||
| `page.common.help` | 帮 助 | Help | 表格工具栏右侧帮助按钮 |
|
||||
|
||||
> 后续发现跨页面共用 key 时,持续往 `page.common` 中补充。
|
||||
|
||||
### 3.3 如何使用
|
||||
|
||||
```vue
|
||||
<page-table :help-text="$t(ckey('help'))" />
|
||||
```
|
||||
|
||||
`ckey('help')` → `'page.common.help'`,由 `i18nMixin` 自动注入。
|
||||
|
||||
### 3.4 判断标准:该不该放 common?
|
||||
|
||||
| 情况 | 放哪里 | 示例 |
|
||||
|------|--------|------|
|
||||
| 多个一级模块都出现 | `page.common` | 帮助、确定、取消 |
|
||||
| 只在本模块内出现 | 模块自己的 key | 所区编码、产线编码 |
|
||||
| 不确定 | 先放在模块内,等第二次出现时提取 | — |
|
||||
|
||||
---
|
||||
|
||||
## 4. 页面中如何使用(i18nMixin)
|
||||
|
||||
### 4.1 页面 Script 部分
|
||||
|
||||
```js
|
||||
import { i18nMixin } from '@/composables/useI18n'
|
||||
|
||||
export default {
|
||||
mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')],
|
||||
data () {
|
||||
return {
|
||||
// 传入完整 i18n key,不在此处翻译,由子组件内部 $t() 处理
|
||||
formCols: [
|
||||
[{ label: this.key('code'), placeholder: this.key('enter_code') }]
|
||||
],
|
||||
rules: {
|
||||
code: [{ required: true, message: this.key('enter_code'), trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.columns = useTableColumns([
|
||||
{ prop: 'code', label: this.key('code') }
|
||||
])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 页面 Template 部分
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 当前页面翻译 -->
|
||||
<el-form-item :label="$t(key('code'))">
|
||||
<el-input :placeholder="$t(key('enter_code'))" />
|
||||
</el-form-item>
|
||||
<el-button>{{ $t(key('search')) }}</el-button>
|
||||
|
||||
<!-- 公共翻译 -->
|
||||
<page-table :help-text="$t(ckey('help'))" />
|
||||
</template>
|
||||
```
|
||||
|
||||
### 4.3 data() 中传参的时机说明
|
||||
|
||||
`data()` 在 `created` 之前执行,此时 `this` 已经可用。因此:
|
||||
|
||||
- **column label / formCols / rules.message**:在 `data()` 中或 `created()` 中用 `this.key()` 传入完整 i18n key
|
||||
- **翻译由子组件负责**:`page-table` 和 `page-dialog-form` 内部使用 `$t()` 翻译传入的 key,切换语言时自动响应更新
|
||||
- **不要用 `k()` 提前翻译**:`k()` 将 key 翻译成静态字符串,语言切换后不会更新
|
||||
|
||||
---
|
||||
|
||||
## 5. 常见场景 Key 模板
|
||||
|
||||
### 5.1 标准 CRUD 页面(每个页面必有的 key)
|
||||
|
||||
```json
|
||||
{
|
||||
"search": "查询",
|
||||
"reset": "重置",
|
||||
"add": "新 增",
|
||||
"edit": "编 辑",
|
||||
"delete": "删 除",
|
||||
"operation": "操作",
|
||||
"add_title": "新增{模块名}",
|
||||
"edit_title": "编辑{模块名}",
|
||||
"operation_success": "操作成功",
|
||||
"confirm_delete": "确定要执行该操作吗?",
|
||||
"enter_xxx": "请输入{字段名}",
|
||||
"select_xxx": "请选择{字段名}"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 列表页字段 Key
|
||||
|
||||
```json
|
||||
{
|
||||
"sort": "序号",
|
||||
"code": "{实体}编码",
|
||||
"name": "{实体}名称",
|
||||
"remark": "备注"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 表单校验 Key
|
||||
|
||||
```json
|
||||
{
|
||||
"enter_code": "请输入{实体}编码",
|
||||
"enter_name": "请输入{实体}名称",
|
||||
"remark_length": "长度在 1 到 100 个字符",
|
||||
"validation_fail": "校验失败"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 登录页 Key
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"login": "登录",
|
||||
"please_enter_username": "请输入用户名",
|
||||
"please_enter_password": "请输入密码",
|
||||
"form_validation_failed": "表单校验失败,请检查"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 旧项目 Key 迁移对照
|
||||
|
||||
### 6.1 旧 Key 结构 vs 新 Key 结构
|
||||
|
||||
旧项目 key 路径深且命名不统一:
|
||||
|
||||
| 旧 key 前缀 | 新 key 前缀 |
|
||||
|-----------|-----------|
|
||||
| `page.system_settings.user_management.role` | `page.system_administration.user_management.role` |
|
||||
| `page.system_settings.user_management.user` | `page.system_administration.user_management.user` |
|
||||
| `page.system_settings.menu_configuration.menu` | `page.system_administration.menu_management.menu_configuration` |
|
||||
| `page.system_settings.system_assistant.operate_log` | `page.system_administration.system_utilities.operation_logs` |
|
||||
| `page.system_settings.system_monitoring.system.login` | `page.system_administration.system_monitoring.login` |
|
||||
| `page.production_configuration.factory_model.factory_area` | `page.production_master_data.factory_model.factory_area` |
|
||||
| `page.production_configuration.matetial_model.*` | `page.production_master_data.material_model.*` |
|
||||
| `page.planning_production.production_batch_management.batch` | `page.planning_production.batch_management.batch_list` |
|
||||
| `page.data_middleground.basic_traceability.*` | `page.data_platform.traceability.*` |
|
||||
| `page.warehouse.*` | `page.warehouse.*`(待确定对照表英文名) |
|
||||
|
||||
### 6.2 搬迁时的操作步序
|
||||
|
||||
1. 确定页面对应的对照表三级模块英文名
|
||||
2. 按 `page.{一级}.{二级}.{三级}` 组装新 key 前缀
|
||||
3. 在 `zh-chs.json` 中按三层嵌套创建节点
|
||||
4. 在 `en.json` 中创建相同结构
|
||||
5. 将旧 key 的翻译值复制到新 key 下
|
||||
6. 页面代码中 `i18nMixin` 参数改为新前缀
|
||||
7. 模板中 `$t('旧key')` 全部改为 `$t(key('新suffix'))`
|
||||
|
||||
---
|
||||
|
||||
## 7. 翻译格式规范
|
||||
|
||||
### 7.1 中文规范
|
||||
|
||||
| 场景 | 格式 | 示例 |
|
||||
|------|------|------|
|
||||
| 按钮文字 | 字间加空格(两字不加) | `"查询"`、`"新 增"`、`"批量删除"` |
|
||||
| 占位提示 | `请输入{名称}` | `"请输入所区编码"` |
|
||||
| 校验消息 | 完整句,不加句号 | `"长度在 1 到 100 个字符"` |
|
||||
| 提示弹框 | 加问号 | `"确定要执行该操作吗?"` |
|
||||
| 操作成功消息 | 不加"成功"后缀的冗余 | `"操作成功"`(而非"保存成功"、"编辑成功"各自定义) |
|
||||
|
||||
### 7.2 英文规范
|
||||
|
||||
| 场景 | 格式 | 示例 |
|
||||
|------|------|------|
|
||||
| 按钮文字 | 首字母大写 | `"Search"`、`"Add"`、`"Edit"` |
|
||||
| 占位提示 | `Please enter {name}` | `"Please enter area code"` |
|
||||
| 校验消息 | 完整句 | `"Length should be 1 to 100 characters"` |
|
||||
| 提示弹框 | 问句 | `"Are you sure to delete?"` |
|
||||
| 操作成功消息 | 过去式或名词 | `"Operation succeeded"` |
|
||||
|
||||
### 7.3 占位符
|
||||
|
||||
使用 `{name}` 格式,不使用 `%s` / `{0}`:
|
||||
|
||||
```json
|
||||
"please_enter": "请输入{name}"
|
||||
```
|
||||
|
||||
```js
|
||||
this.$t('please_enter', { name: '所区编码' })
|
||||
// → "请输入所区编码"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 新增页面 Checklist
|
||||
|
||||
新增一个 CRUD 页面时,按以下顺序操作:
|
||||
|
||||
- [ ] 1. 在 [后台Webman界面截图对照表](./后台Webman界面截图对照表.md) 中找到该页面的三级英文名
|
||||
- [ ] 2. 确定 i18n key 前缀:`page.{一级snake_case}.{二级snake_case}.{三级snake_case}`
|
||||
- [ ] 3. 在 `zh-chs.json` 的对应节点下添加本页面全部 key
|
||||
- [ ] 4. 在 `en.json` 的相同节点下添加对应英文翻译
|
||||
- [ ] 5. 验证 JSON 合法性:`node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))"`
|
||||
- [ ] 6. 页面中使用 `mixins: [i18nMixin('key前缀')]`
|
||||
- [ ] 7. 模板中用 `$t(key('xxx'))` 替代硬编码文字
|
||||
- [ ] 8. data() 中用 `this.key('xxx')` 传入完整 i18n key(不用 `k()` 提前翻译)
|
||||
- [ ] 9. 公共文案(帮助、确定、取消等)用 `ckey('xxx')` 引用
|
||||
- [ ] 10. `pnpm lint` 无报错
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:对照表 → i18n Key 完整映射
|
||||
|
||||
### 一级模块
|
||||
|
||||
| 对照表英文 | snake_case(i18n) | kebab-case(目录) |
|
||||
|-----------|-------------------|-------------------|
|
||||
| System Administration | `system_administration` | `system-administration` |
|
||||
| Production Master Data | `production_master_data` | `production-master-data` |
|
||||
| Equipment Management | `equipment_management` | `equipment-management` |
|
||||
| Planning & Production | `planning_production` | `planning-production` |
|
||||
| Quality Management | `quality_management` | `quality-management` |
|
||||
| Data Platform | `data_platform` | `data-platform` |
|
||||
| Warehouse | `warehouse` | `warehouse` |
|
||||
| SCADA Management | `scada_management` | `scada-management` |
|
||||
|
||||
### 生产配置 → 二级模块
|
||||
|
||||
| 二级(对照表) | snake_case | 三级页面数 |
|
||||
|---------------|-----------|:--:|
|
||||
| Factory Model | `factory_model` | 2 |
|
||||
| Process Model | `process_model` | 3 |
|
||||
| Product Management | `product_management` | 2 |
|
||||
| Material Model | `material_model` | 4 |
|
||||
| SPC Configuration | `spc_configuration` | 1 |
|
||||
| Team Model | `team_model` | 3 |
|
||||
|
||||
### 设备模型 → 二级模块
|
||||
|
||||
| 二级(对照表) | snake_case |
|
||||
|---------------|-----------|
|
||||
| Equipment Category | `equipment_category` |
|
||||
| Equipment Management | `equipment_management` |
|
||||
| Inspection Management | `inspection_management` |
|
||||
| Maintenance Management | `maintenance_management` |
|
||||
| Repair Management | `repair_management` |
|
||||
| Consumables Management | `consumables_management` |
|
||||
|
||||
### 系统设置 → 二级/三级
|
||||
|
||||
| 二级(对照表) | snake_case | 三级 snake_case |
|
||||
|---------------|-----------|----------------|
|
||||
| User Management | `user_management` | `role` / `user` |
|
||||
| Menu Management | `menu_management` | `menu_configuration` |
|
||||
| System Utilities | `system_utilities` | `operation_logs` / `api_logs` |
|
||||
| System Monitoring | `system_monitoring` | `monitoring_configuration` |
|
||||
| Organization | `organization` | `production_team_manage` / `production_shift_management` / `production_shift_calender` |
|
||||
| OCR | `ocr` | `config` / `log` |
|
||||
|
||||
---
|
||||
|
||||
## 附录 B:当前已实施页面
|
||||
|
||||
| 页面 | i18nMixin 前缀 | 语言包节点 |
|
||||
|------|---------------|----------|
|
||||
| 工厂区域 | `page.production_master_data.factory_model.factory_area` | `zh-chs.json` / `en.json` ✅ |
|
||||
368
docs/布局迁移报告.md
Normal file
368
docs/布局迁移报告.md
Normal file
@@ -0,0 +1,368 @@
|
||||
# header-aside 布局组件迁移报告
|
||||
|
||||
> 迁移日期:2026-05-28
|
||||
> 迁移范围:`src/layout/header-aside/`
|
||||
> 适用项目:MES-UI(基于 D2Admin)
|
||||
|
||||
---
|
||||
|
||||
## 一、迁移概览
|
||||
|
||||
| 项目 | 迁改前 | 迁改后 |
|
||||
|------|--------|--------|
|
||||
| **修改文件数** | — | **14 个** |
|
||||
| **硬编码中文处数** | **~35 处** | **0 处** |
|
||||
| **删除死代码** | — | **1 处**(pageKeepAliveClean) |
|
||||
| **新增 i18n key** | 0 | **47 个**(zh-chs/en/ja/zh-cht 各 47 个) |
|
||||
| **语言包覆盖** | 仅 zh-chs + en | zh-chs / en / ja / zh-cht 四语言完整覆盖 |
|
||||
|
||||
---
|
||||
|
||||
## 二、执行逻辑判断结果
|
||||
|
||||
### 全部 16 个组件均在新项目中生效,无废弃组件
|
||||
|
||||
| 文件 | 状态 | 依赖 | 判断依据 |
|
||||
|------|------|------|----------|
|
||||
| `layout.vue` | ✅ 生效 | d2admin store 全模块 | 全局注册组件齐全 |
|
||||
| `menu-side/index.js` | ✅ 生效 | d2admin/menu, d2-scrollbar | 组件已全局注册 |
|
||||
| `menu-header/index.js` | ✅ 生效 | d2admin/menu + resize | throttle 正常 |
|
||||
| `tabs/index.vue` | ✅ 生效 | d2admin/page, sortablejs | sortablejs 已安装 |
|
||||
| `header-search/index.vue` | ✅ 生效 | 纯 emit | 无额外依赖 |
|
||||
| `header-user/index.vue` | ✅ 生效 | d2admin/user + account | store 模块存在 |
|
||||
| `header-fullscreen/index.vue` | ✅ 生效 | d2admin/fullscreen | store 模块存在 |
|
||||
| `header-theme/index.vue` | ✅ 生效 | d2admin/theme | store 模块存在 |
|
||||
| `header-theme/list/index.vue` | ✅ 生效 | d2admin/theme | store 模块存在 |
|
||||
| `header-size/index.vue` | ✅ 生效 | d2admin/size | store 模块存在 |
|
||||
| `header-color/index.vue` | ✅ 生效 | d2admin/color | store 模块存在 |
|
||||
| `header-locales/index.vue` | ✅ 生效 | localeMixin | mixin 正常 |
|
||||
| `header-log/index.vue` | ✅ 生效 | d2admin/log, 路由 log | log 路由存在 |
|
||||
| `contextmenu/index.vue` | ✅ 生效 | 纯 UI | 无额外依赖 |
|
||||
| `panel-search/index.vue` | ✅ 生效 | d2admin/search, fuse.js | fuse.js 已安装 |
|
||||
| `locales/mixin.js` | ✅ 生效 | $i18n | i18n 已初始化 |
|
||||
|
||||
---
|
||||
|
||||
## 三、各文件改动明细
|
||||
|
||||
### 3.1 模板硬编码 → `$t()` (共 11 个文件)
|
||||
|
||||
| 文件 | 改动项 | 旧值 | 新值 |
|
||||
|------|--------|------|------|
|
||||
| **header-user/index.vue** | 用户问候 | `` `你好 ${info.name}` `` | `$t('page.layout.user.greeting', { name })` |
|
||||
| | 未登录占位 | `'未登录'` | `$t('page.layout.user.not_logged_in')` |
|
||||
| | 登出按钮 | `注销` | `$t('page.layout.user.logout')` |
|
||||
| **header-fullscreen/index.vue** | 全屏 tooltip | `'全屏'` / `'退出全屏'` | `$t('page.layout.fullscreen.enter/exit')` |
|
||||
| **header-theme/index.vue** | 主题按钮 tooltip | `content="主题"` | `:content="$t('page.layout.theme.title')"` |
|
||||
| | 弹框标题 | `title="主题"` | `:title="$t('page.layout.theme.title')"` |
|
||||
| **header-theme/list/index.vue** | 预览列标题 | `label="预览"` | `:label="$t('page.layout.theme.preview')"` |
|
||||
| | 已激活按钮 | `已激活` | `$t('page.layout.theme.active')` |
|
||||
| | 使用按钮 | `使用` | `$t('page.common.use')` |
|
||||
| **header-size/index.vue** | 尺寸选项 | `'默认'/'中'/'小'/'最小'` | `$t('page.layout.size.*')` |
|
||||
| | 通知标题 | `'提示'` | `$t('page.layout.size.notification_title')` |
|
||||
| | 通知内容 | 硬编码 HTML | `$t('page.layout.size.notification_message')` |
|
||||
| **header-log/index.vue** | tooltip 文本 | 模板字符串拼接 | `$t('page.layout.log.tooltip/no_log', {...})` |
|
||||
| **menu-side/index.js** | 空菜单提示 | `没有侧栏菜单` | `this.$t('page.layout.menu.no_sidebar')` |
|
||||
| **tabs/index.vue** | Tab 默认名 | `'未命名'` | `$t('page.layout.tabs.unnamed')` |
|
||||
| | 关闭操作文字 | `关闭左侧/右侧/其它/全部` | `$t('page.layout.tabs.close_*')` |
|
||||
| | 刷新文字 | `刷新` | `$t('page.layout.tabs.refresh')` |
|
||||
| | 错误提示 | `'无效的操作'` | `$t('page.layout.tabs.invalid_operation')` |
|
||||
| **panel-search/index.vue** | 搜索 placeholder | `placeholder="搜索页面"` | `:placeholder="$t('page.layout.search.placeholder')"` |
|
||||
| | 快捷键提示 | 含 span 的散装文本 | `v-html="$t('page.layout.search.tip', {...})"` |
|
||||
| **mixin/menu.js** | 临时菜单提示 | `'临时菜单'` | `$t('page.layout.menu.temp_menu')` |
|
||||
| **libs/util.menu.js** | 菜单 fallback | `'未命名菜单'` | `this.$t('page.layout.menu.unnamed_menu')` |
|
||||
| **locales/mixin.js** | 语言切换通知 | `'当前语言:...'/'语言变更'` | `$t('page.layout.locales.*')` |
|
||||
|
||||
### 3.2 data() → computed() 转换(响应式 i18n)
|
||||
|
||||
| 文件 | 属性 | 原因 |
|
||||
|------|------|------|
|
||||
| **header-size/index.vue** | `options` → `sizeOptions` | 确保语言切换时下拉选项也跟随更新 |
|
||||
| **tabs/index.vue** | `contextmenuListIndex` | 确保右键菜单 title 切换语言时跟随更新 |
|
||||
| | `contextmenuList` | 同上 |
|
||||
|
||||
### 3.3 删除死代码
|
||||
|
||||
| 文件 | 删除项 | 原因 |
|
||||
|------|--------|------|
|
||||
| **header-size/index.vue** | `...mapMutations({ pageKeepAliveClean })` | 引入后从未被调用 |
|
||||
| 同步删除了 `import { mapMutations }` | `mapMutations` 不再需要 | 单一用途,移除了整个 import |
|
||||
|
||||
---
|
||||
|
||||
## 四、新增 i18n Key 清单
|
||||
|
||||
所有 key 位于 `page.layout.*` 命名空间下,四语言同步添加:
|
||||
|
||||
```
|
||||
page.layout.
|
||||
├── user.greeting / not_logged_in / logout
|
||||
├── fullscreen.enter / exit
|
||||
├── theme.title / preview / active
|
||||
├── size.default / medium / small / mini / notification_title / notification_message
|
||||
├── log.no_log / tooltip / tooltip_zero
|
||||
├── menu.no_sidebar / temp_menu / unnamed_menu
|
||||
├── tabs.unnamed / refresh / close_left / close_right / close_other / close_all / invalid_operation
|
||||
├── search.placeholder / tip
|
||||
└── locales.changed / notification_title / preview_warning / preview_doc
|
||||
```
|
||||
|
||||
加上 `page.common.use`,共计 **48 个新 key × 4 语言 = 192 条翻译**。
|
||||
|
||||
---
|
||||
|
||||
## 五、开发者注意事项
|
||||
|
||||
### 5.1 涉及 4 个语言包文件
|
||||
|
||||
> ⚠️ 所有修改的语言包 `zh-chs.json / en.json / ja.json / zh-cht.json` 已通过 `JSON.parse()` 验证合法性。
|
||||
|
||||
### 5.2 JSX 渲染函数的特殊语法
|
||||
|
||||
`menu-side/index.js` 和 `libs/util.menu.js` 使用 JSX 渲染函数,`$t()` 调用语法为 `{ this.$t('...') }`(单花括号),与 `.vue` 模板的 `{{ }}`(双花括号)不同。
|
||||
|
||||
### 5.3 `v-html` 使用处
|
||||
|
||||
`panel-search/index.vue` 的提示文本包含内联 `<span class="panel-search__key">` 标签,必须使用 `v-html` 绑定。
|
||||
|
||||
### 5.4 `data()` → `computed()` 的 Reactivity
|
||||
|
||||
`header-size` 和 `tabs` 组件中原来在 `data()` 里的中文文本数组已经移到 `computed`,确保 `$t()` 是响应式的。未来如果要在 `data()` 中放翻译文本,必须使用 computed 或确保使用者是组件内部翻译。
|
||||
|
||||
---
|
||||
|
||||
## 六、回滚指南
|
||||
|
||||
如需回滚任一组件的改动:
|
||||
|
||||
1. **布局组件** — `git checkout` 对应的组件文件即可,所有改动互相独立
|
||||
2. **语言包** — 回滚 `src/locales/*.json`,新增的 `page.layout` 命名空间不影响业务页面
|
||||
3. **localeMixin** — 回滚 `src/locales/mixin.js`
|
||||
|
||||
---
|
||||
|
||||
## 七、验证清单
|
||||
|
||||
| 验证项 | 方法 |
|
||||
|--------|------|
|
||||
| JSON 合法性 | `node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))"` |
|
||||
| 全量 JSON | 逐个检查 zh-chs/en/ja/zh-cht(已通过) |
|
||||
| 语言切换 | 打开页面 → 切换中文/English/日本語/繁體中文 → 观察全部 header 文字是否跟随变化 |
|
||||
| 搜索面板 | `Ctrl+K` 打开搜索面板 → 检查 placeholder + 快捷键提示 |
|
||||
| 右键 Tab | 右键点击 Tab → 检查菜单文字 |
|
||||
| 全屏切换 | 鼠标悬停全屏按钮 → 检查 tooltip |
|
||||
| 主题弹框 | 打开主题弹框 → 检查标题 + "预览"/"已激活"/"使用" |
|
||||
| 组件尺寸 | 切换组件尺寸 → 检查通知弹框 |
|
||||
| 用户区 | 检查用户问候语 + 登出按钮 |
|
||||
|
||||
---
|
||||
|
||||
## 八、菜单 i18n 翻译方案(2026-05-28 追加)
|
||||
|
||||
### 8.1 问题背景
|
||||
|
||||
旧项目中,当用户切换语言(中文 → English)时,侧边栏菜单和顶部一级导航菜单的文字会跟随切换为英文。当前项目迁移后缺少这个能力,菜单文字始终为后端返回的单一语言文本。
|
||||
|
||||
### 8.2 方案设计
|
||||
|
||||
采用**双重保障**机制:
|
||||
|
||||
```
|
||||
┌─ 方案 A:$t() 包装(客户端翻译)
|
||||
语言切换 ──────────┤
|
||||
└─ 方案 B:menuReload(重新拉取后端菜单)
|
||||
```
|
||||
|
||||
#### 方案 A:渲染层 `$t()` 包装
|
||||
|
||||
将菜单渲染节点中的所有 `menu.title` 包裹 `$t()`。
|
||||
|
||||
**修改文件**:
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/layout/header-aside/components/libs/util.menu.js` | `elMenuItem` 和 `elSubmenu` 中 `menu.title` → `this.$t(menu.title)` |
|
||||
| `src/components/d2-module-index-menu/components/group.vue` | `{{menu.title}}` → `{{ $t(menu.title) }}`(2 处) |
|
||||
| `src/components/d2-module-index-menu/components/item.vue` | `{{menu.title}}` → `{{ $t(menu.title) }}` |
|
||||
|
||||
**工作原理**:
|
||||
|
||||
```
|
||||
后端返回 title 是 i18n key → $t() 返回对应语言的翻译 ✓
|
||||
后端返回 title 是纯文本 → $t() 找不到 key,返回原文本(fallback)✓
|
||||
```
|
||||
|
||||
无需修改后端接口即可兼容两种模式。
|
||||
|
||||
#### 方案 B:语言切换时重新拉取菜单
|
||||
|
||||
在 `src/store/modules/d2admin/modules/menu.js` 新增 `menuReload` action:
|
||||
|
||||
```js
|
||||
async menuReload ({ state, dispatch }) {
|
||||
// 1. 清空 localStorage 缓存
|
||||
await dispatch('d2admin/db/set', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
value: [],
|
||||
user: true
|
||||
}, { root: true })
|
||||
state.sourceData = []
|
||||
// 2. 重新从后端拉取(后端根据当前语言返回对应文本)
|
||||
await dispatch('sourceDataLoad')
|
||||
}
|
||||
```
|
||||
|
||||
在 `src/locales/mixin.js` 的 `onChangeLocale` 末尾追加:
|
||||
|
||||
```js
|
||||
this.$store.dispatch('d2admin/menu/menuReload')
|
||||
```
|
||||
|
||||
### 8.3 工作流程
|
||||
|
||||
```
|
||||
用户点击切换语言
|
||||
│
|
||||
├─ this.$i18n.locale = 'en' → 客户端组件重新渲染
|
||||
│ $t(menu.title) 即时生效 ✓
|
||||
│
|
||||
├─ $store.dispatch('menuReload') → 清缓存 → 调后端
|
||||
│ getMenuAll() 返回英文菜单
|
||||
│ menu.install() 重建 header/aside
|
||||
│ 侧栏 + 顶栏全部更新 ✓
|
||||
│
|
||||
└─ $notify({ title: 'Language Changed' }) → 通知用户
|
||||
```
|
||||
|
||||
> **注意**:如果后端 `getMenuAll()` 接口**不支持**根据语言返回不同内容,方案 B 不会生效,此时仅依赖方案 A(`$t()` 包装)。建议后续后端添加多语言菜单数据返回。
|
||||
|
||||
### 8.4 修改清单
|
||||
|
||||
| 文件 | 操作 | 行数 |
|
||||
|------|------|------|
|
||||
| `src/layout/header-aside/components/libs/util.menu.js` | `menu.title` → `this.$t(menu.title)` | 2 处 |
|
||||
| `src/components/d2-module-index-menu/components/group.vue` | `{{menu.title}}` → `{{ $t(menu.title) }}` | 2 处 |
|
||||
| `src/components/d2-module-index-menu/components/item.vue` | `{{menu.title}}` → `{{ $t(menu.title) }}` | 1 处 |
|
||||
| `src/store/modules/d2admin/modules/menu.js` | 新增 `menuReload` action | +15 行 |
|
||||
| `src/locales/mixin.js` | `onChangeLocale` 末尾追加 `menuReload` | +1 行 |
|
||||
|
||||
### 8.5 验证方法
|
||||
|
||||
1. 启动项目,确认**当前展示的菜单文字正确**(方案 A 生效)
|
||||
2. 切换语言,观察侧边栏和顶部导航栏文字是否更新(方案 A + B 双重生效)
|
||||
3. 如果切换语言后菜单文字不变,检查:
|
||||
- 后端 `getMenuAll()` 是否支持多语言
|
||||
- 菜单数据中的 `name` 是否为可翻译的 i18n key 路径
|
||||
|
||||
---
|
||||
|
||||
## 九、一级模块首页复用方案(2026-05-28 追加)
|
||||
|
||||
### 9.1 问题背景
|
||||
|
||||
旧项目中,每个一级模块(如 `planning_production`)都有一个 `index/index.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<d2-container>
|
||||
<page-navi class="cs-m" v-model="root"/>
|
||||
</d2-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'produce-index',
|
||||
components: {
|
||||
PageNavi: () => import('@/layout/header-aside/components/header-navi')
|
||||
},
|
||||
data() { return { root: '/planning_production' } }
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
这些文件结构**高度重合**,仅 `root` 值不同。随着模块增多(对照表中约 20+ 个一级模块),需要逐个创建这些文件。
|
||||
|
||||
### 9.2 方案:通用 `module-index.vue`
|
||||
|
||||
创建一个**共享的路由级组件**,动态读取路由 meta 中的 `root` 来展示模块首页。
|
||||
|
||||
**文件**:`src/views/system/function/module-index.vue`(已创建)
|
||||
|
||||
**核心逻辑**:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<d2-container class="module-index-page">
|
||||
<template #header>
|
||||
<d2-module-index-banner
|
||||
:title="$t(headerMenu.title)"
|
||||
:sub-title="$t(headerMenu.title)" />
|
||||
</template>
|
||||
<d2-module-index-menu :menu="asideMenu" />
|
||||
</d2-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
computed: {
|
||||
rootPath () { return this.$route.meta.root || this.$route.path },
|
||||
headerMenu () {
|
||||
return this.$store.state.d2admin.menu.header.find(
|
||||
m => m.path === this.rootPath
|
||||
) || { title: this.rootPath }
|
||||
},
|
||||
asideMenu () {
|
||||
return this.$store.state.d2admin.menu.aside.find(
|
||||
m => m.path === this.rootPath
|
||||
) || { children: [] }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### 9.3 使用方式
|
||||
|
||||
在路由模块中,为每个一级模块添加一个指向同一组件的空路径路由:
|
||||
|
||||
**路由示例**(`src/router/modules/production-master-data.js`):
|
||||
|
||||
```js
|
||||
export default {
|
||||
path: '/production_configuration',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
// ★ 模块首页 — 所有一级模块共用同一个组件
|
||||
{
|
||||
path: '',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '生产配置', root: '/production_configuration' },
|
||||
component: _import('system/function/module-index')
|
||||
},
|
||||
// 三级模块页面...
|
||||
{
|
||||
path: 'factory_model/factory_area',
|
||||
name: `${pre}factory_model-factory_area`,
|
||||
meta: { ...meta, cache: true, title: '工厂区域' },
|
||||
component: _import('production-master-data/factory-model/factory-area')
|
||||
}
|
||||
])('production_configuration-')
|
||||
}
|
||||
```
|
||||
|
||||
### 9.4 对比
|
||||
|
||||
| 维度 | 旧方案 | 新方案 |
|
||||
|------|--------|--------|
|
||||
| **文件数** | 每个一级模块 1 个 index.vue(20+ 个) | 全局共享 1 个 `module-index.vue` |
|
||||
| **模板代码** | 每个文件 20 行重复代码 | 0 行重复 |
|
||||
| **root 值** | 硬编码在每个 data() 中 | 路由 `meta.root` 动态读取 |
|
||||
| **i18n** | 无 | 通过 `$t(menu.title)` 自动翻译 |
|
||||
| **新增模块** | 创建目录 + 创建 index.vue + 写重复代码 | 仅需添加路由配置行 |
|
||||
|
||||
### 9.5 验证方法
|
||||
|
||||
1. 访问 `/production_configuration`(不带子路径),应显示"生产配置"模块的二级和三级模块导航
|
||||
2. 切换语言,banner 标题和菜单项应跟随翻译
|
||||
3. 点击菜单项,正确跳转到对应页面对应的三级模块页面
|
||||
580
docs/测试文档-工艺流程.md
Normal file
580
docs/测试文档-工艺流程.md
Normal file
@@ -0,0 +1,580 @@
|
||||
# 功能测试文档 — 工艺流程 (Process Routing)
|
||||
|
||||
> 模块路径:生产配置 → 工艺模型 → **工艺流程**
|
||||
> 路由:`/production_configuration/technology_model/technology_flow`
|
||||
> API BASE:`production_configuration/technology_model/technology_flow/`
|
||||
> i18n 前缀:`page.production_master_data.process_model.process_routing`
|
||||
> 文档版本:v1.0
|
||||
> 适用代码版本:基于 [process-routing/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/process-model/process-routing/index.vue)
|
||||
> 测试目标:验证工艺流程页面的列表、查询、新增、编辑、删除、流程卡管理入口等核心操作
|
||||
|
||||
---
|
||||
|
||||
## 0. 文档使用说明
|
||||
|
||||
- 测试人员按顺序执行 **T-XX** 编号的测试用例
|
||||
- 每个用例的 **预期结果** 描述必须与实际界面/接口响应一致才可标记为通过
|
||||
- 失败的用例在 **问题描述** 列填写具体错误信息、截图编号、操作环境
|
||||
- 所有"实际结果"必须如实记录,禁止空填或留白
|
||||
- 整体测试结论位于文末 **测试总结** 处
|
||||
|
||||
---
|
||||
|
||||
## 1. 测试环境配置要求
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| 操作系统 | Windows 10 / 11 64-bit |
|
||||
| Node.js | **18.16.0**(与项目 `.node-version` / `package.json#engines` 一致) |
|
||||
| 包管理器 | **pnpm 10.33.0**(与 `package.json#packageManager` 一致) |
|
||||
| 浏览器 | Chrome 100+ / Edge 100+ |
|
||||
| 后端服务 | Webman 后端已启动且数据库已初始化 |
|
||||
| 数据库 | 工艺流程、流程类别、产品、权限等基础数据已存在 |
|
||||
| 启动命令 | `pnpm install` → `pnpm dev`,访问 `http://localhost:8080` |
|
||||
| 测试账号 | 具备「生产配置-工艺模型-工艺流程」读写权限的账号 |
|
||||
| 浏览器语言 | 中英文均需验证(默认以中文为准) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 测试前置条件
|
||||
|
||||
1. 已使用具备 `production_configuration/technology_model/technology_flow` 下 create / edit / delete 权限的账号登录
|
||||
2. **流程类别** 至少存在 1 条数据(否则新增时的「流程类别」下拉为空)
|
||||
3. **产品列表** 至少存在 1 条数据(否则新增时的「关联产品」下拉为空)
|
||||
4. 浏览器开发者工具 Network 面板可正常观察接口请求
|
||||
5. 已确认 `src/locales/zh-chs.json` 与 `src/locales/en.json` 中存在 `page.production_master_data.process_model.process_routing` 节点
|
||||
|
||||
---
|
||||
|
||||
## 3. 测试用例
|
||||
|
||||
### 3.1 列表与查询
|
||||
|
||||
#### T-01:页面正常加载
|
||||
- **操作步骤**
|
||||
1. 登录系统
|
||||
2. 进入「生产配置 → 工艺模型 → 工艺流程」
|
||||
- **预期结果**
|
||||
- URL 跳转到 `/production_configuration/technology_model/technology_flow`
|
||||
- 页面顶部展示搜索区(编码、名称、查询、重置)
|
||||
- 下方展示表格列:序号 / 工艺流程编码 / 工艺流程名称 / 流程类别 / 关联产品 / 版本 / 状态 / 备注 / 操作
|
||||
- 工具栏右上角显示帮助按钮,鼠标悬停提示「工艺流程用于设置生产工艺流程并管理流程卡…」
|
||||
- 控制台无报错
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-02:默认分页加载列表
|
||||
- **操作步骤**
|
||||
1. 访问工艺流程页面
|
||||
- **预期结果**
|
||||
- 列表自动加载(无需手动点击查询)
|
||||
- 加载时表格显示 loading 遮罩
|
||||
- 加载完成显示数据行(如有数据)
|
||||
- 分页组件显示「共 X 条」并按 10 条/页 显示
|
||||
- 发起请求 `GET production_configuration/technology_model/technology_flow/list?method=production_master_data_process_model_process_routing_list&page_no=1&page_size=10&code=&name=`
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-03:按编码精确搜索
|
||||
- **操作步骤**
|
||||
1. 在「工艺流程编码」输入框输入已存在的完整编码
|
||||
2. 点击「查询」按钮
|
||||
- **预期结果**
|
||||
- 列表仅展示编码完全匹配的数据行
|
||||
- 列表刷新到第 1 页
|
||||
- 接口请求参数 `code=<已输入值>`
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-04:按名称模糊搜索
|
||||
- **操作步骤**
|
||||
1. 在「工艺流程名称」输入框输入名称片段
|
||||
2. 点击「查询」按钮
|
||||
- **预期结果**
|
||||
- 列表仅展示名称包含该片段的数据行
|
||||
- 接口请求参数 `name=<已输入值>`
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-05:按回车键触发查询
|
||||
- **操作步骤**
|
||||
1. 在「工艺流程编码」或「工艺流程名称」输入框聚焦
|
||||
2. 输入关键字后按键盘 `Enter`
|
||||
- **预期结果**
|
||||
- 等同于点击「查询」按钮,触发列表刷新
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-06:重置搜索条件
|
||||
- **操作步骤**
|
||||
1. 在搜索框输入任意条件并点击「查询」
|
||||
2. 点击「重置」按钮
|
||||
- **预期结果**
|
||||
- 两个搜索输入框被清空
|
||||
- 列表自动以空条件重新加载
|
||||
- 页码回到第 1 页
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-07:分页切换
|
||||
- **操作步骤**
|
||||
1. 准备超过 10 条工艺流程数据
|
||||
2. 在分页器中切换到第 2 页或修改每页条数
|
||||
- **预期结果**
|
||||
- 列表按新页码 / 每页条数重新加载
|
||||
- 接口请求参数 `page_no` / `page_size` 与界面一致
|
||||
- 分页总数保持正确
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-08:空数据展示
|
||||
- **操作步骤**
|
||||
1. 在没有任何工艺流程数据时访问页面,或筛选一个不存在的数据
|
||||
- **预期结果**
|
||||
- 表格区域显示 Element UI 空数据占位(无红色错误)
|
||||
- 分页器显示「共 0 条」
|
||||
- 不会弹出错误提示
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-09:i18n 中文切换
|
||||
- **操作步骤**
|
||||
1. 在用户设置中切换语言到「简体中文」
|
||||
2. 访问工艺流程页面
|
||||
- **预期结果**
|
||||
- 所有表头、按钮、提示文本为中文("工艺流程编码"、"新增"、"删除"等)
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-10:i18n 英文切换
|
||||
- **操作步骤**
|
||||
1. 切换语言到「English」
|
||||
2. 访问工艺流程页面
|
||||
- **预期结果**
|
||||
- 所有表头、按钮、提示文本为英文("Routing Code"、"Add"、"Delete" 等)
|
||||
- 切换瞬间不需要刷新页面即生效
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
### 3.2 新增
|
||||
|
||||
#### T-11:打开新增弹窗
|
||||
- **操作步骤**
|
||||
1. 点击工具栏的「新增」按钮
|
||||
- **预期结果**
|
||||
- 弹出标题为「新增工艺流程」的对话框(宽 40%)
|
||||
- 表单为空且编码字段可编辑
|
||||
- 默认值:版本 = `1.0`、状态 = 启用
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-12:新增必填校验 — 编码
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗
|
||||
2. 编码留空,其它字段填写完整
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 编码输入框下方红色提示「请输入工艺流程编码」
|
||||
- 接口未发起,弹窗保持打开
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-13:新增必填校验 — 名称
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗
|
||||
2. 名称留空,其它字段填写完整
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 名称输入框下方红色提示「请输入工艺流程名称」
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-14:新增必填校验 — 流程类别
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗
|
||||
2. 不选择「流程类别」
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 流程类别下拉框下方红色提示「请选择流程类别」
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-15:新增必填校验 — 版本
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗
|
||||
2. 清空「版本」(默认 `1.0`)
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 版本输入框下方红色提示「请输入版本号」
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-16:新增字段长度校验
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗
|
||||
2. 在「编码」输入超过 100 个字符
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 编码输入框下方红色提示「长度在 1 到 100 个字符」
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-17:新增成功
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗
|
||||
2. 填写完整有效数据:编码、名称、流程类别、关联产品、版本、状态、备注
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 接口请求 `POST production_configuration/technology_model/technology_flow/create` 状态码 200,`code=0`
|
||||
- 页面弹出绿色提示「操作成功」
|
||||
- 弹窗自动关闭
|
||||
- 列表自动刷新,新数据展示在表格中
|
||||
- 页码回到第 1 页
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-18:新增重复编码
|
||||
- **操作步骤**
|
||||
1. 准备一个已存在的编码
|
||||
2. 在新增弹窗中输入相同编码
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 后端返回业务错误
|
||||
- 页面弹出红色错误提示(如「编码已存在」)
|
||||
- 弹窗保持打开,用户可修改后重试
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-19:取消新增
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗,填写部分数据
|
||||
2. 点击「取消」按钮或右上角 X
|
||||
- **预期结果**
|
||||
- 弹窗关闭
|
||||
- 不发起任何接口请求
|
||||
- 再次打开新增弹窗,表单恢复为空(默认值除外)
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-20:新增提交中重复点击防护
|
||||
- **操作步骤**
|
||||
1. 打开新增弹窗,填写有效数据
|
||||
2. 连续快速点击「确定」两次
|
||||
- **预期结果**
|
||||
- 第一次点击后按钮进入 loading 状态,禁用重复提交
|
||||
- 接口仅被调用 1 次
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
### 3.3 编辑
|
||||
|
||||
#### T-21:打开编辑弹窗
|
||||
- **操作步骤**
|
||||
1. 在列表中点击某行的「编辑」按钮
|
||||
- **预期结果**
|
||||
- 弹出标题为「编辑工艺流程」的对话框
|
||||
- 表单自动回填该行数据
|
||||
- 「编码」字段为禁用状态(不可编辑)
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-22:编辑必填校验
|
||||
- **操作步骤**
|
||||
1. 打开编辑弹窗
|
||||
2. 清空「名称」
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 名称输入框下方红色提示「请输入工艺流程名称」
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-23:编辑成功
|
||||
- **操作步骤**
|
||||
1. 打开编辑弹窗
|
||||
2. 修改名称、流程类别、版本、状态、备注等字段
|
||||
3. 点击「确定」
|
||||
- **预期结果**
|
||||
- 接口请求 `PUT production_configuration/technology_model/technology_flow/edit` 状态码 200,`code=0`
|
||||
- 页面弹出绿色提示「操作成功」
|
||||
- 弹窗关闭
|
||||
- 列表自动刷新,目标行显示新值
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-24:编辑时编码不可修改
|
||||
- **操作步骤**
|
||||
1. 打开编辑弹窗
|
||||
2. 尝试修改「编码」字段
|
||||
- **预期结果**
|
||||
- 编码字段处于禁用状态,无法输入
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-25:取消编辑
|
||||
- **操作步骤**
|
||||
1. 打开编辑弹窗,修改若干字段
|
||||
2. 点击「取消」按钮
|
||||
- **预期结果**
|
||||
- 弹窗关闭
|
||||
- 不发起任何接口请求
|
||||
- 列表数据保持原样
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
### 3.4 删除
|
||||
|
||||
#### T-26:删除二次确认弹窗
|
||||
- **操作步骤**
|
||||
1. 点击某行的「删除」按钮
|
||||
- **预期结果**
|
||||
- 弹出警告提示「确定要删除该工艺流程吗?」
|
||||
- 提示框有「确定」与「取消」两个按钮
|
||||
- 列表中的该行仍存在
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-27:删除 — 取消
|
||||
- **操作步骤**
|
||||
1. 点击「删除」按钮
|
||||
2. 在确认弹窗中点击「取消」
|
||||
- **预期结果**
|
||||
- 弹窗关闭
|
||||
- 不发起删除接口
|
||||
- 数据行保持原样
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-28:删除成功
|
||||
- **操作步骤**
|
||||
1. 点击「删除」按钮
|
||||
2. 在确认弹窗中点击「确定」
|
||||
- **预期结果**
|
||||
- 接口请求 `DELETE production_configuration/technology_model/technology_flow/delete` 状态码 200,`code=0`
|
||||
- 页面弹出绿色提示「操作成功」
|
||||
- 列表自动刷新,目标行消失
|
||||
- 删除当前页最后一行时,自动跳转到合理页码
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-29:删除 — 关联数据拒绝
|
||||
- **操作步骤**
|
||||
1. 准备一条已被产品、批次或其它模块引用的工艺流程
|
||||
2. 尝试删除
|
||||
- **预期结果**
|
||||
- 后端返回业务错误
|
||||
- 页面弹出红色错误提示(如「该工艺流程已被引用,不可删除」)
|
||||
- 列表数据保持原样
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
### 3.5 流程卡管理入口
|
||||
|
||||
#### T-30:流程卡管理按钮存在
|
||||
- **操作步骤**
|
||||
1. 在列表中查看任意一行的操作列
|
||||
- **预期结果**
|
||||
- 操作列包含「编辑」、「流程卡管理」、「删除」3 个按钮
|
||||
- 「流程卡管理」按钮为黄色文字 + `el-icon-tickets` 图标
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-31:点击流程卡管理
|
||||
- **操作步骤**
|
||||
1. 点击某行的「流程卡管理」按钮
|
||||
- **预期结果**
|
||||
- 当前阶段:弹出信息提示「流程卡管理: <工艺流程名称>」(占位实现)
|
||||
- 后续阶段:应打开独立的流程卡管理抽屉 / 弹窗
|
||||
- 不影响列表状态
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
### 3.6 权限控制
|
||||
|
||||
#### T-32:无「新增」权限
|
||||
- **操作步骤**
|
||||
1. 使用无 `/production_configuration/technology_model/technology_flow/create` 权限的账号登录
|
||||
2. 访问工艺流程页面
|
||||
- **预期结果**
|
||||
- 工具栏不显示「新增」按钮
|
||||
- 列表正常加载
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-33:无「编辑」权限
|
||||
- **操作步骤**
|
||||
1. 使用无 `…/edit` 权限的账号登录
|
||||
2. 访问工艺流程页面
|
||||
- **预期结果**
|
||||
- 行内「编辑」按钮不显示
|
||||
- 「新增」与「删除」按钮仍可正常显示(按权限配置)
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-34:无「删除」权限
|
||||
- **操作步骤**
|
||||
1. 使用无 `…/delete` 权限的账号登录
|
||||
2. 访问工艺流程页面
|
||||
- **预期结果**
|
||||
- 行内「删除」按钮不显示
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-35:未登录访问
|
||||
- **操作步骤**
|
||||
1. 退出登录后,直接在地址栏访问 `/production_configuration/technology_model/technology_flow`
|
||||
- **预期结果**
|
||||
- 自动跳转到登录页
|
||||
- 不会出现 404 或白屏
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
### 3.7 错误处理
|
||||
|
||||
#### T-36:网络异常
|
||||
- **操作步骤**
|
||||
1. 在浏览器开发者工具 Network 面板中,离线(Offline)
|
||||
2. 刷新工艺流程页面
|
||||
- **预期结果**
|
||||
- 页面顶部红色错误提示(如「请求超时」或「网络异常」)
|
||||
- loading 状态关闭
|
||||
- 表格维持之前的数据或显示空数据
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-37:后端 500 错误
|
||||
- **操作步骤**
|
||||
1. 模拟后端列表接口返回 500
|
||||
2. 刷新工艺流程页面
|
||||
- **预期结果**
|
||||
- 拦截器统一弹出红色错误提示
|
||||
- 表格显示空数据
|
||||
- 控制台无未捕获异常
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
#### T-38:参数缺失
|
||||
- **操作步骤**
|
||||
1. 模拟后端创建接口缺参返回 `code=500`
|
||||
2. 在新增弹窗点击「确定」
|
||||
- **预期结果**
|
||||
- 拦截器弹出后端返回的错误信息
|
||||
- 弹窗不关闭
|
||||
- 表单数据保持原样
|
||||
- **实际结果**:__________
|
||||
- **测试状态**:☐ 通过 ☐ 失败
|
||||
- **问题描述**:__________
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试总结
|
||||
|
||||
| 维度 | 统计 |
|
||||
| --- | --- |
|
||||
| 用例总数 | 38 |
|
||||
| 通过数 | __________ |
|
||||
| 失败数 | __________ |
|
||||
| 阻塞数 | __________ |
|
||||
| 跳过数 | __________ |
|
||||
| 整体结论 | ☐ 通过 ☐ 有条件通过 ☐ 不通过 |
|
||||
|
||||
### 4.1 遗留问题清单
|
||||
|
||||
| 编号 | 用例编号 | 严重程度 | 描述 | 负责人 | 计划修复日期 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | | | | | |
|
||||
| 2 | | | | | |
|
||||
| 3 | | | | | |
|
||||
|
||||
### 4.2 测试执行信息
|
||||
|
||||
- **测试执行人**:__________
|
||||
- **测试起止时间**:__________
|
||||
- **测试环境版本**:Node `18.16.0` / pnpm `10.33.0` / 浏览器 `__________`
|
||||
- **后端版本 / 提交**:__________
|
||||
- **测试账号**:__________
|
||||
|
||||
---
|
||||
|
||||
## 5. 附录
|
||||
|
||||
### 5.1 关键文件
|
||||
|
||||
| 类型 | 路径 |
|
||||
| --- | --- |
|
||||
| 主页面 | [process-routing/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/process-model/process-routing/index.vue) |
|
||||
| API 文件 | [process-routing.js](file:///d:/code/mes/mes-ui/src/api/production-master-data/process-routing.js) |
|
||||
| 路由模块 | [production-master-data.js](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js) |
|
||||
| 中文语言包 | [zh-chs.json#process_routing](file:///d:/code/mes/mes-ui/src/locales/zh-chs.json) |
|
||||
| 英文语言包 | [en.json#process_routing](file:///d:/code/mes/mes-ui/src/locales/en.json) |
|
||||
|
||||
### 5.2 关键接口
|
||||
|
||||
| 操作 | Method | URL |
|
||||
| --- | --- | --- |
|
||||
| 查询列表 | GET | `production_configuration/technology_model/technology_flow/list` |
|
||||
| 新增 | POST | `production_configuration/technology_model/technology_flow/create` |
|
||||
| 编辑 | PUT | `production_configuration/technology_model/technology_flow/edit` |
|
||||
| 删除 | DELETE | `production_configuration/technology_model/technology_flow/delete` |
|
||||
|
||||
公共参数:`method=production_master_data_process_model_process_routing_<action>`,`platform=background`
|
||||
|
||||
### 5.3 字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| code | string | 是 | 工艺流程编码,唯一,创建后不可修改 |
|
||||
| name | string | 是 | 工艺流程名称 |
|
||||
| category_id | int | 是 | 流程类别 ID(关联工艺流程类别) |
|
||||
| product_id | int | 否 | 关联产品 ID(关联产品列表) |
|
||||
| version | string | 是 | 版本号,默认 `1.0` |
|
||||
| status | int | 是 | 1=启用,0=禁用 |
|
||||
| remark | string | 否 | 备注 |
|
||||
439
docs/登录页迁移.md
Normal file
439
docs/登录页迁移.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# 登录页面搬迁文档
|
||||
|
||||
> **搬迁源**:`D:\code\company\SCTMES_MES_V5\vue-app`
|
||||
> **搬迁目标**:`d:\code\mes\mes-ui`
|
||||
> **状态**:✅ 已完成(2026-05-26)
|
||||
|
||||
---
|
||||
|
||||
## 一、文件清单与依赖关系
|
||||
|
||||
```
|
||||
登录页面 index.vue
|
||||
│
|
||||
├── ① 页面自身
|
||||
├── ② 依赖的 Store 模块
|
||||
├── ③ 依赖的 API 文件
|
||||
├── ④ 依赖的静态资源(图片)
|
||||
├── ⑤ 依赖的 i18n 语言包
|
||||
├── ⑥ 依赖的 CSS 公共样式(% placeholder)
|
||||
├── ⑦ 依赖的工具库
|
||||
└── ⑧ 路由中已有框架(无需搬迁本文件)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、需要搬迁的文件
|
||||
|
||||
### 2.1 页面核心文件
|
||||
|
||||
| 文件 | 旧路径 | 新目标路径 | 说明 |
|
||||
|------|--------|-----------|------|
|
||||
| 登录页面 | `src/views/system_settings/system_monitoring/system/login/index.vue` | **覆盖** `src/views/system/login/index.vue` | 新项目已有一个简易占位页面,需用旧项目的完整页面替换 |
|
||||
|
||||
> 旧路径嵌套很深:`system_settings/system_monitoring/system/login/`。
|
||||
> 新项目按对照表命名为简化的 `system/login/`,保持一致的命名风格。
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Store 模块(Vuex)
|
||||
|
||||
| 文件 | 旧路径 | 新目标路径 | 说明 |
|
||||
|------|--------|-----------|------|
|
||||
| account | `src/store/modules/d2admin/modules/account.js` | `src/store/modules/d2admin/modules/account.js` | **核心**:含 `login` 和 `logout` action |
|
||||
| user | `src/store/modules/d2admin/modules/user.js` | `src/store/modules/d2admin/modules/user.js` | account.login 中调用 `d2admin/user/set`,**新项目已存在** |
|
||||
|
||||
> `account.js` 的 `login` action 依赖链:
|
||||
> `login()` → `loginAdminUser()` API → 写 cookie(token/uuid) → `dispatch(d2admin/user/set)` → `dispatch(load)`
|
||||
> `load()` → 依次加载 user/theme/transition/page/menu/size/color
|
||||
|
||||
**account.js 依赖的其它模块(新项目已存在,不需搬迁)**:
|
||||
|
||||
| 模块 | 路径 | 状态 |
|
||||
|------|------|:--:|
|
||||
| user | `src/store/modules/d2admin/modules/user.js` | ✅ 已有 |
|
||||
| theme | `src/store/modules/d2admin/modules/theme.js` | ✅ 已有 |
|
||||
| transition | `src/store/modules/d2admin/modules/transition.js` | ✅ 已有 |
|
||||
| page | `src/store/modules/d2admin/modules/page.js` | ✅ 已有 |
|
||||
| menu | `src/store/modules/d2admin/modules/menu.js` | ✅ 已有 |
|
||||
| size | `src/store/modules/d2admin/modules/size.js` | ✅ 已有 |
|
||||
| color | `src/store/modules/d2admin/modules/color.js` | ✅ 已有 |
|
||||
| db | `src/store/modules/d2admin/modules/db.js` | ✅ 已有 |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 API 文件
|
||||
|
||||
| 文件 | 旧路径 | 新目标路径 | 说明 |
|
||||
|------|--------|-----------|------|
|
||||
| 登录/注销 API | `src/api/login.js` | `src/api/auth.js` | 含 `loginAdminUser()` 和 `logoutAdminUser()` |
|
||||
|
||||
> 注意:旧项目 `login.js` 中 import 了 `@/plugin/axios/request`,这是旧项目的 axios 封装。
|
||||
> 新项目使用 `@/api/_service` 中的 `request`,搬迁时需替换 import 路径。
|
||||
|
||||
---
|
||||
|
||||
### 2.4 静态资源(图片)
|
||||
|
||||
| 文件 | 旧路径 | 新目标路径 | 说明 |
|
||||
|------|--------|-----------|------|
|
||||
| 登录页 Logo | `public/image/logo/sc_logo.png` | `public/image/logo/sc_logo.png` | 登录表单上方的 Logo |
|
||||
|
||||
> 旧项目 `public/image/logo/` 目录下共有 9 个文件,但登录页面只用 `sc_logo.png`。
|
||||
> 根据需要可按需搬其他 logo 文件。新项目原有 `src/views/system/login/image/logo@2x.png`,搬迁后可删除。
|
||||
|
||||
---
|
||||
|
||||
### 2.5 i18n 语言包
|
||||
|
||||
**i18n key 前缀**:`page.system_administration.system_monitoring.login`
|
||||
|
||||
| key | 中文 | 英文 |
|
||||
|-----|------|------|
|
||||
| `time_is_most_precious` | 时间是一切财富中最宝贵的财富 | Time is the most precious of all wealth |
|
||||
| `username` | 用户名 | Username |
|
||||
| `password` | 密码 | Password |
|
||||
| `login` | 登录 | Login |
|
||||
| `quick_select_user` | 快速选择用户 | Quick Select User |
|
||||
| `please_enter_username` | 请输入用户名 | Please enter username |
|
||||
| `please_enter_password` | 请输入密码 | Please enter password |
|
||||
| `please_enter_code` | 请输入验证码 | Please enter captcha |
|
||||
| `dev_version` | 开发版本 | Development Version |
|
||||
| `test_version` | 测试版本 | Test Version |
|
||||
| `form_validation_failed` | 表单校验失败,请检查 | Form validation failed |
|
||||
|
||||
**搬迁操作**:
|
||||
|
||||
1. 在 `src/locales/zh-chs.json` 的 `page.system_administration.system_monitoring` 下新增:
|
||||
```json
|
||||
"login": {
|
||||
"time_is_most_precious": "时间是一切财富中最宝贵的财富",
|
||||
"username": "用户名",
|
||||
"password": "密码",
|
||||
"login": "登录",
|
||||
"please_enter_username": "请输入用户名",
|
||||
"please_enter_password": "请输入密码",
|
||||
"dev_version": "开发版本",
|
||||
"test_version": "测试版本",
|
||||
"form_validation_failed": "表单校验失败,请检查"
|
||||
}
|
||||
```
|
||||
|
||||
2. 在 `src/locales/en.json` 中对应添加英文翻译。
|
||||
|
||||
---
|
||||
|
||||
### 2.6 CSS 公共样式(依赖但不在页面内)
|
||||
|
||||
页面中使用了 SCSS 的 `%placeholder` 选择器:
|
||||
|
||||
| placeholder | 作用 | 定义位置 |
|
||||
|-------------|------|---------|
|
||||
| `%unable-select` | 禁止用户选中 + 鼠标变手形 | `src/assets/style/public.scss` |
|
||||
| `%full` | 绝对定位填满父元素 | `src/assets/style/public.scss` |
|
||||
| `%flex-center-col` | flex 垂直水平居中 | `src/assets/style/public.scss` |
|
||||
| `$color-text-normal` | 文字颜色变量 | `src/assets/style/unit/color.scss` |
|
||||
| `$color-primary` | 主色变量 | `src/assets/style/unit/color.scss` |
|
||||
| `$color-bg` | 背景色变量 | `src/assets/style/unit/color.scss` |
|
||||
|
||||
> ✅ 这些在新项目中已存在(通过 `additionalData` 全局注入),不需要额外搬迁。
|
||||
|
||||
---
|
||||
|
||||
### 2.7 工具/插件依赖(新项目已有,不需搬迁)
|
||||
|
||||
| 依赖 | 旧路径 | 新项目状态 |
|
||||
|------|--------|:--:|
|
||||
| `util.cookies` | `src/libs/util.cookies.js` → `src/libs/util.js` | ✅ 已有 |
|
||||
| `localeMixin` | `src/locales/mixin.js` | ✅ 已有 |
|
||||
| `dayjs` | npm 包 | ✅ 已有(`package.json` 中) |
|
||||
| `$baseUrl` | `Vue.prototype.$baseUrl = process.env.BASE_URL` | ✅ 已在 `plugin/d2admin/index.js` 注册 |
|
||||
| `vuex` `mapActions` | npm 包 | ✅ 已有 |
|
||||
|
||||
---
|
||||
|
||||
### 2.8 路由(新项目已配置,不需搬迁)
|
||||
|
||||
旧路由:
|
||||
```js
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: _import('system_settings/system_monitoring/system/login')
|
||||
}
|
||||
```
|
||||
|
||||
新项目路由已在 `routes.js` 的 `frameOut` 中配置:
|
||||
```js
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: _import('system/login')
|
||||
}
|
||||
```
|
||||
|
||||
> ✅ 新路由已指向 `system/login`,对应的 `src/views/system/login/index.vue` 需要替换为旧页面内容。
|
||||
|
||||
---
|
||||
|
||||
## 三、搬迁执行步骤
|
||||
|
||||
| 步骤 | 操作 | 文件 |
|
||||
|:--:|------|------|
|
||||
| 1 | 拷贝登录 API(替换 import 为 `@/api/_service`) | `src/api/login.js` → `src/api/auth.js` |
|
||||
| 2 | 拷贝 account Store(替换 API 引用路径) | `src/store/modules/d2admin/modules/account.js` |
|
||||
| 3 | 拷贝 Logo 图片 | `public/image/logo/sc_logo.png` |
|
||||
| 4 | 更新登录页面(替换 import 路径 + i18n key + logo 路径) | `src/views/system/login/index.vue` |
|
||||
| 5 | 添加 i18n 语言包 | `src/locales/zh-chs.json` / `en.json` |
|
||||
| 6 | 验证:`pnpm serve` 启动 → 访问 `/login` 测试登录流程 |
|
||||
|
||||
---
|
||||
|
||||
## 四、页面中需要修改的内容
|
||||
|
||||
登录页面 `index.vue` 从旧项目拷贝后,需改以下几处:
|
||||
|
||||
### 4.1 import 路径修正
|
||||
|
||||
```diff
|
||||
- import { mapActions } from 'vuex'
|
||||
- import util from '@/libs/util'
|
||||
- import localeMixin from '@/locales/mixin.js'
|
||||
+ // 这些在新项目中路径一致,无需修改
|
||||
+ // util.cookies、localeMixin、mapActions 均同上
|
||||
```
|
||||
|
||||
> 注意:旧页面 mixins 中引用了 `localeMixin`(切换语言),新项目中此 mixin 在 `src/locales/mixin.js` 已存在,不需动。
|
||||
|
||||
### 4.2 Logo 图片路径
|
||||
|
||||
```diff
|
||||
- <img class="page-login--logo" :src="`${$baseUrl}image/logo/sc_logo.png`">
|
||||
+ <img class="page-login--logo" src="/image/logo/sc_logo.png">
|
||||
```
|
||||
|
||||
> 新项目中 `$baseUrl` 同样已注册,两种写法均可。建议用绝对路径 `/image/logo/sc_logo.png` 更直观。
|
||||
|
||||
### 4.3 i18n key 修正
|
||||
|
||||
旧页面使用的 key 前缀为 `page.system_settings.system_monitoring.system.login.xxx`,在新项目中统一简化为 `page.system.login.xxx`,或按新项目目录结构调整。
|
||||
|
||||
建议调整为更简洁的 key:
|
||||
|
||||
| 旧 key(太长) | 新 key(建议) |
|
||||
|---------------|---------------|
|
||||
| `page.system_settings.system_monitoring.system.login.username` | `page.system.login.username` |
|
||||
| `page.system_settings.system_monitoring.system.login.password` | `page.system.login.password` |
|
||||
|
||||
---
|
||||
|
||||
## 五、不涉及的依赖(记录说明)
|
||||
|
||||
| 依赖 | 原因 |
|
||||
|------|------|
|
||||
| `@/libs/websocket.js` | 登录页面本身不 import websocket,但 `account.js` 的 `logout` action 中引用。新项目如不需 websocket,注释掉即可 |
|
||||
| `@/api/modules/sys.user.api.js` | 旧 `account.login` 只调 `login.js` API,不涉及此文件 |
|
||||
| `public/image/logo/` 其他 png | 登录页只用 `sc_logo.png`,其余 logo 按需搬迁 |
|
||||
|
||||
---
|
||||
|
||||
## 登录流程完整流程图
|
||||
|
||||
### 正向流程:用户登录
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant LoginPage as Login 页面
|
||||
participant VUEX as Store account
|
||||
participant API as 后端 POST /login
|
||||
participant Cookie as Cookie
|
||||
participant Router as router.beforeEach
|
||||
participant Home as 首页
|
||||
|
||||
User->>LoginPage: 输入用户名密码,点击登录
|
||||
LoginPage->>LoginPage: el-form.validate 校验表单
|
||||
alt 校验失败
|
||||
LoginPage-->>User: message.error 表单校验失败
|
||||
else 校验通过
|
||||
LoginPage->>VUEX: dispatch login { username, password }
|
||||
VUEX->>API: loginAdminUser
|
||||
alt 登录失败 401 或密码错误
|
||||
API-->>VUEX: throw Error
|
||||
VUEX->>VUEX: remove token 和 uuid
|
||||
VUEX-->>LoginPage: Promise rejected
|
||||
LoginPage-->>User: message.error
|
||||
else 登录成功
|
||||
API-->>VUEX: token 和 userInfo
|
||||
VUEX->>Cookie: 写入 token / uuid 有效期365天
|
||||
VUEX->>VUEX: localStorage.setItem user_id
|
||||
VUEX->>VUEX: dispatch user/set
|
||||
VUEX->>VUEX: dispatch load 加载持久化配置
|
||||
Note over VUEX: load按序加载: user theme transition page menu size color sourceData
|
||||
VUEX-->>LoginPage: Promise resolved
|
||||
LoginPage->>Router: router.replace redirect or /
|
||||
Router->>Router: beforeEach 检查 token
|
||||
Router-->>Home: 鉴权通过,跳转目标页
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 反向流程:用户注销
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant Header as 顶栏用户菜单
|
||||
participant VUEX as Store account
|
||||
participant API as 后端 POST /logout
|
||||
participant Cookie as Cookie
|
||||
participant WS as WebSocket
|
||||
participant LoginPage as Login 页面
|
||||
|
||||
User->>Header: 点击注销
|
||||
Header->>VUEX: dispatch logout { confirm: true }
|
||||
VUEX->>User: confirm 确定要注销吗
|
||||
alt 取消
|
||||
User-->>VUEX: 取消
|
||||
else 确认
|
||||
VUEX->>VUEX: Loading 遮罩
|
||||
VUEX->>WS: closeSock 断开 WebSocket
|
||||
VUEX->>API: logoutAdminUser
|
||||
API-->>VUEX: 服务端清除 session
|
||||
VUEX->>VUEX: dispatch db/set 清空 sourceData
|
||||
VUEX->>VUEX: dispatch user/set 清空用户信息
|
||||
VUEX->>Cookie: remove token / uuid / set block true
|
||||
VUEX->>VUEX: Loading 关闭
|
||||
VUEX->>LoginPage: router.push { name: login }
|
||||
LoginPage-->>User: 返回登录页面
|
||||
end
|
||||
```
|
||||
|
||||
### 路由守卫鉴权流程(分三种情况)
|
||||
|
||||
#### 情况 A:无 Token → 跳转登录页
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["用户访问 URL"] --> B{"路由需要 auth?"}
|
||||
B -->|是| C["Cookie.get(token)"]
|
||||
C -->|"token 为空"| D["重定向到 /login?redirect=原地址"]
|
||||
D --> E["用户在登录页输入账号密码"]
|
||||
E --> F["登录成功"]
|
||||
F --> G["router.replace 回到目标页"]
|
||||
G --> H["进入首页"]
|
||||
```
|
||||
|
||||
#### 情况 B:有 Token → 恢复会话
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["用户访问 URL,Cookie 中已有 token"] --> B{"路由需要 auth?"}
|
||||
B -->|是| C["Cookie.get(token)"]
|
||||
C -->|"token 有效"| D["next 放行"]
|
||||
D --> E["框架渲染 header-aside 布局"]
|
||||
E --> F["d2admin/page/isLoaded"]
|
||||
F --> G["d2admin/size/isLoaded"]
|
||||
G --> H["页面正常加载"]
|
||||
|
||||
style C fill:#f9f,stroke:#333
|
||||
style D fill:#9f9,stroke:#333
|
||||
style H fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### 情况 C:Token 有效但后端已过期 → API 层拦截
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["页面加载后调用业务 API"] --> B["axios 请求携带 Authorization header"]
|
||||
B --> C{"后端 code === 401?"}
|
||||
C -->|否| D["正常返回数据"]
|
||||
C -->|"是 401"| E["axios 响应拦截器 catch 401"]
|
||||
E --> F["清除 Cookie(token / uuid)"]
|
||||
F --> G["Message.error 请重新登录"]
|
||||
G --> H["router.push /login"]
|
||||
|
||||
style C fill:#f96,stroke:#333
|
||||
style H fill:#f96,stroke:#333
|
||||
```
|
||||
|
||||
### 已有 Token 时的完整恢复时序
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as 用户
|
||||
participant Browser as 浏览器
|
||||
participant Router as router.beforeEach
|
||||
participant Cookie as Cookie
|
||||
participant Store as Vuex Store
|
||||
participant DB as Local DB lowdb
|
||||
participant Layout as Layout
|
||||
participant Page as 目标页面
|
||||
|
||||
User->>Browser: 打开网页
|
||||
Browser->>Router: beforeEnter 触发
|
||||
Router->>Store: dispatch page/isLoaded
|
||||
Router->>Store: dispatch size/isLoaded
|
||||
Router->>Cookie: get token
|
||||
Cookie-->>Router: token = eyJ...
|
||||
Router->>Router: next 放行
|
||||
Router->>Layout: 渲染 header-aside
|
||||
Layout->>Store: menu 加载侧边栏
|
||||
Store->>DB: get sys.menu.asideCollapse
|
||||
DB-->>Store: 上次展开收起状态
|
||||
Layout->>Store: theme 加载主题
|
||||
Store->>DB: get sys.theme.activeName
|
||||
DB-->>Store: 上次选择的主题
|
||||
Layout->>Store: page 加载多标签页
|
||||
Store->>DB: get sys.page.opened
|
||||
DB-->>Store: 上次打开的标签页列表
|
||||
Layout->>Page: 目标页面渲染完成
|
||||
Page-->>User: 首页正常显示,主题侧栏标签页全部恢复
|
||||
```
|
||||
|
||||
### 对比:首次登录 vs 已有 Token
|
||||
|
||||
| 维度 | 首次登录(无 Token) | 已有 Token 恢复 |
|
||||
|------|-------------------|----------------|
|
||||
| 入口 | `/login` 登录页 | 任意页面 URL |
|
||||
| 路由守卫 | 拦下 → 重定向 /login | 放行 |
|
||||
| 用户操作 | 输入用户名密码 | 无需操作 |
|
||||
| `account/login` | ✅ 触发 | ❌ 不触发 |
|
||||
| `account/load()` | ✅ 触发(写入 DB) | ❌ 不触发 |
|
||||
| `page/isLoaded` | 触发(首次无缓存) | 触发(从 DB 恢复) |
|
||||
| `size/isLoaded` | 触发 | 触发 |
|
||||
| 主题/侧栏/标签页 | 从登录后 load() 写入 | 从 lowdb 读取上次状态 |
|
||||
| Cookie | 新建 token / uuid | 复用已有 token |
|
||||
|
||||
---
|
||||
|
||||
### 整体状态流转
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> 未登录: 首次访问
|
||||
[*] --> 已登录: 已有 token,恢复会话
|
||||
未登录 --> 登录页面: 路由守卫拦下
|
||||
登录页面 --> 提交中: 输入账号密码,点击登录
|
||||
提交中 --> 登录成功: API 返回 token
|
||||
提交中 --> 登录失败: API 报错 / 401
|
||||
登录失败 --> 登录页面: 清除 cookie,允许重试
|
||||
登录成功 --> 已登录: 写入 cookie + load() 加载配置
|
||||
已登录 --> 已登录: 页面刷新(token 仍有效,Router + DB 恢复)
|
||||
已登录 --> 未登录: API 返回 401,被强制退出
|
||||
已登录 --> 注销中: 点击注销
|
||||
注销中 --> 已登录: 取消确认
|
||||
注销中 --> 未登录: 确认 → logout API → 清 cookie → 跳 /login
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、搬迁后验证清单
|
||||
|
||||
- [ ] `pnpm lint` 无报错
|
||||
- [ ] `pnpm serve` 启动成功
|
||||
- [ ] 访问 `http://localhost:8081/login` 能看到完整登录页面
|
||||
- [ ] 输入正确的用户名密码能登录并跳转首页
|
||||
- [ ] 中文/英文切换按钮文字正确
|
||||
- [ ] 登录失败有表单校验提示
|
||||
- [ ] 浏览器 Cookie 中有 `token` 和 `uuid`
|
||||
601
docs/菜单权限迁移.md
Normal file
601
docs/菜单权限迁移.md
Normal file
@@ -0,0 +1,601 @@
|
||||
# Menu 权限迁移方案
|
||||
|
||||
> 参照项目:`D:\code\company\SCTMES_MES_V5\vue-app`(以下简称"源项目")
|
||||
|
||||
---
|
||||
|
||||
## 一、源项目(SCTMES)Menu 权限架构
|
||||
|
||||
### 1.1 后端接口
|
||||
|
||||
```
|
||||
GET system_settings/menu_configuration/menu/all
|
||||
```
|
||||
|
||||
API 文件:[src/api/system_settings/menu_configuration/menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\api\system_settings\menu_configuration\menu.js)
|
||||
|
||||
返回当前用户可见的**扁平数组**,字段如下:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `menu_id` | number | 主键,用于构建父子关系 |
|
||||
| `parent_id` | number | 父节点 ID,`0` 表示顶栏根节点 |
|
||||
| `url` | string | 前端路由路径,如 `/system_settings/user_management/user` |
|
||||
| `name` | string | 菜单显示名称 |
|
||||
| `icon` | string | 图标名(FontAwesome) |
|
||||
| `is_navi` | number | 是否作为导航菜单展示(1=是) |
|
||||
| `status` | number | 启用/禁用 |
|
||||
| `type` | string | 菜单类型 |
|
||||
| `params` | string | 额外参数 |
|
||||
| `sort` | number | 排序 |
|
||||
| `remark` | string | 备注 |
|
||||
|
||||
### 1.2 菜单数据全链路
|
||||
|
||||
```
|
||||
登录成功 → account.login() → dispatch('load')
|
||||
│
|
||||
▼
|
||||
account.load()
|
||||
│
|
||||
...加载 user/theme/transition/page/menu/size/color...
|
||||
│
|
||||
▼
|
||||
dispatch('d2admin/menu/sourceDataLoad')
|
||||
│
|
||||
┌────────────┴────────────┐
|
||||
│ │
|
||||
localStorage localStorage 为空
|
||||
有缓存 且有 token
|
||||
│ │
|
||||
▼ ▼
|
||||
直接读缓存 GET /menu/all 后端接口
|
||||
│
|
||||
▼
|
||||
写入 localStorage 缓存
|
||||
│
|
||||
└────────────┬────────────┘
|
||||
│
|
||||
▼
|
||||
menu.install(this, sourceData)
|
||||
```
|
||||
|
||||
**Store 文件**:[src/store/modules/d2admin/modules/menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\store\modules\d2admin\modules\menu.js)
|
||||
|
||||
```js
|
||||
state: {
|
||||
header: [],
|
||||
aside: [],
|
||||
asideCollapse: setting.menu.asideCollapse,
|
||||
asideTransition: setting.menu.asideTransition,
|
||||
authKey: {}, // ← 权限字典 { '/path': '菜单名', ... }
|
||||
sourceData: [] // ← 菜单源数据(来自后端或本地缓存)
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 `sourceDataLoad` action 核心逻辑
|
||||
|
||||
```js
|
||||
async sourceDataLoad({ state, dispatch }) {
|
||||
// 1. 优先从 localStorage 读取缓存
|
||||
state.sourceData = await dispatch('d2admin/db/get', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
defaultValue: [],
|
||||
user: true
|
||||
}, { root: true })
|
||||
|
||||
// 2. 缓存为空且已登录 → 调后端获取
|
||||
if (!state.sourceData.length && util.cookies.get('token')) {
|
||||
const res = await getMenuAll(null)
|
||||
state.sourceData = res.data || []
|
||||
await dispatch('d2admin/db/set', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
value: state.sourceData,
|
||||
user: true
|
||||
}, { root: true })
|
||||
}
|
||||
|
||||
// 3. 处理为 header/aside/authKey
|
||||
menu.install(this, state.sourceData)
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 `menu.install()` — 扁平数据 → 菜单树 + 权限字典
|
||||
|
||||
**文件**:[src/menu/index.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\menu\index.js)
|
||||
|
||||
```js
|
||||
export default {
|
||||
install(vm, source) {
|
||||
// 1. 构建权限字典 { '/path': '菜单名' }
|
||||
vm.commit('d2admin/menu/headerAuth', source)
|
||||
|
||||
// 2. 扁平数据转为 {header: [], aside: []} 树结构
|
||||
const { header, aside } = getMenuData(source)
|
||||
|
||||
// 3. 写入 store → 触发菜单渲染
|
||||
vm.commit('d2admin/menu/headerSet', header)
|
||||
vm.commit('d2admin/menu/asideSet', aside)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`getMenuData()` 核心流程:
|
||||
|
||||
```
|
||||
source (扁平数组)
|
||||
│
|
||||
├── 过滤: is_navi !== 1 的跳过
|
||||
│
|
||||
├── parent_id === 0 → 推入 header[]
|
||||
│
|
||||
└── 全部节点 → 推入 aside[]
|
||||
│
|
||||
▼
|
||||
util.formatDataToTree(aside) { menu_id ↔ parent_id }
|
||||
│
|
||||
▼
|
||||
树形结构 aside
|
||||
```
|
||||
|
||||
**`headerAuth` mutation**:遍历 `source`,将每个有 `url` 的节点写入 `authKey[url] = name`:
|
||||
|
||||
```js
|
||||
headerAuth(state, source) {
|
||||
let auth = {}
|
||||
source.forEach(value => {
|
||||
if (value.url) {
|
||||
auth[value.url] = value.name
|
||||
}
|
||||
})
|
||||
state.authKey = auth
|
||||
}
|
||||
```
|
||||
|
||||
### 1.5 权限控制系统
|
||||
|
||||
**`$permission()` 方法** — 定义在 [src/plugin/d2admin/index.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\plugin\d2admin\index.js#L50):
|
||||
|
||||
```js
|
||||
Vue.prototype.$permission = (value, type = 'menu') => {
|
||||
let path = ''
|
||||
const auth = store.state.d2admin.menu.authKey
|
||||
|
||||
switch (type) {
|
||||
case 'menu': // 直接用 URL 查
|
||||
path = value
|
||||
break
|
||||
case 'router': // 路由 name → URL
|
||||
path = value.name.replace(/-/g, '/')
|
||||
path.slice(0, 1) !== '/' && (path = '/' + path)
|
||||
break
|
||||
}
|
||||
|
||||
return !!(path && Object.prototype.hasOwnProperty.call(auth, path))
|
||||
}
|
||||
```
|
||||
|
||||
**`v-permission` 指令** — 定义在 [src/main.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\main.js#L72-L78):
|
||||
|
||||
```js
|
||||
Vue.directive('permission', {
|
||||
bind: (el, binding) => {
|
||||
if (!Vue.prototype.$permission(binding.value)) {
|
||||
el.parentNode ? el.parentNode.removeChild(el) : el.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
使用示例:
|
||||
```html
|
||||
<el-button v-permission="'/system_settings/user_management/user'">新增用户</el-button>
|
||||
```
|
||||
|
||||
### 1.6 菜单导航权限过滤
|
||||
|
||||
**Menu Mixin** — [src/layout/header-aside/components/mixin/menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\layout\header-aside\components\mixin\menu.js):
|
||||
|
||||
点击顶栏一级菜单时,不直接跳转,而是查找第一个有权限的子路由:
|
||||
|
||||
```js
|
||||
getRouterAuthPath(index, indexPath) {
|
||||
// 子路由直接访问
|
||||
if (index === '/index' || !indexPath || indexPath.length > 1) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
// 查找一级路由下第一个有权限的子路由
|
||||
let router = null
|
||||
for (const value of frameInRoutes) {
|
||||
if (value.path === index) {
|
||||
router = value.children
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!router) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
for (const value of router) {
|
||||
const newPath = index + '/' + value.path
|
||||
if (this.$permission(newPath)) {
|
||||
this.$router.push({ path: newPath })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 1.7 路由 — 全部静态注册
|
||||
|
||||
路由在 [src/router/routes.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\router\routes.js) 中**全部静态定义**,不使用 `addRoute`。
|
||||
|
||||
路由守卫([src/router/index.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\router\index.js))仅判断 token 是否存在,不校验具体路由权限。权限控制在**菜单渲染**和**菜单点击时**完成。
|
||||
|
||||
### 1.8 main.js 启动流程
|
||||
|
||||
[src/main.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\main.js#L108-L137)
|
||||
|
||||
```js
|
||||
created() {
|
||||
// 仅初始化页面池,不再设置静态菜单
|
||||
this.$store.commit('d2admin/page/init', frameInRoutes)
|
||||
// 静态菜单设置已注释:
|
||||
// this.$store.commit('d2admin/menu/headerSet', menuHeader)
|
||||
// this.$store.commit('d2admin/search/init', menuHeader)
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('d2admin/account/load') // → sourceDataLoad → 获取菜单
|
||||
},
|
||||
watch: {
|
||||
// $route.matched 切换侧边栏的 watch 已注释
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、mes-ui 当前现状 vs 目标
|
||||
|
||||
| 模块 | mes-ui 现状 | 源项目(目标) |
|
||||
|------|-----------|---------------|
|
||||
| 菜单数据来源 | `src/menu/modules/*.js` 静态硬编码 | 后端 `GET /menu/all` + localStorage 缓存 |
|
||||
| `sourceDataLoad` | 仅 localStorage 读取,不参与菜单构建 | localStorage 兜底 + 调后端 → `menu.install()` |
|
||||
| 权限字典 | 无 | `authKey: { '/path': 'name' }` |
|
||||
| 菜单树构建 | 编译时 `supplementPath()` | 运行时 `getMenuData()` + `formatDataToTree()` |
|
||||
| 权限检查 | 无 | `$permission()` + `v-permission` 指令 |
|
||||
| 菜单点击 | 直接 `$router.push` | `getRouterAuthPath()` 查找首个有权限子路由 |
|
||||
| main.js 菜单初始化 | `created` 中 `headerSet(menuHeader)` | 注释掉静态菜单,由 `mounted` 中的 `load()` 触发 |
|
||||
| 路由注册 | 全部静态 | 全部静态(一致) |
|
||||
| 路由守卫 | 仅 token 检查 | 仅 token 检查(一致) |
|
||||
|
||||
---
|
||||
|
||||
## 三、迁移计划
|
||||
|
||||
### 阶段 1️⃣:约定后端接口格式
|
||||
|
||||
后端接口:`GET /api/menu/all`(或复用源项目的 `system_settings/menu_configuration/menu/all`)
|
||||
|
||||
返回格式(扁平数组):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": [
|
||||
{ "menu_id": 1, "parent_id": 0, "url": "/index", "name": "首页", "icon": "home", "is_navi": 1 },
|
||||
{ "menu_id": 2, "parent_id": 0, "url": "/production_master_data", "name": "生产主数据", "icon": "cogs", "is_navi": 1 },
|
||||
{ "menu_id": 3, "parent_id": 2, "url": "/production_master_data/factory_model/factory_area", "name": "工厂区域", "icon": "map-marker", "is_navi": 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 2️⃣:新增/改造 API 层
|
||||
|
||||
**新增** `src/api/menu.js`(参照源项目 [menu.js](file:///D:\code\company\SCTMES_MES_V5\vue-app\src\api\system_settings\menu_configuration\menu.js)):
|
||||
|
||||
```js
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
export function getMenuAll(data) {
|
||||
return request({
|
||||
url: 'menu/all',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_menu_configuration_menu_all',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 3️⃣:改造 `menu.js` Store
|
||||
|
||||
**改造** [src/store/modules/d2admin/modules/menu.js](file:///d:\code\mes\mes-ui\src\store\modules\d2admin\modules\menu.js)
|
||||
|
||||
| 改动 | 内容 |
|
||||
|------|------|
|
||||
| state | 新增 `authKey: {}` |
|
||||
| `sourceDataLoad` action | 改为:localStorage 兜底 → 调后端 → `menu.install()` |
|
||||
| mutations | 新增 `headerAuth(state, source)` → 构建 `authKey` 字典 |
|
||||
|
||||
```js
|
||||
// 关键变更 — sourceDataLoad:
|
||||
async sourceDataLoad({ state, commit, dispatch }) {
|
||||
// 1. 先读 localStorage 缓存
|
||||
state.sourceData = await dispatch('d2admin/db/get', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
defaultValue: [],
|
||||
user: true
|
||||
}, { root: true })
|
||||
|
||||
// 2. 缓存为空且已登录 → 调后端
|
||||
if (!state.sourceData.length && util.cookies.get('token')) {
|
||||
const res = await getMenuAll()
|
||||
state.sourceData = res || []
|
||||
await dispatch('d2admin/db/set', {
|
||||
dbName: 'database',
|
||||
path: '$menu.sourceData',
|
||||
value: state.sourceData,
|
||||
user: true
|
||||
}, { root: true })
|
||||
}
|
||||
|
||||
// 3. 构建菜单树 + 权限字典
|
||||
menuUtil.install(this, state.sourceData)
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 4️⃣:改造 `src/menu/index.js` — 从静态导出变为工具模块
|
||||
|
||||
将 [src/menu/index.js](file:///d:\code\mes\mes-ui\src\menu\index.js) 从"导出静态菜单配置"改为"菜单处理工具"(参照源项目):
|
||||
|
||||
```js
|
||||
import util from '@/libs/util'
|
||||
|
||||
function getMenuData(arr) {
|
||||
let tree = { header: [], aside: [] }
|
||||
|
||||
arr.forEach(value => {
|
||||
if (!value.is_navi) return // 过滤非导航菜单
|
||||
|
||||
const menuItem = {
|
||||
path: value.url,
|
||||
title: value.name,
|
||||
icon: value.icon,
|
||||
type: value.type,
|
||||
params: value.params
|
||||
}
|
||||
|
||||
if (value.parent_id === 0) {
|
||||
tree.header.push({ ...menuItem })
|
||||
}
|
||||
|
||||
menuItem.menu_id = value.menu_id
|
||||
menuItem.parent_id = value.parent_id
|
||||
tree.aside.push(menuItem)
|
||||
})
|
||||
|
||||
// 扁平数组转树
|
||||
tree.aside = util.formatDataToTree(tree.aside)
|
||||
return tree
|
||||
}
|
||||
|
||||
export default {
|
||||
install(vm, source) {
|
||||
vm.commit('d2admin/menu/headerAuth', source)
|
||||
const { header, aside } = getMenuData(source)
|
||||
vm.commit('d2admin/menu/headerSet', header)
|
||||
vm.commit('d2admin/menu/asideSet', aside)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
原有的静态菜单模块(`src/menu/modules/demo-*.js`)可保留但不再被引用,后续按需清理。
|
||||
|
||||
### 阶段 5️⃣:增加权限控制基础设施
|
||||
|
||||
#### 5.1 `$permission` 方法
|
||||
|
||||
**改造** `src/plugin/d2admin/index.js`,新增:
|
||||
|
||||
```js
|
||||
Vue.prototype.$permission = (value, type = 'menu') => {
|
||||
let path = ''
|
||||
const auth = store.state.d2admin.menu.authKey
|
||||
|
||||
switch (type) {
|
||||
case 'menu':
|
||||
path = value
|
||||
break
|
||||
case 'router':
|
||||
path = value.name.replace(/-/g, '/')
|
||||
path.slice(0, 1) !== '/' && (path = '/' + path)
|
||||
break
|
||||
}
|
||||
|
||||
return !!(path && Object.prototype.hasOwnProperty.call(auth, path))
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.2 `v-permission` 指令
|
||||
|
||||
**改造** `src/main.js`,新增:
|
||||
|
||||
```js
|
||||
Vue.directive('permission', {
|
||||
bind: (el, binding) => {
|
||||
if (!Vue.prototype.$permission(binding.value)) {
|
||||
el.parentNode ? el.parentNode.removeChild(el) : el.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 5.3 菜单点击权限过滤
|
||||
|
||||
**改造** [src/layout/header-aside/components/mixin/menu.js](file:///d:\code\mes\mes-ui\src\layout\header-aside\components\mixin\menu.js),增加 `getRouterAuthPath` 方法:
|
||||
|
||||
```js
|
||||
import { frameInRoutes } from '@/router/routes'
|
||||
|
||||
methods: {
|
||||
handleMenuSelect(index, indexPath) {
|
||||
if (/^d2-menu-empty-\d+$/.test(index) || index === undefined) {
|
||||
this.$message.warning('临时菜单')
|
||||
} else if (/^https:\/\/|http:\/\//.test(index)) {
|
||||
util.open(index)
|
||||
} else {
|
||||
this.getRouterAuthPath(index, indexPath)
|
||||
}
|
||||
},
|
||||
|
||||
getRouterAuthPath(index, indexPath) {
|
||||
if (index === '/index' || !indexPath || indexPath.length > 1) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
let router = null
|
||||
for (const value of frameInRoutes) {
|
||||
if (value.path === index) {
|
||||
router = value.children
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!router) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
for (const value of router) {
|
||||
const newPath = index + '/' + value.path
|
||||
if (this.$permission(newPath)) {
|
||||
this.$router.push({ path: newPath })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 6️⃣:改造 `main.js` 启动流程
|
||||
|
||||
**改造** [src/main.js](file:///d:\code\mes\mes-ui\src\main.js):
|
||||
|
||||
```js
|
||||
created() {
|
||||
this.$store.commit('d2admin/page/init', frameInRoutes)
|
||||
// 注释掉静态菜单设置:
|
||||
// this.$store.commit('d2admin/menu/headerSet', menuHeader)
|
||||
// this.$store.commit('d2admin/search/init', menuHeader)
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('d2admin/releases/versionShow')
|
||||
this.$store.dispatch('d2admin/account/load') // → sourceDataLoad → 获取后端菜单
|
||||
this.$store.commit('d2admin/ua/get')
|
||||
this.$store.dispatch('d2admin/fullscreen/listen')
|
||||
},
|
||||
watch: {
|
||||
// 注释掉 $route.matched 侧边栏切换(由 store.aside 直接驱动)
|
||||
// '$route.matched': { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### 阶段 7️⃣:增加 `util.formatDataToTree` 工具函数
|
||||
|
||||
**改造** `src/libs/util.js`,新增:
|
||||
|
||||
```js
|
||||
util.formatDataToTree = (data, key = 'menu_id', pid = 'parent_id') => {
|
||||
if (!data || data.length <= 0) return []
|
||||
|
||||
let map = {}
|
||||
data.forEach(value => { map[value[key]] = { ...value } })
|
||||
|
||||
let tree = []
|
||||
data.forEach(item => {
|
||||
const id = item[key]
|
||||
if (map[id][pid] && map[map[id][pid]]) {
|
||||
if (!map[map[id][pid]].children) {
|
||||
map[map[id][pid]].children = []
|
||||
}
|
||||
map[map[id][pid]].children.push(map[id])
|
||||
} else {
|
||||
tree.push(map[id])
|
||||
}
|
||||
})
|
||||
return tree
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、影响的文件清单
|
||||
|
||||
| 文件 | 改动类型 | 说明 |
|
||||
|------|----------|------|
|
||||
| `src/api/menu.js` | **新增** | 菜单 API(`getMenuAll`) |
|
||||
| `src/store/modules/d2admin/modules/menu.js` | **改造** | `sourceDataLoad` 加后端调用 + `headerAuth` mutation + `authKey` state |
|
||||
| `src/menu/index.js` | **重写** | 从静态配置改为 `install()` 工具模块 |
|
||||
| `src/plugin/d2admin/index.js` | **改造** | 新增 `$permission` 方法 |
|
||||
| `src/main.js` | **改造** | 注释静态菜单设置;新增 `v-permission` 指令 |
|
||||
| `src/layout/header-aside/components/mixin/menu.js` | **改造** | 增加 `getRouterAuthPath` 权限过滤 |
|
||||
| `src/libs/util.js` | **改造** | 新增 `formatDataToTree` 工具函数 |
|
||||
| `src/menu/modules/demo-*.js` | **后续可删** | 静态菜单数据不再使用,单独清理 |
|
||||
|
||||
---
|
||||
|
||||
## 五、完整时序图
|
||||
|
||||
```
|
||||
用户登录
|
||||
│
|
||||
▼
|
||||
login/index.vue → dispatch('d2admin/account/login')
|
||||
│
|
||||
▼
|
||||
account.js login action
|
||||
├── loginAdminUser() → 后端登录接口
|
||||
├── util.cookies.set() → 存 token/uuid
|
||||
├── dispatch('d2admin/user/set') → 存用户信息
|
||||
└── dispatch('load')
|
||||
│
|
||||
├── d2admin/user/load → 加载用户名
|
||||
├── d2admin/theme/load → 加载主题
|
||||
├── d2admin/transition/load → 加载过渡效果
|
||||
├── d2admin/page/openedLoad → 加载标签页
|
||||
├── d2admin/menu/asideLoad → 加载侧边栏收起设置
|
||||
├── d2admin/size/load → 加载全局尺寸
|
||||
├── d2admin/color/load → 加载颜色设置
|
||||
└── d2admin/menu/sourceDataLoad
|
||||
│
|
||||
├── 先读 localStorage 缓存
|
||||
├── 缓存空 → GET /api/menu/all 后端接口
|
||||
├── 写入 localStorage 缓存
|
||||
└── menu.install(this, sourceData)
|
||||
├── headerAuth → 构建 authKey 权限字典
|
||||
├── getMenuData → 扁平 → {header, aside} 树
|
||||
├── headerSet → 顶栏菜单渲染
|
||||
└── asideSet → 侧边栏菜单渲染
|
||||
│
|
||||
▼
|
||||
跳转到首页 → 菜单已按权限渲染
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、注意事项
|
||||
|
||||
1. **路由仍是静态注册的**:mes-ui 不需要改造成动态 `addRoute`,路由全部在 `router/routes.js` 中静态定义即可,权限通过菜单显示/隐藏 + `getRouterAuthPath` 控制。
|
||||
2. **不要删除源项目代码**:源项目在 `D:\code\company\SCTMES_MES_V5\vue-app`,仅作为参照,不对其做任何修改。
|
||||
3. **localStorage 缓存**是离线兜底:首次登录调后端,后续从缓存读取;注销时清空缓存。
|
||||
4. **`block` cookie**:已在登录页 `mounted` 中设为 `'false'`,注销时设为 `'true'`,用于控制错误日志是否弹出提示。
|
||||
1528
docs/表格组件使用说明.md
Normal file
1528
docs/表格组件使用说明.md
Normal file
File diff suppressed because it is too large
Load Diff
424
docs/迁移任务清单.md
Normal file
424
docs/迁移任务清单.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# MES-UI 搬迁任务列表
|
||||
|
||||
> 从旧项目 `D:\code\company\SCTMES_MES_V5\vue-app` 搬迁至本项目 `d:\code\mes\mes-ui`
|
||||
|
||||
## 搬迁规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 目录结构 | 按 `后台Webman界面截图对照表.md` 中的 一级→二级→三级 模块层级创建 |
|
||||
| 页面视图 | `src/views/{一级}/{二级}/{三级}/index.vue` |
|
||||
| API 接口 | `src/api/{一级}/{二级}.js` |
|
||||
| Vuex Store | `src/store/modules/{一级}/{二级}.js` |
|
||||
| 路由 | `src/router/modules/{一级}.js`,汇总到 `routes.js` |
|
||||
| 组件 | 公用组件放 `src/components/`,模块内组件放 `views` 下的 `components/` |
|
||||
| 表格 | 搬迁时统一替换为 `page-table` 或 `sct-table` + `sct-toolbar`(见 [重构方案](./SCT基础表格重构设计.md)),不再使用旧的 `sct-base-table` |
|
||||
| 代码审查 | 搬迁前检查旧代码中的不合理处(见下方「代码审查与改进清单」),搬迁时一并修复 |
|
||||
|
||||
---
|
||||
|
||||
## 预览:前置准备
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| P0 | 创建目标目录结构(所有一级/二级/三级空文件夹) | — | `src/views/` | ⬜ |
|
||||
| P1 | 迁移公共服务层(axios 实例、工具函数等) | `src/api/service.js` `src/api/tools.js` `src/api/index.js` `src/libs/` | `src/api/` `src/libs/` | ⬜ |
|
||||
| P2 | 迁移登录页面、首页、404/刷新/重定向等系统通用页面 | `src/views/system_settings/system_monitoring/system/` `src/views/system_settings/home_page/` | `src/views/system-settings/` | ⬜ |
|
||||
| P3 | 迁移路由入口和 routes.js 汇总 | `src/router/index.js` `src/router/routes.js` | `src/router/` | ⬜ |
|
||||
| P4 | 迁移全局 Store(d2admin) | `src/store/modules/d2admin/` | `src/store/modules/d2admin/` | ⬜ |
|
||||
| P5 | 迁移布局组件(header-aside)及菜单配置 | `src/layout/` `src/menu/` | `src/layout/` `src/menu/` | ⬜ |
|
||||
| P6 | 迁移第三方依赖(main.js 中的插件、组件注册) | `src/main.js` `src/App.vue` `src/plugin/` | `src/main.js` `src/App.vue` `src/plugin/` | ⬜ |
|
||||
| P7 | 迁移本地化文件(i18n) | `src/locales/` `src/i18n.js` | `src/locales/` `src/i18n.js` | ⬜ |
|
||||
| P8 | 迁移全局设置 | `src/setting.js` `.env` 系列 | `src/setting.js` `.env` 系列 | ⬜ |
|
||||
| P9 | 移植旧项目专用组件(form、sct-ace 等) | `src/components/battery/` `src/components/calculation/` `src/components/device-monitor/` `src/components/dm-print/` `src/components/form/` `src/components/menu-tree/` `src/components/sct-ace/` `src/components/sct-ace-editor/` `src/components/OCR/` | `src/components/` | ⬜ |
|
||||
| P10 | 迁移旧项目 public 目录下的静态资源 | `public/` | `public/` | ⬜ |
|
||||
| P11 | 迁移旧项目 assets 资源 | `src/assets/` | `src/assets/` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 一、系统设置 (System Administration)
|
||||
|
||||
> 目标目录根:`src/views/system-settings/`
|
||||
|
||||
### 1.1 用户管理 (User Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| S1 | 角色 (Role) | `system_settings/user_management/role/` | `system_settings/user_management/role.js` | `sctmesadmin/modules/role.js` | `system-settings/user-management/role/` | ⬜ |
|
||||
| S2 | 用户 (User) | `system_settings/user_management/user/` | `system_settings/user_management/user.js` | `sctmesadmin/modules/user.js` | `system-settings/user-management/user/` | ⬜ |
|
||||
|
||||
### 1.2 菜单管理 (Menu Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| S3 | 菜单配置 | `system_settings/menu_configuration/menu/` | `system_settings/menu_configuration/menu.js` | `system-settings/menu-management/menu-configuration/` | ⬜ |
|
||||
|
||||
### 1.3 系统助手 (System Utilities)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| S4 | 操作日志 | `system_settings/system_assistant/operate_log/` | `system_settings/system_assistant/operate_log.js` | `system-settings/system-utilities/operation-logs/` | ⬜ |
|
||||
| S5 | 接口日志 | `system_settings/system_assistant/interface_log/` | `system_settings/system_assistant/interface_log.js` | `system-settings/system-utilities/api-logs/` | ⬜ |
|
||||
|
||||
### 1.4 系统监控 (System Monitoring)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| S6 | 监控设置 | `system_settings/system_monitoring/setting/` | `system_settings/system_monitor/setting.js` | `system-settings/system-monitoring/monitoring-configuration/` | ⬜ |
|
||||
|
||||
### 1.5 系统设置 - 路由汇总
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| S7 | 系统设置路由 | `router/system_settings/system.js` | `router/modules/system-settings.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 二、生产配置 (Production Master Data)
|
||||
|
||||
> 目标目录根:`src/views/production-configuration/`
|
||||
|
||||
### 2.1 工厂模型 (Factory Model)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| P1 | 产线设置 | `production_configuration/factory_model/factory_line/` | 需从路由确认 | `sctmesadmin/modules/line.js` | `production-configuration/factory-model/production-line/` | ⬜ |
|
||||
| P2 | 工厂区域 | `production_configuration/factory_model/factory_area/` | 需从路由确认 | `sctmesadmin/modules/area.js` | `production-configuration/factory-model/factory-area/` | ⬜ |
|
||||
|
||||
### 2.2 工艺模型 (Process Model)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| P3 | 工艺流程类别 | `production_configuration/technology_model/technology_flow_category/` | 需从路由确认 | `sctmesadmin/modules/technology_category.js` | `production-configuration/process-model/process-category/` | ⬜ |
|
||||
| P4 | 工序单元 | 需确认 | `production_configuration/workerman/workermanSet.js` | `sctmesadmin/modules/steps.js` | `production-configuration/process-model/process-step/` | ⬜ |
|
||||
| P5 | 工艺流程 | `production_configuration/technology_model/technology_flow/` | 需从路由确认 | `sctmesadmin/modules/technologyflow.js` | `production-configuration/process-model/process-routing/` | ⬜ |
|
||||
|
||||
### 2.3 产品管理 (Product Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| P6 | 产品列表 | `production_configuration/product_model/battery_model/` | 需从路由确认 | `sctmesadmin/modules/product_battery.js` | `production-configuration/product-management/product-list/` | ⬜ |
|
||||
| P7 | 不良管理 | `production_configuration/product_model/product_ng_info/` | 需从路由确认 | `sctmesadmin/modules/product_ng_info.js` | `production-configuration/product-management/defect-management/` | ⬜ |
|
||||
|
||||
### 2.4 物料模型 (Material Model)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| P8 | 物料类别列表 | `production_configuration/matetial_model/matetial_category/` | `warehouse/basic/material_category.js` | `sctmesadmin/modules/material_category.js` | `production-configuration/material-model/material-category/` | ⬜ |
|
||||
| P9 | 物料信息管理 | `production_configuration/matetial_model/matetial_management/` | `warehouse/basic/material.js` | — | `production-configuration/material-model/material-master/` | ⬜ |
|
||||
| P10 | BOM物料清单 | `production_configuration/matetial_model/bom/` | `production_configuration/matetial_model/bom.js` | — | `production-configuration/material-model/bom/` | ⬜ |
|
||||
| P11 | 计量单位 | `production_configuration/matetial_model/unit/` | `production_configuration/matetial_model/unit.js` | `sctmesadmin/modules/unit.js` | `production-configuration/material-model/unit-of-measure/` | ⬜ |
|
||||
|
||||
### 2.5 SPC采集模型 (SPC Configuration)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| P12 | SPC采集配置 | `production_configuration/spc_configuration/binding_scada_node/` | 需从路由确认 | `production-configuration/spc-configuration/spc-data-collection/` | ⬜ |
|
||||
|
||||
### 2.6 班组模型 (Team Model)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| P13 | 班组管理 | `system_settings/organization/production_team_manage/` | 需从路由确认 | `sctmesadmin/modules/production_team_manage.js` | `production-configuration/team-model/team-management/` | ⬜ |
|
||||
| P14 | 班次管理 | `system_settings/organization/production_shift_management/` | 需从路由确认 | — | `production-configuration/team-model/shift-management/` | ⬜ |
|
||||
| P15 | 排班日历 | `system_settings/organization/production_shift_calender/` | 需从路由确认 | — | `production-configuration/team-model/scheduling-calendar/` | ⬜ |
|
||||
|
||||
### 2.7 生产配置 - 路由汇总
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| P16 | 生产配置路由 | `router/production_configuration/index.js` | `router/modules/production-configuration.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 三、设备模型 (Equipment Management)
|
||||
|
||||
> 目标目录根:`src/views/equipment-management/`
|
||||
|
||||
### 3.1 设备类别 (Equipment Category)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| E1 | 设备类别 | `production_configuration/device_model/device_category/` | 需从路由确认 | `sctmesadmin/modules/device_category.js` | `equipment-management/equipment-category/` | ⬜ |
|
||||
|
||||
### 3.2 设备信息 (Equipment Info)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| E2 | 设备信息 | `production_configuration/device_model/device_management/` | 需从路由确认 | `sctmesadmin/modules/device.js` | `equipment-management/equipment-info/` | ⬜ |
|
||||
|
||||
### 3.3 设备点检 (Inspection Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| E3 | 设备点检项目 | `device_management/device_check/device_check_items/` | 需从路由确认 | `sctmesadmin/modules/device_check_items.js` | `equipment-management/inspection-management/inspection-items/` | ⬜ |
|
||||
| E4 | 设备点检记录 | `device_management/device_check/device_check_record/` | 需从路由确认 | — | `equipment-management/inspection-management/inspection-records/` | ⬜ |
|
||||
| E5 | 设备点检日志 | `device_management/device_check/device_check_items_log/` | 需从路由确认 | — | `equipment-management/inspection-management/inspection-logs/` | ⬜ |
|
||||
|
||||
### 3.4 设备保养 (Maintenance Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| E6 | 设备保养项目 | `device_management/device_maintain/device_maintain_items/` | 需从路由确认 | `sctmesadmin/modules/device_maintain_items.js` | `equipment-management/maintenance-management/maintenance-items/` | ⬜ |
|
||||
| E7 | 设备保养详情 | `device_management/device_maintain/device_maintain_items_details/` | 需从路由确认 | — | `equipment-management/maintenance-management/maintenance-details/` | ⬜ |
|
||||
| E8 | 设备保养日志 | `device_management/device_maintain/device_maintain_items_log/` | 需从路由确认 | — | `equipment-management/maintenance-management/maintenance-logs/` | ⬜ |
|
||||
|
||||
### 3.5 设备维修 (Repair Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| E9 | 设备维修管理 | `device_management/device_repair/device_repair_management/` | 需从路由确认 | `equipment-management/repair-management/repair-management/` | ⬜ |
|
||||
| E10 | 设备维修日志 | `device_management/device_repair/device_repair_log/` | 需从路由确认 | `equipment-management/repair-management/repair-logs/` | ⬜ |
|
||||
|
||||
### 3.6 设备损耗品 (Consumables Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| E11 | 设备损耗品类别 | `device_management/device_consumables/device_consumables_category/` | 需从路由确认 | `sctmesadmin/modules/device_consumables_category.js` | `equipment-management/consumables/consumables-category/` | ⬜ |
|
||||
| E12 | 设备损耗品项目 | `device_management/device_consumables/device_consumables_items/` | 需从路由确认 | `sctmesadmin/modules/device_consumables_items.js` | `equipment-management/consumables/consumables-items/` | ⬜ |
|
||||
| E13 | 设备损耗品寿命管理 | 需确认 | 需从路由确认 | `sctmesadmin/modules/device_consumables_lifetime_management.js` | `equipment-management/consumables/consumables-lifecycle/` | ⬜ |
|
||||
| E14 | 设备损耗品更换日志 | `device_management/device_consumables/device_consumables_replace_log/` | 需从路由确认 | — | `equipment-management/consumables/replacement-logs/` | ⬜ |
|
||||
|
||||
### 3.7 设备模型 - 路由汇总
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| E15 | 设备模型路由 | `router/device_management/device.js` | `router/modules/equipment-management.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 四、计划与生产 (Planning & Production)
|
||||
|
||||
> 目标目录根:`src/views/planning-production/`
|
||||
|
||||
### 4.1 生产批次管理 (Batch Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| B1 | 批次列表 | `planning_production/production_batch_management/batch/` | `data_dashboards/produce/batch.js` | `sctmesadmin/modules/batch.js` | `planning-production/batch-management/batch-list/` | ⬜ |
|
||||
| B2 | 批次托盘 | `planning_production/production_batch_management/batch_tray/` | 需从路由确认 | — | `planning-production/batch-management/tray-tracking/` | ⬜ |
|
||||
| B3 | 生产批次不良报表 | `planning_production/production_batch_management/bad/` | 需从路由确认 | — | `planning-production/batch-management/batch-defect-report/` | ⬜ |
|
||||
|
||||
### 4.2 预警中心 (Alert Center)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| B4 | 预警中心 | 需确认(可能在 pannel/index) | `planning_production/pannel/index.js` | `planning-production/alert-center/` | ⬜ |
|
||||
|
||||
### 4.3 生产监控 (Production Monitoring)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|-----------|:----:|
|
||||
| B5 | 物料监控 | `planning_production/produce/monitor/wareroom/ic/` 或 WIP | `data_dashboards/produce/material/wip.js` | — | `planning-production/production-monitoring/material-monitoring/` | ⬜ |
|
||||
| B6 | 电池复投管理 | 需确认 | `data_dashboards/produce/battery/replace.js` | — | `planning-production/production-monitoring/rework-management/` | ⬜ |
|
||||
| B7 | 托盘管理 | `planning_production/produce/monitor/tray_manage/` | `data_dashboards/produce/tray/list.js` `planning_production/produce/tray_manage.js` | — | `planning-production/production-monitoring/tray-management/` | ⬜ |
|
||||
| B8 | 托盘登录 | `planning_production/produce/monitor/tray_login/` | `planning_production/battery/login.js` `planning_production/produce/tray_login.js` | — | `planning-production/production-monitoring/tray-registration/` | ⬜ |
|
||||
| B9 | 设备监控 | `planning_production/produce/monitor/device/` | `data_dashboards/produce/report/device.js` | — | `planning-production/production-monitoring/equipment-monitoring/` | ⬜ |
|
||||
| B10 | 电池工序管理 | `planning_production/produce/monitor/batch_battery/` 等 | `planning_production/produce/batch_battery.js` | `sctmesadmin/modules/batch_battery.js` | `planning-production/production-monitoring/process-execution/` | ⬜ |
|
||||
|
||||
### 4.4 计划与生产 - 路由汇总
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| B11 | 计划与生产路由 | `router/planning_production/index.js` | `router/modules/planning-production.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 五、质量管理 (Quality Management)
|
||||
|
||||
> 目标目录根:`src/views/quality-management/`
|
||||
|
||||
### 5.1 过程控制 (Process Control)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| Q1 | 检验类别管理 | `quality_control/first_inspection/category/` | `quality_control/xqc/inspection_category.js` | `quality-management/process-control/inspection-type/` | ⬜ |
|
||||
| Q2 | 首巡检项目配置 | `quality_control/first_inspection/setting/` | 需从路由确认 | `quality-management/process-control/first-article-inspection-config/` | ⬜ |
|
||||
| Q3 | 首巡检录入 | `quality_control/first_inspection/input/` | 需从路由确认 | `quality-management/process-control/first-article-inspection-records/` | ⬜ |
|
||||
| Q4 | 首巡检报表 | `quality_control/first_inspection/report/` | 需从路由确认 | `quality-management/process-control/first-article-inspection-reports/` | ⬜ |
|
||||
|
||||
### 5.2 检验控制 (Inspection Management)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| Q5 | 检验单管理 | `quality_control/xqc/inspection_order_manage/` | `quality_control/xqc/inspection_order_manage.js` | `quality-management/inspection-control/inspection-orders/` | ⬜ |
|
||||
| Q6 | 检验标准 | `quality_control/xqc/inspection_standard/` | `quality_control/xqc/inspection_standard.js` | `quality-management/inspection-control/inspection-standards/` | ⬜ |
|
||||
| Q7 | 接收质量限 (AQL) | `quality_control/xqc/aql_config/` `quality_control/xqc/aql_sample/` | `quality_control/xqc/aql_config.js` `quality_control/xqc/aql_sample.js` | `quality-management/inspection-control/aql-standards/` | ⬜ |
|
||||
| Q8 | 检测方案维护 | `quality_control/xqc/inspection_plan/` | `quality_control/xqc/inspection_plan.js` | `quality-management/inspection-control/inspection-plans/` | ⬜ |
|
||||
| Q9 | 检验项目 | `quality_control/xqc/inspection_item/` | `quality_control/xqc/inspection_item.js` | `quality-management/inspection-control/inspection-items/` | ⬜ |
|
||||
| Q10 | 抽样方案配置 | `quality_control/xqc/sampling_plan/` | `quality_control/xqc/sampling_plan.js` | `quality-management/inspection-control/sampling-plans/` | ⬜ |
|
||||
|
||||
### 5.3 SPC统计过程控制 (SPC Control)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| Q11 | SPC渲染条件配置 | `spc/manage/` | `spc/index.js` | `quality-management/spc-control/spc-configuration/` | ⬜ |
|
||||
|
||||
### 5.4 SPC计量型报表 (SPC Variable Charts)
|
||||
|
||||
| ID | 图表 | 源 views | 目标 views | 状态 |
|
||||
|----|------|---------|----------|:----:|
|
||||
| Q12 | XBar-R | `spc/spc_chart/xbar-r/` | `quality-management/spc-variable-charts/xbar-r/` | ⬜ |
|
||||
| Q13 | XBar-S | `spc/spc_chart/xbar-s/` | `quality-management/spc-variable-charts/xbar-s/` | ⬜ |
|
||||
| Q14 | I-MR | `spc/spc_chart/i-mr/` | `quality-management/spc-variable-charts/i-mr/` | ⬜ |
|
||||
| Q15 | Levey-Jennings | `spc/spc_chart/levey-jennings/` | `quality-management/spc-variable-charts/levey-jennings/` | ⬜ |
|
||||
| Q16 | EWMA | `spc/spc_chart/ewma/` | `quality-management/spc-variable-charts/ewma/` | ⬜ |
|
||||
| Q17 | CUSUM | `spc/spc_chart/cusum/` | `quality-management/spc-variable-charts/cusum/` | ⬜ |
|
||||
| Q18 | MA | `spc/spc_chart/ma/` | `quality-management/spc-variable-charts/ma/` | ⬜ |
|
||||
| Q19 | MAMR | `spc/spc_chart/mamr/` | `quality-management/spc-variable-charts/mamr/` | ⬜ |
|
||||
| Q20 | MAMS | `spc/spc_chart/mams/` | `quality-management/spc-variable-charts/mams/` | ⬜ |
|
||||
| Q21 | CPK | `spc/spc_chart/Cpk/` | `quality-management/spc-variable-charts/cpk/` | ⬜ |
|
||||
|
||||
### 5.5 SPC计数型报表 (SPC Attribute Charts)
|
||||
|
||||
| ID | 图表 | 源 views | 目标 views | 状态 |
|
||||
|----|------|---------|----------|:----:|
|
||||
| Q22 | DPMO | `spc/spc_chart/dpmo/` | `quality-management/spc-attribute-charts/dpmo/` | ⬜ |
|
||||
| Q23 | PChart | `spc/spc_chart/p-chart/` | `quality-management/spc-attribute-charts/p-chart/` | ⬜ |
|
||||
| Q24 | NPChart | `spc/spc_chart/np-chart/` | `quality-management/spc-attribute-charts/np-chart/` | ⬜ |
|
||||
| Q25 | CChart | `spc/spc_chart/c-chart/` | `quality-management/spc-attribute-charts/c-chart/` | ⬜ |
|
||||
| Q26 | UChart | `spc/spc_chart/u-chart/` | `quality-management/spc-attribute-charts/u-chart/` | ⬜ |
|
||||
|
||||
### 5.6 质量管理 - 路由汇总
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| Q27 | 质量管理路由 | `router/quality_control/quality.js` | `router/modules/quality-management.js` | ⬜ |
|
||||
| Q28 | SPC路由 | `router/spc/index.js` | 合并到 `router/modules/quality-management.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 六、数据中台 (Data Platform)
|
||||
|
||||
> 目标目录根:`src/views/data-platform/`
|
||||
|
||||
### 6.1 基础追溯 (Traceability)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| D1 | 反向追溯 | `data_middleground/basic_traceability/reverse_direction_traceability/` 或正向追溯 | `data_dashboards/produce/traceability/battery.js` | `data-platform/traceability/backward/` | ⬜ |
|
||||
| D2 | 正向追溯 | `data_middleground/basic_traceability/` 下确认 | `data_dashboards/produce/traceability/bom_batch.js` | `data-platform/traceability/forward/` | ⬜ |
|
||||
| D3 | 电池曲线 | `planning_production/produce/traceability/curve/` | `data_dashboards/produce/traceability/curve.js` | `data-platform/traceability/battery-curve/` | ⬜ |
|
||||
| D4 | 托盘追溯 | `planning_production/produce/traceability/tray/` | `data_dashboards/produce/traceability/tray.js` | `data-platform/traceability/tray/` | ⬜ |
|
||||
| D5 | 电池追溯 | `planning_production/produce/traceability/battery/` | `data_dashboards/produce/traceability/battery.js` | `data-platform/traceability/battery/` | ⬜ |
|
||||
|
||||
### 6.2 生产报表 (Production Reports)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| D6 | 设备履历报表 | `planning_production/produce/report/battery/` 或设备报告 | `data_dashboards/produce/report/all_report.js` `data_dashboards/produce/report/device.js` | `data-platform/production-reports/equipment-history/` | ⬜ |
|
||||
| D7 | 电池详情报表 | `planning_production/produce/report/battery_details_report/` 或电池报告 | `data_dashboards/produce/report/battery.js` | `data-platform/production-reports/battery-detail/` | ⬜ |
|
||||
|
||||
### 6.3 相关性分析 (Correlation Analysis)
|
||||
|
||||
| ID | 三级模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|---------|---------|--------|----------|:----:|
|
||||
| D8 | 鹰眼 (Hawkeye) | `data_middleground/eagle_eyes/` | `data_middle_office/eagle_eyes/index.js` | `data-platform/correlation-analysis/hawkeye/` | ⬜ |
|
||||
|
||||
### 6.4 数据中台 - 路由汇总
|
||||
|
||||
| ID | 任务 | 源文件 | 目标位置 | 状态 |
|
||||
|----|------|--------|----------|:----:|
|
||||
| D9 | 数据中台路由 | `router/data_middleground/index.js` | `router/modules/data-platform.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 七、仓储管理 (Warehouse)
|
||||
|
||||
> 目标目录根:`src/views/warehouse/`
|
||||
|
||||
| ID | 模块 | 源 views | 源 api | 源 store | 目标 views | 状态 |
|
||||
|----|------|---------|--------|----------|-----------|:----:|
|
||||
| W1 | 仓库设置(业务类型/单据类型/仓库/库区/库位/货架) | `warehouse/setting/` | `warehouse/setting/` | `warehouse/modules/` | `warehouse/settings/` | ⬜ |
|
||||
| W2 | 基础数据(客户/物料类别/物料/供应商/领料单) | `warehouse/basic/` | `warehouse/basic/` | `warehouse/modules/` | `warehouse/basic/` | ⬜ |
|
||||
| W3 | ERP接口(接口/采购订单/产品出库/发送日志) | `warehouse/erp/` | `warehouse/erp/` | — | `warehouse/erp/` | ⬜ |
|
||||
| W4 | 收货管理(采购入库/打印/来料检验/上架等) | `warehouse/receiving_management/` | `warehouse/receiving_management/` | — | `warehouse/receiving/` | ⬜ |
|
||||
| W5 | 发货管理(出库/委外出库等) | `warehouse/shipping_management/` | 需确认 | — | `warehouse/shipping/` | ⬜ |
|
||||
| W6 | 库存管理(库存/冻结/锁定/操作日志) | `warehouse/stock_management/` | `warehouse/stock_management/` | — | `warehouse/stock/` | ⬜ |
|
||||
| W7 | 作业管理(库存移动/盘点) | `warehouse/working/` | `warehouse/working/` | — | `warehouse/operations/` | ⬜ |
|
||||
| W8 | 仓储路由 | `router/warehouse/index.js` | — | — | `router/modules/warehouse.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 八、SCADA管理(保留旧模块,不在主文档中)
|
||||
|
||||
> 目标目录根:`src/views/scada-management/`
|
||||
|
||||
| ID | 模块 | 源 views | 源 api | 目标 views | 状态 |
|
||||
|----|------|---------|--------|----------|:----:|
|
||||
| SC1 | 车间管理(配置/点位) | `scada_manage/workshop_manage/` | `scada_manage/workshop_manage/` | `scada-management/workshop/` | ⬜ |
|
||||
| SC2 | 基础配置(SCADA配置/查询/节点映射/EMS) | `scada_manage/basic_configuration/` | `modules/scada.configure.api.js` | `scada-management/basic-config/` | ⬜ |
|
||||
| SC3 | 边缘服务器(配置/监控/日志) | `scada_manage/lecpserver/` `scada_manage/EdgeProcessorsManage/` | `modules/edgeServer.api.js` | `scada-management/edge-server/` | ⬜ |
|
||||
| SC4 | 设备采集监控 | `scada_manage/device_gather/` | `scada_manage/huankong_management/` | `scada-management/device-gather/` | ⬜ |
|
||||
| SC5 | SCADA路由 | `router/scada_management/scada.js` | — | `router/modules/scada-management.js` | ⬜ |
|
||||
|
||||
---
|
||||
|
||||
## 代码审查与改进清单
|
||||
|
||||
> 在搬迁过程中,逐项检查旧代码中的不合理之处,搬迁时一并修复。每个问题标注 **发现时间** 和 **处理状态**。
|
||||
|
||||
### 🔴 严重问题(必须修复)
|
||||
|
||||
| # | 问题描述 | 影响范围 | 改进方案 | 状态 |
|
||||
|---|---------|---------|---------|:--:|
|
||||
| CR01 | **拼写错误遍布路由和目录名**:`matetial`→应为`material`,`freezeunfreeze`→`freeze-unfreeze`,出现 23+ 处 | `router/production_configuration/index.js`<br>`views/production_configuration/matetial_model/` | 搬迁时统一修正为正确拼写,路由 path 也一并修正 | ⬜ |
|
||||
| CR02 | **生产代码残留 `console.log`**:285+ 处 `console.log/warn/error` 分布在 100+ 个文件中 | `src/views/` 几乎所有模块 | 移除所有调试用 `console.log`,仅保留关键 `console.error` 并使用统一日志工具 `util.log` | ⬜ |
|
||||
| CR03 | **重复/拷贝目录残留**:`scada_query copy/` 显式拷贝备份目录 | `views/scada_manage/basic_configuration/scada_query copy/` | 删除该拷贝目录,如需要保留则合并到正式目录 | ⬜ |
|
||||
|
||||
### 🟡 中等问题(建议修复)
|
||||
|
||||
| # | 问题描述 | 影响范围 | 改进方案 | 状态 |
|
||||
|---|---------|---------|---------|:--:|
|
||||
| CR04 | **API 注入模式混乱**:旧模式用 `require.context` 动态注入(`api/modules/*.api.js`),新模式用直接 `import`(`api/system_settings/`),两套并存 | `api/index.js` + `api/modules/` | 统一为直接 `import` 模式,移除 `require.context` 动态注入和 `api/modules/` 目录 | ⬜ |
|
||||
| CR05 | **`let` 滥用**:API 文件中大量 `let url = urls + 'xxx'`,URL 拼接后从不重新赋值 | `api/` 下所有文件 | 全部改为 `const` | ⬜ |
|
||||
| CR06 | **Store 初始化直接用 `localStorage` 无容错**:`JSON.parse(localStorage.getItem('roleData'))` 放在 state 声明顶层,解析失败会导致模块加载崩溃 | `store/modules/sctmesadmin/modules/role.js` 等 36 个模块 | 改为在 getter 或 action 中惰性读取,并用 try-catch 包裹 `JSON.parse` | ⬜ |
|
||||
| CR07 | **每个 API 函数都手动传 `method` 和 `platform` 参数**,冗余且容易遗漏 | `api/` 下 80+ 文件 | 在 `request` 公共层统一注入 `platform: 'background'`;`method` 参数通过约定 url 自动映射 | ⬜ |
|
||||
| CR08 | **axios 拦截器 `switch-case` 冗长**:响应拦截器整段 switch-case 映射 HTTP 状态码到错误消息 | `api/service.js` | 改用 Map 结构 | error.message = statusMessages[status] \|\| error.message,更简洁 | ⬜ |
|
||||
| CR09 | **路由文件命名不一致**:有 `system.js`、`device.js`、`scada.js`、`index.js` 混用 | `router/` 所有文件 | 统一为 `index.js`,按文件夹区分模块 | ⬜ |
|
||||
| CR10 | **`system_Assistant` 大小写不一致**:路由 path 中是 `system_Assistant`(大写A),目录名是 `system_assistant`(小写a) | `router/system_settings/system.js` | 统一为 `system-assistant`(kebab-case) | ⬜ |
|
||||
| CR11 | **Store 模块 `namespaced: true` 命名不规范**:部分模块路径很深 `sctmesadmin/modules/xxx`,难以维护 | `store/modules/sctmesadmin/` | 按功能打平为 `store/modules/{功能名}.js`,去掉多余嵌套 | ⬜ |
|
||||
|
||||
### 🟢 建议优化(可选)
|
||||
|
||||
| # | 问题描述 | 影响范围 | 改进方案 | 状态 |
|
||||
|---|---------|---------|---------|:--:|
|
||||
| CR12 | **页面组件 `PageHeader/PageMain` 内联模式冗余**:绝大多数页面都是 `index.vue` + `components/PageHeader/index.vue` + `components/PageMain/index.vue` 三层结构,PageHeader 大多只传几个 props | 所有 views | 评估是否可将简单的 PageHeader/PageMain 合并为单文件组件,减少目录嵌套 | ⬜ |
|
||||
| CR13 | **`Promise.resolve(...)` 包裹 sync 数据**:Store actions 中 `return Promise.resolve(res.data)` 在 `async` 函数里是多余的 | `store/modules/` 所有 actions | 直接 `return res.data`,`async` 函数自动包装返回值 | ⬜ |
|
||||
| CR14 | **国际化键名硬编码**:大量组件中 `$t('xxx')` 的 key 没有类型约束,容易拼错 | 所有 Vue 组件 | 搬迁后统一整理 i18n key,考虑用常量池管理 | ⬜ |
|
||||
| CR15 | **公共组件未按功能分组**:`components/` 下散落 `battery/`、`calculation/`、`tray/`、`technology/` 等多个业务组件与通用组件混放 | `src/components/` | 业务组件迁入对应模块 views 的 `components/`,公共组件保留并文档化 | ⬜ |
|
||||
| CR16 | **`sct-base-table` 组件架构臃肿**:215 行代码包含致命 bug(引入 yargs)、60% 死代码、buttonList/columns 在 100+ 页面中重复定义 | `components/sct-base-table/` 及所有引用页面 | 📄 详见 [SCT基础表格重构设计](./SCT基础表格重构设计.md):拆分为 7 个小组件 + 5 个 composable 函数,消除重复,迁移与搬迁同步进行 | ⬜ |
|
||||
|
||||
### 📄 专项重构方案
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [SCT基础表格重构设计](./SCT基础表格重构设计.md) | 组合式重构:拆组件+composable,消除 buttonList/columns 手动定义,覆盖 100+ 页面 |
|
||||
|
||||
---
|
||||
|
||||
## 搬迁统计
|
||||
|
||||
| 一级模块 | 三级页面数 | 路由文件 | API文件 | Store模块 | 已完成 |
|
||||
|---------|-----------|---------|---------|----------|:----:|
|
||||
| 系统设置 | 6 | 1 | 5 | 2 | 0/13 |
|
||||
| 生产配置 | 15 | 1 | ~10 | 12 | 0/37 |
|
||||
| 设备模型 | 14 | 1 | ~10 | 7 | 0/31 |
|
||||
| 计划与生产 | 10 | 1 | ~8 | 3 | 0/21 |
|
||||
| 质量管理 | 26 | 2 | ~15 | 0 | 0/43 |
|
||||
| 数据中台 | 8 | 1 | ~8 | 0 | 0/17 |
|
||||
| 仓储管理 | 25+ | 1 | ~20 | 11 | 0/56 |
|
||||
| SCADA管理 | 10+ | 1 | ~6 | 0 | 0/16 |
|
||||
| 前置准备 | — | 2 | — | 1 | 0/11 |
|
||||
| **合计** | **114+** | **11** | **82+** | **36** | **0/245** |
|
||||
|
||||
---
|
||||
|
||||
## 搬迁流程说明
|
||||
|
||||
每条 ID 任务完成后,将状态从 ⬜ 改为 ✅,并在下方补充日期和备注。
|
||||
|
||||
示例:
|
||||
```
|
||||
| S1 | 角色 (Role) | `...` | `...` | `...` | ✅ |
|
||||
```
|
||||
359
docs/迁移功能测试流程.md
Normal file
359
docs/迁移功能测试流程.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# MES-UI 迁移功能测试流程文档(累积追加版)
|
||||
|
||||
> 本文档用于记录每次 MES-UI 迁移完成后的功能测试流程。
|
||||
> 采用**累积追加**模式:每次完成新功能迁移,将对应测试内容追加到本文件末尾,并在「目录」中补充章节索引。
|
||||
> 测试人员按章节顺序执行用例,每完成一项需在「[ ]」中改为「[x]」并填写实际结果;对未通过项在「问题记录」区域详细描述。
|
||||
>
|
||||
> **测试结果约定**:
|
||||
> - ✅ 通过(Pass)
|
||||
> - ❌ 失败(Fail)
|
||||
> - ⚠️ 阻塞(Block,依赖其他功能未通过)
|
||||
>
|
||||
> **填写示例**:
|
||||
> - `[x] 1.1 列表正常加载 ✅ 实际:10 条记录,分页正常`
|
||||
> - `[ ] 1.2 编码为空校验 ❌ 实际:未拦截空值,提示词缺失`
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [一、测试环境与通用前置条件](#一测试环境与通用前置条件)
|
||||
- [二、物料类别列表(Material Category)](#二物料类别列表material-category)
|
||||
- [三、物料信息管理(Material Master)](#三物料信息管理material-master)
|
||||
|
||||
---
|
||||
|
||||
## 一、测试环境与通用前置条件
|
||||
|
||||
> 所有功能测试前,请先确认以下环境和基础数据准备就绪。
|
||||
|
||||
### 1.1 测试环境
|
||||
|
||||
| 项目 | 要求 |
|
||||
|------|------|
|
||||
| 操作系统 | Windows 10 / 11 或 macOS |
|
||||
| 浏览器 | Chrome ≥ 100(推荐)、Edge ≥ 100 |
|
||||
| Node.js | ≥ 16.0(用于本地构建) |
|
||||
| MES-UI 仓库 | `d:\code\mes\mes-ui`,已安装依赖 `pnpm install` |
|
||||
| 后端服务 | Webman 后台已启动(地址、端口按部署环境),数据库表 `material_category` 已存在 |
|
||||
| 启动命令 | `pnpm dev` 或 `npm run serve`,默认 `http://localhost:8080` |
|
||||
| 登录账号 | 具备「生产配置」菜单权限的账号,建议使用 `admin` 角色 |
|
||||
| 网络 | 能正常访问后端 API |
|
||||
|
||||
### 1.2 通用前置条件
|
||||
|
||||
1. 已完成 `pnpm install`,依赖安装无错误。
|
||||
2. `pnpm dev` 启动成功,浏览器可访问 `http://localhost:8080`。
|
||||
3. 使用具有「生产配置 / 物料模型 / 物料类别列表」访问权限的账号登录。
|
||||
4. 后端数据库中 `material_category` 表至少有 1 条数据,用于列表展示和编辑。
|
||||
5. 中英文语言切换正常:在右上角下拉框切换「中文 / English」,所有标签翻译均能显示。
|
||||
6. 浏览器 DevTools 的 Network 面板可见请求 URL 类似 `production_configuration/matetial_model/matetial_category/list?...`(迁移后保持旧 URL)。
|
||||
|
||||
### 1.3 测试结果总览(按功能)
|
||||
|
||||
| 功能 | 章节 | 用例数 | 通过 | 失败 | 阻塞 | 测试人 / 日期 |
|
||||
|------|------|:----:|:----:|:----:|:----:|---------------|
|
||||
| 物料类别列表 | 二 | 22 | | | | |
|
||||
| 物料信息管理 | 三 | 30 | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 二、物料类别列表(Material Category)
|
||||
|
||||
### 2.1 功能概述
|
||||
|
||||
- **一级模块**:生产配置(Production Master Data)
|
||||
- **二级模块**:物料模型(Material Model)
|
||||
- **三级模块**:物料类别列表(Material Category)
|
||||
- **功能说明**:用于维护物料类别(区分原材料和半成品),支持编码、名称、备注、创建时间等字段的增删改查
|
||||
- **菜单路径**:生产配置 → 物料模型 → 物料类别列表
|
||||
- **路由地址**:`/production_configuration/matetial_model/matetial_category`(迁移阶段沿用旧 URL)
|
||||
- **涉及文件**:
|
||||
- 页面:[src/views/production-master-data/material-model/material-category/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/material-model/material-category/index.vue)
|
||||
- API:[src/api/production-master-data/material-category.js](file:///d:/code/mes/mes-ui/src/api/production-master-data/material-category.js)
|
||||
- 路由:[src/router/modules/production-master-data.js](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js)
|
||||
- i18n:`src/locales/zh-chs.json` & `en.json`(key 前缀 `page.production_master_data.material_model.material_category.*`)
|
||||
|
||||
### 2.2 字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 长度限制 | 说明 |
|
||||
|------|------|:----:|---------|------|
|
||||
| 序号 | 数字 | — | — | 表格自增序号,由 `useTableColumns` 自动生成 |
|
||||
| 物料类别编码 | 字符串 | ✅ | 1~45 | 唯一标识 |
|
||||
| 物料类别名称 | 字符串 | ✅ | 1~45 | 类别中文名 |
|
||||
| 备注 | 字符串 | ❌ | — | 多行文本 |
|
||||
| 创建时间 | 日期 | — | — | 后端自动填充 |
|
||||
| 操作 | — | — | — | 行内「编辑」「删除」按钮 |
|
||||
|
||||
### 2.3 前置条件
|
||||
|
||||
1. 已登录具备「生产配置 / 物料模型 / 物料类别列表」权限的账号。
|
||||
2. 后端 `material_category` 表存在;若为空,可通过本测试用例的「新增」步骤补足。
|
||||
3. 浏览器地址栏输入 `/production_configuration/matetial_model/matetial_category` 可直接访问。
|
||||
4. 当前页面右上角语言切换为「中文」;切换为「English」后再次验证关键标签。
|
||||
|
||||
### 2.4 测试用例
|
||||
|
||||
#### 2.4.1 列表与搜索
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 1.1 | 打开「物料类别列表」页面 | 表格自动加载,列表正常显示;表头依次为:序号 / 物料类别编码 / 物料类别名称 / 备注 / 创建时间 / 操作 | | ☐ |
|
||||
| 1.2 | 在「物料类别编码」输入框输入已存在的关键字,点击「查询」 | 列表仅显示编码模糊匹配的数据,页码回到 1;URL 不暴露搜索值 | | ☐ |
|
||||
| 1.3 | 在「物料类别名称」输入框输入已存在的关键字,点击「查询」 | 列表仅显示名称模糊匹配的数据 | | ☐ |
|
||||
| 1.4 | 同时输入编码 + 名称后点击「查询」 | 列表为 AND 条件过滤结果 | | ☐ |
|
||||
| 1.5 | 在搜索框内按 `Enter` 键 | 触发查询,效果与点击「查询」一致 | | ☐ |
|
||||
| 1.6 | 点击「重置」按钮 | 搜索框清空,列表恢复初始数据,页码回到 1 | | ☐ |
|
||||
| 1.7 | 翻到第 2 页,点击「重置」 | 页码回到 1,列表刷新 | | ☐ |
|
||||
| 1.8 | 列表行数超过 1 页时,切换「每页显示条数」 | 列表按新分页重新加载,URL / 请求参数同步变化 | | ☐ |
|
||||
| 1.9 | 切换右上角语言为「English」 | 表头列名翻译正确:No. / Category Code / Category Name / Remark / Create Time / Actions | | ☐ |
|
||||
| 1.10 | 后端返回空数据 | 表格区域显示空数据占位,分页总数为 0 | | ☐ |
|
||||
|
||||
#### 2.4.2 新增物料类别
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 2.1 | 点击工具栏「新增」按钮 | 弹出「新增物料类别」对话框,含编码 / 名称 / 备注三个表单项,编码默认聚焦 | | ☐ |
|
||||
| 2.2 | 编码留空,名称输入「测试类别 A」,备注输入「原材料」,点击「确定」 | 弹框内编码字段红色错误提示「请输入物料类别编码」;不会提交成功 | | ☐ |
|
||||
| 2.3 | 编码输入「TEST_001」,名称留空,点击「确定」 | 名称字段红色错误提示「请输入物料类别名称」 | | ☐ |
|
||||
| 2.4 | 编码输入 46 个字符(如 46 个 `a`),名称输入「测试类别 A」,点击「确定」 | 编码字段提示「长度在 1 到 45 个字符」 | | ☐ |
|
||||
| 2.5 | 名称输入 46 个字符,编码输入「TEST_002」,点击「确定」 | 名称字段提示「长度在 1 到 45 个字符」 | | ☐ |
|
||||
| 2.6 | 编码输入已存在的编码(如数据库现存 `RAW_001`),名称输入「重复测试」,点击「确定」 | 提交失败,弹出后端返回的错误提示(如「编码已存在」);列表不刷新 | | ☐ |
|
||||
| 2.7 | 编码输入「TEST_001」,名称输入「测试类别 A」,备注输入「用于半成品分类」,点击「确定」 | 提交成功,弹框关闭,顶部提示「操作成功」;列表新增一行 | | ☐ |
|
||||
| 2.8 | 在弹框中点击「取消」 | 弹框关闭,不提交任何数据 | | ☐ |
|
||||
| 2.9 | 打开新增弹框后点击右上角 × 关闭 | 弹框关闭,再次打开时表单已重置 | | ☐ |
|
||||
| 2.10 | 重复 2.7 步骤的编码「TEST_001」再次提交 | 后端返回「编码已存在」或类似错误,不重复创建 | | ☐ |
|
||||
|
||||
#### 2.4.3 编辑物料类别
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 3.1 | 在列表中找到「测试类别 A」(2.7 创建),点击行内「编辑」 | 弹出「编辑物料类别」对话框,编码 / 名称 / 备注自动回填 | | ☐ |
|
||||
| 3.2 | 将名称修改为「测试类别 A1」,备注改为「原材料-已更新」,点击「确定」 | 提交成功,弹框关闭,提示「操作成功」;列表行名称 / 备注同步更新 | | ☐ |
|
||||
| 3.3 | 编辑时清空编码并保存 | 编码字段必填提示「请输入物料类别编码」 | | ☐ |
|
||||
| 3.4 | 编辑时清空名称并保存 | 名称字段必填提示「请输入物料类别名称」 | | ☐ |
|
||||
| 3.5 | 编辑时将编码修改为与另一行相同的值(如 `RAW_001`),点击「确定」 | 后端返回「编码已存在」错误 | | ☐ |
|
||||
| 3.6 | 编辑弹框打开后点击「取消」 | 数据不保存,列表无变化 | | ☐ |
|
||||
| 3.7 | 编辑后重新打开编辑弹框 | 表单仍展示上一次保存后的数据 | | ☐ |
|
||||
| 3.8 | 编辑成功后检查「创建时间」字段 | 创建时间未变化(仅首次新增时赋值) | | ☐ |
|
||||
|
||||
#### 2.4.4 删除物料类别
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 4.1 | 在列表中找到「测试类别 A1」,点击行内「删除」 | 弹出确认框「确定要执行该操作吗?」,标题为「提示」 | | ☐ |
|
||||
| 4.2 | 在确认框中点击「取消」 | 删除取消,列表无变化 | | ☐ |
|
||||
| 4.3 | 在确认框中点击「确定」 | 确认框关闭,顶部提示「操作成功」;该行从列表中消失 | | ☐ |
|
||||
| 4.4 | 在仅剩 1 条数据的列表上点击「删除」并确认 | 删除成功后,列表变为空,分页回到第 1 页 | | ☐ |
|
||||
| 4.5 | 尝试删除仍被「物料信息」引用的物料类别 | 后端返回「存在引用,无法删除」或类似错误 | | ☐ |
|
||||
| 4.6 | 删除操作期间切换「每页显示条数」 | 删除后页码自动修正,避免出现空页 | | ☐ |
|
||||
|
||||
#### 2.4.5 权限与异常场景
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 5.1 | 使用无「新增」权限的账号登录 | 工具栏「新增」按钮不显示或不可点击 | | ☐ |
|
||||
| 5.2 | 使用无「编辑」权限的账号登录 | 行内「编辑」按钮不显示 | | ☐ |
|
||||
| 5.3 | 使用无「删除」权限的账号登录 | 行内「删除」按钮不显示 | | ☐ |
|
||||
| 5.4 | 断网后刷新页面 | 表格显示加载失败或空态,DevTools Network 显示请求失败 | | ☐ |
|
||||
| 5.5 | 后端返回 500 错误(如临时停服) | 顶部红色错误提示(如「操作失败」),不卡死页面 | | ☐ |
|
||||
| 5.6 | 同时打开两个浏览器标签页,在 A 标签新增数据后,B 标签刷新列表 | B 标签列表应能看到 A 标签新增的数据 | | ☐ |
|
||||
|
||||
#### 2.4.6 国际化与界面
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 6.1 | 中文状态下打开新增弹框 | 弹框标题、字段标签、按钮文字均为中文 | | ☐ |
|
||||
| 6.2 | 切换为「English」后再次打开新增弹框 | 弹框标题「Add Material Category」,按钮「Confirm / Cancel」 | | ☐ |
|
||||
| 6.3 | 中文状态下点击删除,确认弹框标题为「提示」 | 确认框标题与按钮文字中文显示 | | ☐ |
|
||||
| 6.4 | 英文状态下点击删除,确认弹框标题为「Tip」 | 确认框英文显示 | | ☐ |
|
||||
| 6.5 | 列表右上角「帮助」按钮(? 图标) | 鼠标悬停或点击显示「物料类别用于区分原材料和半成品」 | | ☐ |
|
||||
| 6.6 | 备注字段为空时点击保存 | 提交成功,列表中备注列空白(无 `null` / `undefined` 文本) | | ☐ |
|
||||
|
||||
### 2.5 接口契约(用于前后端联调核对)
|
||||
|
||||
| 方法 | URL | 旧 method 名 | 入参关键 key | 备注 |
|
||||
|------|-----|-------------|-------------|------|
|
||||
| GET | `production_configuration/matetial_model/matetial_category/list` | `production_configuration_matetial_model_matetial_category_list` | `code / name / page_no / page_size` | 列表查询 |
|
||||
| GET | `…/all` | `…_all` | — | 全部类别(下拉框用,可选) |
|
||||
| POST | `…/create` | `…_create` | `code / name / remark` | 新增 |
|
||||
| PUT | `…/edit` | `…_edit` | `id / code / name / remark` | 编辑(`id` 为行主键) |
|
||||
| DELETE | `…/delete` | `…_delete` | `id: [rowId]` | 删除,参数名 `id` 数组(与旧项目保持一致) |
|
||||
|
||||
### 2.6 问题记录
|
||||
|
||||
> 测试人员发现问题时填写。每条问题一行,格式:`[编号] [用例编号] [现象] [复现步骤] [期望] [截图/日志] [负责人] [处理状态]`。
|
||||
|
||||
| 编号 | 用例 | 现象 | 复现步骤 | 期望 | 截图/日志 | 负责人 | 状态 |
|
||||
|:---:|:---:|------|---------|------|----------|:----:|:----:|
|
||||
| | | | | | | | |
|
||||
|
||||
### 2.7 备注
|
||||
|
||||
- 本页面沿用旧 URL 与后台 method 名,待后台数据库路由表统一更新后统一修改。
|
||||
- 涉及 `confirmMixin` / `useTableColumns` / `useTableButtons` / `i18nMixin` 等公共 Composable,如其他页面出现类似问题,先确认是否在迁移组件时有遗漏。
|
||||
- 若新增 / 编辑成功后接口返回结构变化(`res.data` 包裹层调整),需要同步更新 `fetchData` 的解析逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 三、物料信息管理(Material Master)
|
||||
|
||||
### 3.1 功能概述
|
||||
|
||||
- **一级模块**:生产配置(Production Master Data)
|
||||
- **二级模块**:物料模型(Material Model)
|
||||
- **三级模块**:物料信息管理(Material Master)
|
||||
- **功能说明**:用于维护物料主数据,包括物料编码、名称、所属物料类别、单位、备注等,支持新增、编辑、删除、批量删除、按编码/名称/类别检索。
|
||||
- **菜单路径**:生产配置 → 物料模型 → 物料信息管理
|
||||
- **路由地址**:`/production_configuration/matetial_model/matetial_management`(迁移阶段沿用旧 URL)
|
||||
- **涉及文件**:
|
||||
- 页面:[src/views/production-master-data/material-model/material-master/index.vue](file:///d:/code/mes/mes-ui/src/views/production-master-data/material-model/material-master/index.vue)
|
||||
- API:[src/api/production-master-data/material-master.js](file:///d:/code/mes/mes-ui/src/api/production-master-data/material-master.js)
|
||||
- 路由:[src/router/modules/production-master-data.js](file:///d:/code/mes/mes-ui/src/router/modules/production-master-data.js)
|
||||
- i18n:`src/locales/zh-chs.json` & `en.json`(key 前缀 `page.production_master_data.material_model.material_master.*`)
|
||||
|
||||
### 3.2 字段说明
|
||||
|
||||
| 字段 | 类型 | 必填 | 长度限制 | 说明 |
|
||||
|------|------|:----:|---------|------|
|
||||
| 序号 | 数字 | — | — | 表格自增序号 |
|
||||
| 物料编码 | 字符串 | ✅ | 1~100 | 唯一标识 |
|
||||
| 物料名称 | 字符串 | ✅ | 1~100 | 物料中文名 |
|
||||
| 物料类别 | 下拉 | ✅ | — | 来源于物料类别下拉(依赖「物料类别列表」已迁移) |
|
||||
| 单位 | 输入/下拉 | ❌ | — | 当前版本以文本框占位;待「计量单位」模块迁移后接入下拉数据源 |
|
||||
| 备注 | 字符串 | ❌ | — | 多行文本,超过 20 字符鼠标悬停展示全量 |
|
||||
| 创建人 | 字符串 | — | — | 后端返回 |
|
||||
| 创建时间 | 日期 | — | — | 后端自动填充 |
|
||||
| 操作 | — | — | — | 行内「编辑」「删除」按钮 |
|
||||
|
||||
### 3.3 前置条件
|
||||
|
||||
1. 已登录具备「生产配置 / 物料模型 / 物料信息管理」权限的账号。
|
||||
2. 已完成「物料类别列表」迁移,「物料类别」下拉数据正常加载(用于新增/编辑时的类别选择)。
|
||||
3. 后端 `material` 表存在;若为空,可通过本测试用例的「新增」步骤补足。
|
||||
4. 浏览器地址栏输入 `/production_configuration/matetial_model/matetial_management` 可直接访问。
|
||||
5. 当前页面右上角语言切换为「中文」;切换为「English」后再次验证关键标签。
|
||||
|
||||
### 3.4 测试用例
|
||||
|
||||
#### 3.4.1 列表与搜索
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 1.1 | 打开「物料信息管理」页面 | 表格自动加载,列表正常显示;表头依次为:序号 / 物料编码 / 物料名称 / 物料类别 / 单位 / 备注 / 创建人 / 创建时间 / 操作 | | ☐ |
|
||||
| 1.2 | 物料类别下拉可正常展开,选项来源于物料类别数据 | 下拉选项与「物料类别列表」一致,支持搜索过滤 | | ☐ |
|
||||
| 1.3 | 在「物料编码」输入框输入已存在的关键字,点击「查询」 | 列表仅显示编码模糊匹配的数据,页码回到 1 | | ☐ |
|
||||
| 1.4 | 在「物料名称」输入框输入已存在的关键字,点击「查询」 | 列表仅显示名称模糊匹配的数据 | | ☐ |
|
||||
| 1.5 | 在「物料类别」下拉中选择某一项,点击「查询」 | 列表仅显示该类别下的物料 | | ☐ |
|
||||
| 1.6 | 同时输入编码 + 名称 + 类别后点击「查询」 | 列表为 AND 条件过滤结果 | | ☐ |
|
||||
| 1.7 | 在搜索框内按 `Enter` 键 | 触发查询,效果与点击「查询」一致 | | ☐ |
|
||||
| 1.8 | 点击「重置」按钮 | 搜索框清空,列表恢复初始数据,页码回到 1 | | ☐ |
|
||||
| 1.9 | 翻到第 2 页,点击「重置」 | 页码回到 1,列表刷新 | | ☐ |
|
||||
| 1.10 | 列表行数超过 1 页时,切换「每页显示条数」 | 列表按新分页重新加载,请求参数 `page_size` 同步变化 | | ☐ |
|
||||
| 1.11 | 切换右上角语言为「English」 | 表头列名翻译正确:No. / Material Code / Material Name / Category / Unit / Remark / Created By / Created At / Action | | ☐ |
|
||||
| 1.12 | 备注长度 > 20 字符时,鼠标悬停单元格 | 弹层显示完整备注内容 | | ☐ |
|
||||
| 1.13 | 备注长度 <= 20 字符时 | 单元格直接展示文本,不显示弹层 | | ☐ |
|
||||
| 1.14 | 后端返回空数据 | 表格区域显示空数据占位,分页总数为 0 | | ☐ |
|
||||
|
||||
#### 3.4.2 新增物料信息
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 2.1 | 点击工具栏「新增」按钮 | 弹出「新增物料信息」对话框,依次为:物料编码 / 物料名称 / 物料类别 / 单位 / 备注 | | ☐ |
|
||||
| 2.2 | 编码留空,名称输入「测试物料 A」,类别下拉选择「原材料」,点击「确定」 | 编码字段红色错误提示「请输入物料编码」 | | ☐ |
|
||||
| 2.3 | 编码输入「MAT_001」,名称留空,点击「确定」 | 名称字段红色错误提示「请输入物料名称」 | | ☐ |
|
||||
| 2.4 | 编码、名称都填写,类别留空,点击「确定」 | 类别字段红色错误提示「请选择物料类别」 | | ☐ |
|
||||
| 2.5 | 编码输入 101 个字符(如 101 个 `a`),名称输入「测试物料 A」,点击「确定」 | 编码字段提示「长度在 1 到 100 个字符」 | | ☐ |
|
||||
| 2.6 | 名称输入 101 个字符,编码输入「MAT_002」,点击「确定」 | 名称字段提示「长度在 1 到 100 个字符」 | | ☐ |
|
||||
| 2.7 | 编码输入已存在的编码,名称输入「重复测试」,点击「确定」 | 提交失败,弹出后端返回的错误提示(如「编码已存在」);列表不刷新 | | ☐ |
|
||||
| 2.8 | 编码输入「MAT_001」,名称输入「测试物料 A」,类别选择「原材料」,单位输入「个」,备注输入「用于产线领料」,点击「确定」 | 提交成功,弹框关闭,顶部提示「操作成功」;列表新增一行且所有字段正确展示 | | ☐ |
|
||||
| 2.9 | 在弹框中点击「取消」 | 弹框关闭,不提交任何数据 | | ☐ |
|
||||
| 2.10 | 打开新增弹框后点击右上角 × 关闭,再次点击「新增」 | 弹框再次打开时表单已重置 | | ☐ |
|
||||
| 2.11 | 重复 2.8 步骤的编码「MAT_001」再次提交 | 后端返回「编码已存在」或类似错误,不重复创建 | | ☐ |
|
||||
|
||||
#### 3.4.3 编辑物料信息
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 3.1 | 在列表中找到「MAT_001」(2.8 创建),点击行内「编辑」 | 弹出「编辑物料信息」对话框,编码 / 名称 / 类别 / 单位 / 备注自动回填 | | ☐ |
|
||||
| 3.2 | 将名称修改为「测试物料 A1」,类别改为「半成品」,单位改为「箱」,备注改为「用于产线补料」,点击「确定」 | 提交成功,弹框关闭,提示「操作成功」;列表行各字段同步更新 | | ☐ |
|
||||
| 3.3 | 编辑时清空编码并保存 | 编码字段必填提示「请输入物料编码」 | | ☐ |
|
||||
| 3.4 | 编辑时清空名称并保存 | 名称字段必填提示「请输入物料名称」 | | ☐ |
|
||||
| 3.5 | 编辑时清空类别并保存 | 类别字段必填提示「请选择物料类别」 | | ☐ |
|
||||
| 3.6 | 编辑时将编码修改为与另一行相同的值,点击「确定」 | 后端返回「编码已存在」错误 | | ☐ |
|
||||
| 3.7 | 编辑弹框打开后点击「取消」 | 数据不保存,列表无变化 | | ☐ |
|
||||
| 3.8 | 编辑后重新打开编辑弹框 | 表单仍展示上一次保存后的数据 | | ☐ |
|
||||
| 3.9 | 编辑成功后检查「创建时间」字段 | 创建时间未变化(仅首次新增时赋值) | | ☐ |
|
||||
| 3.10 | 编辑时清空备注并保存 | 提交成功,列表中备注列空白(无 `null` / `undefined` 文本) | | ☐ |
|
||||
|
||||
#### 3.4.4 删除与批量删除
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 4.1 | 在列表中找到「MAT_001」,点击行内「删除」 | 弹出确认框「确定要执行该操作吗?」,标题为「提示」 | | ☐ |
|
||||
| 4.2 | 在确认框中点击「取消」 | 删除取消,列表无变化 | | ☐ |
|
||||
| 4.3 | 在确认框中点击「确定」 | 确认框关闭,顶部提示「操作成功」;该行从列表中消失 | | ☐ |
|
||||
| 4.4 | 在仅剩 1 条数据的列表上点击「删除」并确认 | 删除成功后,列表变为空,分页回到第 1 页 | | ☐ |
|
||||
| 4.5 | 不勾选任何数据,直接点击工具栏「批量删除」 | 顶部红色错误提示「请先选择要删除的数据」 | | ☐ |
|
||||
| 4.6 | 勾选 3 条数据,点击「批量删除」,确认框标题与按钮中文显示 | 弹出确认框「确定要删除所选物料吗?」 | | ☐ |
|
||||
| 4.7 | 在 4.6 弹出的确认框中点击「确定」 | 3 条数据全部删除,顶部提示「操作成功」;列表刷新 | | ☐ |
|
||||
| 4.8 | 尝试删除被其他业务(如 BOM)引用的物料 | 后端返回「存在引用,无法删除」或类似错误 | | ☐ |
|
||||
| 4.9 | 删除操作期间切换「每页显示条数」 | 删除后页码自动修正,避免出现空页 | | ☐ |
|
||||
|
||||
#### 3.4.5 权限与异常场景
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 5.1 | 使用无「新增」权限的账号登录 | 工具栏「新增」按钮不显示或不可点击 | | ☐ |
|
||||
| 5.2 | 使用无「编辑」权限的账号登录 | 行内「编辑」按钮不显示 | | ☐ |
|
||||
| 5.3 | 使用无「删除」权限的账号登录 | 行内「删除」按钮不显示 | | ☐ |
|
||||
| 5.4 | 使用无「批量删除」权限的账号登录 | 工具栏「批量删除」按钮不显示或不可点击 | | ☐ |
|
||||
| 5.5 | 断网后刷新页面 | 表格显示加载失败或空态,DevTools Network 显示请求失败 | | ☐ |
|
||||
| 5.6 | 后端返回 500 错误(如临时停服) | 顶部红色错误提示(如「操作失败」),不卡死页面 | | ☐ |
|
||||
| 5.7 | 物料类别下拉数据加载失败时进入新增弹框 | 类别下拉为空,但仍可正常打开弹框 | | ☐ |
|
||||
| 5.8 | 同时打开两个浏览器标签页,在 A 标签新增数据后,B 标签刷新列表 | B 标签列表应能看到 A 标签新增的数据 | | ☐ |
|
||||
|
||||
#### 3.4.6 国际化与界面
|
||||
|
||||
| # | 操作步骤 | 预期结果 | 实际结果 | 通过 |
|
||||
|---|---------|---------|---------|:----:|
|
||||
| 6.1 | 中文状态下打开新增弹框 | 弹框标题、字段标签、按钮文字均为中文 | | ☐ |
|
||||
| 6.2 | 切换为「English」后再次打开新增弹框 | 弹框标题「Add Material」,按钮「Confirm / Cancel」 | | ☐ |
|
||||
| 6.3 | 中文状态下点击删除,确认弹框标题为「提示」 | 确认框标题与按钮文字中文显示 | | ☐ |
|
||||
| 6.4 | 英文状态下点击删除,确认弹框标题为「Tip」 | 确认框英文显示 | | ☐ |
|
||||
| 6.5 | 列表右上角「帮助」按钮(? 图标) | 鼠标悬停或点击显示「物料信息用于维护物料编码、名称、规格等属性」 | | ☐ |
|
||||
| 6.6 | 中文状态下批量删除无选中数据 | 错误提示「请先选择要删除的数据」 | | ☐ |
|
||||
| 6.7 | 英文状态下批量删除无选中数据 | 错误提示「Please select data first」 | | ☐ |
|
||||
|
||||
### 3.5 接口契约(用于前后端联调核对)
|
||||
|
||||
| 方法 | URL | 旧 method 名 | 入参关键 key | 备注 |
|
||||
|------|-----|-------------|-------------|------|
|
||||
| GET | `production_configuration/matetial_model/matetial_management/list` | `production_configuration_matetial_model_matetial_management_list` | `code / name / bom_source_category_id / page_no / page_size` | 列表查询 |
|
||||
| GET | `…/all` | `…_all` | — | 全部物料(下拉框用,可选) |
|
||||
| POST | `…/create` | `…_create` | `code / name / bom_source_category_id / unit_id / remark` | 新增 |
|
||||
| PUT | `…/edit` | `…_edit` | `id / code / name / bom_source_category_id / unit_id / remark` | 编辑(`id` 为行主键) |
|
||||
| DELETE | `…/delete` | `…_delete` | `id: [rowId]` | 单条删除,参数名 `id` 数组(与旧项目保持一致) |
|
||||
| DELETE | `…/batch_delete` | `…_batch_delete` | `id: [id1, id2, …]` | 批量删除 |
|
||||
| POST | `…/get_import_template` | `…_get_import_template` | — | 导入模板下载(响应 `blob`),本版本预留 |
|
||||
| POST | `…/matetial_data_import` | `…_matetial_data_import` | `import_data: JSON.stringify([...])` | 物料数据导入,本版本预留 |
|
||||
| POST | `…/matetial_data_export_task` | `…_matetial_data_export_task` | — | 物料数据导出任务创建,本版本预留 |
|
||||
|
||||
> 说明:本版本优先实现列表 / 新增 / 编辑 / 删除 / 批量删除五大核心功能;导入 / 导出(Excel 模板下载、批量导入、导出任务)保留 API 方法和钩子位置,待后续迭代中补齐弹框 UI。
|
||||
|
||||
### 3.6 问题记录
|
||||
|
||||
> 测试人员发现问题时填写。每条问题一行,格式:`[编号] [用例编号] [现象] [复现步骤] [期望] [截图/日志] [负责人] [处理状态]`。
|
||||
|
||||
| 编号 | 用例 | 现象 | 复现步骤 | 期望 | 截图/日志 | 负责人 | 状态 |
|
||||
|:---:|:---:|------|---------|------|----------|:----:|:----:|
|
||||
| | | | | | | | |
|
||||
|
||||
### 3.7 备注
|
||||
|
||||
- 本页面沿用旧 URL 与后台 method 名,待后台数据库路由表统一更新后统一修改。
|
||||
- 「物料类别」下拉数据由 `getMaterialCategoryAll` 提供,依赖「物料类别列表」功能已迁移完成;如该下拉为空,请先检查该模块是否正常运行。
|
||||
- 「单位」字段在当前版本以文本框占位,依赖「计量单位」模块迁移后接入下拉数据源(与 BOM 等联动),测试时需在「备注」中注明该功能尚未启用。
|
||||
- 涉及 `confirmMixin` / `useTableColumns` / `useTableButtons` / `i18nMixin` 等公共 Composable,如其他页面出现类似问题,先确认是否在迁移组件时有遗漏。
|
||||
- 备注长度 > 20 字符时使用 `el-popover` 悬停展示全量内容;该交互与旧项目保持一致。
|
||||
- 若新增 / 编辑成功后接口返回结构变化(`res.data` 包裹层调整),需要同步更新 `fetchData` 的解析逻辑。
|
||||
161
docs/迁移提示词.md
Normal file
161
docs/迁移提示词.md
Normal file
@@ -0,0 +1,161 @@
|
||||
## 任务:迁移旧 CRUD 页面到新版 page-table + page-dialog-form 方案
|
||||
|
||||
### 输入
|
||||
用户会提供一个旧 CRUD 页面的文件路径列表和对照表信息。
|
||||
|
||||
---
|
||||
|
||||
### 迁移规则(必须严格遵守)
|
||||
|
||||
#### 1. 组件方案
|
||||
- 使用 `<page-table>` + `<page-dialog-form>` 替代旧版 `<sct-base-table>` + `<sct-base-dialog>` + `<SctBaseForm>`
|
||||
- 使用 `useTableColumns()` 生成列定义(不再手动分配 idx)
|
||||
- 使用 `useTableButtons()` 生成工具栏按钮和行内按钮(不再分开写 buttonList / tableButtonList)
|
||||
- 使用 `i18nMixin(prefix)` 注入 `key()` 和 `ckey()` 方法
|
||||
- 参考文档:[表格组件使用说明.md](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md)
|
||||
- 参考示例:`src/views/production-master-data/factory-model/factory-area/index.vue`
|
||||
|
||||
#### 1.1 特殊弹出框组件迁移(重要)
|
||||
|
||||
当旧页面中存在**超出 `page-dialog-form` 能力的弹出框**时(权限分配树、批量导入、关联选择器、详情查看、多步骤向导等),必须遵循以下流程:
|
||||
|
||||
##### 第一步:分析旧代码
|
||||
- **仔细阅读旧项目的弹出框代码**,理解其数据结构、交互逻辑、API 调用
|
||||
- 关注旧代码的数据来源路径(如 `row.menu_admin` 直接从列表获取、还是额外调 API)
|
||||
- 记录旧的参数名、method 名,确保迁移时不遗漏
|
||||
|
||||
##### 第二步:提出优化方案
|
||||
迁移时**不能简单照搬旧代码**,必须结合新版能力提出优化:
|
||||
|
||||
| 旧写法 | 优化方向 |
|
||||
|--------|---------|
|
||||
| `el-dialog` 内联在页面中 | 抽离为独立组件 → `components/` |
|
||||
| `sct-base-dialog` + 内联表单 | 独立组件 + `el-drawer`(抽屉式体验更佳) |
|
||||
| 额外调 API 获取数据(如行数据已包含) | 直接从 `row.xxx` 取值,减少请求 |
|
||||
| `setTimeout` 硬编码延迟 | 提出来讨论,结合 `v-if` + `$nextTick` 优化 |
|
||||
| `$parent` / `$refs` 跨组件通信 | 改为 `props` + `$emit` |
|
||||
|
||||
示例:角色权限分配迁移优化方案
|
||||
|
||||
```
|
||||
旧方案 优化方案
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
el-dialog 内联 42 行代码 → 独立 PermDrawer 组件(components/PermDrawer/index.vue)
|
||||
getRoleMenu API 额外请求 → 直接从 row.menu_admin JSON.parse 获取(列表自带)
|
||||
setTimeout 1000ms 关闭 → 保留(树渲染需要等待),但抽取到子组件内部
|
||||
索引页 data 6 个 perm 字段 → 仅 2 个(permVisible + permRole)
|
||||
```
|
||||
|
||||
##### 第三步:按标准实现
|
||||
参考 [表格组件使用说明.md 第 13 节](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md#13-特殊弹出框组件规范),严格遵循:
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| 目录位置 | `src/views/{模块}/{功能}/components/{英文PascalCase}/index.vue` |
|
||||
| 文件夹命名 | 英文 PascalCase,描述功能(如 `PermDrawer`、`ImportDialog`) |
|
||||
| 组件通信 | 父→子 `props`(含 `.sync`),子→父 `$emit('saved')`,禁止 `$parent` / `$refs` |
|
||||
| 主页面数据 | 只存 `visible` Boolean + 业务对象,其余状态在子组件内部 |
|
||||
|
||||
##### 第四步:完整案例
|
||||
|
||||
参考角色权限分配抽屉的完整迁移:
|
||||
- 旧代码:`D:\code\company\SCTMES_MES_V5\vue-app\src\views\system_settings\user_management\role\components\PageMain\index.vue`(`dialogVisibleGive` + `el-tree`)
|
||||
- 新代码:[`src/views/system-administration/user-management/role/components/PermDrawer/index.vue`](file:///d:/code/mes/mes-ui/src/views/system-administration/user-management/role/components/PermDrawer/index.vue)
|
||||
- 主页面引用:[`src/views/system-administration/user-management/role/index.vue`](file:///d:/code/mes/mes-ui/src/views/system-administration/user-management/role/index.vue#L79-L87)
|
||||
|
||||
---
|
||||
|
||||
#### 2. i18n 国际化(重要)
|
||||
- 使用 `mixins: [i18nMixin('完整i18n前缀')]`,不要在每个页面手动定义 `T` 常量和 `tkey()` 方法
|
||||
- `data()` 中用 `this.key('xxx')` 传完整 i18n key,**不要用 `k()` 提前翻译**(翻译由 page-table / page-dialog-form 内部处理,切换语言自动响应)
|
||||
- 模板搜索区用 `$t(key('xxx'))`,公共 key 用 `$t(ckey('xxx'))`
|
||||
- 语言包必须中英文同步添加(`zh-chs.json` + `en.json`)
|
||||
- 参考文档:[国际化规则.md](file:///d:/code/mes/mes-ui/docs/国际化规则.md)
|
||||
|
||||
#### 3. 文件夹命名(重要)
|
||||
- 文件夹名称遵循 [后台Webman界面截图对照表](file:///d:/code/mes/mes-ui/后台Webman界面截图对照表.md) 的 snake_case/kebab-case 命名
|
||||
- 目录示例:`src/views/production-master-data/factory-model/factory-area/`
|
||||
- 路由模块:`src/router/modules/production-master-data.js`
|
||||
- API 文件:`src/api/production-master-data/factory-area.js`
|
||||
|
||||
#### 4. 路由 path 和 API BASE URL(重要 — 临时方案)
|
||||
- **路由 path** 和 **API BASE URL** **暂时保留旧项目的命名**,因为后台数据库的路由表还未更新
|
||||
- 示例:目录按对照表叫 `production-master-data`,但路由 path 保持旧值 `production_configuration`
|
||||
```js
|
||||
// src/router/modules/production-master-data.js
|
||||
path: '/production_configuration', // 旧值暂留
|
||||
```
|
||||
```js
|
||||
// src/api/production-master-data/factory-area.js
|
||||
const BASE = 'production_configuration/factory_model/factory_area/' // 旧值暂留
|
||||
```
|
||||
- 后续统一修改路由后再批量替换
|
||||
|
||||
#### 4.1 API 参数名必须对齐旧项目(重要)
|
||||
- **API 函数入参的 key 名称必须与旧项目保持一致**,不能自行发明参数名
|
||||
- 例如:旧项目角色菜单接口参数是 `role_id`,不能写成 `id`
|
||||
```js
|
||||
// ❌ 错误 — 自行发明参数名
|
||||
getRoleMenu({ id: this.roleId })
|
||||
// ✅ 正确 — 与旧项目参数名一致
|
||||
getRoleMenu({ role_id: this.roleId })
|
||||
```
|
||||
- **迁移 API 时必须仔细阅读旧项目接口代码**,逐个核对每个接口的入参 key 名称
|
||||
- 如果后端报「xxx 不能为空」,首先检查参数 key 是否与旧项目一致
|
||||
|
||||
#### 5. 旧 key → 新 key 映射
|
||||
如果用户提供的是旧页面代码,需要根据对照表做 key 映射。已知映射如下(持续补充):
|
||||
|
||||
| 旧 key 前缀 | 新 key 前缀 |
|
||||
|------------|------------|
|
||||
| `page.production_configuration.factory_model.factory_area` | `page.production_master_data.factory_model.factory_area` |
|
||||
| `page.system_settings.user_management.role` | `page.system_administration.user_management.role` |
|
||||
| `page.system_settings.user_management.user` | `page.system_administration.user_management.user` |
|
||||
| `page.system_settings.menu_configuration.menu` | `page.system_administration.menu_management.menu_configuration` |
|
||||
| `page.system_settings.system_assistant.operate_log` | `page.system_administration.system_utilities.operation_logs` |
|
||||
| `page.system_settings.system_assistant.api_log` | `page.system_administration.system_utilities.api_logs` |
|
||||
| `page.system_settings.system_assistant.problem_help` | `page.system_administration.system_utilities.problem_help` |
|
||||
| `page.system_settings.system_monitoring.system.login` | `page.system_administration.system_monitoring.login` |
|
||||
| `page.production_configuration.matetial_model.*` | `page.production_master_data.material_model.*` |
|
||||
| `page.planning_production.production_batch_management.batch` | `page.planning_production.batch_management.batch_list` |
|
||||
| `page.data_middleground.basic_traceability.*` | `page.data_platform.traceability.*` |
|
||||
|
||||
#### 6. 处理流程(每迁移一个页面执行以下步骤)
|
||||
1. **确定对照表位置**:从 [后台Webman界面截图对照表](file:///d:/code/mes/mes-ui/后台Webman界面截图对照表.md) 找到对应行的英文名,转换为 snake_case
|
||||
2. **创建目录结构**:`src/views/{一级snake_case}/{二级snake_case}/{三级snake_case}/`
|
||||
3. **创建 API 文件**:`src/api/{一级snake_case}/{二级snake_case}/{三级snake_case}.js`,BASE URL 暂用旧值
|
||||
4. **添加路由模块**:`src/router/modules/{一级snake_case}.js`,path 暂用旧值
|
||||
5. **编写页面代码**:使用 page-table + page-dialog-form 方案
|
||||
6. **添加 i18n**:在 `zh-chs.json` 和 `en.json` 中添加对应 key
|
||||
7. **验证 JSON 合法性**:`node -e "JSON.parse(require('fs').readFileSync('src/locales/zh-chs.json','utf8'))"`
|
||||
|
||||
---
|
||||
|
||||
### 页面模板(通用 CRUD 页面骨架)
|
||||
|
||||
参考 `src/views/production-master-data/factory-model/factory-area/index.vue` 的结构,核心模式:
|
||||
|
||||
- **Script**:`mixins: [i18nMixin('page.{一}.{二}.{三}')]` + `data()` 中用 `this.key('xxx')` 传 key
|
||||
- **Template**:`<page-table>` 传 columns/data/loading/toolbarButtons/rowButtons/pagination + `<page-dialog-form>` 传 formCols/formData/rules/title
|
||||
- **Buttons**:`useTableButtons({ toolbar: [...], row: [...] }, this.$permission)`
|
||||
- **Columns**:`useTableColumns([...])`
|
||||
- **Methods**:`fetchData / onSearch / onReset / onPageChange / openAdd / openEdit / onDialogSubmit / handleDelete`
|
||||
- **i18n key 模板**(每个页面至少要有这些):`search / reset / add / edit / delete / operation / add_title / edit_title / code / name / remark / enter_code / enter_name / remark_length / operation_success / confirm / cancel / tip / confirm_delete`
|
||||
- **搜索条件过多时**参考 [表格组件使用说明.md - 场景 8](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md#场景-8折叠式搜索区搜索条件过多时),使用 `searchExpanded` + `v-show` 实现折叠式搜索区,需额外添加 i18n key:`expand`(展开更多/Expand)、`collapse`(收起/Collapse)
|
||||
|
||||
#### 6.1 依赖安装
|
||||
|
||||
当迁移涉及新的第三方库时:
|
||||
|
||||
1. **包管理器**:本项目使用 **pnpm**,安装命令为 `pnpm add <package-name>`
|
||||
2. **安装前检查**:先在 `package.json` 中确认依赖是否已存在,避免重复安装
|
||||
3. **版本兼容**:确保安装的包支持 Vue 2.x(本项目为 Vue 2.7)
|
||||
4. **参考标准**:[表格组件使用说明.md - 第 11 节](file:///d:/code/mes/mes-ui/docs/表格组件使用说明.md#11-依赖安装规范)
|
||||
|
||||
---
|
||||
|
||||
### 输出要求
|
||||
- 创建完整的页面文件、路由模块、API 文件
|
||||
- 在 `zh-chs.json` 和 `en.json` 中添加对应 i18n key(中文和英文同步)
|
||||
- 完成后提醒用户验证 JSON 合法性
|
||||
- 如涉及对照表中已有映射的 key 前缀变更,自动纠正为新的 key 前缀
|
||||
39947
package-lock.json
generated
39947
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -1,10 +1,15 @@
|
||||
{
|
||||
"name": "d2-admin",
|
||||
"version": "1.25.0",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"engines": {
|
||||
"node": ">=18.16.0 <19",
|
||||
"pnpm": ">=10.33.0 <11"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --open",
|
||||
"start": "npm run serve",
|
||||
"dev": "npm run serve",
|
||||
"start": "pnpm run serve",
|
||||
"dev": "pnpm run serve",
|
||||
"build": "vue-cli-service build --report",
|
||||
"build:preview": "NODE_OPTIONS=--max_old_space_size=4096 vue-cli-service build --mode preview",
|
||||
"lint": "vue-cli-service lint --fix",
|
||||
@@ -31,6 +36,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"lowdb": "^1.0.0",
|
||||
"marked": "^2.1.3",
|
||||
"mavon-editor": "^2.10.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"qs": "^6.11.0",
|
||||
"quill": "^1.3.7",
|
||||
@@ -45,7 +51,8 @@
|
||||
"vue-router": "^3.6.2",
|
||||
"vue-splitpane": "^1.0.6",
|
||||
"vue-ueditor-wrap": "^2.5.6",
|
||||
"vuex": "^3.6.2"
|
||||
"vuex": "^3.6.2",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@d2-projects/vue-filename-injector": "^1.1.1",
|
||||
@@ -71,7 +78,7 @@
|
||||
"less": "^3.13.1",
|
||||
"less-loader": "^7.3.0",
|
||||
"sass": "^1.54.5",
|
||||
"sass-loader": "^10.3.1",
|
||||
"sass-loader": "~10.5.2",
|
||||
"svg-sprite-loader": "^4.3.0",
|
||||
"text-loader": "^0.0.1",
|
||||
"vue-cli-plugin-i18n": "^1.0.1",
|
||||
@@ -87,5 +94,13 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/d2-projects/d2-admin.git"
|
||||
},
|
||||
"volta": {
|
||||
"node": "18.16.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@achrinza/node-ipc": ">=9.2.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15961
pnpm-lock.yaml
generated
Normal file
15961
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/image/logo/sc_logo.png
Normal file
BIN
public/image/logo/sc_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
@@ -5,6 +5,7 @@ import { get, isEmpty, merge } from 'lodash'
|
||||
import qs from 'qs'
|
||||
import util from '@/libs/util'
|
||||
import store from '@/store'
|
||||
import router from '@/router'
|
||||
|
||||
/**
|
||||
* @description 记录和显示错误
|
||||
@@ -80,20 +81,30 @@ function createService () {
|
||||
}
|
||||
|
||||
// 有 code 判断为项目接口请求
|
||||
let errorMessage = ''
|
||||
switch (response.data.code) {
|
||||
// 返回响应内容
|
||||
case 0: return response.data.data
|
||||
// 例如在 code 401 情况下退回到登录页面
|
||||
case 401: throw new Error('请重新登录')
|
||||
// 根据需要添加其它判断
|
||||
default: throw new Error(`${response.data.msg}: ${response.config.url}`)
|
||||
case 401:
|
||||
util.cookies.remove('token')
|
||||
router.replace({ name: 'login' })
|
||||
break
|
||||
default:
|
||||
errorMessage = response.data.msg || '请求失败'
|
||||
break
|
||||
}
|
||||
const businessError = new Error(`${errorMessage}: ${response.config.url}`)
|
||||
handleError(businessError)
|
||||
throw businessError
|
||||
},
|
||||
error => {
|
||||
const status = get(error, 'response.status')
|
||||
switch (status) {
|
||||
case 400: error.message = '请求错误'; break
|
||||
case 401: error.message = '未授权,请登录'; break
|
||||
case 401:
|
||||
util.cookies.remove('token')
|
||||
router.replace({ name: 'login' })
|
||||
error.message = '未授权,请登录'
|
||||
break
|
||||
case 403: error.message = '拒绝访问'; break
|
||||
case 404: error.message = `请求地址出错: ${error.response.config.url}`; break
|
||||
case 408: error.message = '请求超时'; break
|
||||
@@ -125,13 +136,15 @@ function createRequest (service) {
|
||||
const token = util.cookies.get('token')
|
||||
const configDefault = {
|
||||
headers: {
|
||||
Authorization: token,
|
||||
'Content-Type': get(config, 'headers.Content-Type', 'application/json')
|
||||
},
|
||||
timeout: 5000,
|
||||
baseURL: process.env.VUE_APP_API,
|
||||
data: {}
|
||||
}
|
||||
if (token) {
|
||||
configDefault.headers.Authorization = 'Bearer ' + token
|
||||
}
|
||||
const option = merge(configDefault, config)
|
||||
// 处理 get 请求的参数
|
||||
// 请根据实际需要修改
|
||||
@@ -141,7 +154,7 @@ function createRequest (service) {
|
||||
}
|
||||
// 当需要以 form 形式发送时 处理发送的数据
|
||||
// 请根据实际需要修改
|
||||
if (!isEmpty(option.data) && option.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
|
||||
if (!isEmpty(option.data) && option.headers['Content-Type'] === 'application/x-www-form-urlen·coded') {
|
||||
option.data = stringify(option.data)
|
||||
}
|
||||
return service(option)
|
||||
|
||||
33
src/api/auth.js
Normal file
33
src/api/auth.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
/**
|
||||
* 账号登录
|
||||
* @param {Object} data
|
||||
* @param {string} data.username
|
||||
* @param {string} data.password
|
||||
*/
|
||||
export function loginAdminUser (data) {
|
||||
return request({
|
||||
url: 'login',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'login',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销当前已登录的账号
|
||||
*/
|
||||
export function logoutAdminUser () {
|
||||
return request({
|
||||
url: 'logout',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'logout',
|
||||
platform: 'background'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import { find, map, random } from 'lodash'
|
||||
import faker from 'faker/locale/zh_CN'
|
||||
import { requestForMock, mock } from '@/api/_service.js'
|
||||
import * as tools from '@/api/_tools.js'
|
||||
|
||||
const db = [
|
||||
{ id: '1', name: '用户 1', address: '上海市普陀区金沙江路 1518 弄' },
|
||||
{ id: '2', name: '用户 2', address: '上海市普陀区金沙江路 1517 弄' },
|
||||
{ id: '3', name: '用户 3', address: '上海市普陀区金沙江路 1519 弄' },
|
||||
{ id: '4', name: '用户 4', address: '上海市普陀区金沙江路 1516 弄' }
|
||||
]
|
||||
|
||||
/**
|
||||
* @description 列表
|
||||
*/
|
||||
export function DEMO_MOCK_LIST () {
|
||||
// 模拟数据
|
||||
mock
|
||||
.onAny('/demo/business/issues/142/fetch')
|
||||
.reply(...tools.responseSuccess({ list: db }))
|
||||
// 接口请求
|
||||
return requestForMock({
|
||||
url: '/demo/business/issues/142/fetch',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 详情
|
||||
* @param {String} id 项目 ID
|
||||
*/
|
||||
export function DEMO_MOCK_DETAIL (id) {
|
||||
// 模拟数据
|
||||
mock
|
||||
.onAny('/demo/business/issues/142/detail')
|
||||
.reply(config => tools.responseSuccess(find(db, { id: config.params.id })))
|
||||
// 接口请求
|
||||
return requestForMock({
|
||||
url: '/demo/business/issues/142/detail',
|
||||
method: 'get',
|
||||
params: {
|
||||
id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 列表
|
||||
*/
|
||||
export function DEMO_MOCK_LIST2 (params = {}) {
|
||||
// 模拟数据
|
||||
mock
|
||||
.onAny('/demo/business/table/1/fetch')
|
||||
.reply(config => tools.responseSuccess({
|
||||
page: {
|
||||
total: 1000
|
||||
},
|
||||
list: map(Array(config.params.pageSize), () => ({
|
||||
key: faker.random.uuid(),
|
||||
value: [10, 100, 200, 500][random(0, 3)],
|
||||
type: faker.random.boolean(),
|
||||
admin: faker.name.firstName() + faker.name.lastName(),
|
||||
adminNote: faker.random.words(),
|
||||
dateTimeCreat: faker.date.past(),
|
||||
used: faker.random.boolean(),
|
||||
dateTimeUse: faker.date.past()
|
||||
}))
|
||||
}))
|
||||
// 接口请求
|
||||
return requestForMock({
|
||||
url: '/demo/business/table/1/fetch',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 错误日志示例 请求一个不存在的地址
|
||||
*/
|
||||
export function DEMO_LOG_AJAX () {
|
||||
// 接口请求
|
||||
return requestForMock({
|
||||
url: '/invalid-url',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
51
src/api/equipment-management/equipment-category.js
Normal file
51
src/api/equipment-management/equipment-category.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/device_model/device_category/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `equipment_management_equipment_model_equipment_category_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getEquipmentCategoryList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getEquipmentCategoryALL (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createEquipmentCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editEquipmentCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteEquipmentCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
15
src/api/menu.js
Normal file
15
src/api/menu.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const urls = 'system_settings/menu_configuration/menu/'
|
||||
|
||||
export function getMenuAll (data) {
|
||||
return request({
|
||||
url: urls + 'all',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_menu_configuration_menu_all',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/spc_configuration/binding_scada_node/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_spc_configuration_data_collection_configuration_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getDataCollectionConfigList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createDataCollectionConfig (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editDataCollectionConfig (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteDataCollectionConfig (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
19
src/api/production-master-data/device-category.js
Normal file
19
src/api/production-master-data/device-category.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/device_model/device_category/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_device_model_device_category_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getDeviceCategoryAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
51
src/api/production-master-data/factory-area.js
Normal file
51
src/api/production-master-data/factory-area.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/factory_model/factory_area/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_factory_model_factory_area_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getFactoryAreaALL (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getFactoryAreaList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createFactoryArea (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editFactoryArea (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteFactoryArea (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
57
src/api/production-master-data/material-category.js
Normal file
57
src/api/production-master-data/material-category.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
// 注:BASE URL 与后台 method 名暂沿用旧项目命名,等待后台数据库路由表统一更新
|
||||
const BASE = 'production_configuration/matetial_model/matetial_category/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_matetial_model_matetial_category_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全部物料类别
|
||||
export function getMaterialCategoryAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取物料类别列表
|
||||
export function getMaterialCategoryList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建物料类别
|
||||
export function createMaterialCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑物料类别
|
||||
export function editMaterialCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除物料类别
|
||||
export function deleteMaterialCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
94
src/api/production-master-data/material-master.js
Normal file
94
src/api/production-master-data/material-master.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
// 注:BASE URL 与后台 method 名暂沿用旧项目命名,等待后台数据库路由表统一更新
|
||||
const BASE = 'production_configuration/matetial_model/matetial_management/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_matetial_model_matetial_management_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全部物料
|
||||
export function getMaterialMasterAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取物料列表
|
||||
export function getMaterialMasterList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建物料
|
||||
export function createMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑物料
|
||||
export function editMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除物料
|
||||
export function deleteMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除物料
|
||||
export function batchDeleteMaterialMaster (data) {
|
||||
return request({
|
||||
url: BASE + 'batch_delete',
|
||||
method: 'delete',
|
||||
data: apiParams('batch_delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取物料数据导入模板
|
||||
export function getImportTemplate (data) {
|
||||
return request({
|
||||
url: BASE + 'get_import_template',
|
||||
method: 'post',
|
||||
responseType: 'blob',
|
||||
data: apiParams('get_import_template', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 物料数据导入
|
||||
export function importMaterialData (data) {
|
||||
return request({
|
||||
url: BASE + 'matetial_data_import',
|
||||
method: 'post',
|
||||
data: apiParams('matetial_data_import', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 物料数据导出任务创建
|
||||
export function exportMaterialTask (data) {
|
||||
return request({
|
||||
url: BASE + 'matetial_data_export_task',
|
||||
method: 'post',
|
||||
data: apiParams('matetial_data_export_task', data)
|
||||
})
|
||||
}
|
||||
57
src/api/production-master-data/material-unit.js
Normal file
57
src/api/production-master-data/material-unit.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
// 注:BASE URL 与后台 method 名暂沿用旧项目命名,等待后台数据库路由表统一更新
|
||||
const BASE = 'production_configuration/matetial_model/unit/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_matetial_model_unit_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
// 获取全部单位(下拉框用)
|
||||
export function getUnitAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取单位列表
|
||||
export function getUnitList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 创建单位
|
||||
export function createUnit (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑单位
|
||||
export function editUnit (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
// 删除单位
|
||||
export function deleteUnit (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
64
src/api/production-master-data/optional-params.js
Normal file
64
src/api/production-master-data/optional-params.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/optional_params/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_process_model_optional_params_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getOptionalParamsList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createOptionalParams (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editOptionalParams (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteOptionalParams (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getImportTemplate (data) {
|
||||
return request({
|
||||
url: BASE + 'get_import_template',
|
||||
method: 'post',
|
||||
responseType: 'blob',
|
||||
data: {
|
||||
method: 'production_master_data_process_model_optional_params_get_import_template',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function importDataCreate (data) {
|
||||
return request({
|
||||
url: BASE + 'import_data_create',
|
||||
method: 'post',
|
||||
data: apiParams('import_data_create', data)
|
||||
})
|
||||
}
|
||||
55
src/api/production-master-data/process-category.js
Normal file
55
src/api/production-master-data/process-category.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/technology_flow_category/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_process_model_process_category_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProcessCategoryList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getProcessCategoryAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'production_configuration_technology_model_technology_flow_category_all',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function createProcessCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProcessCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProcessCategory (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
67
src/api/production-master-data/process-routing-card.js
Normal file
67
src/api/production-master-data/process-routing-card.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/technology_flow_process/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_technology_model_technology_flow_process_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProcessCardAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createProcessCard (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProcessCard (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProcessCard (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function setSetting (data) {
|
||||
return request({
|
||||
url: BASE + 'set_setting',
|
||||
method: 'post',
|
||||
data: apiParams('set_setting', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function moveUp (data) {
|
||||
return request({
|
||||
url: BASE + 'move_up',
|
||||
method: 'post',
|
||||
data: apiParams('move_up', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function moveDown (data) {
|
||||
return request({
|
||||
url: BASE + 'move_down',
|
||||
method: 'post',
|
||||
data: apiParams('move_down', data)
|
||||
})
|
||||
}
|
||||
51
src/api/production-master-data/process-routing.js
Normal file
51
src/api/production-master-data/process-routing.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/technology_flow/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_process_model_process_routing_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProcessRoutingList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createProcessRouting (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProcessRouting (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProcessRouting (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function copyProcessRouting (data) {
|
||||
return request({
|
||||
url: BASE + 'copy',
|
||||
method: 'post',
|
||||
data: apiParams('copy', data)
|
||||
})
|
||||
}
|
||||
67
src/api/production-master-data/process-step.js
Normal file
67
src/api/production-master-data/process-step.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/technology_model/technology_flow_workingsubclass/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_process_model_process_step_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProcessStepList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getWorkingsubclassAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'production_configuration_technology_model_technology_flow_workingsubclass_all',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function createProcessStep (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProcessStep (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProcessStep (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function settingSubmit (data) {
|
||||
return request({
|
||||
url: BASE + 'setting_submit',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'production_master_data_process_model_process_step_setting_submit',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
55
src/api/production-master-data/product-management.js
Normal file
55
src/api/production-master-data/product-management.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/product_model/battery_model/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_product_management_product_list_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProductList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getProductBatteryAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'production_configuration_product_model_battery_model_all',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function createProduct (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProduct (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProduct (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
76
src/api/production-master-data/product-ng-info.js
Normal file
76
src/api/production-master-data/product-ng-info.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/product_model/product_ng_info/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_configuration_product_model_product_ng_info_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProductNgInfoAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: { ...data }
|
||||
})
|
||||
}
|
||||
|
||||
export function getProductNgInfoList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: { ...data }
|
||||
})
|
||||
}
|
||||
|
||||
export function createProductNgInfo (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProductNgInfo (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProductNgInfo (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getImportTemplate (data) {
|
||||
return request({
|
||||
url: BASE + 'get_import_template',
|
||||
method: 'post',
|
||||
responseType: 'blob',
|
||||
data: apiParams('get_import_template', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function productNgInfoImport (data) {
|
||||
return request({
|
||||
url: BASE + 'import',
|
||||
method: 'post',
|
||||
data: apiParams('import', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function productNgInfoExportTask (data) {
|
||||
return request({
|
||||
url: BASE + 'product_ng_info_export_task',
|
||||
method: 'post',
|
||||
data: apiParams('product_ng_info_export_task', data)
|
||||
})
|
||||
}
|
||||
43
src/api/production-master-data/production-line.js
Normal file
43
src/api/production-master-data/production-line.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'production_configuration/factory_model/factory_line/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `production_master_data_factory_model_factory_line_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getProductionLineList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createProductionLine (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editProductionLine (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProductionLine (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
15
src/api/system-administration/api-logs.js
Normal file
15
src/api/system-administration/api-logs.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'system_settings/system_assistant/interface_log/'
|
||||
|
||||
export function getInterfaceLogList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_system_assistant_interface_log_list',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
67
src/api/system-administration/menu-configuration.js
Normal file
67
src/api/system-administration/menu-configuration.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'system_settings/menu_configuration/menu/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `system_settings_menu_configuration_menu_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getMenuAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getMenuList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createMenu (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editMenu (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteMenu (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function updateMenuStatus (data) {
|
||||
return request({
|
||||
url: BASE + 'update_status',
|
||||
method: 'put',
|
||||
data: apiParams('update_status', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function sortMenu (data) {
|
||||
return request({
|
||||
url: BASE + 'sort',
|
||||
method: 'put',
|
||||
data: apiParams('sort', data)
|
||||
})
|
||||
}
|
||||
15
src/api/system-administration/operation-logs.js
Normal file
15
src/api/system-administration/operation-logs.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'system_settings/system_assistant/operate_log/'
|
||||
|
||||
export function getOperateLogList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_system_assistant_operate_log_list',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
149
src/api/system-administration/problem-help.js
Normal file
149
src/api/system-administration/problem-help.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const CATEGORY_BASE = 'system_settings/system_assistant/problem_help/category/'
|
||||
const MARKDOWN_BASE = 'system_settings/system_assistant/problem_help/markdown/'
|
||||
|
||||
export function getCategoryTree (data) {
|
||||
return request({
|
||||
url: CATEGORY_BASE + 'tree',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_system_assistant_problem_category_tree',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function setCategoryAdd (data) {
|
||||
return request({
|
||||
url: CATEGORY_BASE + 'add',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'system_settings_system_assistant_problem_category_add',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function setCategoryUpdate (data) {
|
||||
return request({
|
||||
url: CATEGORY_BASE + 'edit',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_system_assistant_problem_category_edit',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function delCategory (data) {
|
||||
return request({
|
||||
url: CATEGORY_BASE + 'delete',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'system_settings_system_assistant_problem_category_delete',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getProblemTree (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'tree',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_system_assistant_problem_markdown_tree',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getMarkdownDetails (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'details',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_system_assistant_problem_markdown_details',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function setMarkdownAdd (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'add',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'system_settings_system_assistant_problem_markdown_add',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function setMarkdownEdit (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'edit',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_system_assistant_problem_markdown_edit',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function delMarkdownDetails (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'del',
|
||||
method: 'delete',
|
||||
data: {
|
||||
method: 'system_settings_system_assistant_problem_markdown_del',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteProblem (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'delete',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'del.problem.item',
|
||||
platform: 'admin',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function searchMarkdown (data) {
|
||||
return request({
|
||||
url: MARKDOWN_BASE + 'search',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_system_assistant_problem_markdown_search',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleAll (data) {
|
||||
return request({
|
||||
url: 'system_settings/user_management/role/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_user_management_role_list',
|
||||
platform: 'background',
|
||||
page_size: 9999,
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
83
src/api/system-administration/role.js
Normal file
83
src/api/system-administration/role.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'system_settings/user_management/role/'
|
||||
|
||||
function apiParams (method, data = {}) {
|
||||
return {
|
||||
method: `system_settings_user_management_role_${method}`,
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
}
|
||||
|
||||
export function getRoleAll (data) {
|
||||
return request({
|
||||
url: BASE + 'all',
|
||||
method: 'get',
|
||||
params: apiParams('all', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: apiParams('list', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function createRole (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: apiParams('create', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function editRole (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: apiParams('edit', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteRole (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: apiParams('delete', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRoleStatus (data) {
|
||||
return request({
|
||||
url: BASE + 'update_status',
|
||||
method: 'put',
|
||||
data: apiParams('update_status', data)
|
||||
})
|
||||
}
|
||||
|
||||
export function giveRoleMenu (data) {
|
||||
return request({
|
||||
url: BASE + 'give',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_user_management_give_role_menu',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoleMenu (data) {
|
||||
return request({
|
||||
url: BASE + 'menu',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_user_management_role_menu',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
99
src/api/system-administration/user.js
Normal file
99
src/api/system-administration/user.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import { request } from '@/api/_service'
|
||||
|
||||
const BASE = 'system_settings/user_management/user/'
|
||||
|
||||
export function getUserList (data) {
|
||||
return request({
|
||||
url: BASE + 'list',
|
||||
method: 'get',
|
||||
params: {
|
||||
method: 'system_settings_user_management_user_list',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function createUser (data) {
|
||||
return request({
|
||||
url: BASE + 'create',
|
||||
method: 'post',
|
||||
data: {
|
||||
method: 'system_settings_user_management_user_create',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function editUser (data) {
|
||||
return request({
|
||||
url: BASE + 'edit',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_user_management_user_edit',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteUser (data) {
|
||||
return request({
|
||||
url: BASE + 'delete',
|
||||
method: 'delete',
|
||||
data: {
|
||||
method: 'system_settings_user_management_user_delete',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function batchDeleteUser (data) {
|
||||
return request({
|
||||
url: BASE + 'batch_delete',
|
||||
method: 'delete',
|
||||
data: {
|
||||
method: 'system_settings_user_management_user_batch_delete',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function enableUser (data) {
|
||||
return request({
|
||||
url: BASE + 'enable',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_user_management_enable_user',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function disableUser (data) {
|
||||
return request({
|
||||
url: BASE + 'disable',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_user_management_disable_user',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function resetUserPwd (data) {
|
||||
return request({
|
||||
url: BASE + 'reset_pwd',
|
||||
method: 'put',
|
||||
data: {
|
||||
method: 'system_settings_user_management_user_reset_pwd',
|
||||
platform: 'background',
|
||||
...data
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
@use 'sass:list';
|
||||
@import 'public';
|
||||
|
||||
// 补丁 base
|
||||
@@ -39,17 +40,17 @@
|
||||
$sizes: (0, 5, 10, 15, 20);
|
||||
|
||||
@for $index from 1 to 6 {
|
||||
.#{$prefix}-m-#{nth($sizes, $index)} { margin: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-mt-#{nth($sizes, $index)} { margin-top: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-mr-#{nth($sizes, $index)} { margin-right: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-mb-#{nth($sizes, $index)} { margin-bottom: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-ml-#{nth($sizes, $index)} { margin-left: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-m-#{list.nth($sizes, $index)} { margin: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-mt-#{list.nth($sizes, $index)} { margin-top: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-mr-#{list.nth($sizes, $index)} { margin-right: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-mb-#{list.nth($sizes, $index)} { margin-bottom: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-ml-#{list.nth($sizes, $index)} { margin-left: #{list.nth($sizes, $index)}px !important; }
|
||||
|
||||
.#{$prefix}-p-#{nth($sizes, $index)} { padding: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pt-#{nth($sizes, $index)} { padding-top: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pr-#{nth($sizes, $index)} { padding-right: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pb-#{nth($sizes, $index)} { padding-bottom: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pl-#{nth($sizes, $index)} { padding-left: #{nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-p-#{list.nth($sizes, $index)} { padding: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pt-#{list.nth($sizes, $index)} { padding-top: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pr-#{list.nth($sizes, $index)} { padding-right: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pb-#{list.nth($sizes, $index)} { padding-bottom: #{list.nth($sizes, $index)}px !important; }
|
||||
.#{$prefix}-pl-#{list.nth($sizes, $index)} { padding-left: #{list.nth($sizes, $index)}px !important; }
|
||||
}
|
||||
|
||||
// 快速使用
|
||||
|
||||
663
src/components/d2-icon/font-awesome-4.7.0/css/iconfont.css
Normal file
663
src/components/d2-icon/font-awesome-4.7.0/css/iconfont.css
Normal file
@@ -0,0 +1,663 @@
|
||||
@font-face {
|
||||
font-family: "FontAwesome"; /* Project id 3355288 */
|
||||
src: url('../fonts2/iconfont.woff2?t=1704275967311') format('woff2'),
|
||||
url('../fonts2/iconfont.woff?t=1704275967311') format('woff'),
|
||||
url('../fonts2/iconfont.ttf?t=1704275967311') format('truetype');
|
||||
}
|
||||
|
||||
.FontAwesome {
|
||||
font-family: "FontAwesome" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.fa-daifasong:before {
|
||||
content: "\e69c";
|
||||
}
|
||||
|
||||
.fa-chuku:before {
|
||||
content: "\e69b";
|
||||
}
|
||||
|
||||
.fa-quxiao:before {
|
||||
content: "\e695";
|
||||
}
|
||||
|
||||
.fa-jieshou:before {
|
||||
content: "\e696";
|
||||
}
|
||||
|
||||
.fa-shangjia:before {
|
||||
content: "\e697";
|
||||
}
|
||||
|
||||
.fa-xiajia:before {
|
||||
content: "\e699";
|
||||
}
|
||||
|
||||
.fa-zaitu:before {
|
||||
content: "\e69a";
|
||||
}
|
||||
|
||||
.fa-zuofei01:before {
|
||||
content: "\ea30";
|
||||
}
|
||||
|
||||
.fa-butongguo1:before {
|
||||
content: "\e694";
|
||||
}
|
||||
|
||||
.fa-weiwancheng:before {
|
||||
content: "\e7fc";
|
||||
}
|
||||
|
||||
.fa-butongguo2:before {
|
||||
content: "\e7c4";
|
||||
}
|
||||
|
||||
.fa-scjyrwu:before {
|
||||
content: "\e89c";
|
||||
}
|
||||
|
||||
.fa-jyrwu:before {
|
||||
content: "\e693";
|
||||
}
|
||||
|
||||
.fa-daijianyan:before {
|
||||
content: "\e602";
|
||||
}
|
||||
|
||||
.fa-baoshui:before {
|
||||
content: "\e692";
|
||||
}
|
||||
|
||||
.fa-yikuzhong:before {
|
||||
content: "\e70f";
|
||||
}
|
||||
|
||||
.fa-pandianzhong:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.fa-daichuku:before {
|
||||
content: "\e68f";
|
||||
}
|
||||
|
||||
.fa-kehu:before {
|
||||
content: "\e690";
|
||||
}
|
||||
|
||||
.fa-kucun:before {
|
||||
content: "\e691";
|
||||
}
|
||||
|
||||
.fa-daijianhuo:before {
|
||||
content: "\e6c3";
|
||||
}
|
||||
|
||||
.fa-kucunrizhi:before {
|
||||
content: "\e756";
|
||||
}
|
||||
|
||||
.fa-quanbu:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.fa-shouhuomingxichaxun:before {
|
||||
content: "\e689";
|
||||
}
|
||||
|
||||
.fa-daifenjian:before {
|
||||
content: "\e68a";
|
||||
}
|
||||
|
||||
.fa-fabudehuodong-daishenhe:before {
|
||||
content: "\e611";
|
||||
}
|
||||
|
||||
.fa-daishangjia:before {
|
||||
content: "\e68e";
|
||||
}
|
||||
|
||||
.fa-shebeixinxitongdaoguanli2:before {
|
||||
content: "\e68b";
|
||||
}
|
||||
|
||||
.fa-jiguixinxiguanli2:before {
|
||||
content: "\e68c";
|
||||
}
|
||||
|
||||
.fa-chanxianshezhi3:before {
|
||||
content: "\e68d";
|
||||
}
|
||||
|
||||
.fa-dantixudianchiliebiao:before {
|
||||
content: "\e685";
|
||||
}
|
||||
|
||||
.fa-jiguixinxiguanli:before {
|
||||
content: "\e681";
|
||||
}
|
||||
|
||||
.fa-shebeixinxitongdaoguanli:before {
|
||||
content: "\e682";
|
||||
}
|
||||
|
||||
.fa-chanxianshezhi2:before {
|
||||
content: "\e683";
|
||||
}
|
||||
|
||||
.fa-rizhiguanli_jiekourizhiguanli:before {
|
||||
content: "\e684";
|
||||
}
|
||||
|
||||
.fa-dianchigaishujuguanli2:before {
|
||||
content: "\e686";
|
||||
}
|
||||
|
||||
.fa-chongfangdianjiankong:before {
|
||||
content: "\e676";
|
||||
}
|
||||
|
||||
.fa-caozuorizhi:before {
|
||||
content: "\e677";
|
||||
}
|
||||
|
||||
.fa-chanpinliebiao:before {
|
||||
content: "\e678";
|
||||
}
|
||||
|
||||
.fa-shebeitaizhang1:before {
|
||||
content: "\e679";
|
||||
}
|
||||
|
||||
.fa-a-4-3zhongkongshi1:before {
|
||||
content: "\e67a";
|
||||
}
|
||||
|
||||
.fa-gongyiliuchengleibie:before {
|
||||
content: "\e67b";
|
||||
}
|
||||
|
||||
.fa-gongdanliebiao1:before {
|
||||
content: "\e67c";
|
||||
}
|
||||
|
||||
.fa-jihuapaicheng:before {
|
||||
content: "\e67d";
|
||||
}
|
||||
|
||||
.fa-chanxianshezhi1:before {
|
||||
content: "\e67e";
|
||||
}
|
||||
|
||||
.fa-OCRshibiepeizhi:before {
|
||||
content: "\e67f";
|
||||
}
|
||||
|
||||
.fa-banchengpinshengchanguanli:before {
|
||||
content: "\e680";
|
||||
}
|
||||
|
||||
.fa-SCADAjiedianpeizhi:before {
|
||||
content: "\e66f";
|
||||
}
|
||||
|
||||
.fa-huankongshujuchaxun:before {
|
||||
content: "\e670";
|
||||
}
|
||||
|
||||
.fa-wentibangzhu:before {
|
||||
content: "\e671";
|
||||
}
|
||||
|
||||
.fa-jiliangdanwei:before {
|
||||
content: "\e672";
|
||||
}
|
||||
|
||||
.fa-shengchanpiciwangongbaogong:before {
|
||||
content: "\e673";
|
||||
}
|
||||
|
||||
.fa-huankongshujuyichangjilu:before {
|
||||
content: "\e674";
|
||||
}
|
||||
|
||||
.fa-SCADAshujuchaxun:before {
|
||||
content: "\e675";
|
||||
}
|
||||
|
||||
.fa-qianzhifuwupeizhi:before {
|
||||
content: "\e665";
|
||||
}
|
||||
|
||||
.fa-huachengshangjiajilu:before {
|
||||
content: "\e666";
|
||||
}
|
||||
|
||||
.fa-OCRshibierizhi:before {
|
||||
content: "\e667";
|
||||
}
|
||||
|
||||
.fa-gongxudanyuan:before {
|
||||
content: "\e668";
|
||||
}
|
||||
|
||||
.fa-tuopanguanli1:before {
|
||||
content: "\e669";
|
||||
}
|
||||
|
||||
.fa-shengchanpicijiankong:before {
|
||||
content: "\e66a";
|
||||
}
|
||||
|
||||
.fa-gongyiliucheng:before {
|
||||
content: "\e66b";
|
||||
}
|
||||
|
||||
.fa-shujuluru:before {
|
||||
content: "\e66c";
|
||||
}
|
||||
|
||||
.fa-chejiandianweiguanli:before {
|
||||
content: "\e66d";
|
||||
}
|
||||
|
||||
.fa-APSpaichengshezhi:before {
|
||||
content: "\e66e";
|
||||
}
|
||||
|
||||
.fa-ketibianmaguanli:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.fa-qianzhifuwujiankong:before {
|
||||
content: "\e653";
|
||||
}
|
||||
|
||||
.fa-shebeijiankong-1:before {
|
||||
content: "\e656";
|
||||
}
|
||||
|
||||
.fa-shebeileibie:before {
|
||||
content: "\e657";
|
||||
}
|
||||
|
||||
.fa-shebeixinxiguanli:before {
|
||||
content: "\e658";
|
||||
}
|
||||
|
||||
.fa-tuopangeshi:before {
|
||||
content: "\e659";
|
||||
}
|
||||
|
||||
.fa-chongfangdianjiguixinxi:before {
|
||||
content: "\e65a";
|
||||
}
|
||||
|
||||
.fa-huankongjiankong:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.fa-piciliebiao:before {
|
||||
content: "\e65c";
|
||||
}
|
||||
|
||||
.fa-chejianguanli:before {
|
||||
content: "\e65d";
|
||||
}
|
||||
|
||||
.fa-dantibianmaguanli:before {
|
||||
content: "\e65e";
|
||||
}
|
||||
|
||||
.fa-guigehaoguanli:before {
|
||||
content: "\e65f";
|
||||
}
|
||||
|
||||
.fa-tuopandenglu1:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
||||
.fa-chanpinpici:before {
|
||||
content: "\e661";
|
||||
}
|
||||
|
||||
.fa-chongfangdianshebeitongdao:before {
|
||||
content: "\e663";
|
||||
}
|
||||
|
||||
.fa-a-800tongdaoyuanchengkongzhi:before {
|
||||
content: "\e664";
|
||||
}
|
||||
|
||||
.fa-shengchanpicibuliangbaobiao:before {
|
||||
content: "\e649";
|
||||
}
|
||||
|
||||
.fa-picituopan:before {
|
||||
content: "\e64a";
|
||||
}
|
||||
|
||||
.fa-jiekourizhi:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.fa-yuancailiaochukudan:before {
|
||||
content: "\e64c";
|
||||
}
|
||||
|
||||
.fa-shujuqinglishezhi:before {
|
||||
content: "\e64d";
|
||||
}
|
||||
|
||||
.fa-suoqushezhi:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.fa-shebeixinxi:before {
|
||||
content: "\e64f";
|
||||
}
|
||||
|
||||
.fa-huankongyichangjilu:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.fa-shengchanqianguanli:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.fa-tongzhimoban:before {
|
||||
content: "\e662";
|
||||
}
|
||||
|
||||
.fa-shuxian:before {
|
||||
content: "\e606";
|
||||
}
|
||||
|
||||
.fa-jinyong:before {
|
||||
content: "\e648";
|
||||
}
|
||||
|
||||
.fa-qiyong:before {
|
||||
content: "\e6b6";
|
||||
}
|
||||
|
||||
.fa-finish:before {
|
||||
content: "\e655";
|
||||
}
|
||||
|
||||
.fa-kaishi:before {
|
||||
content: "\e647";
|
||||
}
|
||||
|
||||
.fa-paichangantetu:before {
|
||||
content: "\e687";
|
||||
}
|
||||
|
||||
.fa-lingquline:before {
|
||||
content: "\e804";
|
||||
}
|
||||
|
||||
.fa-zuofei:before {
|
||||
content: "\e86c";
|
||||
}
|
||||
|
||||
.fa-shenhe:before {
|
||||
content: "\100d9";
|
||||
}
|
||||
|
||||
.fa-wuliaodanweiliebiao:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.fa-excel:before {
|
||||
content: "\e646";
|
||||
}
|
||||
|
||||
.fa-exportexcel:before {
|
||||
content: "\e71b";
|
||||
}
|
||||
|
||||
.fa-wenjian_excel:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.fa-kucunyidong:before {
|
||||
content: "\e698";
|
||||
}
|
||||
|
||||
.fa-daishouhuo:before {
|
||||
content: "\e6cd";
|
||||
}
|
||||
|
||||
.fa-kuweishezhi:before {
|
||||
content: "\e6cb";
|
||||
}
|
||||
|
||||
.fa-fahuoliebiao:before {
|
||||
content: "\e9b9";
|
||||
}
|
||||
|
||||
.fa-shouhuoliebiao:before {
|
||||
content: "\e645";
|
||||
}
|
||||
|
||||
.fa-cangkuguanli-kuquguanli:before {
|
||||
content: "\e6c1";
|
||||
}
|
||||
|
||||
.fa-kucunpandian:before {
|
||||
content: "\e688";
|
||||
}
|
||||
|
||||
.fa-wuliaoliebiao:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
||||
.fa-gongyingshangliebiao:before {
|
||||
content: "\e642";
|
||||
}
|
||||
|
||||
.fa-guochengguanli:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.fa-yichangchulipeizhi:before {
|
||||
content: "\e654";
|
||||
}
|
||||
|
||||
.fa-gongdanliebiao:before {
|
||||
content: "\e639";
|
||||
}
|
||||
|
||||
.fa-diepanguanli:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.fa-dianchidenglu:before {
|
||||
content: "\e63b";
|
||||
}
|
||||
|
||||
.fa-gongdanbaobiao:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.fa-jijuanpicibaobiao:before {
|
||||
content: "\e63d";
|
||||
}
|
||||
|
||||
.fa-shebeilvlibaobiao:before {
|
||||
content: "\e63e";
|
||||
}
|
||||
|
||||
.fa-wuliaolvli:before {
|
||||
content: "\e63f";
|
||||
}
|
||||
|
||||
.fa-tuopanguanli:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
.fa-tuopandenglu:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.fa-dianchidengjizhuisu:before {
|
||||
content: "\e635";
|
||||
}
|
||||
|
||||
.fa-dianchizhuisu:before {
|
||||
content: "\e636";
|
||||
}
|
||||
|
||||
.fa-tuopanzhuisu:before {
|
||||
content: "\e637";
|
||||
}
|
||||
|
||||
.fa-wuliaopicizhuisu:before {
|
||||
content: "\e638";
|
||||
}
|
||||
|
||||
.fa-gongzuozhanshezhi:before {
|
||||
content: "\e612";
|
||||
}
|
||||
|
||||
.fa-shujubaobiao:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.fa-liuchengleibie:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.fa-gongyizishuxing:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.fa-changqushezhi:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.fa-chanxianshezhi:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.fa-zhiliangguanli:before {
|
||||
content: "\e62d";
|
||||
}
|
||||
|
||||
.fa-gongzuoliucheng:before {
|
||||
content: "\e62e";
|
||||
}
|
||||
|
||||
.fa-shebeiguanli:before {
|
||||
content: "\e62f";
|
||||
}
|
||||
|
||||
.fa-chanpinzhuisu:before {
|
||||
content: "\e630";
|
||||
}
|
||||
|
||||
.fa-wuliaoxinxiguanli:before {
|
||||
content: "\e631";
|
||||
}
|
||||
|
||||
.fa-paibanguanli:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.fa-bancipeizhi:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.fa-dianchixinghao:before {
|
||||
content: "\e634";
|
||||
}
|
||||
|
||||
.fa-jiankongshouye:before {
|
||||
content: "\e618";
|
||||
}
|
||||
|
||||
.fa-shebeibaoyangxiangmu:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
||||
.fa-shebeidianjianxiangmu:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
.fa-shebeitaizhang:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.fa-shebeibaoyangjihua:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.fa-shebeidianjianguanli:before {
|
||||
content: "\e624";
|
||||
}
|
||||
|
||||
.fa-shebeidianjianjilu:before {
|
||||
content: "\e625";
|
||||
}
|
||||
|
||||
.fa-shebeileibieguanli:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.fa-xiaohaopingenghuanchaxun:before {
|
||||
content: "\e627";
|
||||
}
|
||||
|
||||
.fa-yuedudianjianbiao:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.fa-xiaohaopinshoumingguanli:before {
|
||||
content: "\e629";
|
||||
}
|
||||
|
||||
.fa-xiaohaopinxiangmu:before {
|
||||
content: "\e62a";
|
||||
}
|
||||
|
||||
.fa-chongfangdiankuweijiankong:before {
|
||||
content: "\e62b";
|
||||
}
|
||||
|
||||
.fa-shebeitongdaoyichangjiankong:before {
|
||||
content: "\e62c";
|
||||
}
|
||||
|
||||
.fa-pinzhiyichangguanli:before {
|
||||
content: "\e61b";
|
||||
}
|
||||
|
||||
.fa-shouxunjianxiangmupeizhi:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
.fa-zhichengbaobiao:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.fa-shouxunjianluru:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.fa-shouxunjianbaobiao:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.fa-zhichengxiangmupeizhi:before {
|
||||
content: "\e620";
|
||||
}
|
||||
|
||||
.fa-zhichengxiangcaiyang:before {
|
||||
content: "\e621";
|
||||
}
|
||||
|
||||
1143
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.json
Normal file
1143
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.ttf
Normal file
BIN
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.ttf
Normal file
Binary file not shown.
BIN
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.woff
Normal file
BIN
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.woff
Normal file
Binary file not shown.
BIN
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.woff2
Normal file
BIN
src/components/d2-icon/font-awesome-4.7.0/fonts2/iconfont.woff2
Normal file
Binary file not shown.
@@ -4,6 +4,7 @@
|
||||
|
||||
<script>
|
||||
import './font-awesome-4.7.0/css/font-awesome.min.css'
|
||||
import './font-awesome-4.7.0/css/iconfont.css'
|
||||
export default {
|
||||
name: 'd2-icon',
|
||||
props: {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<p
|
||||
class="d2-module-index-menu-group--title"
|
||||
:class="titleClassName">
|
||||
{{menu.title}}
|
||||
{{ $t(menu.title) }}
|
||||
</p>
|
||||
<template v-for="(item, index) in menu.children">
|
||||
<d2-module-index-menu-group
|
||||
@@ -27,7 +27,7 @@
|
||||
<p
|
||||
class="d2-module-index-menu-group--title"
|
||||
:class="titleClassName">
|
||||
{{menu.title}}
|
||||
{{ $t(menu.title) }}
|
||||
</p>
|
||||
<d2-module-index-menu-item :menu="menu"/>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-button
|
||||
class="d2-module-index-menu-item"
|
||||
@click="handleMenuSelect(menu.path)">
|
||||
{{menu.title}}
|
||||
{{ $t(menu.title) }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
|
||||
345
src/components/page-dialog-form/index.vue
Normal file
345
src/components/page-dialog-form/index.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<!--
|
||||
page-dialog-form — 增删改查弹框组件
|
||||
===================================
|
||||
这是一个「带表单验证的弹框」组件,配合 page-table 使用。
|
||||
负责:弹框显隐控制 + 表单渲染 + 表单验证 + 确定/取消按钮。
|
||||
|
||||
不支持复杂表单联动——如有需要,通过默认插槽自定义内容。
|
||||
|
||||
依赖:element-ui 的 <el-dialog> <el-form> <el-input> <el-select>
|
||||
i18n:组件内部通过 $t() 翻译传入的 i18n key,切换语言时自动响应
|
||||
|
||||
@author 前端团队
|
||||
@since 2026-05
|
||||
-->
|
||||
|
||||
<!--
|
||||
el-dialog:
|
||||
visible.sync → 通过 .sync 修饰符双向绑定父组件的 visible prop
|
||||
destroy-on-close → 每次关闭销毁 DOM 重建,避免表单残留上次数据
|
||||
close-on-click-modal → 禁止点击遮罩关闭,防止误操作丢失填写数据
|
||||
-->
|
||||
<el-dialog
|
||||
:visible.sync="visibleProxy"
|
||||
:title="$t(title)"
|
||||
:width="width"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<!-- ==================== 表单区 ==================== -->
|
||||
<!--
|
||||
el-form:
|
||||
rules 由父组件传入,字段名与 formData 的 key 一一对应
|
||||
label-width 默认 100px,可自定义
|
||||
-->
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="formData"
|
||||
:rules="translatedRules"
|
||||
:label-width="labelWidth || '100px'"
|
||||
>
|
||||
<!--
|
||||
遍历 formCols 中所有行 → 每行中的每个字段 → 渲染对应的表单项
|
||||
当前支持两种字段类型:
|
||||
- type='input' → <el-input>(支持 textarea、密码等)
|
||||
- type='select' → <el-select> + <el-option>
|
||||
|
||||
由调用方负责翻译,传已翻译的文本即可
|
||||
-->
|
||||
<el-form-item
|
||||
v-for="col in flatFormCols"
|
||||
:key="col.prop"
|
||||
:label="$t(col.label)"
|
||||
:prop="col.prop"
|
||||
>
|
||||
<!-- ===== 输入框类型 ===== -->
|
||||
<!--
|
||||
input:
|
||||
inputType='textarea' → 多行文本框
|
||||
不传 inputType → 普通文本输入框
|
||||
clearable → 默认 true,传 false 可关闭
|
||||
-->
|
||||
<el-input
|
||||
v-if="col.type === 'input'"
|
||||
v-model="formData[col.prop]"
|
||||
:placeholder="$t(col.placeholder)"
|
||||
:type="col.inputType || 'text'"
|
||||
:autosize="col.autosize"
|
||||
:clearable="col.clearable !== false"
|
||||
:disabled="!!col.disabled"
|
||||
:style="col.style"
|
||||
@focus="handleFieldEvent(col, 'focus', $event)"
|
||||
@blur="handleFieldEvent(col, 'blur', $event)"
|
||||
/>
|
||||
<!-- ===== 下拉选择类型 ===== -->
|
||||
<!--
|
||||
select:
|
||||
options → [{ label: '苹果', value: 1 }]
|
||||
filterable → 默认 true,支持搜索过滤
|
||||
-->
|
||||
<el-select
|
||||
v-else-if="col.type === 'select'"
|
||||
v-model="formData[col.prop]"
|
||||
:placeholder="$t(col.placeholder)"
|
||||
:clearable="col.clearable !== false"
|
||||
:style="col.style"
|
||||
:filterable="col.filterable !== false"
|
||||
:disabled="!!col.disabled"
|
||||
@change="handleFieldEvent(col, 'change', $event)"
|
||||
@focus="handleFieldEvent(col, 'focus', $event)"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in col.options"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- ===== 自定义表单内容插槽 ===== -->
|
||||
<!--
|
||||
如果需要超出 input/select 的复杂表单控件
|
||||
父组件可以用此插槽添加任意内容
|
||||
-->
|
||||
<slot />
|
||||
</el-form>
|
||||
|
||||
<!-- ==================== 底部按钮 ==================== -->
|
||||
<!--
|
||||
确定:type='primary' + submitting loading 状态
|
||||
取消:调用 onClose → 重置表单 + 关闭弹框
|
||||
由调用方负责翻译,传已翻译的文本即可
|
||||
-->
|
||||
<template #footer>
|
||||
<el-button @click="onClose">{{ $t(cancelText) }}</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="onSubmit">{{ $t(confirmText) }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* PageDialogForm — 增删改查弹框组件
|
||||
*
|
||||
* 【核心职责】
|
||||
* 1. 管理弹框显隐
|
||||
* 2. 根据 formCols 声明式渲染表单
|
||||
* 3. 表单验证 + 验证失败提示
|
||||
* 4. 提交/取消事件通知父组件
|
||||
*
|
||||
* 【典型用法】
|
||||
* <page-dialog-form
|
||||
* ref="dialogForm"
|
||||
* :visible.sync="dialogVisible"
|
||||
* :title="dialogTitle"
|
||||
* width="35%"
|
||||
* :form-cols="formCols"
|
||||
* :form-data="formData"
|
||||
* :rules="rules"
|
||||
* :submitting="submitting"
|
||||
* @submit="onDialogSubmit"
|
||||
* @close="onDialogClose"
|
||||
* />
|
||||
*
|
||||
* 【父组件调用的方法(通过 ref)】
|
||||
* this.$refs.dialogForm.reset() — 重置表单
|
||||
* this.$refs.dialogForm.validate() — 手动验证,返回 Promise<boolean>
|
||||
*
|
||||
* 【formCols 数据结构说明】
|
||||
* 二维数组:外层是行,里层是该行的字段。大多数场景每行一个字段即可。
|
||||
*
|
||||
* 例:
|
||||
* formCols: [
|
||||
* [ { type: 'input', prop: 'code', label: T+'.code', placeholder: T+'.enter_code' } ],
|
||||
* [ { type: 'input', prop: 'remark', label: T+'.remark', inputType: 'textarea', autosize: { minRows: 2 } } ],
|
||||
* [ { type: 'select', prop: 'area_id', label: T+'.area', options: [{ label: 'A区', value: 1 }] } ]
|
||||
* ]
|
||||
*
|
||||
* 【表单字段支持的属性】
|
||||
* 基础:type / prop / label / placeholder
|
||||
* input:inputType('textarea' | 'text')/ autosize / clearable
|
||||
* select:options / filterable
|
||||
* 通用:style(如 { width: '90%' })
|
||||
*
|
||||
* 【表单验证(rules)】
|
||||
* rules 与 el-form 的 rules 完全一致:
|
||||
* rules: {
|
||||
* code: [{ required: true, message: '请输入编码', trigger: 'blur' }]
|
||||
* }
|
||||
* 验证失败时,组件会用 $message.warning 显示第一条错误信息
|
||||
* message 字段支持 i18n key,父组件用 this.$t() 传值即可
|
||||
*/
|
||||
export default {
|
||||
name: 'PageDialogForm',
|
||||
props: {
|
||||
/**
|
||||
* 弹框显隐状态,父组件用 .sync 绑定
|
||||
* 例::visible.sync="dialogVisible"
|
||||
*/
|
||||
visible: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 弹框标题,由调用方负责翻译
|
||||
*/
|
||||
title: { type: String, default: '' },
|
||||
|
||||
/**
|
||||
* 弹框宽度,可以是百分比或像素
|
||||
* 例:'35%' / '600px'
|
||||
*/
|
||||
width: { type: String, default: '35%' },
|
||||
|
||||
/**
|
||||
* 表单字段结构,二维数组
|
||||
* 外层数组每一个元素代表表单的一行
|
||||
* 内层数组每个元素代表该行中的一个字段
|
||||
*
|
||||
* 每个字段对象支持的属性见组件顶部的注释
|
||||
*/
|
||||
formCols: { type: Array, default: () => [] },
|
||||
|
||||
/**
|
||||
* 表单数据对象,key 与 formCols 中的 prop 一一对应
|
||||
* 编辑时父组件将 row 数据赋值给 formData
|
||||
*/
|
||||
formData: { type: Object, default: () => ({}) },
|
||||
|
||||
/**
|
||||
* 表单验证规则,与 el-form 的 rules 完全一致
|
||||
* 例:{ code: [{ required: true, message: '请输入编码', trigger: 'blur' }] }
|
||||
*/
|
||||
rules: { type: Object, default: () => ({}) },
|
||||
|
||||
/**
|
||||
* 表单 label 宽度,默认 '100px'
|
||||
*/
|
||||
labelWidth: { type: String, default: '100px' },
|
||||
|
||||
/**
|
||||
* 提交 loading 状态,提交期间显示转圈防止重复点击
|
||||
*/
|
||||
submitting: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 确定按钮文字,默认 '确定',由调用方负责 i18n 翻译
|
||||
*/
|
||||
confirmText: { type: String, default: '确定' },
|
||||
|
||||
/**
|
||||
* 取消按钮文字,默认 '取消',由调用方负责 i18n 翻译
|
||||
*/
|
||||
cancelText: { type: String, default: '取消' }
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* visible 代理:用于 .sync 双向绑定
|
||||
* get → 返回父组件传入的 visible
|
||||
* set → emit update:visible 通知父组件
|
||||
*/
|
||||
visibleProxy: {
|
||||
get () { return this.visible },
|
||||
set (val) { this.$emit('update:visible', val) }
|
||||
},
|
||||
|
||||
/**
|
||||
* 将二维 formCols 打平为一维数组,方便 v-for 遍历
|
||||
* 二维数组 → 一维数组:[ [{a}], [{b},{c}] ] → [a, b, c]
|
||||
*/
|
||||
flatFormCols () {
|
||||
return this.formCols.flat()
|
||||
},
|
||||
|
||||
translatedRules () {
|
||||
const rules = this.rules || {}
|
||||
const translated = {}
|
||||
for (const [field, validators] of Object.entries(rules)) {
|
||||
translated[field] = validators.map(v => ({ ...v, message: this.$t(v.message) }))
|
||||
}
|
||||
return translated
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleFieldEvent (col, eventName, value) {
|
||||
if (!col) return
|
||||
const handlerKey = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1)
|
||||
const handler = col[handlerKey]
|
||||
if (typeof handler === 'function') {
|
||||
handler(value, col, this.formData)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 点击确定按钮
|
||||
* 1. 调用 el-form 的 validate 方法验证所有字段
|
||||
* 2. 验证通过 → emit('submit') 通知父组件执行提交逻辑
|
||||
* 3. 验证失败 → 用 $message.warning 显示第一条错误信息
|
||||
*
|
||||
* 注意:rules 中的 message 由父组件传入时已用 $t() 翻译
|
||||
* 这里只负责显示,翻译在父组件或 i18n 语言包中完成
|
||||
*/
|
||||
onSubmit () {
|
||||
this.$refs.form.validate((valid, invalidFields) => {
|
||||
if (!valid) {
|
||||
const firstKey = Object.keys(invalidFields)[0]
|
||||
if (firstKey && invalidFields[firstKey].length) {
|
||||
const msg = invalidFields[firstKey][0].message
|
||||
this.$message.warning(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
this.$emit('submit')
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭弹框
|
||||
* 1. 重置表单清除验证状态和输入内容
|
||||
* 2. emit update:visible 关闭弹框(.sync 机制)
|
||||
* 3. emit close 通知父组件执行清理逻辑
|
||||
*/
|
||||
onClose () {
|
||||
this.$refs.form && this.$refs.form.resetFields()
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
/**
|
||||
* 手动验证表单,返回 Promise<boolean>
|
||||
* 主要用于父组件需要自行控制验证时机的场景
|
||||
*
|
||||
* 用法:const ok = await this.$refs.dialogForm.validate()
|
||||
*/
|
||||
validate () {
|
||||
return new Promise(resolve => {
|
||||
this.$refs.form.validate(valid => resolve(valid))
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置表单(清除验证状态和输入值)
|
||||
* 通常在打开新增弹框时调用
|
||||
*/
|
||||
reset () {
|
||||
this.$refs.form && this.$refs.form.resetFields()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 表单项间距固定 22px,不受 Element UI 状态覆盖 */
|
||||
/deep/ .el-form-item {
|
||||
margin-bottom: 22px !important;
|
||||
}
|
||||
/* 错误文字下沉到 margin 间隙中,不挤占表单项自身高度 */
|
||||
/deep/ .el-form-item__error {
|
||||
position: absolute;
|
||||
top: auto;
|
||||
bottom: -18px;
|
||||
}
|
||||
/* textarea 对齐顶部 */
|
||||
/deep/ .el-textarea {
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
522
src/components/page-table/index.vue
Normal file
522
src/components/page-table/index.vue
Normal file
@@ -0,0 +1,522 @@
|
||||
<template>
|
||||
<!--
|
||||
page-table — CRUD 表格便捷组合体
|
||||
================================
|
||||
这是一个把「顶部按钮栏 + 数据表格 + 底部分页」打包在一起的高层组件。
|
||||
适合 80% 的增删改查页面,你只需要传列定义、按钮定义、数据和分页参数即可。
|
||||
|
||||
依赖:element-ui 的 <el-table> <el-button> <el-pagination>
|
||||
i18n:由调用方负责翻译,组件直接渲染传入的文本
|
||||
|
||||
@author 前端团队
|
||||
@since 2026-05
|
||||
-->
|
||||
<div
|
||||
ref="root"
|
||||
class="page-table"
|
||||
:class="{ 'page-table--auto': autoHeight || height === 'auto' }"
|
||||
v-loading="loading"
|
||||
>
|
||||
<!-- ==================== 顶部:工具栏按钮区 ==================== -->
|
||||
<!--
|
||||
toolbarButtons 由 useTableButtons({ toolbar: [...] }) 生成
|
||||
内部自动过滤 hasPermission === false 的按钮(无权限不显示)
|
||||
按钮被 disabled 只在前端计算(如 needSelection 需要选中行),实际权限由后端校验
|
||||
-->
|
||||
<div ref="toolbar" class="page-table__toolbar" v-if="toolbarButtons.length || helpUrl">
|
||||
<div class="page-table__toolbar-left">
|
||||
<el-button
|
||||
v-for="btn in visibleToolbarButtons"
|
||||
:key="btn.key"
|
||||
:type="btn.type"
|
||||
:size="btn.size"
|
||||
:icon="btn.icon"
|
||||
:style="btn.cssStyle"
|
||||
:disabled="btn.needSelection && !selectedCount"
|
||||
@click="btn.onClick"
|
||||
>
|
||||
{{ $t(btn.label) }}
|
||||
</el-button>
|
||||
<!-- 自定义工具栏内容:如打印按钮、列筛选器等 -->
|
||||
<slot name="toolbar-extra" />
|
||||
</div>
|
||||
<!-- 帮助按钮:始终显示在工具栏最右侧,点击新窗口打开帮助文档 -->
|
||||
<el-button
|
||||
v-if="helpUrl"
|
||||
class="page-table__help-btn"
|
||||
size="mini"
|
||||
icon="el-icon-question"
|
||||
@click="openHelp"
|
||||
>
|
||||
{{ helpText }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- ==================== 中部:数据表格 ==================== -->
|
||||
<!--
|
||||
height 由外部传入或自动计算(autoHeight=true 时)
|
||||
v-bind="tableAttrs" / v-on="tableListeners" 透传 el-table 原生属性和事件
|
||||
-->
|
||||
<div ref="tableWrapper" class="page-table__body">
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="data"
|
||||
:height="tableHeight"
|
||||
:border="border"
|
||||
:row-key="rowKey"
|
||||
:header-cell-style="headerCellStyle"
|
||||
v-bind="tableAttrs"
|
||||
v-on="tableListeners"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<!--
|
||||
遍历 columns,有五种列类型:
|
||||
1. type='selection' → 复选框列
|
||||
2. type='index' → 序号列
|
||||
3. slot='_actions' → 操作列(自动渲染 rowButtons)
|
||||
4. slot/headerSlot → 自定义插槽列
|
||||
5. 其他 → 普通数据列
|
||||
-->
|
||||
<template v-for="col in columns">
|
||||
<!-- 1. 复选框列 -->
|
||||
<el-table-column
|
||||
v-if="col.type === 'selection'"
|
||||
:key="'sel-' + col.idx"
|
||||
type="selection"
|
||||
:width="col.width"
|
||||
/>
|
||||
<!-- 2. 序号列 -->
|
||||
<el-table-column
|
||||
v-else-if="col.type === 'index'"
|
||||
:key="'idx-' + col.idx"
|
||||
type="index"
|
||||
:width="col.width"
|
||||
:label="$t(col.label) || '#'"
|
||||
/>
|
||||
<!-- 3. 操作列:自动渲染 rowButtons(编辑/删除等行内按钮) -->
|
||||
<!--
|
||||
约定:columns 中 prop='_actions' 的列会被识别为操作列
|
||||
rowButtons 由 useTableButtons({ row: [...] }) 生成
|
||||
红色按钮通过 color='danger' 控制
|
||||
-->
|
||||
<el-table-column
|
||||
v-else-if="col.slot === '_actions' && rowButtons.length"
|
||||
:key="'act-' + col.idx"
|
||||
v-bind="colAttrs(col)"
|
||||
>
|
||||
<template #default="{ row, $index }">
|
||||
<template v-for="btn in visibleRowButtons">
|
||||
<span
|
||||
v-if="btn.visible(row)"
|
||||
:key="btn.key"
|
||||
:style="btn.cssStyle"
|
||||
:class="{ 'action-btn--danger': btn.color === 'danger' }"
|
||||
class="action-btn"
|
||||
@click="btn.onClick(row, $index)"
|
||||
>
|
||||
<i v-if="btn.icon" :class="btn.icon" />
|
||||
{{ $t(btn.label) }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 4. 自定义插槽列:列内容或表头可由父组件自定义 -->
|
||||
<!--
|
||||
使用方式:
|
||||
- col.slot='status' → 父组件用 <template #col-status="{ row }">
|
||||
- col.headerSlot='xyz' → 父组件用 <template #xyz> 自定义表头
|
||||
-->
|
||||
<el-table-column
|
||||
v-else-if="col.slot || col.headerSlot"
|
||||
:key="'slot-' + col.idx"
|
||||
v-bind="colAttrs(col)"
|
||||
>
|
||||
<template #header="{ column }">
|
||||
<slot v-if="col.headerSlot" :name="col.headerSlot" :data="col" :column="column">
|
||||
<span>{{ column.label }}</span>
|
||||
</slot>
|
||||
<span v-else>{{ column.label }}</span>
|
||||
</template>
|
||||
<template #default="{ row, $index }">
|
||||
<slot :name="'col-' + col.slot" :row="row" :index="$index">
|
||||
<span>{{ row[col.prop] }}</span>
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 5. 普通数据列:直接按 prop 取 row 数据 -->
|
||||
<el-table-column
|
||||
v-else
|
||||
:key="'col-' + col.idx"
|
||||
v-bind="colAttrs(col)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 表格无数据时显示的占位内容 -->
|
||||
<template #empty>
|
||||
<slot name="empty" />
|
||||
</template>
|
||||
<!-- 表格最后一行之后追加的内容 -->
|
||||
<template #append>
|
||||
<slot name="append" />
|
||||
</template>
|
||||
<slot />
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- ==================== 底部:分页组件 ==================== -->
|
||||
<!--
|
||||
只传 pagination 对象({ current, size, total })才显示分页
|
||||
page-change 事件回传 { current, size, total },父组件更新 pagination 后重新 fetchData
|
||||
-->
|
||||
<div ref="footer" class="page-table__footer" v-if="pagination">
|
||||
<el-pagination
|
||||
:current-page="pagination.current"
|
||||
:page-size="pagination.size || 10"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[10, 25, 50, 100, 250, 500]"
|
||||
:disabled="loading"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="onSizeChange"
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 自定义尾部内容 -->
|
||||
<slot name="extra" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* PageTable — CRUD 表格便捷组合体
|
||||
*
|
||||
* 【核心职责】
|
||||
* 1. 渲染顶部工具栏(增删改查等操作按钮,自动权限过滤)
|
||||
* 2. 渲染数据表格(5 种列类型,自动 i18n 翻译)
|
||||
* 3. 渲染底部分页
|
||||
* 4. 表格高度自适应(可选)
|
||||
*
|
||||
* 【典型用法】
|
||||
* <page-table
|
||||
* :columns="columns"
|
||||
* :data="tableData"
|
||||
* :loading="loading"
|
||||
* :toolbar-buttons="toolbarButtons"
|
||||
* :row-buttons="rowButtons"
|
||||
* :pagination="pagination"
|
||||
* auto-height
|
||||
* @page-change="onPageChange"
|
||||
* @selection-change="onSelect"
|
||||
* />
|
||||
*
|
||||
* 【内部事件流】
|
||||
* toolbarButtons[i].onClick() → 父组件方法 → fetchData → data 更新 → 表格刷新
|
||||
* el-pagination @change → emit('page-change') → 父组件更新 pagination → fetchData
|
||||
* el-table @selection-change → emit('selection-change') → 父组件获取选中行
|
||||
*
|
||||
* 【与 useTableColumns / useTableButtons 配合】
|
||||
* 详见文档:docs/sct-base-table-refactor-design.md
|
||||
*/
|
||||
export default {
|
||||
name: 'PageTable',
|
||||
props: {
|
||||
/**
|
||||
* 列定义,由 useTableColumns() 生成
|
||||
*
|
||||
* 普通列:{ prop: 'code', label: '编码', minWidth: 120 }
|
||||
* 操作列:{ prop: '_actions', label: '操作', width: 160, fixed: 'right' }
|
||||
* 自定义列:{ prop: 'status', label: '状态', slot: true }
|
||||
*
|
||||
* 组件内部会按 type/slot/prop 自动分类渲染不同的 <el-table-column>
|
||||
*/
|
||||
columns: { type: Array, default: () => [] },
|
||||
|
||||
/**
|
||||
* 表格行数据,直接传接口返回的数组
|
||||
*/
|
||||
data: { type: Array, default: () => [] },
|
||||
|
||||
/**
|
||||
* 是否显示 loading 遮罩,通常在 fetchData 期间为 true
|
||||
*/
|
||||
loading: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 表格高度,不传则 el-table 按内容撑开
|
||||
* 传 'auto' 或同时传 auto-height 启用自动计算(窗口高度 - 搜索区 - 工具栏 - 分页)
|
||||
* 传数字/字符串(如 '500px')则固定高度
|
||||
*/
|
||||
height: { type: [String, Number], default: undefined },
|
||||
|
||||
/**
|
||||
* 启用表格高度自适应:根据容器可视区域自动计算高度
|
||||
* 启用后 height prop 被忽略,组件内部通过 ResizeObserver 持续计算
|
||||
*/
|
||||
autoHeight: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 是否显示表格边框,默认 true
|
||||
*/
|
||||
border: { type: Boolean, default: true },
|
||||
|
||||
/**
|
||||
* 行数据的唯一 key,默认 'id',用于 el-table 的 row-key
|
||||
* 树形表格或需要保留选中状态时必需
|
||||
*/
|
||||
rowKey: { type: String, default: 'id' },
|
||||
|
||||
/**
|
||||
* 顶部工具栏按钮列表,由 useTableButtons({ toolbar: [...] }) 生成
|
||||
*
|
||||
* 每个按钮字段:
|
||||
* key - 唯一标识(必填)
|
||||
* label - 显示文本,支持 i18n key(组件自动 $t() 翻译)
|
||||
* icon - element-ui 图标名('el-icon-plus' 等)
|
||||
* type - 按钮样式类型('primary' | 'success' | 'warning' | 'danger')
|
||||
* auth - 权限 key,传给 useTableButtons 的第二个参数判断
|
||||
* cssStyle - 自定义样式对象
|
||||
* needSelection - true 的话需要表格有选中行才能点击
|
||||
* onClick - 点击回调(由父组件绑定方法)
|
||||
* hasPermission - 自动注入,由 useTableButtons 计算(不要手动设)
|
||||
*/
|
||||
toolbarButtons: { type: Array, default: () => [] },
|
||||
|
||||
/**
|
||||
* 行内操作按钮列表,由 useTableButtons({ row: [...] }) 生成
|
||||
*
|
||||
* 每个按钮字段:
|
||||
* key / label / icon / auth / onClick — 同 toolbarButtons
|
||||
* color - 'danger' 文字变红,用于删除等危险操作
|
||||
* cssStyle - 默认 { marginRight: '10px', cursor: 'pointer' }
|
||||
*/
|
||||
rowButtons: { type: Array, default: () => [] },
|
||||
|
||||
/**
|
||||
* 分页参数,传此 prop 才显示分页组件
|
||||
*
|
||||
* { current: 1, size: 10, total: 0 }
|
||||
*
|
||||
* total 通常在 getList 接口返回 res.count
|
||||
*/
|
||||
pagination: { type: Object, default: null },
|
||||
|
||||
/**
|
||||
* 额外透传给 el-table 的原生属性,如 { stripe: true, size: 'medium' }
|
||||
*/
|
||||
tableAttrs: { type: Object, default: () => ({}) },
|
||||
|
||||
/**
|
||||
* 额外透传给 el-table 的原生事件,如 { 'sort-change': handleSort }
|
||||
*/
|
||||
tableListeners: { type: Object, default: () => ({}) },
|
||||
|
||||
/**
|
||||
* 帮助文档的跳转 URL。传了才显示工具栏右侧的问号按钮,点击新窗口打开
|
||||
* 例:'/docs/factory-area-help.html'
|
||||
*/
|
||||
helpUrl: { type: String, default: '' },
|
||||
|
||||
/**
|
||||
* 帮助按钮的文字,由调用方负责 i18n 翻译
|
||||
* 默认 '帮助'
|
||||
*/
|
||||
helpText: { type: String, default: '帮助' }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tableSelected: [], // 当前选中的行数据
|
||||
computedHeight: null // autoHeight 时动态计算的高度
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 最终使用的表格高度:
|
||||
* - 用户明确传了 height(非 'auto')→ 直接用
|
||||
* - autoHeight=true 或 height='auto' → 用动态计算的 computedHeight
|
||||
* - 否则 undefined(el-table 按内容自适应)
|
||||
*/
|
||||
tableHeight () {
|
||||
if (this.height && this.height !== 'auto') return this.height
|
||||
if (this.autoHeight || this.height === 'auto') return this.computedHeight || undefined
|
||||
return undefined
|
||||
},
|
||||
|
||||
/**
|
||||
* 过滤后的工具栏按钮(排除无权限的)
|
||||
*/
|
||||
visibleToolbarButtons () {
|
||||
return this.toolbarButtons.filter(b => b.hasPermission !== false)
|
||||
},
|
||||
|
||||
/**
|
||||
* 过滤后的行内操作按钮(排除无权限的)
|
||||
*/
|
||||
visibleRowButtons () {
|
||||
return this.rowButtons.filter(b => b.hasPermission !== false)
|
||||
},
|
||||
|
||||
/**
|
||||
* 选中的行数
|
||||
*/
|
||||
selectedCount () {
|
||||
return this.tableSelected.length
|
||||
},
|
||||
|
||||
/**
|
||||
* 表头样式:浅灰背景 + 黑色加粗文字
|
||||
*/
|
||||
headerCellStyle () {
|
||||
return () => 'background-color: #F8F8F8; color: #000000; font-weight: 500;'
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.autoHeight || this.height === 'auto') {
|
||||
this.startAutoHeight()
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.stopAutoHeight()
|
||||
},
|
||||
methods: {
|
||||
/* ============ 列处理 ============ */
|
||||
|
||||
colAttrs (col) {
|
||||
const attrs = { ...col }
|
||||
delete attrs.idx
|
||||
delete attrs.slot
|
||||
delete attrs.headerSlot
|
||||
if (attrs.label) {
|
||||
attrs.label = this.$t(attrs.label)
|
||||
}
|
||||
return attrs
|
||||
},
|
||||
|
||||
/* ============ 表格事件 ============ */
|
||||
|
||||
onSelectionChange (val) {
|
||||
this.tableSelected = val
|
||||
this.$emit('selection-change', val)
|
||||
},
|
||||
|
||||
/* ============ 帮助按钮 ============ */
|
||||
|
||||
openHelp () {
|
||||
if (this.helpUrl) {
|
||||
window.open(this.helpUrl, '_blank')
|
||||
}
|
||||
},
|
||||
|
||||
/* ============ 分页事件 ============ */
|
||||
|
||||
onSizeChange (size) {
|
||||
this.$emit('page-change', {
|
||||
current: 1,
|
||||
size,
|
||||
total: this.pagination.total
|
||||
})
|
||||
},
|
||||
|
||||
onCurrentChange (current) {
|
||||
this.$emit('page-change', {
|
||||
current,
|
||||
size: this.pagination.size,
|
||||
total: this.pagination.total
|
||||
})
|
||||
},
|
||||
|
||||
/* ============ 自适应高度 ============ */
|
||||
|
||||
/**
|
||||
* 启动表格高度自适应
|
||||
*
|
||||
* 原理:
|
||||
* 1. CSS flex 布局让 page-table 占满父容器 100% 高度
|
||||
* 2. 工具栏和分页各占自然高度(flex-shrink: 0)
|
||||
* 3. tableWrapper 中间区域自动填充剩余空间(flex: 1 + min-height: 0)
|
||||
* 4. ResizeObserver 监听 tableWrapper 的实际渲染高度,赋给 el-table
|
||||
*
|
||||
* 这样 el-table 永远精确等于可用空间,不会产生外层滚动条
|
||||
*/
|
||||
startAutoHeight () {
|
||||
this.stopAutoHeight()
|
||||
|
||||
this._resizeObserver = new ResizeObserver(() => {
|
||||
this.$nextTick(() => {
|
||||
const wrapper = this.$refs.tableWrapper
|
||||
if (wrapper) {
|
||||
this.computedHeight = wrapper.clientHeight
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (this.$refs.tableWrapper) {
|
||||
this._resizeObserver.observe(this.$refs.tableWrapper)
|
||||
this.$nextTick(() => {
|
||||
const wrapper = this.$refs.tableWrapper
|
||||
if (wrapper) {
|
||||
this.computedHeight = wrapper.clientHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
stopAutoHeight () {
|
||||
if (this._resizeObserver) {
|
||||
this._resizeObserver.disconnect()
|
||||
this._resizeObserver = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
/* 自适应高度模式:占满父容器 100%,禁止自身溢出 */
|
||||
.page-table--auto {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-table__toolbar {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.page-table__toolbar-left {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.page-table__help-btn {
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
}
|
||||
.page-table__body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
.page-table__footer {
|
||||
flex-shrink: 0;
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.action-btn {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.action-btn--danger {
|
||||
color: #F56C6C;
|
||||
}
|
||||
</style>
|
||||
60
src/composables/useConfirmHandle.js
Normal file
60
src/composables/useConfirmHandle.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* $confirm + API 调用的标准包装 mixin
|
||||
*
|
||||
* 解决问题:$confirm 取消也会 reject,和 API 失败混在同一个 catch 里无法区分
|
||||
*
|
||||
* 调用方无需 try/catch,只需判断返回值:
|
||||
* - 返回 true → 用户取消,静默返回
|
||||
* - 返回 false → action 已执行(成功或失败),拦截器已处理 Message
|
||||
*
|
||||
* @example
|
||||
* import { confirmMixin } from '@/composables/useConfirmHandle'
|
||||
* export default {
|
||||
* mixins: [confirmMixin, i18nMixin('page.xxx')],
|
||||
* methods: {
|
||||
* async handleDelete (row) {
|
||||
* const cancelled = await this.$confirmAction(
|
||||
* { message: this.key('confirm_delete'), title: this.key('tip') },
|
||||
* () => deleteApi({ id: row.id })
|
||||
* )
|
||||
* if (cancelled) return
|
||||
* this.$message.success(this.$t(this.key('operation_success')))
|
||||
* this.fetchData()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export const confirmMixin = {
|
||||
methods: {
|
||||
/**
|
||||
* @param {Object} confirmOpts - { message, title, ...$confirm 其余参数 }
|
||||
* @param {Function} action - 确认后执行的 async 函数
|
||||
* @returns {Promise<boolean>} true = 已取消, false = 已执行
|
||||
*/
|
||||
async $confirmAction (confirmOpts, action) {
|
||||
try {
|
||||
await this.$confirm(
|
||||
confirmOpts.message ? this.$t(confirmOpts.message) : '',
|
||||
confirmOpts.title ? this.$t(confirmOpts.title) : '',
|
||||
{
|
||||
confirmButtonText: this.$t(confirmOpts.confirmButtonText || 'page.common.confirm'),
|
||||
cancelButtonText: this.$t(confirmOpts.cancelButtonText || 'page.common.cancel'),
|
||||
type: confirmOpts.type || 'warning',
|
||||
closeOnClickModal: confirmOpts.closeOnClickModal !== false
|
||||
}
|
||||
)
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
await action()
|
||||
} catch {
|
||||
// 拦截器已弹出 Message
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default confirmMixin
|
||||
28
src/composables/useI18n.js
Normal file
28
src/composables/useI18n.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* i18n Mixin 工厂:消除每个页面重复写 tkey() 方法
|
||||
*
|
||||
* @param {string} prefix 页面的 i18n key 前缀,如 'page.production_master_data.factory_model.factory_area'
|
||||
* @returns {Object} Vue mixin
|
||||
*
|
||||
* @example
|
||||
* import { i18nMixin } from '@/composables/useI18n'
|
||||
* export default {
|
||||
* mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')],
|
||||
* // 模板中当前页面用 $t(key('code'))
|
||||
* // 公共翻译用 $t(ckey('help')) → 'page.common.help'
|
||||
* }
|
||||
*/
|
||||
export function i18nMixin (prefix) {
|
||||
return {
|
||||
methods: {
|
||||
key (suffix) {
|
||||
return prefix + '.' + suffix
|
||||
},
|
||||
ckey (suffix) {
|
||||
return 'page.common.' + suffix
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default i18nMixin
|
||||
49
src/composables/useTableButtons.js
Normal file
49
src/composables/useTableButtons.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 按钮定义工具:消除 buttonList / tableButtonList 重复定义
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Array} options.toolbar 顶部工具栏按钮
|
||||
* @param {Array} options.row 行内操作按钮
|
||||
* @param {Function} permissionCheck 权限校验函数,默认 $permission
|
||||
* @returns {{ toolbarButtons: Array, rowButtons: Array }}
|
||||
*
|
||||
* @example
|
||||
* import useTableButtons from '@/composables/useTableButtons'
|
||||
* // 在组件 data() 或 created() 中调用
|
||||
* const { toolbarButtons, rowButtons } = useTableButtons({
|
||||
* toolbar: [{ key: 'add', label: '新建', type: 'primary', auth: '/xxx/create', onClick: this.openDialog }],
|
||||
* row: [{ key: 'edit', label: '编辑', auth: '/xxx/edit', onClick: this.handleEdit }],
|
||||
* }, this.$permission)
|
||||
*/
|
||||
export function useTableButtons (options = {}, permissionCheck) {
|
||||
const check = permissionCheck || (() => true)
|
||||
|
||||
const toolbarButtons = (options.toolbar || []).map(btn => ({
|
||||
key: btn.key || btn.label,
|
||||
label: btn.label,
|
||||
icon: btn.icon,
|
||||
type: btn.type || '',
|
||||
size: btn.size || 'mini',
|
||||
auth: btn.auth,
|
||||
cssStyle: btn.cssStyle || {},
|
||||
onClick: btn.onClick,
|
||||
hasPermission: btn.auth ? check(btn.auth) : true
|
||||
}))
|
||||
|
||||
const rowButtons = (options.row || []).map(btn => ({
|
||||
key: btn.key || btn.label,
|
||||
label: btn.label,
|
||||
icon: btn.icon,
|
||||
color: btn.color || '',
|
||||
cssStyle: btn.cssStyle || { marginRight: '10px', cursor: 'pointer' },
|
||||
auth: btn.auth,
|
||||
confirm: btn.confirm || false,
|
||||
onClick: btn.onClick,
|
||||
hasPermission: btn.auth ? check(btn.auth) : true,
|
||||
visible: btn.visible || (() => true)
|
||||
}))
|
||||
|
||||
return { toolbarButtons, rowButtons }
|
||||
}
|
||||
|
||||
export default useTableButtons
|
||||
46
src/composables/useTableColumns.js
Normal file
46
src/composables/useTableColumns.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 列定义工具:消除手动分配 idx,支持 _actions 约定
|
||||
*
|
||||
* @param {Array<Object>} rawColumns 列配置数组
|
||||
* @param {Object} options
|
||||
* @param {number} options.selectionWidth 复选框列宽度,默认 55,传 0 不显示
|
||||
* @param {number} options.indexWidth 序号列宽度,默认 60,传 0 不显示
|
||||
* @returns {Array<Object>} 补齐 idx 后的列数组
|
||||
*
|
||||
* @example
|
||||
* const columns = useTableColumns([
|
||||
* { prop: 'code', label: '编码', minWidth: 120 },
|
||||
* { prop: 'name', label: '名称' },
|
||||
* { prop: '_actions', label: '操作', width: 180, fixed: 'right' },
|
||||
* ])
|
||||
*/
|
||||
export function useTableColumns (rawColumns, options = {}) {
|
||||
const { selectionWidth = 55, indexWidth = 0 } = options
|
||||
|
||||
const result = []
|
||||
|
||||
if (selectionWidth > 0) {
|
||||
result.push({ idx: 0, type: 'selection', width: selectionWidth })
|
||||
}
|
||||
if (indexWidth > 0) {
|
||||
result.push({ idx: result.length, type: 'index', width: indexWidth, label: '#' })
|
||||
}
|
||||
|
||||
let slotIdx = 100
|
||||
rawColumns.forEach((col, i) => {
|
||||
const clean = { ...col }
|
||||
if (clean.prop === '_actions') {
|
||||
clean.slot = '_actions'
|
||||
clean.idx = slotIdx++
|
||||
} else if (clean.slot || clean.headerSlot) {
|
||||
clean.idx = slotIdx++
|
||||
} else {
|
||||
clean.idx = result.length
|
||||
}
|
||||
result.push(clean)
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export default useTableColumns
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip effect="dark" :content="active ? '退出全屏' : '全屏'" placement="bottom">
|
||||
<el-tooltip effect="dark" :content="active ? $t('page.layout.fullscreen.exit') : $t('page.layout.fullscreen.enter')" placement="bottom">
|
||||
<el-button class="d2-mr btn-text can-hover" type="text" @click="toggle">
|
||||
<d2-icon v-if="active" name="compress"/>
|
||||
<d2-icon v-else name="arrows-alt" style="font-size: 16px"/>
|
||||
|
||||
@@ -19,10 +19,8 @@ export default {
|
||||
}),
|
||||
tooltipContent () {
|
||||
return this.logLength === 0
|
||||
? '没有日志或异常'
|
||||
: `${this.logLength} 条日志${this.logLengthError > 0
|
||||
? ` | 包含 ${this.logLengthError} 个异常`
|
||||
: ''}`
|
||||
? this.$t('page.layout.log.no_log')
|
||||
: this.$t('page.layout.log.tooltip', { count: this.logLength, error: this.logLengthError })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<d2-icon name="font" style="font-size: 16px;"/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="item in options" :key="item.value" :command="item.value">
|
||||
<el-dropdown-item v-for="item in sizeOptions" :key="item.value" :command="item.value">
|
||||
<d2-icon :name="iconName(item.value)" class="d2-mr-5"/>{{item.label}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
@@ -12,37 +12,32 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations, mapActions } from 'vuex'
|
||||
import { mapState, mapActions } from 'vuex'
|
||||
export default {
|
||||
name: 'd2-header-size',
|
||||
data () {
|
||||
return {
|
||||
options: [
|
||||
{ label: '默认', value: 'default' },
|
||||
{ label: '中', value: 'medium' },
|
||||
{ label: '小', value: 'small' },
|
||||
{ label: '最小', value: 'mini' }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('d2admin/size', [
|
||||
'value'
|
||||
])
|
||||
]),
|
||||
sizeOptions () {
|
||||
return [
|
||||
{ label: this.$t('page.layout.size.default'), value: 'default' },
|
||||
{ label: this.$t('page.layout.size.medium'), value: 'medium' },
|
||||
{ label: this.$t('page.layout.size.small'), value: 'small' },
|
||||
{ label: this.$t('page.layout.size.mini'), value: 'mini' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
pageKeepAliveClean: 'd2admin/page/keepAliveClean'
|
||||
}),
|
||||
...mapActions({
|
||||
sizeSet: 'd2admin/size/set'
|
||||
}),
|
||||
handleChange (value) {
|
||||
this.sizeSet(value)
|
||||
this.$notify({
|
||||
title: '提示',
|
||||
title: this.$t('page.layout.size.notification_title'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: '已更新页面内 <b>组件</b> 的 <b>默认尺寸</b><br/>例如按钮大小,<b>非字号</b>',
|
||||
message: this.$t('page.layout.size.notification_message'),
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<el-table :data="list" v-bind="table">
|
||||
<el-table-column prop="title" align="center" width="160"/>
|
||||
<el-table-column label="预览" width="120">
|
||||
<el-table-column :label="$t('page.layout.theme.preview')" width="120">
|
||||
<div slot-scope="scope" class="theme-preview" :style="{ backgroundImage: `url(${$baseUrl}${scope.row.preview})` }"/>
|
||||
</el-table-column>
|
||||
<el-table-column prop="address" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="activeName === scope.row.name" type="success" icon="el-icon-check" round>已激活</el-button>
|
||||
<el-button v-else round @click="handleSelectTheme(scope.row.name)">使用</el-button>
|
||||
<el-button v-if="activeName === scope.row.name" type="success" icon="el-icon-check" round>{{ $t('page.layout.theme.active') }}</el-button>
|
||||
<el-button v-else round @click="handleSelectTheme(scope.row.name)">{{ $t('page.common.use') }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
content="主题"
|
||||
:content="$t('page.layout.theme.title')"
|
||||
placement="bottom">
|
||||
<el-button
|
||||
class="d2-ml-0 d2-mr btn-text can-hover"
|
||||
@@ -14,7 +14,7 @@
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-dialog
|
||||
title="主题"
|
||||
:title="$t('page.layout.theme.title')"
|
||||
width="600px"
|
||||
:visible.sync="dialogVisible"
|
||||
:append-to-body="true">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<el-dropdown size="small" class="d2-mr">
|
||||
<span class="btn-text">{{info.name ? `你好 ${info.name}` : '未登录'}}</span>
|
||||
<span class="btn-text">{{info.name ? $t('page.layout.user.greeting', { name: info.name }) : $t('page.layout.user.not_logged_in')}}</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="logOff">
|
||||
<d2-icon name="power-off" class="d2-mr-5"/>
|
||||
注销
|
||||
{{ $t('page.layout.user.logout') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
@@ -12,7 +12,7 @@ export function elMenuItem (h, menu) {
|
||||
key={ menu.path }
|
||||
index={ menu.path }>
|
||||
{ icon }
|
||||
<span slot="title">{ menu.title || '未命名菜单' }</span>
|
||||
<span slot="title">{ menu.title ? this.$t(menu.title) : this.$t('page.layout.menu.unnamed_menu') }</span>
|
||||
</el-menu-item>
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function elSubmenu (h, menu) {
|
||||
key={ menu.path }
|
||||
index={ menu.path }>
|
||||
{ icon }
|
||||
<span slot="title">{ menu.title || '未命名菜单' }</span>
|
||||
<span slot="title">{ menu.title ? this.$t(menu.title) : this.$t('page.layout.menu.unnamed_menu') }</span>
|
||||
{ menu.children.map(child => createMenu.call(this, h, child)) }
|
||||
</el-submenu>
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default {
|
||||
this.aside.length === 0 && !this.asideCollapse
|
||||
? <div class="d2-layout-header-aside-menu-empty" flex="dir:top main:center cross:center">
|
||||
<d2-icon name="inbox"/>
|
||||
<span>没有侧栏菜单</span>
|
||||
<span>{ this.$t('page.layout.menu.no_sidebar') }</span>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
|
||||
@@ -1,16 +1,51 @@
|
||||
import util from '@/libs/util.js'
|
||||
import { frameInRoutes } from '@/router/routes'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
handleMenuSelect (index, indexPath) {
|
||||
if (/^d2-menu-empty-\d+$/.test(index) || index === undefined) {
|
||||
this.$message.warning('临时菜单')
|
||||
this.$message.warning(this.$t('page.layout.menu.temp_menu'))
|
||||
} else if (/^https:\/\/|http:\/\//.test(index)) {
|
||||
util.open(index)
|
||||
} else {
|
||||
this.$router.push({
|
||||
path: index
|
||||
})
|
||||
this.getRouterAuthPath(index, indexPath)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 点击菜单时进行权限过滤,查找第一个有权限的子路由
|
||||
* @param {String} index 选中菜单项的 index (path)
|
||||
* @param {Array} indexPath 选中菜单项的 index path
|
||||
*/
|
||||
getRouterAuthPath (index, indexPath) {
|
||||
// 首页或子级路由直接访问
|
||||
if (index === '/index' || !indexPath || indexPath.length > 1) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
// 查找一级路由下是否存在子路由
|
||||
let router = null
|
||||
for (const value of frameInRoutes) {
|
||||
if (value.path === index) {
|
||||
router = value.children
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 不存在子路由则直接进入
|
||||
if (!router) {
|
||||
this.$router.push({ path: index })
|
||||
return
|
||||
}
|
||||
|
||||
// 存在子路由时,跳转第一个有权限的子页面
|
||||
for (const value of router) {
|
||||
const newPath = index + '/' + value.path
|
||||
if (this.$permission(newPath)) {
|
||||
this.$router.push({ path: newPath })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
ref="input"
|
||||
v-model="searchText"
|
||||
suffix-icon="el-icon-search"
|
||||
placeholder="搜索页面"
|
||||
:placeholder="$t('page.layout.search.placeholder')"
|
||||
:fetch-suggestions="querySearch"
|
||||
:trigger-on-focus="false"
|
||||
:clearable="true"
|
||||
@@ -15,13 +15,7 @@
|
||||
@select="handleSelect">
|
||||
<d2-panel-search-item slot-scope="{ item }" :item="item"/>
|
||||
</el-autocomplete>
|
||||
<div class="panel-search__tip">
|
||||
您可以使用快捷键
|
||||
<span class="panel-search__key">{{hotkey.open}}</span>
|
||||
唤醒搜索面板,按
|
||||
<span class="panel-search__key">{{hotkey.close}}</span>
|
||||
关闭
|
||||
</div>
|
||||
<div class="panel-search__tip" v-html="$t('page.layout.search.tip', { open: hotkey.open, close: hotkey.close })"></div>
|
||||
</div>
|
||||
<div v-if="resultsList.length > 0" class="panel-search__results-group" flex-box="1">
|
||||
<el-card shadow="never">
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<el-tab-pane
|
||||
v-for="page in opened"
|
||||
:key="page.fullPath"
|
||||
:label="page.meta.title || '未命名'"
|
||||
:label="page.meta.title || $t('page.layout.tabs.unnamed')"
|
||||
:name="page.fullPath"
|
||||
:closable="isTabClosable(page)"/>
|
||||
</el-tabs>
|
||||
@@ -36,19 +36,19 @@
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="left">
|
||||
<d2-icon name="arrow-left" class="d2-mr-10"/>
|
||||
关闭左侧
|
||||
{{ $t('page.layout.tabs.close_left') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="right">
|
||||
<d2-icon name="arrow-right" class="d2-mr-10"/>
|
||||
关闭右侧
|
||||
{{ $t('page.layout.tabs.close_right') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="other">
|
||||
<d2-icon name="times" class="d2-mr-10"/>
|
||||
关闭其它
|
||||
{{ $t('page.layout.tabs.close_other') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="all">
|
||||
<d2-icon name="times-circle" class="d2-mr-10"/>
|
||||
全部关闭
|
||||
{{ $t('page.layout.tabs.close_all') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
@@ -70,16 +70,6 @@ export default {
|
||||
contextmenuFlag: false,
|
||||
contentmenuX: 0,
|
||||
contentmenuY: 0,
|
||||
contextmenuListIndex: [
|
||||
{ icon: 'times-circle', title: '关闭全部', value: 'all' }
|
||||
],
|
||||
contextmenuList: [
|
||||
{ icon: 'refresh', title: '刷新', value: 'refresh' },
|
||||
{ icon: 'arrow-left', title: '关闭左侧', value: 'left' },
|
||||
{ icon: 'arrow-right', title: '关闭右侧', value: 'right' },
|
||||
{ icon: 'times', title: '关闭其它', value: 'other' },
|
||||
{ icon: 'times-circle', title: '关闭全部', value: 'all' }
|
||||
],
|
||||
tagName: '/index'
|
||||
}
|
||||
},
|
||||
@@ -87,7 +77,21 @@ export default {
|
||||
...mapState('d2admin/page', [
|
||||
'opened',
|
||||
'current'
|
||||
])
|
||||
]),
|
||||
contextmenuListIndex () {
|
||||
return [
|
||||
{ icon: 'times-circle', title: this.$t('page.layout.tabs.close_all'), value: 'all' }
|
||||
]
|
||||
},
|
||||
contextmenuList () {
|
||||
return [
|
||||
{ icon: 'refresh', title: this.$t('page.layout.tabs.refresh'), value: 'refresh' },
|
||||
{ icon: 'arrow-left', title: this.$t('page.layout.tabs.close_left'), value: 'left' },
|
||||
{ icon: 'arrow-right', title: this.$t('page.layout.tabs.close_right'), value: 'right' },
|
||||
{ icon: 'times', title: this.$t('page.layout.tabs.close_other'), value: 'other' },
|
||||
{ icon: 'times-circle', title: this.$t('page.layout.tabs.close_all'), value: 'all' }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('d2admin/page', [
|
||||
@@ -148,7 +152,7 @@ export default {
|
||||
case 'right': this.closeRight(params); break
|
||||
case 'other': this.closeOther(params); break
|
||||
case 'all': this.closeAll(); break
|
||||
default: this.$message.error('无效的操作'); break
|
||||
default: this.$message.error(this.$t('page.layout.tabs.invalid_operation')); break
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,35 @@ const util = {
|
||||
log
|
||||
}
|
||||
|
||||
/**
|
||||
* 扁平数组转树形结构
|
||||
* @param {Array} data 扁平数组
|
||||
* @param {String} key 主键字段名
|
||||
* @param {String} pid 父节点字段名
|
||||
*/
|
||||
util.formatDataToTree = (data, key = 'menu_id', pid = 'parent_id') => {
|
||||
if (!data || data.length <= 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const map = {}
|
||||
data.forEach(value => { map[value[key]] = { ...value } })
|
||||
|
||||
const tree = []
|
||||
data.forEach(item => {
|
||||
const id = item[key]
|
||||
if (map[id][pid] && map[map[id][pid]]) {
|
||||
if (!map[map[id][pid]].children) {
|
||||
map[map[id][pid]].children = []
|
||||
}
|
||||
map[map[id][pid]].children.push(map[id])
|
||||
} else {
|
||||
tree.push(map[id])
|
||||
}
|
||||
})
|
||||
return tree
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 更新标题
|
||||
* @param {String} title 标题
|
||||
|
||||
1118
src/locales/en.json
1118
src/locales/en.json
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"_element": "ja",
|
||||
"_name": "日本語",
|
||||
"page": {
|
||||
"demo": {
|
||||
"playground": {
|
||||
"locales": {
|
||||
"text": "D2Adminは、最新のフロントエンドテクノロジースタックを使用した、完全にオープンソースの無料エンタープライズバックエンド製品フロントエンド統合ソリューションであり、プロジェクトのほとんどの準備を整えており、システムのアジャイル開発の管理に役立つ多くのサンプルコードを備えています。"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,20 @@ export default {
|
||||
methods: {
|
||||
onChangeLocale (command) {
|
||||
this.$i18n.locale = command
|
||||
let message = `当前语言:${this.$t('_name')} [ ${this.$i18n.locale} ]`
|
||||
let message = this.$t('page.layout.locales.changed', { name: this.$t('_name'), locale: this.$i18n.locale })
|
||||
if (process.env.VUE_APP_BUILD_MODE === 'PREVIEW') {
|
||||
message = [
|
||||
`当前语言:${this.$t('_name')} [ ${this.$i18n.locale} ]`,
|
||||
'仅提供切换功能,没有配置具体的语言数据 ',
|
||||
'文档参考:<a class="el-link el-link--primary is-underline" target="_blank" href="https://d2.pub/doc/d2-admin/locales">《国际化 | D2Admin》</a>'
|
||||
this.$t('page.layout.locales.changed', { name: this.$t('_name'), locale: this.$i18n.locale }),
|
||||
this.$t('page.layout.locales.preview_warning'),
|
||||
this.$t('page.layout.locales.preview_doc')
|
||||
].join('<br/>')
|
||||
}
|
||||
this.$notify({
|
||||
title: '语言变更',
|
||||
title: this.$t('page.layout.locales.notification_title'),
|
||||
dangerouslyUseHTMLString: true,
|
||||
message
|
||||
})
|
||||
this.$store.dispatch('d2admin/menu/menuReload')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"_element": "zh-TW",
|
||||
"_name": "繁體中文",
|
||||
"page": {
|
||||
"demo": {
|
||||
"playground": {
|
||||
"locales": {
|
||||
"text": "D2Admin 是一個完全 開源免費 的企業中後台產品前端集成方案,使用最新的前端技術棧,已經做好大部分項目前期準備工作,並且帶有大量示例代碼,助力管理系統敏捷開發。"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/main.js
29
src/main.js
@@ -9,12 +9,20 @@ import store from '@/store/index'
|
||||
|
||||
// 菜单和路由设置
|
||||
import router from './router'
|
||||
import { menuHeader, menuAside } from '@/menu'
|
||||
import { frameInRoutes } from '@/router/routes'
|
||||
|
||||
// 核心插件
|
||||
Vue.use(d2Admin)
|
||||
|
||||
// 权限控制指令
|
||||
Vue.directive('permission', {
|
||||
bind: (el, binding) => {
|
||||
if (!Vue.prototype.$permission(binding.value)) {
|
||||
el.parentNode ? el.parentNode.removeChild(el) : el.style.display = 'none'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
@@ -23,31 +31,16 @@ new Vue({
|
||||
created () {
|
||||
// 处理路由 得到每一级的路由设置
|
||||
this.$store.commit('d2admin/page/init', frameInRoutes)
|
||||
// 设置顶栏菜单
|
||||
this.$store.commit('d2admin/menu/headerSet', menuHeader)
|
||||
// 初始化菜单搜索功能
|
||||
this.$store.commit('d2admin/search/init', menuHeader)
|
||||
// 静态菜单和搜索由 account.load → sourceDataLoad 从后端动态加载
|
||||
},
|
||||
mounted () {
|
||||
// 展示系统信息
|
||||
this.$store.commit('d2admin/releases/versionShow')
|
||||
// 用户登录后从数据库加载一系列的设置
|
||||
// 用户登录后从数据库加载一系列的设置(含菜单)
|
||||
this.$store.dispatch('d2admin/account/load')
|
||||
// 获取并记录用户 UA
|
||||
this.$store.commit('d2admin/ua/get')
|
||||
// 初始化全屏监听
|
||||
this.$store.dispatch('d2admin/fullscreen/listen')
|
||||
},
|
||||
watch: {
|
||||
// 检测路由变化切换侧边栏内容
|
||||
'$route.matched': {
|
||||
handler (matched) {
|
||||
if (matched.length > 0) {
|
||||
const _side = menuAside.filter(menu => menu.path === matched[0].path)
|
||||
this.$store.commit('d2admin/menu/asideSet', _side.length > 0 ? _side[0].children : [])
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
}
|
||||
}).$mount('#app')
|
||||
|
||||
@@ -1,42 +1,53 @@
|
||||
import { uniqueId } from 'lodash'
|
||||
|
||||
// 插件
|
||||
import demoPlugins from './modules/demo-plugins'
|
||||
// 组件
|
||||
import demoComponents from './modules/demo-components'
|
||||
// 功能
|
||||
import demoPlayground from './modules/demo-playground'
|
||||
import util from '@/libs/util'
|
||||
|
||||
/**
|
||||
* @description 给菜单数据补充上 path 字段
|
||||
* @description https://github.com/d2-projects/d2-admin/issues/209
|
||||
* @param {Array} menu 原始的菜单数据
|
||||
* 将后端返回的扁平菜单数组转为 { header: [], aside: [] } 树结构
|
||||
* @param {Array} arr 后端返回的扁平菜单数据
|
||||
*/
|
||||
function supplementPath (menu) {
|
||||
return menu.map(e => ({
|
||||
...e,
|
||||
path: e.path || uniqueId('d2-menu-empty-'),
|
||||
...e.children ? {
|
||||
children: supplementPath(e.children)
|
||||
} : {}
|
||||
}))
|
||||
function getMenuData (arr) {
|
||||
const tree = { header: [], aside: [] }
|
||||
|
||||
arr.forEach(value => {
|
||||
if (!value.is_navi) {
|
||||
return
|
||||
}
|
||||
|
||||
// 菜单 侧边栏
|
||||
export const menuAside = supplementPath([
|
||||
demoComponents,
|
||||
demoPlugins,
|
||||
demoPlayground
|
||||
])
|
||||
const menuItem = {
|
||||
path: value.url || ('d2-menu-empty-' + value.menu_id),
|
||||
title: value.name,
|
||||
icon: value.icon,
|
||||
type: value.type,
|
||||
params: value.params,
|
||||
remark: value.remark || ''
|
||||
}
|
||||
|
||||
// 菜单 顶栏
|
||||
export const menuHeader = supplementPath([
|
||||
{
|
||||
path: '/index',
|
||||
title: '首页',
|
||||
icon: 'home'
|
||||
},
|
||||
demoPlayground,
|
||||
demoComponents,
|
||||
demoPlugins
|
||||
])
|
||||
// parent_id 为 0 的节点放入顶栏
|
||||
if (value.parent_id === 0) {
|
||||
tree.header.push({ ...menuItem })
|
||||
}
|
||||
|
||||
// 所有节点保留 menu_id / parent_id 用于构建侧栏树形结构
|
||||
menuItem.menu_id = value.menu_id
|
||||
menuItem.parent_id = value.parent_id
|
||||
tree.aside.push(menuItem)
|
||||
})
|
||||
|
||||
// 扁平 aside 数组转为嵌套树
|
||||
tree.aside = util.formatDataToTree(tree.aside)
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 由 sourceDataLoad 调用,将菜单源数据写入 store
|
||||
* @param {Object} vm vuex store 实例 (this)
|
||||
* @param {Array} source 后端返回的扁平菜单数组
|
||||
*/
|
||||
install (vm, source) {
|
||||
vm.commit('d2admin/menu/headerAuth', source)
|
||||
const { header, aside } = getMenuData(source)
|
||||
vm.commit('d2admin/menu/headerSet', header)
|
||||
vm.commit('d2admin/menu/asideSet', aside)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
export default {
|
||||
path: '/demo/components',
|
||||
title: '组件',
|
||||
icon: 'puzzle-piece',
|
||||
children: [
|
||||
{ path: '/demo/components/index', title: '扩展组件', icon: 'home' },
|
||||
{
|
||||
path: '/demo/components/container',
|
||||
title: '布局容器',
|
||||
icon: 'window-restore',
|
||||
children: [
|
||||
{
|
||||
title: '填充型',
|
||||
children: [
|
||||
{ path: '/demo/components/container/full', title: '基础', icon: '' },
|
||||
{ path: '/demo/components/container/full-slot', title: '插槽', icon: '' },
|
||||
{ path: '/demo/components/container/full-bs', title: '滚动优化', icon: '' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '隐形模式',
|
||||
children: [
|
||||
{ path: '/demo/components/container/ghost', title: '基础', icon: '' },
|
||||
{ path: '/demo/components/container/ghost-slot', title: '插槽', icon: '' },
|
||||
{ path: '/demo/components/container/ghost-bs', title: '滚动优化', icon: '' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '卡片型',
|
||||
children: [
|
||||
{ path: '/demo/components/container/card', title: '基础', icon: '' },
|
||||
{ path: '/demo/components/container/card-slot', title: '插槽', icon: '' },
|
||||
{ path: '/demo/components/container/card-bs', title: '滚动优化', icon: '' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '方法',
|
||||
children: [
|
||||
{ path: '/demo/components/container/api?bs=false', title: '滚动控制', icon: '' },
|
||||
{ path: '/demo/components/container/api?bs=true', title: '滚动控制 BS', icon: '' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/components/layout/grid',
|
||||
title: '高级布局',
|
||||
icon: 'tasks',
|
||||
children: [
|
||||
{ path: '/demo/components/layout/grid', title: '拖拽位置和大小' },
|
||||
{ path: '/demo/components/layout/splitpane', title: '区域划分' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/components/editor',
|
||||
title: '编辑器',
|
||||
icon: 'pencil-square-o',
|
||||
children: [
|
||||
{ path: '/demo/components/editor-ueditor', title: 'UEditor', icon: '' },
|
||||
{ path: '/demo/components/editor-quill', title: 'Quill', icon: '' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/components/icon',
|
||||
title: '图标',
|
||||
icon: 'star',
|
||||
children: [
|
||||
{ path: '/demo/components/icon/icon', title: '图标组件' },
|
||||
{ path: '/demo/components/icon/icon-svg', title: 'svg 图标组件' },
|
||||
{ path: '/demo/components/icon/select', title: '图标选择器' },
|
||||
{ path: '/demo/components/icon/select-svg', title: 'svg 图标选择器' },
|
||||
{ path: '/demo/components/icon/list', title: 'FontAwesome' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/components/markdown',
|
||||
title: 'markdown 解析',
|
||||
icon: 'file-text-o',
|
||||
children: [
|
||||
{ path: '/demo/components/markdown/source', title: '指定资源' },
|
||||
{ path: '/demo/components/markdown/url', title: '异步加载文件' }
|
||||
]
|
||||
},
|
||||
{ path: '/demo/components/countup', title: '数字动画', icon: 'motorcycle' },
|
||||
{ path: '/demo/components/highlight', title: '代码高亮显示', icon: 'code' },
|
||||
{ path: '/demo/components/json-tree', title: 'JSON 展示', icon: 'sitemap' }
|
||||
]
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
export default {
|
||||
path: '/demo/playground',
|
||||
title: '功能',
|
||||
icon: 'flask',
|
||||
children: [
|
||||
{ path: '/demo/playground/index', title: '功能', icon: 'home' },
|
||||
{
|
||||
title: 'svg 菜单图标',
|
||||
iconSvg: 'd2-admin',
|
||||
children: [
|
||||
{ title: 'add', iconSvg: 'add' },
|
||||
{ title: 'alarm', iconSvg: 'alarm' },
|
||||
{ title: 'camera', iconSvg: 'camera' },
|
||||
{ title: 'history', iconSvg: 'history' },
|
||||
{ title: 'like', iconSvg: 'like' },
|
||||
{ title: 'love', iconSvg: 'love' },
|
||||
{ title: 'message', iconSvg: 'message' },
|
||||
{ title: 'notice', iconSvg: 'notice' },
|
||||
{ title: 'search', iconSvg: 'search' },
|
||||
{ title: 'share', iconSvg: 'share' },
|
||||
{ title: 'star', iconSvg: 'star' },
|
||||
{ title: 'user', iconSvg: 'user' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '空菜单演示',
|
||||
icon: 'folder-o',
|
||||
children: [
|
||||
{
|
||||
title: '正在开发 1',
|
||||
children: [
|
||||
{ title: '正在开发 1-1' },
|
||||
{ title: '正在开发 1-2' }
|
||||
]
|
||||
},
|
||||
{ title: '正在开发 2' },
|
||||
{ title: '正在开发 3' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/frame',
|
||||
title: '内嵌网页',
|
||||
icon: 'globe',
|
||||
children: [
|
||||
{ path: '/demo/playground/frame/d2-doc', title: 'D2Admin 中文文档', iconSvg: 'd2-admin' },
|
||||
{ path: '/demo/playground/frame/html', title: '静态 HTML', icon: 'code' },
|
||||
{ path: '/demo/playground/frame/report', title: '构建分析', icon: 'pie-chart' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '新窗口打开链接',
|
||||
icon: 'link',
|
||||
children: [
|
||||
{ path: 'https://github.com/d2-projects/d2-admin', title: 'D2Admin Github', icon: 'github' },
|
||||
{ path: 'https://juejin.im/user/57a48b632e958a006691b946/posts', title: '掘金', icon: 'globe' },
|
||||
{ path: 'https://my.oschina.net/u/3871516', title: '开源中国', icon: 'globe' },
|
||||
{ path: 'https://www.zhihu.com/people/fairy-ever/activities', title: '知乎', icon: 'globe' },
|
||||
{ path: 'https://segmentfault.com/blog/liyang-note-book', title: 'segmentfault 专栏', icon: 'globe' },
|
||||
{ path: 'http://www.fairyever.com/', title: 'www.fairyever.com', icon: 'globe' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/store',
|
||||
title: '全局状态管理',
|
||||
icon: 'bolt',
|
||||
children: [
|
||||
{ path: '/demo/playground/store/page', title: '多标签页控制', icon: 'window-restore' },
|
||||
{ path: '/demo/playground/store/menu', title: '菜单控制', icon: 'bars' },
|
||||
{ path: '/demo/playground/store/size', title: '全局尺寸', icon: 'font' },
|
||||
{ path: '/demo/playground/store/ua', title: '浏览器信息', icon: 'info-circle' },
|
||||
{ path: '/demo/playground/store/gray', title: '灰度模式', icon: 'eye' },
|
||||
{ path: '/demo/playground/store/fullscreen', title: '全屏', icon: 'arrows-alt' },
|
||||
{ path: '/demo/playground/store/theme', title: '主题', icon: 'diamond' },
|
||||
{ path: '/demo/playground/store/transition', title: '页面过渡开关', icon: 'toggle-on' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/page-cache',
|
||||
title: '页面缓存',
|
||||
icon: 'hdd-o',
|
||||
children: [
|
||||
{ path: '/demo/playground/page-cache/on', title: '开启缓存' },
|
||||
{ path: '/demo/playground/page-cache/off', title: '关闭缓存' },
|
||||
{ path: '/demo/playground/page-cache/params/1', title: '带参路由缓存 1' },
|
||||
{ path: '/demo/playground/page-cache/params/2', title: '带参路由缓存 2' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/page-argu',
|
||||
title: '参数传递和留存',
|
||||
icon: 'assistive-listening-systems',
|
||||
children: [
|
||||
{ path: '/demo/playground/page-argu/send', title: '发送' },
|
||||
{ path: '/demo/playground/page-argu/get/username-from-menu?userid=userid-from-menu', title: '接收' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/db',
|
||||
title: '数据持久化',
|
||||
icon: 'database',
|
||||
children: [
|
||||
{ path: '/demo/playground/db/all', title: '总览', icon: 'table' },
|
||||
{ path: '/demo/playground/db/public', title: '公共存储', icon: 'users' },
|
||||
{ path: '/demo/playground/db/user', title: '私有数据', icon: 'user' },
|
||||
{ path: '/demo/playground/db/page-public', title: '路由存储', icon: 'file-o' },
|
||||
{ path: '/demo/playground/db/page-user', title: '私有路由存储', icon: 'file-o' },
|
||||
{ path: '/demo/playground/db/page-snapshot-public', title: '路由快照', icon: 'file' },
|
||||
{ path: '/demo/playground/db/page-snapshot-user', title: '私有路由快照', icon: 'file' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/log',
|
||||
title: '日志',
|
||||
icon: 'bullseye',
|
||||
children: [
|
||||
{ path: '/demo/playground/log/log', title: '日志记录', icon: 'dot-circle-o' },
|
||||
{ path: '/demo/playground/log/error', title: '错误捕捉', icon: 'bug' },
|
||||
{ path: '/demo/playground/log/ajax', title: 'Ajax 错误', icon: 'bug' },
|
||||
{ path: '/demo/playground/log/console', title: '控制台日志', icon: 'lightbulb-o' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/playground/add-routes',
|
||||
title: '动态添加路由',
|
||||
icon: 'plus-square',
|
||||
children: [
|
||||
{ path: '/demo/playground/add-routes/routes', title: '添加页面', icon: 'file-o' }
|
||||
]
|
||||
},
|
||||
{ path: '/demo/playground/env', title: '环境信息', icon: 'exclamation-circle' },
|
||||
{ path: '/demo/playground/locales', title: '国际化', icon: 'language' }
|
||||
]
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
export default {
|
||||
path: '/demo/plugins',
|
||||
title: '插件',
|
||||
icon: 'plug',
|
||||
children: [
|
||||
{ path: '/demo/plugins/index', title: '插件', icon: 'home' },
|
||||
{
|
||||
path: '/demo/plugins/import',
|
||||
title: '导入',
|
||||
icon: 'download',
|
||||
children: [
|
||||
{ path: '/demo/plugins/import/csv', title: 'csv' },
|
||||
{ path: '/demo/plugins/import/xlsx', title: 'xlsx' }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/demo/plugins/export',
|
||||
title: '导出',
|
||||
icon: 'upload',
|
||||
children: [
|
||||
{ path: '/demo/plugins/export/table', title: '表格' },
|
||||
{ path: '/demo/plugins/export/txt', title: '文本' }
|
||||
]
|
||||
},
|
||||
{ path: '/demo/plugins/clipboard-polyfill', title: '剪贴板访问', icon: 'clipboard' },
|
||||
{ path: '/demo/plugins/day', title: '日期计算', icon: 'clock-o' },
|
||||
{ path: '/demo/plugins/js-cookie', title: 'Cookie 读写', icon: 'asterisk' }
|
||||
]
|
||||
}
|
||||
@@ -7,9 +7,13 @@ import 'flex.css'
|
||||
import '@/components'
|
||||
// svg 图标
|
||||
import '@/assets/svg-icons'
|
||||
// JSON 树形视图
|
||||
import vueJsonTreeView from 'vue-json-tree-view'
|
||||
// 国际化
|
||||
import i18n from '@/i18n.js'
|
||||
|
||||
import store from '@/store'
|
||||
|
||||
// 功能插件
|
||||
import pluginError from '@/plugin/error'
|
||||
import pluginLog from '@/plugin/log'
|
||||
@@ -28,6 +32,25 @@ export default {
|
||||
Vue.prototype.$version = process.env.VUE_APP_VERSION
|
||||
// 构建时间
|
||||
Vue.prototype.$buildTime = process.env.VUE_APP_BUILD_TIME
|
||||
|
||||
// 权限检查方法
|
||||
Vue.prototype.$permission = (value, type = 'menu') => {
|
||||
let path = ''
|
||||
const auth = store.state.d2admin.menu.authKey
|
||||
|
||||
switch (type) {
|
||||
case 'menu':
|
||||
path = value
|
||||
break
|
||||
case 'router':
|
||||
path = value.name.replace(/-/g, '/')
|
||||
path.slice(0, 1) !== '/' && (path = '/' + path)
|
||||
break
|
||||
}
|
||||
|
||||
return !!(path && Object.prototype.hasOwnProperty.call(auth, path))
|
||||
}
|
||||
|
||||
// Element
|
||||
Vue.use(ElementUI, {
|
||||
i18n: (key, value) => i18n.t(key, value)
|
||||
@@ -36,5 +59,7 @@ export default {
|
||||
Vue.use(pluginError)
|
||||
Vue.use(pluginLog)
|
||||
Vue.use(pluginOpen)
|
||||
// JSON 树形视图组件(tree-view)
|
||||
Vue.use(vueJsonTreeView)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
export default {
|
||||
path: '/demo/components',
|
||||
name: 'demo-components',
|
||||
meta,
|
||||
redirect: { name: 'demo-components-index' },
|
||||
component: layoutHeaderAside,
|
||||
children: [
|
||||
{ path: 'container/full', name: 'demo-components-container-full', component: _import('demo/components/container/full.vue'), meta: { ...meta, title: '布局组件 填充' } },
|
||||
{ path: 'container/full-slot', name: 'demo-components-container-full-slot', component: _import('demo/components/container/full-slot.vue'), meta: { ...meta, title: '布局组件 填充 插槽' } },
|
||||
{ path: 'container/full-bs', name: 'demo-components-container-full-bs', component: _import('demo/components/container/full-bs.vue'), meta: { ...meta, title: '布局组件 填充 滚动优化' } },
|
||||
{ path: 'container/ghost', name: 'demo-components-container-ghost', component: _import('demo/components/container/ghost.vue'), meta: { ...meta, title: '布局组件 隐形' } },
|
||||
{ path: 'container/ghost-slot', name: 'demo-components-container-ghost-slot', component: _import('demo/components/container/ghost-slot.vue'), meta: { ...meta, title: '布局组件 隐形 插槽' } },
|
||||
{ path: 'container/ghost-bs', name: 'demo-components-container-ghost-bs', component: _import('demo/components/container/ghost-bs.vue'), meta: { ...meta, title: '布局组件 隐形 滚动优化' } },
|
||||
{ path: 'container/card', name: 'demo-components-container-card', component: _import('demo/components/container/card.vue'), meta: { ...meta, title: '布局组件 卡片' } },
|
||||
{ path: 'container/card-slot', name: 'demo-components-container-card-slot', component: _import('demo/components/container/card-slot.vue'), meta: { ...meta, title: '布局组件 卡片 插槽' } },
|
||||
{ path: 'container/card-bs', name: 'demo-components-container-card-bs', component: _import('demo/components/container/card-bs.vue'), meta: { ...meta, title: '布局组件 卡片 滚动优化' } },
|
||||
{ path: 'container/api', name: 'demo-components-container-api', component: _import('demo/components/container/api.vue'), meta: { ...meta, title: '布局组件 API' } },
|
||||
{ path: 'countup', name: 'demo-components-countup', component: _import('demo/components/countup'), meta: { ...meta, title: '数字动画' } },
|
||||
{ path: 'editor-ueditor', name: 'demo-components-editor-ueditor', component: _import('demo/components/editor-ueditor'), meta: { ...meta, title: 'UEditor' } },
|
||||
{ path: 'editor-quill', name: 'demo-components-editor-quill', component: _import('demo/components/editor-quill'), meta: { ...meta, title: '富文本编辑器' } },
|
||||
{ path: 'highlight', name: 'demo-components-highlight', component: _import('demo/components/highlight'), meta: { ...meta, title: '代码高亮组件' } },
|
||||
{ path: 'icon/icon', name: 'demo-components-icon-icon', component: _import('demo/components/icon/icon.vue'), meta: { ...meta, title: '图标组件' } },
|
||||
{ path: 'icon/icon-svg', name: 'demo-components-icon-icon-svg', component: _import('demo/components/icon/icon-svg.vue'), meta: { ...meta, title: 'svg 图标' } },
|
||||
{ path: 'icon/select', name: 'demo-components-icon-select', component: _import('demo/components/icon/select.vue'), meta: { ...meta, title: '图标选择器' } },
|
||||
{ path: 'icon/select-svg', name: 'demo-components-icon-select-svg', component: _import('demo/components/icon/select-svg.vue'), meta: { ...meta, title: 'svg 图标选择器' } },
|
||||
{ path: 'icon/list', name: 'demo-components-icon-list', component: _import('demo/components/icon/list.vue'), meta: { ...meta, title: '图标列表' } },
|
||||
{ path: 'index', name: 'demo-components-index', component: _import('demo/components/index'), meta: { ...meta, title: '组件首页' } },
|
||||
{ path: 'json-tree', name: 'demo-components-json-tree', component: _import('demo/components/json-tree'), meta: { ...meta, title: 'JSON 展示' } },
|
||||
{ path: 'layout/grid', name: 'demo-components-layout-grid', component: _import('demo/components/layout/grid.vue'), meta: { ...meta, title: '拖拽网格布局' } },
|
||||
{ path: 'layout/splitpane', name: 'demo-components-layout-splitpane', component: _import('demo/components/layout/splitpane.vue'), meta: { ...meta, title: '区域布局' } },
|
||||
{ path: 'markdown/source', name: 'demo-components-markdown-source', component: _import('demo/components/markdown/source.vue'), meta: { ...meta, title: 'markdown指定资源渲染' } },
|
||||
{ path: 'markdown/url', name: 'demo-components-markdown-url', component: _import('demo/components/markdown/url.vue'), meta: { ...meta, title: 'markdown指定url渲染' } }
|
||||
]
|
||||
}
|
||||
18
src/router/modules/data-platform.js
Normal file
18
src/router/modules/data-platform.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
export default {
|
||||
path: '/data_middleground',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
{
|
||||
path: 'index',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '数据中台', root: '/data_middleground' },
|
||||
component: _import('system/function/module-index')
|
||||
}
|
||||
])('data_middleground-')
|
||||
}
|
||||
24
src/router/modules/equipment-management.js
Normal file
24
src/router/modules/equipment-management.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
export default {
|
||||
path: '/device_management',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
{
|
||||
path: 'index',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '设备管理', root: '/device_management' },
|
||||
component: _import('system/function/module-index')
|
||||
},
|
||||
{
|
||||
path: 'device_model/device_category',
|
||||
name: `${pre}equipment_model-equipment_category`,
|
||||
meta: { ...meta, cache: true, title: '设备类别' },
|
||||
component: _import('equipment-management/equipment-model/equipment-category')
|
||||
}
|
||||
])('device_management-')
|
||||
}
|
||||
18
src/router/modules/planning-production.js
Normal file
18
src/router/modules/planning-production.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
export default {
|
||||
path: '/planning_production',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
{
|
||||
path: 'index',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '计划与生产', root: '/planning_production' },
|
||||
component: _import('system/function/module-index')
|
||||
}
|
||||
])('planning_production-')
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
export default {
|
||||
path: '/demo/playground',
|
||||
name: 'demo-playground',
|
||||
meta,
|
||||
redirect: { name: 'demo-playground-index' },
|
||||
component: layoutHeaderAside,
|
||||
children: [
|
||||
{ path: 'index', name: 'demo-playground-index', component: _import('demo/playground/index'), meta: { ...meta, title: '功能首页' } },
|
||||
{ path: 'store/page', name: 'demo-playground-store-page', component: _import('demo/playground/store/page'), meta: { ...meta, cache: true, title: '多标签页控制' } },
|
||||
{ path: 'store/menu', name: 'demo-playground-store-menu', component: _import('demo/playground/store/menu'), meta: { ...meta, title: '菜单控制' } },
|
||||
{ path: 'store/size', name: 'demo-playground-store-size', component: _import('demo/playground/store/size'), meta: { ...meta, title: '全局尺寸' } },
|
||||
{ path: 'store/ua', name: 'demo-playground-store-ua', component: _import('demo/playground/store/ua'), meta: { ...meta, title: '浏览器信息' } },
|
||||
{ path: 'store/gray', name: 'demo-playground-store-gray', component: _import('demo/playground/store/gray'), meta: { ...meta, title: '灰度模式' } },
|
||||
{ path: 'store/fullscreen', name: 'demo-playground-store-fullscreen', component: _import('demo/playground/store/fullscreen'), meta: { ...meta, title: '全屏' } },
|
||||
{ path: 'store/theme', name: 'demo-playground-store-theme', component: _import('demo/playground/store/theme'), meta: { ...meta, title: '主题' } },
|
||||
{ path: 'store/transition', name: 'demo-playground-store-transition', component: _import('demo/playground/store/transition'), meta: { ...meta, title: '页面过渡开关' } },
|
||||
{ path: 'page-cache/on', name: 'demo-playground-page-cache-on', component: _import('demo/playground/page-cache/on.vue'), meta: { ...meta, cache: true, title: '开启缓存' } },
|
||||
{ path: 'page-cache/off', name: 'demo-playground-page-cache-off', component: _import('demo/playground/page-cache/off.vue'), meta: { ...meta, title: '关闭缓存' } },
|
||||
{ path: 'page-cache/params/:id', name: 'demo-playground-page-cache-params', component: _import('demo/playground/page-cache/params.vue'), meta: { ...meta, cache: true, title: '带参路由缓存' }, props: true },
|
||||
{ path: 'page-argu/send', name: 'demo-playground-page-argu-send', component: _import('demo/playground/page-argu/send.vue'), meta: { ...meta, title: '参数传递 发送' } },
|
||||
{ path: 'page-argu/get/:username', name: 'demo-playground-page-argu-get', component: _import('demo/playground/page-argu/get.vue'), meta: { ...meta, title: '参数传递 接收' } },
|
||||
{ path: 'db/all', name: 'demo-playground-db-all', component: _import('demo/playground/db/all'), meta: { ...meta, title: '总览' } },
|
||||
{ path: 'db/public', name: 'demo-playground-db-public', component: _import('demo/playground/db/public'), meta: { ...meta, title: '公共存储' } },
|
||||
{ path: 'db/user', name: 'demo-playground-db-user', component: _import('demo/playground/db/user'), meta: { ...meta, title: '私有存储' } },
|
||||
{ path: 'db/page-public', name: 'demo-playground-db-page-public', component: _import('demo/playground/db/page-public'), meta: { ...meta, title: '路由存储' } },
|
||||
{ path: 'db/page-user', name: 'demo-playground-db-page-user', component: _import('demo/playground/db/page-user'), meta: { ...meta, title: '私有路由存储' } },
|
||||
{ path: 'db/page-snapshot-public', name: 'demo-playground-db-page-snapshot-public', component: _import('demo/playground/db/page-snapshot-public'), meta: { ...meta, title: '路由快照' } },
|
||||
{ path: 'db/page-snapshot-user', name: 'demo-playground-db-page-snapshot-user', component: _import('demo/playground/db/page-snapshot-user'), meta: { ...meta, title: '私有路由快照' } },
|
||||
{ path: 'log/ajax', name: 'demo-playground-log-ajax', component: _import('demo/playground/log/ajax'), meta: { ...meta, title: 'Ajax 错误' } },
|
||||
{ path: 'log/console', name: 'demo-playground-log-console', component: _import('demo/playground/log/console'), meta: { ...meta, title: '控制台日志' } },
|
||||
{ path: 'log/error', name: 'demo-playground-log-error', component: _import('demo/playground/log/error'), meta: { ...meta, title: '错误捕捉' } },
|
||||
{ path: 'log/log', name: 'demo-playground-log-log', component: _import('demo/playground/log/log'), meta: { ...meta, title: '日志记录' } },
|
||||
{ path: 'add-routes/routes', name: 'demo-playground-add-routes-routes', component: _import('demo/playground/add-routes/routes'), meta: { ...meta, title: '添加页面' } },
|
||||
{ path: 'env', name: 'demo-playground-env', component: _import('demo/playground/env'), meta: { ...meta, title: '环境信息' } },
|
||||
{ path: 'locales', name: 'demo-playground-locales', component: _import('demo/playground/locales'), meta: { ...meta, title: '国际化' } },
|
||||
{ path: 'frame/html', name: 'demo-playground-frame-html', component: _import('demo/playground/frame/html'), meta: { ...meta, title: '静态 HTML' } },
|
||||
{ path: 'frame/report', name: 'demo-playground-frame-report', component: _import('demo/playground/frame/report'), meta: { ...meta, title: 'Size report' } },
|
||||
{ path: 'frame/d2-doc', name: 'demo-playground-frame-d2-doc', component: _import('demo/playground/frame/d2-doc'), meta: { ...meta, title: 'D2Admin 中文文档' } }
|
||||
]
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
export default {
|
||||
path: '/demo/plugins',
|
||||
name: 'demo-plugins',
|
||||
meta,
|
||||
redirect: { name: 'demo-plugins-index' },
|
||||
component: layoutHeaderAside,
|
||||
children: [
|
||||
{ path: 'clipboard-polyfill', name: 'demo-plugins-clipboard-polyfill', component: _import('demo/plugins/clipboard-polyfill'), meta: { ...meta, title: '剪贴板访问' } },
|
||||
{ path: 'day', name: 'demo-plugins-day', component: _import('demo/plugins/day'), meta: { ...meta, title: '日期计算' } },
|
||||
{ path: 'export/table', name: 'demo-plugins-export-table', component: _import('demo/plugins/export/table.vue'), meta: { ...meta, title: '导出表格' } },
|
||||
{ path: 'export/txt', name: 'demo-plugins-export-txt', component: _import('demo/plugins/export/txt.vue'), meta: { ...meta, title: '导出文本' } },
|
||||
{ path: 'import/csv', name: 'demo-plugins-import-csv', component: _import('demo/plugins/import/csv.vue'), meta: { ...meta, title: '导入 csv' } },
|
||||
{ path: 'import/xlsx', name: 'demo-plugins-import-xlsx', component: _import('demo/plugins/import/xlsx.vue'), meta: { ...meta, title: '导入 xlsx' } },
|
||||
{ path: 'index', name: 'demo-plugins-index', component: _import('demo/plugins/index'), meta: { ...meta, title: '插件首页' } },
|
||||
{ path: 'js-cookie', name: 'demo-plugins-js-cookie', component: _import('demo/plugins/js-cookie'), meta: { ...meta, title: 'Cookie' } }
|
||||
]
|
||||
}
|
||||
90
src/router/modules/production-master-data.js
Normal file
90
src/router/modules/production-master-data.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
export default {
|
||||
path: '/production_configuration',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
{
|
||||
path: 'index',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '生产配置', root: '/production_configuration' },
|
||||
component: _import('system/function/module-index')
|
||||
},
|
||||
{
|
||||
path: 'factory_model/factory_area',
|
||||
name: `${pre}factory_model-factory_area`,
|
||||
meta: { ...meta, cache: true, title: '工厂区域' },
|
||||
component: _import('production-master-data/factory-model/factory-area')
|
||||
},
|
||||
{
|
||||
path: 'factory_model/factory_line',
|
||||
name: `${pre}factory_model-factory_line`,
|
||||
meta: { ...meta, cache: true, title: '产线设置' },
|
||||
component: _import('production-master-data/factory-model/production-line')
|
||||
},
|
||||
{
|
||||
path: 'technology_model/technology_flow_category',
|
||||
name: `${pre}technology_model-technology_flow_category`,
|
||||
meta: { ...meta, cache: true, title: '工艺流程类别' },
|
||||
component: _import('production-master-data/process-model/process-category')
|
||||
},
|
||||
{
|
||||
path: 'technology_model/technology_flow_workingsubclass',
|
||||
name: `${pre}technology_model-technology_flow_workingsubclass`,
|
||||
meta: { ...meta, cache: true, title: '工序单元' },
|
||||
component: _import('production-master-data/process-model/process-step')
|
||||
},
|
||||
{
|
||||
path: 'technology_model/technology_flow',
|
||||
name: `${pre}technology_model-technology_flow`,
|
||||
meta: { ...meta, cache: true, title: '工艺流程' },
|
||||
component: _import('production-master-data/process-model/process-routing')
|
||||
},
|
||||
{
|
||||
path: 'technology_model/technology_flow/:flow_id/process',
|
||||
name: `${pre}technology_model-technology_flow-process`,
|
||||
meta: { ...meta, cache: false, title: '工艺详情' },
|
||||
component: _import('production-master-data/process-model/process-routing-card')
|
||||
},
|
||||
{
|
||||
path: 'product_model/battery_model',
|
||||
name: `${pre}product_management-product_list`,
|
||||
meta: { ...meta, cache: true, title: '产品列表' },
|
||||
component: _import('production-master-data/product-management/product-list')
|
||||
},
|
||||
{
|
||||
path: 'matetial_model/matetial_category',
|
||||
name: `${pre}material_model-material_category`,
|
||||
meta: { ...meta, cache: true, title: '物料类别列表' },
|
||||
component: _import('production-master-data/material-model/material-category')
|
||||
},
|
||||
{
|
||||
path: 'matetial_model/matetial_management',
|
||||
name: `${pre}material_model-material_master`,
|
||||
meta: { ...meta, cache: true, title: '物料信息管理' },
|
||||
component: _import('production-master-data/material-model/material-master')
|
||||
},
|
||||
{
|
||||
path: 'matetial_model/unit',
|
||||
name: `${pre}material_model-material_unit`,
|
||||
meta: { ...meta, cache: true, title: '计量单位' },
|
||||
component: _import('production-master-data/material-model/material-unit')
|
||||
},
|
||||
{
|
||||
path: 'product_model/product_ng_info',
|
||||
name: `${pre}product_model-product_ng_info`,
|
||||
meta: { ...meta, cache: true, title: '异常不良管理' },
|
||||
component: _import('production-master-data/product-model/product-ng-info')
|
||||
},
|
||||
{
|
||||
path: 'spc_configuration/binding_scada_node',
|
||||
name: `${pre}spc_configuration-data_collection_configuration`,
|
||||
meta: { ...meta, cache: true, title: 'SPC采集配置' },
|
||||
component: _import('production-master-data/spc-configuration/data-collection-configuration')
|
||||
}
|
||||
])('production_configuration-')
|
||||
}
|
||||
18
src/router/modules/quality-management.js
Normal file
18
src/router/modules/quality-management.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
export default {
|
||||
path: '/quality_control',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
{
|
||||
path: 'index',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '质量管理', root: '/quality_control' },
|
||||
component: _import('system/function/module-index')
|
||||
}
|
||||
])('quality_control-')
|
||||
}
|
||||
54
src/router/modules/system-administration.js
Normal file
54
src/router/modules/system-administration.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
const meta = { auth: true }
|
||||
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
export default {
|
||||
path: '/system_settings',
|
||||
component: layoutHeaderAside,
|
||||
children: (pre => [
|
||||
{
|
||||
path: 'index',
|
||||
name: `${pre}index`,
|
||||
meta: { ...meta, title: '系统设置', root: '/system_settings' },
|
||||
component: _import('system/function/module-index')
|
||||
},
|
||||
{
|
||||
path: 'user_management/role',
|
||||
name: `${pre}user_management-role`,
|
||||
meta: { ...meta, cache: true, title: '角色' },
|
||||
component: _import('system-administration/user-management/role')
|
||||
},
|
||||
{
|
||||
path: 'user_management/user',
|
||||
name: `${pre}user_management-user`,
|
||||
meta: { ...meta, cache: true, title: '用户' },
|
||||
component: _import('system-administration/user-management/user')
|
||||
},
|
||||
{
|
||||
path: 'menu_configuration/menu',
|
||||
name: `${pre}menu_configuration-menu`,
|
||||
meta: { ...meta, cache: true, title: '菜单配置' },
|
||||
component: _import('system-administration/menu-management/menu-configuration')
|
||||
},
|
||||
{
|
||||
path: 'system_assistant/interface_log',
|
||||
name: `${pre}system_assistant-interface_log`,
|
||||
meta: { ...meta, cache: true, title: '接口日志' },
|
||||
component: _import('system-administration/system-utilities/api-logs')
|
||||
},
|
||||
{
|
||||
path: 'system_assistant/operate_log',
|
||||
name: `${pre}system_assistant-operate_log`,
|
||||
meta: { ...meta, cache: true, title: '操作日志' },
|
||||
component: _import('system-administration/system-utilities/operation-logs')
|
||||
},
|
||||
{
|
||||
path: 'system_assistant/problem_help',
|
||||
name: `${pre}system_assistant-problem_help`,
|
||||
meta: { ...meta, cache: true, title: '问题帮助' },
|
||||
component: _import('system-administration/system-utilities/problem-help')
|
||||
}
|
||||
])('system_settings-')
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import playground from './modules/playground'
|
||||
import plugins from './modules/plugins'
|
||||
import components from './modules/components'
|
||||
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
import productionMasterData from './modules/production-master-data'
|
||||
import qualityManagement from './modules/quality-management'
|
||||
import planningProduction from './modules/planning-production'
|
||||
import dataPlatform from './modules/data-platform'
|
||||
import systemAdministration from './modules/system-administration'
|
||||
import equipmentManagement from './modules/equipment-management'
|
||||
|
||||
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
@@ -52,9 +54,12 @@ const frameIn = [
|
||||
}
|
||||
]
|
||||
},
|
||||
playground,
|
||||
plugins,
|
||||
components
|
||||
productionMasterData,
|
||||
qualityManagement,
|
||||
planningProduction,
|
||||
dataPlatform,
|
||||
systemAdministration,
|
||||
equipmentManagement
|
||||
]
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user