Files
MES/yawei-mes/.tasks/2025-12-20_FIIH质量管理配置二级菜单.md
2026-04-02 10:39:03 +08:00

673 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# FIIH质量管理模块菜单改造方案无需修改数据库
## 1. 需求背景
目前FIIH质量管理模块的所有质检数据都在同一个质检配置表中展示需求是将不同对象体(`fiih_object_name`)的质检数据拆分为独立的二级菜单,并实现自动创建这些二级菜单的功能。且要求不修改数据库结构,不添加新表。
## 2. 现有结构分析
### 2.1 菜单结构
当前FIIH质量管理模块的菜单结构
- FIIH质量管理一级菜单ID: 2421
- 质检配置表二级菜单ID: 2422
### 2.2 相关数据表
- `sys_menu`: 系统菜单表
- `ymes_fiih_config`: FIIH质量管理配置表包含fiih_object_name字段
- `sys_config`: 系统参数表(用于存储菜单相关配置)
### 2.3 代码结构
- 前端:
- 质检配置表页面:`mes-ui/src/views/mes/fiih/index.vue`
- 质检详情页面:`mes-ui/src/views/mes/fiih/fiihDetailByTaskId.vue`
- API接口`mes-ui/src/api/mes/fiih/fiihConfig.js`
- 后端:
- 控制器:`FiihConfigController.java`
- 服务接口:`IFiihConfigService.java`
- 服务实现:`FiihConfigServiceImpl.java`
### 2.4 FIIH配置表结构
```sql
CREATE TABLE `ymes_fiih_config` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`fiih_object_name` varchar(50) NOT NULL COMMENT '对象体名称', -- 作为菜单分类依据
`fiih_link_id` bigint NOT NULL COMMENT '环节ID(单次环节)',
`fiih_link_name` varchar(100) NOT NULL COMMENT '环节名称',
`fiih_task_id` bigint NOT NULL COMMENT '任务ID(总体)',
`fiih_task_name` varchar(100) NOT NULL COMMENT '任务名称',
-- 其他字段省略
`fiih_info_json` text COMMENT '以上信息属性JSON',
`fiih_query_json` text COMMENT '以上信息查询属性JSON',
-- 其他字段省略
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='FIIH质量管理配置表';
```
## 3. 改造方案
### 3.1 利用现有数据结构
不创建新表,直接利用现有的`fiih_object_name`字段作为菜单分类依据:
1. **使用`fiih_object_name`字段区分不同类型的质检数据**
每个独特的`fiih_object_name`值对应一个二级菜单
2. **使用系统参数表sys_config存储已创建的菜单映射**
```json
config_key: fiih_object_menus
config_value: [
{"objectName":"产品A","menuId":2450,"orderNum":1},
{"objectName":"产品B","menuId":2451,"orderNum":2}
]
```
### 3.2 后端实现
#### 3.2.1 基于对象名称实现菜单创建
需要实现的核心功能是:对`fiih_object_name`进行分组,为每个唯一值创建一个二级菜单。
1. **添加获取所有对象名称的方法**
```java
/**
* 获取所有唯一的对象名称
*/
public List<String> getAllObjectNames() {
return fiihConfigMapper.selectDistinctObjectNames();
}
```
2. **添加对应的Mapper方法**
```xml
<!-- 查询所有唯一的对象名称 -->
<select id="selectDistinctObjectNames" resultType="String">
SELECT DISTINCT fiih_object_name
FROM ymes_fiih_config
WHERE tenant_id = #{tenantId}
ORDER BY fiih_object_name
</select>
```
#### 3.2.2 实现菜单创建和更新
在`FiihConfigController.java`中添加自动创建菜单的方法:
```java
/**
* 自动创建所有对象的菜单
*/
@PreAuthorize("@ss.hasPermi('fiih:config:menu')")
@PostMapping("/createObjectMenus")
public AjaxResult createObjectMenus() {
try {
// 获取所有对象名称
List<String> objectNames = fiihConfigService.getAllObjectNames();
// 获取已存在的对象菜单映射
Map<String, Long> existingMenus = getExistingObjectMenus();
// 创建或更新对象菜单
List<Map<String, Object>> results = new ArrayList<>();
for (String objectName : objectNames) {
if (!existingMenus.containsKey(objectName)) {
// 创建新菜单
Long menuId = createObjectMenu(objectName);
if (menuId != null) {
Map<String, Object> result = new HashMap<>();
result.put("objectName", objectName);
result.put("menuId", menuId);
result.put("status", "created");
results.add(result);
// 更新已存在菜单映射
existingMenus.put(objectName, menuId);
}
} else {
Map<String, Object> result = new HashMap<>();
result.put("objectName", objectName);
result.put("menuId", existingMenus.get(objectName));
result.put("status", "existing");
results.add(result);
}
}
// 更新系统参数中的对象菜单映射
updateObjectMenusConfig(existingMenus);
return AjaxResult.success("FIIH对象菜单创建成功", results);
} catch (Exception e) {
log.error("创建对象菜单失败", e);
return AjaxResult.error("创建对象菜单失败: " + e.getMessage());
}
}
/**
* 为对象创建二级菜单
*/
private Long createObjectMenu(String objectName) {
try {
// 生成路径:将对象名称转换为小写并移除空格
String path = objectName.toLowerCase().replaceAll("\\s+", "");
// 检查菜单是否已存在
SysMenu existingMenu = sysMenuMapper.selectMenuByPath("fiih/" + path);
if (existingMenu != null) {
return existingMenu.getMenuId();
}
// 获取FIIH质量管理菜单ID
Long parentId = 2421L; // FIIH质量管理的菜单ID
// 创建新菜单
SysMenu menu = new SysMenu();
menu.setMenuName(objectName);
menu.setParentId(parentId);
menu.setOrderNum(10);
menu.setPath(path);
menu.setComponent("mes/fiih/objectView/index");
menu.setIsFrame("1");
menu.setIsCache("0");
menu.setMenuType("C");
menu.setVisible("0");
menu.setStatus("0");
menu.setPerms("fiih:config:list");
menu.setIcon("clipboard");
int result = sysMenuMapper.insertMenu(menu);
if (result > 0) {
return menu.getMenuId();
}
} catch (Exception e) {
log.error("创建对象菜单失败: " + objectName, e);
}
return null;
}
/**
* 获取已存在的对象菜单映射
*/
private Map<String, Long> getExistingObjectMenus() {
String configValue = configService.selectConfigByKey("fiih_object_menus");
Map<String, Long> result = new HashMap<>();
if (StringUtils.isNotEmpty(configValue)) {
try {
JSONArray jsonArray = JSON.parseArray(configValue);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject json = jsonArray.getJSONObject(i);
String objectName = json.getString("objectName");
Long menuId = json.getLong("menuId");
if (StringUtils.isNotEmpty(objectName) && menuId != null) {
result.put(objectName, menuId);
}
}
} catch (Exception e) {
log.error("解析对象菜单映射失败", e);
}
}
return result;
}
/**
* 更新系统参数中的对象菜单映射
*/
private void updateObjectMenusConfig(Map<String, Long> objectMenus) {
JSONArray jsonArray = new JSONArray();
int orderNum = 1;
for (Map.Entry<String, Long> entry : objectMenus.entrySet()) {
JSONObject json = new JSONObject();
json.put("objectName", entry.getKey());
json.put("menuId", entry.getValue());
json.put("orderNum", orderNum++);
jsonArray.add(json);
}
String configValue = jsonArray.toJSONString();
// 检查配置是否已存在
SysConfig config = configService.checkConfigKeyUnique("fiih_object_menus");
if (config != null) {
// 更新现有配置
config.setConfigValue(configValue);
configService.updateConfig(config);
} else {
// 创建新配置
config = new SysConfig();
config.setConfigName("FIIH对象菜单映射");
config.setConfigKey("fiih_object_menus");
config.setConfigValue(configValue);
config.setConfigType("N"); // 非内置
configService.insertConfig(config);
}
}
```
#### 3.2.3 实现按对象名称查询配置的接口
在`FiihConfigController.java`中添加方法:
```java
/**
* 根据对象名称查询FIIH质量管理配置列表
*/
@PreAuthorize("@ss.hasPermi('fiih:config:list')")
@GetMapping("/listByObject/{objectName}")
public TableDataInfo listByObjectName(@PathVariable("objectName") String objectName) {
startPage();
FiihConfig fiihConfig = new FiihConfig();
fiihConfig.setFiihObjectName(objectName);
List<FiihConfig> list = fiihConfigService.selectFiihConfigList(fiihConfig);
return getDataTable(list);
}
/**
* 获取所有对象名称及其菜单ID
*/
@PreAuthorize("@ss.hasPermi('fiih:config:list')")
@GetMapping("/objects")
public AjaxResult getAllObjects() {
List<String> objectNames = fiihConfigService.getAllObjectNames();
Map<String, Long> objectMenus = getExistingObjectMenus();
List<Map<String, Object>> result = new ArrayList<>();
for (String objectName : objectNames) {
Map<String, Object> obj = new HashMap<>();
obj.put("objectName", objectName);
obj.put("menuId", objectMenus.getOrDefault(objectName, null));
result.add(obj);
}
return AjaxResult.success(result);
}
### 3.3 前端实现
#### 3.3.1 创建对象菜单API接口
在 `mes-ui/src/api/mes/fiih/fiihConfig.js` 中添加新的接口方法:
```javascript
import request from '@/utils/request'
// 获取所有对象名称
export function getAllObjects() {
return request({
url: '/fiih/config/objects',
method: 'get'
})
}
// 根据对象名称查询配置数据
export function listFiihConfigByObject(objectName) {
return request({
url: '/fiih/config/listByObject/' + encodeURIComponent(objectName),
method: 'get'
})
}
// 自动创建所有对象菜单
export function createObjectMenus() {
return request({
url: '/fiih/config/createObjectMenus',
method: 'post'
})
}
```
#### 3.3.2 创建对象视图组件
创建 `mes-ui/src/views/mes/fiih/objectView/index.vue` 页面,用于展示特定对象的数据:
```vue
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="任务名称" prop="fiihTaskName">
<el-input
v-model="queryParams.fiihTaskName"
placeholder="请输入任务名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="环节名称" prop="fiihLinkName">
<el-input
v-model="queryParams.fiihLinkName"
placeholder="请输入环节名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['fiih:config:add']"
>新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="fiihList"
@selection-change="handleSelectionChange"
>
<el-table-column label="序号" type="index" width="50" align="center">
<template slot-scope="scope">
<span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
</template>
</el-table-column>
<el-table-column label="任务ID" align="center" prop="fiihTaskId" />
<el-table-column label="任务名称" align="center" prop="fiihTaskName" />
<el-table-column label="环节名称" align="center" prop="fiihLinkName" />
<el-table-column label="状态" align="center" prop="fiihStatus">
<template slot-scope="scope">
<dict-tag :options="statusOptions" :value="scope.row.fiihStatus"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-view"
@click="handleView(scope.row)"
v-hasPermi="['fiih:config:query']"
>查看数据</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['fiih:config:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['fiih:config:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import { listFiihConfigByObject, delFiihConfig } from "@/api/mes/fiih/fiihConfig";
export default {
name: "FiihObjectView",
data() {
return {
// 默认展开搜索
showSearch: true,
// 总条目数
total: 0,
// FIIH数据列表
fiihList: [],
// 状态选项
statusOptions: [
{ dictValue: 1, dictLabel: '进行中' },
{ dictValue: 2, dictLabel: '完成' },
{ dictValue: 3, dictLabel: '作废' }
],
// 当前对象名称
objectName: "",
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
fiihTaskName: null,
fiihLinkName: null
},
// 加载状态
loading: false
};
},
created() {
// 从路由获取对象名称
const path = this.$route.path;
// 提取路径中的对象名称
const pathSegments = path.split("/");
const objectNameEncoded = pathSegments[pathSegments.length - 1];
this.objectName = decodeURIComponent(objectNameEncoded);
document.title = this.objectName + " - FIIH质量管理";
this.getList();
},
methods: {
/** 查询指定对象的数据列表*/
getList() {
this.loading = true;
listFiihConfigByObject(this.objectName, this.queryParams).then(response => {
this.fiihList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 构造查询的参数列表 */
getQueryParams() {
const params = { ...this.queryParams };
params.objectName = this.objectName;
return params;
},
// 其他方法如handleAdd/handleUpdate/handleView/handleDelete等保持不变
}
};
</script>
```
#### 3.3.3 修改主页面以添加菜单创建功能
在 `mes-ui/src/views/mes/fiih/index.vue` 中添加自动创建菜单的功能:
```vue
<!-- 在页面操作区域添加一个新的按钮 -->
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-menu"
size="mini"
@click="handleCreateMenus"
v-hasPermi="['fiih:config:menu']"
>创建对象菜单</el-button>
</el-col>
```
添加对应的方法:
```javascript
// 自动创建对象菜单
handleCreateMenus() {
this.$modal.confirm('确认要自动创建所有对象的菜单吗?').then(() => {
this.loading = true;
createObjectMenus().then(response => {
this.$modal.msgSuccess("菜单创建成功");
this.loading = false;
// 刷新页面以显示新菜单
setTimeout(() => {
window.location.reload();
}, 1000);
}).catch(() => {
this.loading = false;
});
});
}
```
#### 3.3.4 添加对象名称筛选功能
在 `mes-ui/src/views/mes/fiih/index.vue` 中添加对象名称筛选功能:
```vue
<el-form-item label="对象名称" prop="fiihObjectName">
<el-select v-model="queryParams.fiihObjectName" placeholder="请选择对象名称" clearable>
<el-option
v-for="item in objectOptions"
:key="item.objectName"
:label="item.objectName"
:value="item.objectName">
</el-option>
</el-select>
</el-form-item>
```
添加相关数据和方法:
```javascript
data() {
return {
// 其他数据...
objectOptions: [], // 对象名称选项
queryParams: {
// 原有参数...
fiihObjectName: null
}
}
},
created() {
this.getList();
this.getObjectOptions();
},
methods: {
// 获取对象名称选项
getObjectOptions() {
getAllObjects().then(response => {
this.objectOptions = response.data;
});
}
}
```
#### 3.3.5 修改动态路由处理
在系统的路由配置中添加对象视图的路由处理。由于对象视图的路由是动态创建的,需要确保路由配置能正确处理。
在Vue路由配置中确保有如下配置
```javascript
// 在路由文件中添加动态路由匹配(默认系统已有)
{
path: 'fiih/:path*',
component: () => import('@/views/mes/fiih/objectView/index'),
name: 'FiihObjectView',
meta: { title: 'FIIH对象视图', activeMenu: '/mes/fiih' }
}
```
## 4. 使用流程
### 4.1 管理员配置流程
1. 管理员进入FIIH质量管理配置页面
2. 点击创建对象菜单按钮,自动扫描已有的`fiih_object_name`值
3. 系统自动创建对应的二级菜单,并将映射保存在`sys_config`表中
### 4.2 数据管理流程
1. 添加新配置时,指定`fiih_object_name`值
2. 系统自动将该数据与对应的对象菜单关联
3. 可通过对象名称筛选相关配置数据
### 4.3 用户访问流程
1. 用户点击FIIH质量管理下的二级菜单如某个对象名称
2. 系统自动调用`listByObject`接口,查询并显示对应对象的所有数据
## 5. 测试计划
1. 测试自动获取对象名称
- 验证`selectDistinctObjectNames`方法是否正常返回所有唯一对象名称
- 验证数据完整性
2. 测试自动创建菜单功能
- 用不同的对象名称测试菜单创建
- 验证菜单权限是否正确
- 验证菜单映射存储是否正常
3. 测试对象数据筛选
- 在主页面上验证对象名称筛选功能
- 验证筛选结果的准确性
4. 测试二级菜单数据访问
- 通过生成的二级菜单访问特定对象数据
- 验证数据筛选是否正确
- 验证数据展示是否正常
## 6. 无需修改数据库的部署计划
1. 系统参数初始化
```sql
-- 初始化对象菜单映射配置
INSERT INTO `sys_config` (`config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `remark`)
VALUES ('FIIH对象菜单映射', 'fiih_object_menus', '[]', 'N', 'admin', NOW(), 'FIIH对象菜单映射用于存储对象和菜单的关联关系');
```
2. 后端代码部署
- 增加获取所有对象名称的方法
- 实现自动创建菜单的功能
- 添加按对象名称查询的接口
- 实现对象菜单映射的存储和读取
3. 前端代码部署
- 添加创建对象菜单按钮
- 添加对象名称筛选功能
- 创建对象视图组件,用于显示特定对象的数据
- 更新路由配置以支持动态对象菜单
## 7. 方案优势
1. **利用现有数据结构**:直接使用现有表中的`fiih_object_name`字段作为分类依据,无需添加额外字段或新表
2. **实现简单**:只需实现自动扫描和菜单创建功能,无需修改现有数据存储逻辑
3. **自动发现**:系统自动发现并创建所有对象菜单,无需手动配置
4. **动态扩展**:新添加的对象名称可自动创建相应菜单,无需修改代码
5. **向后兼容**:不破坏现有功能,原有的质检配置表页面保持不变
6. **部署简便**:无需执行数据库变更脚本,降低了部署风险