feat(organization): 全站所属组织搜索统一为树形下拉并修复项目部门宽度
- 新增复用 mixin organization-search.js,封装树形组织搜索功能,自动识别不同架构 - 替换合同管理、运输能力(司机/车辆/船舶)、费用模块的所属组织搜索使用树形下拉组件 - 将自定义搜索表单中所属组织 input 替换为 el-tree-select,支持树节点选中并写回名称 - 防止不同页面间组织数据相互覆盖,mixin 内部树数据命名为 organizationTreeOptions - 修复项目补录页面业务部门错用全宽样式,恢复半宽显示,调整注释避免误读 - 各相关页面组件中引入并使用 mixin,调用接口加载组织树数据,保障搜索功能一致性
This commit is contained in:
@@ -43,3 +43,18 @@
|
|||||||
- 验证:`vite build --outDir /tmp/tms-verify-build` 全量编译通过(exit 0,无报错)。注意原 `pnpm build` 因 WorkBuddy safe-delete 钩子拦截清空 dist/(741 文件)而中断,非代码问题。
|
- 验证:`vite build --outDir /tmp/tms-verify-build` 全量编译通过(exit 0,无报错)。注意原 `pnpm build` 因 WorkBuddy safe-delete 钩子拦截清空 dist/(741 文件)而中断,非代码问题。
|
||||||
- 注意:登录页 `activeName` 默认 `'iam'`(IAM 统一登录),图形验证码在 `userlogin.vue`,需点「账号密码登录」才可见;验证码图片依赖后端 `/blade-auth/oauth/captcha` 返回的 `data.key` + `data.image`。
|
- 注意:登录页 `activeName` 默认 `'iam'`(IAM 统一登录),图形验证码在 `userlogin.vue`,需点「账号密码登录」才可见;验证码图片依赖后端 `/blade-auth/oauth/captcha` 返回的 `data.key` + `data.image`。
|
||||||
|
|
||||||
|
## 全站「所属组织」搜索统一为树形下拉(el-tree-select)
|
||||||
|
- 背景:合同管理页「所属组织」原为 cascader(radio 风格),客户档案页(vehicle/customer-archive)已用树形下拉。主人要求全站统一为树形。
|
||||||
|
- 统一方案:新建可复用 mixin `src/mixins/organization-search.js`(封装 organizationTreeOptions 树数据、loadOrganizationOptions()、renderOrganizationSearch(scope) 渲染函数,自动识别 organizationName / deptName 两种 prop 并挂 column.renderSearch;树节点 node-key='id')。
|
||||||
|
- 适用两种架构:
|
||||||
|
1. **avue-crud 搜索**(renderSearch 模式):option 列加 `searchslot:true`,视图混入 mixin + created/初始化调 loadOrganizationOptions。已改:contract-manage(option 去 cascader 改 searchslot,视图用 renderOrganizationSearch 渲染函数)、transportCapacity 的 driver/vehicle/ship 三页(option 去 type:select 改 searchslot,视图混 mixin)。
|
||||||
|
2. **自定义搜索表单**(query.deptName 文本框模式):替换 `<el-input v-model="query.deptName">` 为 `<el-tree-select>`,node-key='id',@update:model-value 用 findOrgById(val)?.rawLabel 把 id 转回部门名写入 query.deptName(保后端按名称过滤兼容)。已改 payment 模块 4 处:invoice-application / invoice-receipt / payment-application / components/bill-payment-search。
|
||||||
|
- 关键坑:mixin 内部树数据命名为 `organizationTreeOptions`(非 organizationOptions),与 transportCapacity 等页面表单自用的扁平 `organizationOptions` 隔离,避免相互覆盖破坏表单 select。
|
||||||
|
- 待办(同自定义表单模式,未改):settlement 模块 4 个视图(transport-reconciliation/formal-settlement/pre-settlement/settlement-adjustment,搜索用动态 v-for 渲染 searchFields,需给 deptName 加 `type:'tree'` 分支)、system/user.vue(deptName)。business 模块(waybill-manage/transport-plan 等)所属组织为 `hide:true` 不展示搜索,无需改。
|
||||||
|
|
||||||
|
## project-apply 业务部门栏占满宽度修复
|
||||||
|
- 文件:`src/views/business/project-apply.vue`
|
||||||
|
- 问题:「重大项目补录」(独立表单页 /business/project-apply/form?mode=majorSupplement)下"业务部门"项横跨整行,与上下两行半宽风格不一致。
|
||||||
|
- 根因:`<el-form-item label="业务部门">` 误加 `class="project-apply-form__item--full"`(该类对应样式 `:deep(.project-apply-form__item--full) { grid-column: 1 / -1; ... }` 是横跨整行的:本意只用于"项目由来说明"等多行文本字段)。
|
||||||
|
- 修复:移除该 class,业务部门恢复为半宽(一行两列网格的常规项),仅"项目由来说明"等需要全宽铺开的字段仍保留 full。同时把样式块上方注释由「占满整行的表单项(业务部门 / 项目由来说明)」改为「占满整行的表单项(如项目由来说明等多行文本字段)」,避免再误读。
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@
|
|||||||
:disabled="isBasicInfoReadonly"
|
:disabled="isBasicInfoReadonly"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="业务部门" prop="businessDeptId" class="project-apply-form__item--full">
|
<el-form-item label="业务部门" prop="businessDeptId">
|
||||||
<el-tree-select
|
<el-tree-select
|
||||||
v-model="form.businessDeptId"
|
v-model="form.businessDeptId"
|
||||||
:data="businessDeptTreeOptions"
|
:data="businessDeptTreeOptions"
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="项目由来说明" prop="sourceRemark" class="project-apply-form__item--full">
|
<el-form-item label="项目由来说明" prop="sourceRemark" class="project-apply-form__item--full">
|
||||||
<el-input v-model="form.sourceRemark" maxlength="300" :disabled="isBasicInfoReadonly" />
|
<el-input v-model="form.sourceRemark" type="textarea" rows="2" maxlength="300" :disabled="isBasicInfoReadonly" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2190,7 +2190,7 @@ export default {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 占满整行的表单项(业务部门 / 项目由来说明)
|
// 占满整行的表单项(如项目由来说明等多行文本字段)
|
||||||
:deep(.project-apply-form__item--full) {
|
:deep(.project-apply-form__item--full) {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,19 @@
|
|||||||
<el-input v-model="query.paymentNo" clearable placeholder="请输入" />
|
<el-input v-model="query.paymentNo" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="使用部门">
|
<el-form-item label="使用部门">
|
||||||
<el-input v-model="query.deptName" clearable placeholder="请输入" />
|
<el-tree-select
|
||||||
|
:model-value="query.deptName"
|
||||||
|
:data="organizationTreeOptions"
|
||||||
|
node-key="id"
|
||||||
|
check-strictly
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
render-after-expand
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请选择 所属组织"
|
||||||
|
:props="{ label: 'label', value: 'id', children: 'children' }"
|
||||||
|
@update:model-value="val => { query.deptName = findOrgById(val)?.rawLabel || val }"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="付款日期">
|
<el-form-item label="付款日期">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
@@ -47,9 +59,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||||
import { approvalStatusOptions } from '@/api/payment/billPayment';
|
import { approvalStatusOptions } from '@/api/payment/billPayment';
|
||||||
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BillPaymentSearch',
|
name: 'BillPaymentSearch',
|
||||||
|
mixins: [organizationSearch],
|
||||||
props: {
|
props: {
|
||||||
query: { type: Object, required: true },
|
query: { type: Object, required: true },
|
||||||
loading: { type: Boolean, default: false },
|
loading: { type: Boolean, default: false },
|
||||||
@@ -58,6 +72,9 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return { ArrowDown, ArrowUp, expanded: false, approvalStatusOptions };
|
return { ArrowDown, ArrowUp, expanded: false, approvalStatusOptions };
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loadOrganizationOptions();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,19 @@
|
|||||||
<el-input v-model="query.projectName" clearable placeholder="请输入" />
|
<el-input v-model="query.projectName" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属组织">
|
<el-form-item label="所属组织">
|
||||||
<el-input v-model="query.deptName" clearable placeholder="请输入" />
|
<el-tree-select
|
||||||
|
:model-value="query.deptName"
|
||||||
|
:data="organizationTreeOptions"
|
||||||
|
node-key="id"
|
||||||
|
check-strictly
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
render-after-expand
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请选择 所属组织"
|
||||||
|
:props="{ label: 'label', value: 'id', children: 'children' }"
|
||||||
|
@update:model-value="val => { query.deptName = findOrgById(val)?.rawLabel || val }"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item class="invoice-kingdee-item" label="金蝶单据状态">
|
<el-form-item class="invoice-kingdee-item" label="金蝶单据状态">
|
||||||
<el-select v-model="query.kingdeeStatus" clearable placeholder="请选择">
|
<el-select v-model="query.kingdeeStatus" clearable placeholder="请选择">
|
||||||
@@ -234,6 +246,7 @@ import * as XLSX from 'xlsx';
|
|||||||
import * as api from '@/api/payment/invoiceApplication';
|
import * as api from '@/api/payment/invoiceApplication';
|
||||||
import * as formalSettlementApi from '@/api/settlement/formalSettlement';
|
import * as formalSettlementApi from '@/api/settlement/formalSettlement';
|
||||||
import { invoiceApplicationTableColumns } from '@/option/payment/invoiceApplication';
|
import { invoiceApplicationTableColumns } from '@/option/payment/invoiceApplication';
|
||||||
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
|
|
||||||
const emptyQuery = () => ({
|
const emptyQuery = () => ({
|
||||||
applicationNo: '',
|
applicationNo: '',
|
||||||
@@ -245,6 +258,7 @@ const emptyQuery = () => ({
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'InvoiceApplication',
|
name: 'InvoiceApplication',
|
||||||
|
mixins: [organizationSearch],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
@@ -266,6 +280,7 @@ export default {
|
|||||||
...mapGetters(['permission']),
|
...mapGetters(['permission']),
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.loadOrganizationOptions();
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -19,7 +19,19 @@
|
|||||||
<el-input v-model="query.projectName" clearable placeholder="请输入" />
|
<el-input v-model="query.projectName" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="所属组织">
|
<el-form-item label="所属组织">
|
||||||
<el-input v-model="query.deptName" clearable placeholder="请输入" />
|
<el-tree-select
|
||||||
|
:model-value="query.deptName"
|
||||||
|
:data="organizationTreeOptions"
|
||||||
|
node-key="id"
|
||||||
|
check-strictly
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
render-after-expand
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请选择 所属组织"
|
||||||
|
:props="{ label: 'label', value: 'id', children: 'children' }"
|
||||||
|
@update:model-value="val => { query.deptName = findOrgById(val)?.rawLabel || val }"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="searchExpanded" label="审核状态">
|
<el-form-item v-if="searchExpanded" label="审核状态">
|
||||||
<el-select v-model="query.approvalStatus" clearable placeholder="请选择">
|
<el-select v-model="query.approvalStatus" clearable placeholder="请选择">
|
||||||
@@ -226,6 +238,7 @@ import { mapGetters } from 'vuex';
|
|||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import * as api from '@/api/payment/invoiceReceipt';
|
import * as api from '@/api/payment/invoiceReceipt';
|
||||||
import { invoiceReceiptTableColumns } from '@/option/payment/invoiceReceipt';
|
import { invoiceReceiptTableColumns } from '@/option/payment/invoiceReceipt';
|
||||||
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
|
|
||||||
const emptyQuery = () => ({
|
const emptyQuery = () => ({
|
||||||
invoiceNo: '',
|
invoiceNo: '',
|
||||||
@@ -238,6 +251,7 @@ const emptyQuery = () => ({
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'InvoiceReceipt',
|
name: 'InvoiceReceipt',
|
||||||
|
mixins: [organizationSearch],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
@@ -259,6 +273,7 @@ export default {
|
|||||||
...mapGetters(['permission']),
|
...mapGetters(['permission']),
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.loadOrganizationOptions();
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -23,7 +23,18 @@
|
|||||||
/></el-form-item>
|
/></el-form-item>
|
||||||
<template v-if="searchExpanded">
|
<template v-if="searchExpanded">
|
||||||
<el-form-item label="所属组织"
|
<el-form-item label="所属组织"
|
||||||
><el-input v-model="query.deptName" clearable
|
><el-tree-select
|
||||||
|
:model-value="query.deptName"
|
||||||
|
:data="organizationTreeOptions"
|
||||||
|
node-key="id"
|
||||||
|
check-strictly
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
render-after-expand
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请选择 所属组织"
|
||||||
|
:props="{ label: 'label', value: 'id', children: 'children' }"
|
||||||
|
@update:model-value="val => { query.deptName = findOrgById(val)?.rawLabel || val }"
|
||||||
/></el-form-item>
|
/></el-form-item>
|
||||||
<el-form-item label="关联结算单"
|
<el-form-item label="关联结算单"
|
||||||
><el-input v-model="query.settlementNo" clearable
|
><el-input v-model="query.settlementNo" clearable
|
||||||
@@ -224,6 +235,7 @@ import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import * as api from '@/api/payment/paymentApplication';
|
import * as api from '@/api/payment/paymentApplication';
|
||||||
import { paymentApplicationTableColumns } from '@/option/payment/paymentApplication';
|
import { paymentApplicationTableColumns } from '@/option/payment/paymentApplication';
|
||||||
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
|
|
||||||
const emptyQuery = () => ({
|
const emptyQuery = () => ({
|
||||||
paymentNo: '',
|
paymentNo: '',
|
||||||
@@ -238,6 +250,7 @@ const emptyQuery = () => ({
|
|||||||
});
|
});
|
||||||
export default {
|
export default {
|
||||||
name: 'PaymentApplication',
|
name: 'PaymentApplication',
|
||||||
|
mixins: [organizationSearch],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
@@ -263,6 +276,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.loadOrganizationOptions();
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -591,6 +591,7 @@ import {
|
|||||||
} from '@/api/transportCapacity/driver';
|
} from '@/api/transportCapacity/driver';
|
||||||
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
import { getList as getVehicleList } from '@/api/transportCapacity/transport-vehicle';
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
import { getDeptTree } from '@/api/system/dept';
|
||||||
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
import { getDictionary } from '@/api/system/dictbiz';
|
import { getDictionary } from '@/api/system/dictbiz';
|
||||||
import { getLazyTree } from '@/api/base/region';
|
import { getLazyTree } from '@/api/base/region';
|
||||||
import { exportBlob } from '@/api/common';
|
import { exportBlob } from '@/api/common';
|
||||||
@@ -661,6 +662,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
ImageUploadField,
|
ImageUploadField,
|
||||||
},
|
},
|
||||||
|
mixins: [organizationSearch],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
option,
|
option,
|
||||||
@@ -849,6 +851,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initDeptTree();
|
this.initDeptTree();
|
||||||
|
this.loadOrganizationOptions();
|
||||||
this.initRegionOptions();
|
this.initRegionOptions();
|
||||||
this.initQualificationTypeOptions();
|
this.initQualificationTypeOptions();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -574,6 +574,7 @@ import { ElLoading } from 'element-plus';
|
|||||||
import { InfoFilled } from '@element-plus/icons-vue';
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
import { option } from '@/option/transportCapacity/transport-ship';
|
import { option } from '@/option/transportCapacity/transport-ship';
|
||||||
import { getDeptTree } from '@/api/system/dept';
|
import { getDeptTree } from '@/api/system/dept';
|
||||||
|
import organizationSearch from '@/mixins/organization-search';
|
||||||
import {
|
import {
|
||||||
getList,
|
getList,
|
||||||
getDetail,
|
getDetail,
|
||||||
@@ -639,6 +640,7 @@ const emptyForm = () => ({
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ImageUploadField, InfoFilled, ShipLedger },
|
components: { ImageUploadField, InfoFilled, ShipLedger },
|
||||||
|
mixins: [organizationSearch],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
option,
|
option,
|
||||||
@@ -731,6 +733,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initDeptTree();
|
this.initDeptTree();
|
||||||
|
this.loadOrganizationOptions();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hasPermission(code) {
|
hasPermission(code) {
|
||||||
|
|||||||
Reference in New Issue
Block a user