feat(vehicle): 将客商档案搜索区所属组织改为树形下拉组件
- 搜索区所属组织从扁平 select 切换到多选树形下拉,保持后端字段不变 - 使用 Vue3 h 函数动态渲染 element-plus ElTreeSelect 组件,支持多选和折叠标签 - 修复 Vue 渲染字符串组件名导致控件无法显示的问题,改为直接导入组件对象 - 解决多选模型值写为字符串导致选中失效的状态管理问题,保持 modelValue 为 ID 数组 - 在搜索变更回调中将部门 ID 数组转换为部门名称逗号拼接
This commit is contained in:
@@ -29,6 +29,23 @@
|
|||||||
- 风控:确认 Avue 的 `.avue-form__menu`/`.avue-dialog__footer` 自身不带按钮间距,去掉 gap 后由 EP 默认 12px margin 接管(不会贴死)。
|
- 风控:确认 Avue 的 `.avue-form__menu`/`.avue-dialog__footer` 自身不带按钮间距,去掉 gap 后由 EP 默认 12px margin 接管(不会贴死)。
|
||||||
- 范围说明:本次聚焦近几轮在统一的模块(vehicle/payment/business/settlement)。若要把 loading-manage、contract-manage、transit 等其它模块的底部栏也纳入,需再处理。
|
- 范围说明:本次聚焦近几轮在统一的模块(vehicle/payment/business/settlement)。若要把 loading-manage、contract-manage、transit 等其它模块的底部栏也纳入,需再处理。
|
||||||
|
|
||||||
|
## 客商档案搜索区「所属组织」从扁平下拉改为树形下拉
|
||||||
|
- 起因:用户截图 `/vehicle/customer-archive` 搜索区,指出「所属组织」是扁平下拉,要求统一按新增/编辑表单(多选 `el-tree-select`)实现。
|
||||||
|
- 原 column 配置:`prop: 'deptName'` + `search: true` + `searchType: 'select'` + `dicData: [{label:'全部',value:''}, ...flattenDept]`,initDeptTree() 异步把 dicData 写回 column(数据已加载,只是渲染风格不一致)。
|
||||||
|
- 改动:
|
||||||
|
- column 移除 `searchType: 'select'`、`searchValue: ''`、`dicData`,仅保留 `search: true, searchOrder: 1`。
|
||||||
|
- `initDeptTree()` 中改写 `deptColumn.renderSearch = scope => this.renderDeptSearch(scope)`。
|
||||||
|
- 新增 `methods.renderDeptSearch(scope)`:通过 `vue` 的 `h` 函数返回一个 `el-tree-select` vnode(多选 + check-strictly + 树形 + 折叠 tag + 全宽),onUpdate 时把 ID 数组按 `deptOptions` 查 rawLabel,逗号拼接写入 `scope.row.deptName`(即 searchForm.deptName),保持后端接口字段不变。
|
||||||
|
- `searchChange/searchReset` 链路无需改:`onLoad` 已通过 `query` 透传 deptName 到后端。
|
||||||
|
- `src/styles/element-ui.scss` 新增 `.avue-crud__search .el-form-item__content > .el-tree-select { width:100%; max-width:100%; }`:搜索区树形下拉全宽兜底,不影响新增/编辑表单(用 `el-col :span=6` 栅格自然分开)。
|
||||||
|
- 方案说明:Avue-crud 搜索 form-temp 内部 render 函数支持 column.renderSearch 自定义渲染(`header-search2.mjs` 行 93 `render: ele.renderSearch`),不需要重写整个搜索区;自定义 slot `<template #search-deptName>` 在 el-input 等自闭合组件上无法落地(slot 名不会被 el-input 识别),所以走 renderSearch。
|
||||||
|
- 选择项:用户确认「仅替换所属组织一格」「多选」「传 deptName」三项。
|
||||||
|
- 排查「出不来了」反馈:主人实测搜索区只有 label,控件区域空白。原因 `h('el-tree-select', {...})` 传字符串时 Vue 3 `_createVNode` 直接把 type 当 ELEMENT(shapeFlag=1)处理,**不会调用 resolveComponent**,结果浏览器渲染出未知标签 `<el-tree-select>`,内容为空。
|
||||||
|
- 修复:从 `element-plus` import `ElTreeSelect` 组件对象,`h(ElTreeSelect, {...})` 走组件 vnode 路径;同时把 `modelValue` 默认值改为 `[]`(多选模式数组要求,避免 el-tree-select 警告)。
|
||||||
|
- 教训:Avue `column.render`/`renderSearch` 等接受 vnode 的字段,传字符串组件名时 Vue 不会 resolve,必须直接 import 组件对象或在 setup 内 `resolveComponent()`。
|
||||||
|
- 排查「选中不了」反馈:控件显示出来了,但点击树节点无法选中/再选。根因 `onUpdate:modelValue` 里把 `scope.row.deptName` 写成了名字**字符串**,而该字段正是 el-tree-select 的 `modelValue`(多选模式必须是**ID 数组**),第一次选中后 modelValue 变字符串,el-tree-select 内部状态错乱,后续点击无法更新。
|
||||||
|
- 修复:renderDeptSearch 的 `onUpdate:modelValue` 只把 `scope.row.deptName` 写成 ID 数组(保留数组形态);在 `searchChange(params, done)` 里把 Avue 拷贝出来的 `params.deptName` 数组(注意:`filterParams` 走 `deepClone`,params 与 scope.row 是不同引用,改它不破坏搜索框显示)按 `deptOptions` 查 rawLabel 再 `join(',')` 转成名字字符串写入 `query`,后端字段 deptName 不变。重置由 Avue `resetForm` 清空 scope.row.deptName 为 `''`,renderDeptSearch 读到非数组回退 `[]` 显示空,无需额外处理。
|
||||||
|
|
||||||
## 船舶表单「安放龙骨日期/建造完工日期」label 收窄并换行
|
## 船舶表单「安放龙骨日期/建造完工日期」label 收窄并换行
|
||||||
- 起因:用户截图 `transportCapacity/ship` 新增船舶弹窗,指出该字段 label 宽达 185px,要求最大 88px 并允许换行。
|
- 起因:用户截图 `transportCapacity/ship` 新增船舶弹窗,指出该字段 label 宽达 185px,要求最大 88px 并允许换行。
|
||||||
- 改动:`src/views/transportCapacity/ship.vue` 第 169 行,给该 `el-form-item` 增加 `label-width="88px"` 覆盖全局 `label-width="100"`;全局 `.ship-form :deep(.el-form-item__label)` 已设置 `white-space: normal; line-height: 20px`,换行已支持。
|
- 改动:`src/views/transportCapacity/ship.vue` 第 169 行,给该 `el-form-item` 增加 `label-width="88px"` 覆盖全局 `label-width="100"`;全局 `.ship-form :deep(.el-form-item__label)` 已设置 `white-space: normal; line-height: 20px`,换行已支持。
|
||||||
|
|||||||
@@ -135,6 +135,13 @@
|
|||||||
//white-space: nowrap;
|
//white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 全宽兜底:搜索表单内树形下拉占满(覆盖默认搜索项宽度)
|
||||||
|
// 注意:搜索 form 内的 el-tree-select 也走该规则;新增/编辑表单用 span=6 的栅格自然分开,不受影响。
|
||||||
|
.avue-crud__search .el-form-item__content > .el-tree-select {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.avue-crud__search.el-card {
|
.avue-crud__search.el-card {
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
|||||||
@@ -1361,6 +1361,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { h } from 'vue';
|
||||||
|
import { ElTreeSelect } from 'element-plus';
|
||||||
import {
|
import {
|
||||||
getList,
|
getList,
|
||||||
getDetail,
|
getDetail,
|
||||||
@@ -1762,11 +1764,8 @@ export default {
|
|||||||
label: '所属组织',
|
label: '所属组织',
|
||||||
prop: 'deptName',
|
prop: 'deptName',
|
||||||
search: true,
|
search: true,
|
||||||
searchType: 'select',
|
|
||||||
searchOrder: 1,
|
searchOrder: 1,
|
||||||
searchValue: '',
|
|
||||||
minWidth: 180,
|
minWidth: 180,
|
||||||
dicData: [{ label: '全部', value: '' }],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '准入类型',
|
label: '准入类型',
|
||||||
@@ -2428,13 +2427,7 @@ export default {
|
|||||||
this.deptTree = this.normalizeDeptTree(res.data.data || []);
|
this.deptTree = this.normalizeDeptTree(res.data.data || []);
|
||||||
this.deptOptions = this.flattenDept(this.deptTree);
|
this.deptOptions = this.flattenDept(this.deptTree);
|
||||||
const deptColumn = this.findColumn(this.option.column, 'deptName');
|
const deptColumn = this.findColumn(this.option.column, 'deptName');
|
||||||
deptColumn.dicData = [
|
deptColumn.renderSearch = scope => this.renderDeptSearch(scope);
|
||||||
{ label: '全部', value: '' },
|
|
||||||
...this.deptOptions.map(item => ({
|
|
||||||
label: item.label,
|
|
||||||
value: item.rawLabel,
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.deptSearchInitialized = true;
|
this.deptSearchInitialized = true;
|
||||||
@@ -2633,6 +2626,31 @@ export default {
|
|||||||
this.archiveForm.deptName = deptNames.join(',');
|
this.archiveForm.deptName = deptNames.join(',');
|
||||||
this.$refs.archiveForm?.validateField('deptName');
|
this.$refs.archiveForm?.validateField('deptName');
|
||||||
},
|
},
|
||||||
|
renderDeptSearch(scope) {
|
||||||
|
// 搜索区所属组织:多选树形下拉(与新增/编辑表单一致)
|
||||||
|
// 多选返回 ID 数组,转换为部门名称按逗号拼接写入 searchForm.deptName
|
||||||
|
return h(ElTreeSelect, {
|
||||||
|
modelValue: Array.isArray(scope.row?.deptName) ? scope.row.deptName : [],
|
||||||
|
'onUpdate:modelValue': value => {
|
||||||
|
// 多选模式:modelValue 必须是 ID 数组,否则 el-tree-select 内部状态会错乱导致无法再次选中
|
||||||
|
const ids = Array.isArray(value) ? value : value ? [value] : [];
|
||||||
|
if (scope.row) scope.row.deptName = ids;
|
||||||
|
},
|
||||||
|
data: this.deptTree,
|
||||||
|
'node-key': 'value',
|
||||||
|
'check-strictly': true,
|
||||||
|
multiple: true,
|
||||||
|
filterable: true,
|
||||||
|
clearable: true,
|
||||||
|
'render-after-expand': false,
|
||||||
|
collapseTags: true,
|
||||||
|
'collapse-tags-tooltip': true,
|
||||||
|
'default-expand-all': false,
|
||||||
|
style: 'width: 100%',
|
||||||
|
placeholder: '请选择 所属组织',
|
||||||
|
props: { label: 'label', value: 'value', children: 'children' },
|
||||||
|
});
|
||||||
|
},
|
||||||
handleNoFixedTermChange(checked) {
|
handleNoFixedTermChange(checked) {
|
||||||
this.archiveForm.businessTermType = checked ? '长期' : '固定期限';
|
this.archiveForm.businessTermType = checked ? '长期' : '固定期限';
|
||||||
if (checked) {
|
if (checked) {
|
||||||
@@ -4440,6 +4458,13 @@ export default {
|
|||||||
this.onLoad(this.page);
|
this.onLoad(this.page);
|
||||||
},
|
},
|
||||||
searchChange(params, done) {
|
searchChange(params, done) {
|
||||||
|
// 搜索区所属组织多选返回的是部门 ID 数组,需转成部门名称逗号串传给后端(与列表接口 deptName 字段一致)
|
||||||
|
if (Array.isArray(params.deptName)) {
|
||||||
|
params.deptName = params.deptName
|
||||||
|
.map(id => this.deptOptions.find(item => String(item.value) === String(id))?.rawLabel)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(',');
|
||||||
|
}
|
||||||
this.query = this.buildQuery(params);
|
this.query = this.buildQuery(params);
|
||||||
this.page.currentPage = 1;
|
this.page.currentPage = 1;
|
||||||
this.onLoad(this.page);
|
this.onLoad(this.page);
|
||||||
|
|||||||
Reference in New Issue
Block a user