Merge remote-tracking branch 'websoft/master'
# Conflicts: # src/views/settlement/receivable-payable-detail.vue
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# 2026-08-31
|
||||
|
||||
## 客商档案「所属组织」改为单选级联(customer-archive.vue)
|
||||
- 表单区 `el-tree-select`(多选勾选树)→ `el-cascader` 单选级联;`deptCascaderProps` 开 `checkStrictly: true`,可任意层级选中(不必选到叶子)。
|
||||
- 引入 computed `deptCascaderValue`(get/set)做「部门 ID ↔ 根到节点路径」双向转换:
|
||||
- `archiveForm.deptId` 仍为选中部门 ID 数组(单选只一个),后端字段 `deptIds` 逗号串 / `deptName` 不变,保存逻辑 `normalizeArchive` 无需改。
|
||||
- getter 依赖 `deptTree`,部门树异步加载完成后详情回显自动刷新(解决 created 里 initDeptTree 与 openArchive 并发的时序问题)。
|
||||
- `onDeptChange(path)` 改为取路径末级 → 写 `deptId / deptIds / deptName`;新增 `findDeptPath(id)`、`findDeptLabel(id)` 递归查找。
|
||||
- `normalizeDeptIds(detail)` 单选只取第一个 ID(历史多选数据自动收敛)。
|
||||
- 列表搜索区 `renderDeptSearch` 同步改为 `h(ElCascader, ...)`(import 从 ElTreeSelect 换 ElCascader),`searchChange` 取路径末级 ID 转部门名称。
|
||||
</content>
|
||||
</invoke>
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getDeptTree } from '@/api/system/dept';
|
||||
import { ElTreeSelect } from 'element-plus';
|
||||
import { ElCascader } from 'element-plus';
|
||||
import { h } from 'vue';
|
||||
|
||||
/**
|
||||
* 所属组织 统一搜索 mixin
|
||||
* ------------------------------------------------------------------
|
||||
* 全站「所属组织」搜索栏统一为树形下拉(el-tree-select),风格与客户档案
|
||||
* 全站「所属组织」搜索栏统一为级联选择器(el-cascader,单选),风格与客商档案
|
||||
* (vehicle/customer-archive.vue)保持一致。
|
||||
*
|
||||
* 接入步骤(在使用了 avue-crud 的页面中):
|
||||
@@ -15,24 +15,40 @@ import { h } from 'vue';
|
||||
*
|
||||
* 说明:
|
||||
* - 自动识别 organizationName / deptName 两种 prop,并挂载 column.renderSearch
|
||||
* - 树节点标识统一用 id(node-key='id'),返回值为部门 id
|
||||
* - 树节点标识统一用 id,级联值为「根 → 选中节点」的 id 路径数组
|
||||
* - checkStrictly 打开,任意层级(含非末级)都可直接选中
|
||||
* - 页面若已有 excludeExternalOrganization / normalizeOrgTree 等方法,
|
||||
* 组件自身方法优先级更高,会覆盖本 mixin 的默认实现
|
||||
* - 搜索值(id)需在各自的 searchChange / normalizeSearch 中按后端要求
|
||||
* 转换为部门名称(参考 contract-manage 的 normalizeSearch)
|
||||
* - 搜索值需在各自的 searchChange / normalizeSearch 中按后端要求转换为部门名称:
|
||||
* 级联值是路径数组,统一用 findOrgById(路径)?.rawLabel 取末级名称
|
||||
* - 表单区(新增/编辑)需要回显时,可用 findOrgPath(deptId) 把 id 转成路径,
|
||||
* 参考 customer-archive 的 deptCascaderValue 计算属性
|
||||
*/
|
||||
const ORG_PROPS = ['organizationName', 'deptName'];
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// 树形组织数据,仅供搜索栏 el-tree-select 使用;
|
||||
// 树形组织数据,仅供搜索栏 el-cascader 使用;
|
||||
// 与页面自身 organizationOptions(多为表单扁平列表)隔离,避免相互覆盖
|
||||
organizationTreeOptions: [],
|
||||
organizationTreeFlatOptions: [],
|
||||
organizationLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 所属组织级联配置:checkStrictly 允许选中任意层级,不必选到末级
|
||||
organizationCascaderProps() {
|
||||
return {
|
||||
value: 'id',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadOrganizationOptions() {
|
||||
if (this.organizationLoading) return;
|
||||
@@ -90,25 +106,67 @@ export default {
|
||||
...this.flattenOrgTree(item.children || []),
|
||||
]);
|
||||
},
|
||||
// 兼容两种入参:级联路径数组(取末级)或单个部门 id
|
||||
findOrgById(id) {
|
||||
const value = Array.isArray(id) ? id.at(-1) : id;
|
||||
return this.organizationTreeFlatOptions.find(item => String(item.id) === String(value));
|
||||
},
|
||||
renderOrganizationSearch(scope, prop = 'organizationName') {
|
||||
return h(ElTreeSelect, {
|
||||
modelValue: scope.row?.[prop] || '',
|
||||
'onUpdate:modelValue': value => {
|
||||
if (scope.row) scope.row[prop] = value;
|
||||
// 由部门 id 反查「根 → 该节点」的 id 路径,供级联回显(找不到返回 null)
|
||||
findOrgPath(id, tree = this.organizationTreeOptions, parents = []) {
|
||||
const target = Array.isArray(id) ? id.at(-1) : id;
|
||||
if (target === undefined || target === null || target === '') return null;
|
||||
for (const node of tree || []) {
|
||||
const path = [...parents, String(node.id)];
|
||||
if (String(node.id) === String(target)) return path;
|
||||
const matched = this.findOrgPath(target, node.children, path);
|
||||
if (matched) return matched;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
// 由部门 id 取组织名称(末级),供表单提交
|
||||
findOrgName(id) {
|
||||
return this.findOrgById(id)?.rawLabel || '';
|
||||
},
|
||||
// 组织名称 → 级联路径(表单回显用;部门树异步加载完成后会自动重算)
|
||||
resolveOrgPath(name) {
|
||||
const value = String(name ?? '').trim();
|
||||
if (!value) return [];
|
||||
const node = this.organizationTreeFlatOptions.find(item => item.rawLabel === value);
|
||||
return node ? this.findOrgPath(node.id) || [] : [];
|
||||
},
|
||||
// 级联路径 → 组织名称(表单/搜索提交用,取末级节点名称)
|
||||
resolveOrgName(path) {
|
||||
const value = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
return value.length ? this.findOrgById(value)?.rawLabel || '' : '';
|
||||
},
|
||||
// 搜索提交前把级联路径数组就地转成组织名称(后端按名称过滤)
|
||||
normalizeOrganizationSearch(params, prop = 'organizationName') {
|
||||
if (Array.isArray(params?.[prop])) {
|
||||
params[prop] = this.resolveOrgName(params[prop]);
|
||||
}
|
||||
return params;
|
||||
},
|
||||
renderOrganizationSearch(scope, prop = 'organizationName') {
|
||||
return h(ElCascader, {
|
||||
modelValue: Array.isArray(scope.row?.[prop]) ? scope.row[prop] : [],
|
||||
'onUpdate:modelValue': value => {
|
||||
// 级联单选:modelValue 必须是 id 路径数组,否则控件内部状态会错乱导致无法再次选中
|
||||
if (scope.row) scope.row[prop] = Array.isArray(value) ? value : [];
|
||||
},
|
||||
options: this.organizationTreeOptions,
|
||||
props: {
|
||||
label: 'label',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
},
|
||||
data: this.organizationTreeOptions,
|
||||
'node-key': 'id',
|
||||
'check-strictly': true,
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
'render-after-expand': false,
|
||||
style: 'width: 100%',
|
||||
placeholder: '请选择 所属组织',
|
||||
props: { label: 'label', value: 'id', children: 'children' },
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -38,7 +38,7 @@ export const formalSettlementSearchFields = [
|
||||
{ label: '结算单号', prop: 'formalSettlementNo', type: 'input' },
|
||||
{ label: '预结算单号', prop: 'preSettlementNo', type: 'input' },
|
||||
{ label: '项目', prop: 'projectName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'cascader' },
|
||||
{ label: '合同编号', prop: 'contractNo', type: 'input' },
|
||||
{ label: '付款方', prop: 'payerName', type: 'input' },
|
||||
{ label: '收款方', prop: 'payeeName', type: 'input' },
|
||||
|
||||
@@ -10,7 +10,7 @@ export const preSettlementSearchFields = [
|
||||
{ label: '预结算单号', prop: 'preSettlementNo', type: 'input' },
|
||||
{ label: '预付单号', prop: 'advanceNo', type: 'input' },
|
||||
{ label: '项目名称', prop: 'projectName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'cascader' },
|
||||
{ label: '合同名称', prop: 'contractName', type: 'input' },
|
||||
{ label: '合同编号', prop: 'contractNo', type: 'input' },
|
||||
{ label: '收款方', prop: 'payeeName', type: 'input' },
|
||||
|
||||
@@ -14,7 +14,7 @@ export const searchFields = [
|
||||
{ label: '单据号', prop: 'documentNo', type: 'input' },
|
||||
{ label: '生成日期', prop: 'generateDateRange', type: 'daterange' },
|
||||
{ label: '客户名称', prop: 'customerName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'cascader' },
|
||||
{ label: '项目名称', prop: 'projectName', type: 'input' },
|
||||
{ label: '货物类型', prop: 'cargoType', type: 'input' },
|
||||
{ label: '货物名称', prop: 'cargoName', type: 'input' },
|
||||
|
||||
@@ -5,7 +5,7 @@ export const settlementAdjustmentSearchFields = [
|
||||
{ label: '日期', prop: 'createDateRange', type: 'daterange' },
|
||||
{ label: '客户名称', prop: 'customerName', type: 'input' },
|
||||
{ label: '项目名称', prop: 'projectName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'input' },
|
||||
{ label: '所属组织', prop: 'deptName', type: 'cascader' },
|
||||
{ label: '关联结算单', prop: 'formalSettlementNo', type: 'input' },
|
||||
{
|
||||
label: '结算单类型',
|
||||
|
||||
@@ -15,7 +15,7 @@ export const transportReconciliationSearchFields = [
|
||||
{ prop: 'reconciliationNo', label: '对账单号', type: 'input' },
|
||||
{ prop: 'preSettlementNos', label: '预结算单号', type: 'input' },
|
||||
{ prop: 'projectName', label: '项目', type: 'input' },
|
||||
{ prop: 'deptName', label: '所属组织', type: 'input' },
|
||||
{ prop: 'deptName', label: '所属组织', type: 'cascader' },
|
||||
{ prop: 'contractNo', label: '合同编号', type: 'input' },
|
||||
{ prop: 'payerName', label: '付款方', type: 'input' },
|
||||
{ prop: 'payeeName', label: '收款方', type: 'input' },
|
||||
|
||||
@@ -135,9 +135,10 @@
|
||||
//white-space: nowrap;
|
||||
}
|
||||
|
||||
// 全宽兜底:搜索表单内树形下拉占满(覆盖默认搜索项宽度)
|
||||
// 注意:搜索 form 内的 el-tree-select 也走该规则;新增/编辑表单用 span=6 的栅格自然分开,不受影响。
|
||||
.avue-crud__search .el-form-item__content > .el-tree-select {
|
||||
// 全宽兜底:搜索表单内树形下拉 / 级联选择器占满(覆盖默认搜索项宽度)
|
||||
// 注意:搜索 form 内的 el-tree-select、el-cascader 均走该规则;新增/编辑表单用 span=6 的栅格自然分开,不受影响。
|
||||
.avue-crud__search .el-form-item__content > .el-tree-select,
|
||||
.avue-crud__search .el-form-item__content > .el-cascader {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@@ -216,18 +216,14 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属组织" prop="organizationName">
|
||||
<el-tree-select
|
||||
v-model="selectedOrganizationId"
|
||||
:data="organizationOptions"
|
||||
:props="organizationTreeSelectProps"
|
||||
node-key="id"
|
||||
<el-cascader
|
||||
v-model="organizationCascaderValue"
|
||||
:options="organizationOptions"
|
||||
:props="organizationCascaderProps"
|
||||
placeholder="请选择"
|
||||
check-strictly
|
||||
clearable
|
||||
filterable
|
||||
:render-after-expand="false"
|
||||
@visible-change="visible => visible && loadOrganizationOptions()"
|
||||
@change="handleOrganizationChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="经办人">
|
||||
@@ -794,7 +790,7 @@ import { getToken } from '@/utils/auth';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||
import { ElImageViewer, ElTreeSelect } from 'element-plus';
|
||||
import { ElImageViewer, ElCascader } from 'element-plus';
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import { fallbackPlugin, imagePlugin, officePlugin, textPlugin } from '@open-file-viewer/core';
|
||||
import '@open-file-viewer/core/style.css';
|
||||
@@ -1230,13 +1226,32 @@ export default {
|
||||
contractFormatOptions() {
|
||||
return this.columnOptions('contractFormat');
|
||||
},
|
||||
organizationTreeSelectProps() {
|
||||
organizationCascaderProps() {
|
||||
return {
|
||||
label: 'label',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 所属组织级联:selectedOrganizationId 存部门 id,级联控件用根到节点的 id 路径,
|
||||
// 两者在此双向转换;依赖 organizationOptions,树加载完成后回显自动刷新
|
||||
organizationCascaderValue: {
|
||||
get() {
|
||||
const id = this.selectedOrganizationId || this.form.organizationId;
|
||||
if (!id) return [];
|
||||
return this.findOrganizationPath(id) || [String(id)];
|
||||
},
|
||||
set(path) {
|
||||
const value = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
this.selectedOrganizationId = value.length ? String(value.at(-1)) : '';
|
||||
this.handleOrganizationChange(value);
|
||||
},
|
||||
},
|
||||
settlementRuleEnabled() {
|
||||
return Number(this.settlementRuleForm.autoGenerate) === 1;
|
||||
},
|
||||
@@ -1728,6 +1743,18 @@ export default {
|
||||
const value = Array.isArray(id) ? id.at(-1) : id;
|
||||
return this.organizationFlatOptions.find(item => String(item.id) === String(value));
|
||||
},
|
||||
// 由部门 id 反查「根 → 该节点」的 id 路径,供级联回显(找不到返回 null)
|
||||
findOrganizationPath(id, tree = this.organizationOptions, parents = []) {
|
||||
const target = Array.isArray(id) ? id.at(-1) : id;
|
||||
if (target === undefined || target === null || target === '') return null;
|
||||
for (const node of tree || []) {
|
||||
const path = [...parents, String(node.id)];
|
||||
if (String(node.id) === String(target)) return path;
|
||||
const matched = this.findOrganizationPath(target, node.children, path);
|
||||
if (matched) return matched;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
handleOrganizationChange(id) {
|
||||
const organization = this.findOrganization(id);
|
||||
Object.assign(
|
||||
@@ -1752,21 +1779,26 @@ export default {
|
||||
this.organizationFlatOptions.unshift(item);
|
||||
},
|
||||
renderOrganizationSearch(scope) {
|
||||
// 搜索区所属组织:树形下拉(与客户档案统一)
|
||||
return h(ElTreeSelect, {
|
||||
modelValue: scope.row?.organizationName || '',
|
||||
// 搜索区所属组织:单选级联(与新增/编辑表单一致)
|
||||
// 级联返回 id 路径数组,提交时由 normalizeSearch 取末级 id 转组织名称
|
||||
return h(ElCascader, {
|
||||
modelValue: Array.isArray(scope.row?.organizationName) ? scope.row.organizationName : [],
|
||||
'onUpdate:modelValue': value => {
|
||||
if (scope.row) scope.row.organizationName = value;
|
||||
// 级联单选:modelValue 必须是 id 路径数组,否则控件内部状态会错乱导致无法再次选中
|
||||
if (scope.row) scope.row.organizationName = Array.isArray(value) ? value : [];
|
||||
},
|
||||
options: this.organizationOptions,
|
||||
props: {
|
||||
label: 'label',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
},
|
||||
data: this.organizationOptions,
|
||||
'node-key': 'id',
|
||||
'check-strictly': true,
|
||||
filterable: true,
|
||||
clearable: true,
|
||||
'render-after-expand': false,
|
||||
style: 'width: 100%',
|
||||
placeholder: '请选择 所属组织',
|
||||
props: { label: 'label', value: 'id', children: 'children' },
|
||||
});
|
||||
},
|
||||
integerInput(prop, value) {
|
||||
|
||||
@@ -166,33 +166,25 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务部门" prop="businessDeptId">
|
||||
<el-tree-select
|
||||
v-model="form.businessDeptId"
|
||||
:data="businessDeptTreeOptions"
|
||||
:props="deptTreeSelectProps"
|
||||
node-key="value"
|
||||
<el-cascader
|
||||
v-model="businessDeptCascaderValue"
|
||||
:options="businessDeptTreeOptions"
|
||||
:props="deptCascaderProps"
|
||||
placeholder="请选择业务部门"
|
||||
check-strictly
|
||||
filterable
|
||||
clearable
|
||||
:render-after-expand="false"
|
||||
:disabled="isBasicInfoReadonly"
|
||||
@change="handleBusinessDeptChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="承办部门" prop="undertakeDeptId">
|
||||
<el-tree-select
|
||||
v-model="form.undertakeDeptId"
|
||||
:data="deptTreeOptions"
|
||||
:props="deptTreeSelectProps"
|
||||
node-key="value"
|
||||
<el-cascader
|
||||
v-model="undertakeDeptCascaderValue"
|
||||
:options="deptTreeOptions"
|
||||
:props="deptCascaderProps"
|
||||
placeholder="请选择承办部门"
|
||||
check-strictly
|
||||
clearable
|
||||
filterable
|
||||
:render-after-expand="false"
|
||||
:disabled="isBasicInfoReadonly"
|
||||
@change="handleUndertakeDeptChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="项目由来" prop="projectSource">
|
||||
@@ -1047,11 +1039,6 @@ export default {
|
||||
deptOptions: [],
|
||||
businessDeptTreeOptions: [],
|
||||
deptTreeOptions: [],
|
||||
deptTreeSelectProps: {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
},
|
||||
cargoTypeOptions: [],
|
||||
selectedCustomerId: [],
|
||||
selectedCarrierIds: [],
|
||||
@@ -1110,6 +1097,45 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
// 部门级联配置:checkStrictly 允许选中任意层级,不必选到末级
|
||||
deptCascaderProps() {
|
||||
return {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 业务部门:form 存部门 id,级联控件用根到节点的 id 路径,两者在此双向转换
|
||||
businessDeptCascaderValue: {
|
||||
get() {
|
||||
const id = this.form.businessDeptId;
|
||||
return id ? this.findDeptPath(id, this.businessDeptTreeOptions) || [id] : [];
|
||||
},
|
||||
set(path) {
|
||||
const value = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
this.form.businessDeptId = value.length ? value.at(-1) : '';
|
||||
this.handleBusinessDeptChange(this.form.businessDeptId);
|
||||
},
|
||||
},
|
||||
// 承办部门:同上
|
||||
undertakeDeptCascaderValue: {
|
||||
get() {
|
||||
const id = this.form.undertakeDeptId;
|
||||
return id ? this.findDeptPath(id, this.deptTreeOptions) || [id] : [];
|
||||
},
|
||||
set(path) {
|
||||
const value = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
this.form.undertakeDeptId = value.length ? value.at(-1) : '';
|
||||
this.handleUndertakeDeptChange(this.form.undertakeDeptId);
|
||||
},
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.canCreate,
|
||||
@@ -1843,15 +1869,30 @@ export default {
|
||||
return `${base}-BG${this.$dayjs().format('YYYYMMDD')}`;
|
||||
},
|
||||
handleBusinessDeptChange(value) {
|
||||
const dept = this.deptOptions.find(item => item.value === value);
|
||||
// 兼容级联路径数组(取末级)与单个部门 id
|
||||
const id = Array.isArray(value) ? value.at(-1) : value;
|
||||
const dept = this.deptOptions.find(item => String(item.value) === String(id));
|
||||
this.form.businessDeptName = dept ? dept.rawLabel : '';
|
||||
this.$refs.projectForm?.validateField('businessDeptId');
|
||||
},
|
||||
handleUndertakeDeptChange(value) {
|
||||
const dept = this.deptOptions.find(item => item.value === value);
|
||||
const id = Array.isArray(value) ? value.at(-1) : value;
|
||||
const dept = this.deptOptions.find(item => String(item.value) === String(id));
|
||||
this.form.undertakeDeptName = dept ? dept.rawLabel : '';
|
||||
this.$refs.projectForm?.validateField('undertakeDeptId');
|
||||
},
|
||||
// 由部门 id 反查「根 → 该节点」的 id 路径,供级联回显(找不到返回 null)
|
||||
findDeptPath(id, tree = [], parents = []) {
|
||||
const target = Array.isArray(id) ? id.at(-1) : id;
|
||||
if (target === undefined || target === null || target === '') return null;
|
||||
for (const node of tree || []) {
|
||||
const path = [...parents, node.value];
|
||||
if (String(node.value) === String(target)) return path;
|
||||
const matched = this.findDeptPath(target, node.children, path);
|
||||
if (matched) return matched;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
handleProjectTypeChange() {
|
||||
this.normalizeAttachmentFileTypes();
|
||||
this.$refs.projectForm?.validateField('projectType');
|
||||
|
||||
@@ -48,6 +48,7 @@ import BillPaymentTable from './components/bill-payment-table.vue';
|
||||
const emptyQuery = () => ({
|
||||
paymentNo: '',
|
||||
deptName: '',
|
||||
deptPath: [],
|
||||
paymentDateRange: [],
|
||||
approvalStatus: '',
|
||||
});
|
||||
|
||||
@@ -6,18 +6,15 @@
|
||||
<el-input v-model="query.paymentNo" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item label="使用部门">
|
||||
<el-tree-select
|
||||
:model-value="query.deptName"
|
||||
:data="organizationTreeOptions"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
<el-cascader
|
||||
v-model="query.deptPath"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
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 }"
|
||||
@change="val => { query.deptName = findOrgById(val)?.rawLabel || '' }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="付款日期">
|
||||
|
||||
@@ -10,18 +10,15 @@
|
||||
<el-input v-model="query.projectName" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属组织">
|
||||
<el-tree-select
|
||||
:model-value="query.deptName"
|
||||
:data="organizationTreeOptions"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
<el-cascader
|
||||
v-model="query.deptPath"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
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 }"
|
||||
@change="val => { query.deptName = findOrgById(val)?.rawLabel || '' }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="invoice-kingdee-item" label="金蝶单据状态">
|
||||
@@ -252,6 +249,7 @@ const emptyQuery = () => ({
|
||||
applicationNo: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptPath: [],
|
||||
kingdeeStatus: '',
|
||||
approvalStatus: '',
|
||||
});
|
||||
|
||||
@@ -19,18 +19,15 @@
|
||||
<el-input v-model="query.projectName" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属组织">
|
||||
<el-tree-select
|
||||
:model-value="query.deptName"
|
||||
:data="organizationTreeOptions"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
<el-cascader
|
||||
v-model="query.deptPath"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
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 }"
|
||||
@change="val => { query.deptName = findOrgById(val)?.rawLabel || '' }"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="searchExpanded" label="审核状态">
|
||||
@@ -245,6 +242,7 @@ const emptyQuery = () => ({
|
||||
invoiceDate: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptPath: [],
|
||||
approvalStatus: '',
|
||||
kingdeeStatus: '',
|
||||
});
|
||||
|
||||
@@ -23,18 +23,15 @@
|
||||
/></el-form-item>
|
||||
<template v-if="searchExpanded">
|
||||
<el-form-item label="所属组织"
|
||||
><el-tree-select
|
||||
:model-value="query.deptName"
|
||||
:data="organizationTreeOptions"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
><el-cascader
|
||||
v-model="query.deptPath"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
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 }"
|
||||
@change="val => { query.deptName = findOrgById(val)?.rawLabel || '' }"
|
||||
/></el-form-item>
|
||||
<el-form-item label="关联结算单"
|
||||
><el-input v-model="query.settlementNo" clearable
|
||||
@@ -243,6 +240,7 @@ const emptyQuery = () => ({
|
||||
payeeName: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptPath: [],
|
||||
settlementNo: '',
|
||||
paymentType: '',
|
||||
approvalStatus: '',
|
||||
|
||||
@@ -27,6 +27,16 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-cascader
|
||||
v-else-if="field.type === 'cascader'"
|
||||
v-model="query[field.prop]"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<div class="formal-page__search-actions">
|
||||
@@ -168,6 +178,7 @@ import {
|
||||
formalSettlementSearchFields,
|
||||
paymentStatusOptions,
|
||||
} from '@/option/settlement/formalSettlementSearch';
|
||||
import organizationSearch from '@/mixins/organization-search';
|
||||
import { formalSettlementTableColumns } from '@/option/settlement/formalSettlementTable';
|
||||
import FormalSettlementEditor from './components/formal-settlement-editor.vue';
|
||||
import FormalSettlementTablePanel from './components/formal-settlement-table-panel.vue';
|
||||
@@ -176,7 +187,7 @@ const emptyQuery = () => ({
|
||||
formalSettlementNo: '',
|
||||
preSettlementNo: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptName: [],
|
||||
contractNo: '',
|
||||
payerName: '',
|
||||
payeeName: '',
|
||||
@@ -190,6 +201,7 @@ const emptyQuery = () => ({
|
||||
export default {
|
||||
name: 'FormalSettlement',
|
||||
components: { FormalSettlementEditor, FormalSettlementTablePanel },
|
||||
mixins: [organizationSearch],
|
||||
data() {
|
||||
return {
|
||||
ArrowDown,
|
||||
@@ -231,6 +243,7 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadOrganizationOptions();
|
||||
this.loadTable();
|
||||
this.openRouteDetail();
|
||||
},
|
||||
@@ -517,7 +530,8 @@ export default {
|
||||
XLSX.writeFile(workbook, `正式结算单${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`);
|
||||
},
|
||||
buildQueryParams() {
|
||||
const params = { ...this.query };
|
||||
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
|
||||
const params = this.normalizeOrganizationSearch({ ...this.query }, 'deptName');
|
||||
params.settlementType = this.activeSettlementType;
|
||||
const range = params.createDateRange || [];
|
||||
delete params.createDateRange;
|
||||
|
||||
@@ -29,6 +29,16 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-cascader
|
||||
v-else-if="field.type === 'cascader'"
|
||||
v-model="query[field.prop]"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
@@ -283,6 +293,7 @@ import {
|
||||
voidBill,
|
||||
} from '@/api/settlement/preSettlement';
|
||||
import { preSettlementSearchFields } from '@/option/settlement/preSettlementSearch';
|
||||
import organizationSearch from '@/mixins/organization-search';
|
||||
import { preSettlementTableColumns } from '@/option/settlement/preSettlementTable';
|
||||
import { createSettlementTransfer } from '@/utils/settlement-transfer';
|
||||
import { downloadFile } from '@/utils/util';
|
||||
@@ -292,7 +303,7 @@ const emptyQuery = () => ({
|
||||
preSettlementNo: '',
|
||||
advanceNo: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptName: [],
|
||||
contractName: '',
|
||||
contractNo: '',
|
||||
payeeName: '',
|
||||
@@ -304,6 +315,7 @@ const emptyQuery = () => ({
|
||||
export default {
|
||||
name: 'PreSettlement',
|
||||
components: { PreSettlementEditor },
|
||||
mixins: [organizationSearch],
|
||||
data() {
|
||||
return {
|
||||
ArrowDown,
|
||||
@@ -357,6 +369,7 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadOrganizationOptions();
|
||||
this.loadTable();
|
||||
this.openRouteDetail();
|
||||
},
|
||||
@@ -377,8 +390,10 @@ export default {
|
||||
},
|
||||
buildQuery() {
|
||||
const range = this.query.createDateRange || [];
|
||||
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
|
||||
const query = this.normalizeOrganizationSearch({ ...this.query }, 'deptName');
|
||||
return {
|
||||
...this.query,
|
||||
...query,
|
||||
createDateRange: undefined,
|
||||
createStartDate: range[0],
|
||||
createEndDate: range[1],
|
||||
|
||||
@@ -25,6 +25,16 @@
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/></el-select>
|
||||
<el-cascader
|
||||
v-else-if="field.type === 'cascader'"
|
||||
v-model="query[field.prop]"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
<div class="settlement-adjustment-page__search-actions">
|
||||
@@ -159,6 +169,7 @@ import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import * as api from '@/api/settlement/settlementAdjustment';
|
||||
import { settlementAdjustmentSearchFields } from '@/option/settlement/settlementAdjustmentSearch';
|
||||
import organizationSearch from '@/mixins/organization-search';
|
||||
import { settlementAdjustmentTableColumns } from '@/option/settlement/settlementAdjustmentTable';
|
||||
import SettlementAdjustmentEditor from './components/settlement-adjustment-editor.vue';
|
||||
|
||||
@@ -167,7 +178,7 @@ const emptyQuery = () => ({
|
||||
createDateRange: [],
|
||||
customerName: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptName: [],
|
||||
formalSettlementNo: '',
|
||||
settlementType: '',
|
||||
approvalStatus: '',
|
||||
@@ -175,6 +186,7 @@ const emptyQuery = () => ({
|
||||
export default {
|
||||
name: 'SettlementAdjustment',
|
||||
components: { SettlementAdjustmentEditor },
|
||||
mixins: [organizationSearch],
|
||||
data: () => ({
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
@@ -199,6 +211,7 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadOrganizationOptions();
|
||||
this.loadTable();
|
||||
},
|
||||
methods: {
|
||||
@@ -210,7 +223,8 @@ export default {
|
||||
try {
|
||||
const range = this.query.createDateRange || [];
|
||||
const { data } = await api.getList(this.page.current, this.page.size, {
|
||||
...this.query,
|
||||
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
|
||||
...this.normalizeOrganizationSearch({ ...this.query }, 'deptName'),
|
||||
createDateRange: undefined,
|
||||
createStartDate: range[0],
|
||||
createEndDate: range[1],
|
||||
|
||||
@@ -17,6 +17,16 @@
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-cascader
|
||||
v-else-if="field.type === 'cascader'"
|
||||
v-model="query[field.prop]"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<el-input v-else v-model="query[field.prop]" clearable placeholder="请输入" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
@@ -87,6 +97,7 @@ import * as XLSX from 'xlsx';
|
||||
import * as api from '@/api/settlement/transportReconciliation';
|
||||
import { transportReconciliationSearchFields } from '@/option/settlement/transportReconciliationSearch';
|
||||
import { transportReconciliationTableColumns } from '@/option/settlement/transportReconciliationTable';
|
||||
import organizationSearch from '@/mixins/organization-search';
|
||||
import TransportReconciliationEditor from './components/transport-reconciliation-editor.vue';
|
||||
import TransportReconciliationTablePanel from './components/transport-reconciliation-table-panel.vue';
|
||||
|
||||
@@ -94,7 +105,7 @@ const emptyQuery = () => ({
|
||||
reconciliationNo: '',
|
||||
preSettlementNos: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
deptName: [],
|
||||
contractNo: '',
|
||||
payerName: '',
|
||||
payeeName: '',
|
||||
@@ -105,6 +116,7 @@ const emptyQuery = () => ({
|
||||
export default {
|
||||
name: 'TransportReconciliation',
|
||||
components: { TransportReconciliationEditor, TransportReconciliationTablePanel },
|
||||
mixins: [organizationSearch],
|
||||
data() {
|
||||
return {
|
||||
ArrowDown,
|
||||
@@ -129,6 +141,7 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadOrganizationOptions();
|
||||
this.loadTable();
|
||||
},
|
||||
methods: {
|
||||
@@ -139,7 +152,8 @@ export default {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await api.getList(this.page.current, this.page.size, {
|
||||
...this.query,
|
||||
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
|
||||
...this.normalizeOrganizationSearch({ ...this.query }, 'deptName'),
|
||||
settlementType: this.settlementType,
|
||||
});
|
||||
const data = response?.data?.data || response?.data || response || {};
|
||||
|
||||
@@ -195,7 +195,7 @@
|
||||
<el-col :span="6"><el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="性别"><el-select v-model="form.sex"><el-option label="男" :value="1" /><el-option label="女" :value="2" /><el-option label="未知" :value="3" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="岗位" prop="postId"><el-tree-select v-model="form.postId" :data="postList" :props="{ label: 'postName', value: 'id' }" check-strictly /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="所属组织" prop="deptId"><el-tree-select v-model="form.deptId" :data="permissionDeptTree" :props="{ label: 'title', value: 'id' }" multiple check-strictly show-checkbox /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="所属组织" prop="deptId"><el-cascader v-model="deptCascaderValue" :options="permissionDeptTree" :props="deptCascaderProps" clearable filterable placeholder="请选择 所属组织" /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="工号"><el-input v-model="form.code" /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="人员类别"><el-select v-model="form.personCategory"><el-option label="内部员工" :value="1" /><el-option label="承运商" :value="2" /></el-select></el-form-item></el-col>
|
||||
<el-col :span="18"><el-form-item label="数据权限范围"><el-radio-group v-model="form.dataScopeRange"><el-radio :value="1">仅所属组织</el-radio><el-radio :value="2">全部组织</el-radio><el-radio :value="3">自定义</el-radio></el-radio-group></el-form-item></el-col>
|
||||
@@ -443,6 +443,36 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo', 'permission']),
|
||||
// 所属组织级联配置:checkStrictly 允许选中任意层级,不必选到末级
|
||||
deptCascaderProps() {
|
||||
return {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 所属组织为单选级联:form.deptId 仍为数组(提交时 join),此处与 id 路径双向转换
|
||||
deptCascaderValue: {
|
||||
get() {
|
||||
const ids = Array.isArray(this.form.deptId)
|
||||
? this.form.deptId
|
||||
: this.form.deptId
|
||||
? [this.form.deptId]
|
||||
: [];
|
||||
const id = ids.find(item => item !== undefined && item !== null && item !== '');
|
||||
return id === undefined ? [] : this.findDeptPath(id) || [id];
|
||||
},
|
||||
set(path) {
|
||||
const value = Array.isArray(path)
|
||||
? path.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
this.form.deptId = value.length ? [value.at(-1)] : [];
|
||||
this.$refs.userFormRef?.validateField('deptId');
|
||||
},
|
||||
},
|
||||
permissionList() {
|
||||
return {
|
||||
addBtn: this.validData(this.permission.user_add, false),
|
||||
@@ -565,6 +595,18 @@ export default {
|
||||
this.customerList = res.data.data || [];
|
||||
});
|
||||
},
|
||||
// 由部门 id 反查「根 → 该节点」的 id 路径,供级联回显(找不到返回 null)
|
||||
findDeptPath(id, tree = this.permissionDeptTree, parents = []) {
|
||||
const target = Array.isArray(id) ? id.at(-1) : id;
|
||||
if (target === undefined || target === null || target === '') return null;
|
||||
for (const node of tree || []) {
|
||||
const path = [...parents, node.id];
|
||||
if (String(node.id) === String(target)) return path;
|
||||
const matched = this.findDeptPath(target, node.children, path);
|
||||
if (matched) return matched;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
submitRole() {
|
||||
const roleList = this.$refs.treeRole.getCheckedKeys().join(',');
|
||||
if (this.roleFormMode) {
|
||||
|
||||
@@ -490,14 +490,14 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="所属组织" prop="organizationName">
|
||||
<el-select v-model="driverForm.organizationName" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in organizationOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
<el-cascader
|
||||
v-model="organizationCascaderValue"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择 所属组织"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -815,6 +815,15 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
// 所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
|
||||
organizationCascaderValue: {
|
||||
get() {
|
||||
return this.resolveOrgPath(this.driverForm.organizationName);
|
||||
},
|
||||
set(path) {
|
||||
this.driverForm.organizationName = this.resolveOrgName(path);
|
||||
},
|
||||
},
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
@@ -1613,6 +1622,8 @@ export default {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
// 所属组织级联返回的是 id 路径,转成组织名称传给后端
|
||||
this.normalizeOrganizationSearch(params);
|
||||
this.searchForm = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
|
||||
@@ -145,19 +145,14 @@
|
||||
<el-row :gutter="18">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="船舶所属组织" prop="organizationName">
|
||||
<el-select
|
||||
v-model="shipForm.organizationName"
|
||||
<el-cascader
|
||||
v-model="organizationCascaderValue"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in organizationOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
placeholder="请选择 船舶所属组织"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@@ -707,6 +702,15 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
// 船舶所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
|
||||
organizationCascaderValue: {
|
||||
get() {
|
||||
return this.resolveOrgPath(this.shipForm.organizationName);
|
||||
},
|
||||
set(path) {
|
||||
this.shipForm.organizationName = this.resolveOrgName(path);
|
||||
},
|
||||
},
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
@@ -1100,6 +1104,8 @@ export default {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
// 所属组织级联返回的是 id 路径,转成组织名称传给后端
|
||||
this.normalizeOrganizationSearch(params);
|
||||
this.searchForm = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
|
||||
@@ -159,19 +159,14 @@
|
||||
<el-row :gutter="18">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="所有组织" prop="organizationName">
|
||||
<el-select
|
||||
v-model="vehicleForm.organizationName"
|
||||
<el-cascader
|
||||
v-model="organizationCascaderValue"
|
||||
:options="organizationTreeOptions"
|
||||
:props="organizationCascaderProps"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in organizationOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
placeholder="请选择 所属组织"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -709,6 +704,15 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
// 所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
|
||||
organizationCascaderValue: {
|
||||
get() {
|
||||
return this.resolveOrgPath(this.vehicleForm.organizationName);
|
||||
},
|
||||
set(path) {
|
||||
this.vehicleForm.organizationName = this.resolveOrgName(path);
|
||||
},
|
||||
},
|
||||
isAdmin() {
|
||||
const authority = this.userInfo.authority || '';
|
||||
return authority.includes('admin');
|
||||
@@ -1275,6 +1279,8 @@ export default {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
// 所属组织级联返回的是 id 路径,转成组织名称传给后端
|
||||
this.normalizeOrganizationSearch(params);
|
||||
this.searchForm = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
|
||||
@@ -335,17 +335,13 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="所属组织" prop="deptName">
|
||||
<el-tree-select
|
||||
v-model="archiveForm.deptId"
|
||||
:data="deptTree"
|
||||
:props="{ label: 'label', value: 'value', children: 'children' }"
|
||||
multiple
|
||||
node-key="value"
|
||||
check-strictly
|
||||
filterable
|
||||
<el-cascader
|
||||
v-model="deptCascaderValue"
|
||||
:options="deptTree"
|
||||
:props="deptCascaderProps"
|
||||
clearable
|
||||
:render-after-expand="false"
|
||||
@change="onDeptChange"
|
||||
filterable
|
||||
placeholder="请选择 所属组织"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -1367,7 +1363,7 @@
|
||||
|
||||
<script>
|
||||
import { h } from 'vue';
|
||||
import { ElTreeSelect } from 'element-plus';
|
||||
import { ElCascader } from 'element-plus';
|
||||
import {
|
||||
getList,
|
||||
getDetail,
|
||||
@@ -1875,6 +1871,34 @@ export default {
|
||||
emitPath: true,
|
||||
};
|
||||
},
|
||||
deptCascaderProps() {
|
||||
return {
|
||||
value: 'value',
|
||||
label: 'label',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
};
|
||||
},
|
||||
// 所属组织级联值:archiveForm.deptId 只存选中的部门 ID(单选),
|
||||
// 级联控件需要根到选中节点的完整路径,两者在此做双向转换——
|
||||
// 部门树是异步加载的,依赖 deptTree 可保证树加载完成后回显自动刷新。
|
||||
deptCascaderValue: {
|
||||
get() {
|
||||
const ids = Array.isArray(this.archiveForm.deptId)
|
||||
? this.archiveForm.deptId
|
||||
: this.archiveForm.deptId
|
||||
? [this.archiveForm.deptId]
|
||||
: [];
|
||||
const currentId = ids.length ? String(ids[ids.length - 1]) : '';
|
||||
if (!currentId) return [];
|
||||
return this.findDeptPath(currentId) || [currentId];
|
||||
},
|
||||
set(path) {
|
||||
this.onDeptChange(path);
|
||||
},
|
||||
},
|
||||
ids() {
|
||||
let ids = [];
|
||||
this.selectionList.forEach(ele => {
|
||||
@@ -2645,38 +2669,57 @@ export default {
|
||||
this.onDeptChange([firstTopLevelDept.value]);
|
||||
},
|
||||
onDeptChange(value) {
|
||||
const deptIds = Array.isArray(value) ? value : value ? [value] : [];
|
||||
const deptNames = deptIds
|
||||
.map(id => this.deptOptions.find(item => String(item.value) === String(id))?.rawLabel)
|
||||
.filter(Boolean);
|
||||
this.archiveForm.deptId = deptIds;
|
||||
this.archiveForm.deptIds = deptIds.join(',');
|
||||
this.archiveForm.deptName = deptNames.join(',');
|
||||
// 级联单选:value 为根到选中节点的 ID 路径,取末级作为客商所属组织
|
||||
const path = Array.isArray(value)
|
||||
? value.filter(item => item !== undefined && item !== null && item !== '')
|
||||
: [];
|
||||
const deptId = path.length ? String(path[path.length - 1]) : '';
|
||||
this.archiveForm.deptId = deptId ? [deptId] : [];
|
||||
this.archiveForm.deptIds = deptId;
|
||||
this.archiveForm.deptName = deptId ? this.findDeptLabel(deptId) : '';
|
||||
this.$refs.archiveForm?.validateField('deptName');
|
||||
},
|
||||
findDeptPath(id, tree = this.deptTree, parents = []) {
|
||||
for (const node of tree || []) {
|
||||
const path = [...parents, String(node.value)];
|
||||
if (String(node.value) === String(id)) return path;
|
||||
const children = node.children || [];
|
||||
if (children.length) {
|
||||
const matched = this.findDeptPath(id, children, path);
|
||||
if (matched) return matched;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
findDeptLabel(id, tree = this.deptTree) {
|
||||
for (const node of tree || []) {
|
||||
if (String(node.value) === String(id)) return node.label;
|
||||
const matched = this.findDeptLabel(id, node.children || []);
|
||||
if (matched) return matched;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
renderDeptSearch(scope) {
|
||||
// 搜索区所属组织:多选树形下拉(与新增/编辑表单一致)
|
||||
// 多选返回 ID 数组,转换为部门名称按逗号拼接写入 searchForm.deptName
|
||||
return h(ElTreeSelect, {
|
||||
// 搜索区所属组织:单选级联(与新增/编辑表单一致)
|
||||
// 级联返回的是根到选中节点的 ID 路径数组,提交时取末级 ID 转部门名称
|
||||
return h(ElCascader, {
|
||||
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;
|
||||
// 级联单选:modelValue 必须是 ID 路径数组,否则控件内部状态会错乱导致无法再次选中
|
||||
if (scope.row) scope.row.deptName = Array.isArray(value) ? value : [];
|
||||
},
|
||||
options: this.deptTree,
|
||||
props: {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
children: 'children',
|
||||
checkStrictly: true,
|
||||
expandTrigger: 'click',
|
||||
},
|
||||
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) {
|
||||
@@ -2717,13 +2760,15 @@ export default {
|
||||
: [];
|
||||
},
|
||||
normalizeDeptIds(detail) {
|
||||
// 所属组织为单选级联,只保留第一个部门 ID(历史多选数据自动收敛)
|
||||
const value = detail.deptIds || detail.deptId;
|
||||
return value
|
||||
const ids = value
|
||||
? String(value)
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(item => item !== '')
|
||||
: [];
|
||||
return ids.length ? [ids[0]] : [];
|
||||
},
|
||||
formatArchiveAddress(archive = {}) {
|
||||
return [archive.registeredRegionName, archive.registeredDetailAddress]
|
||||
@@ -4539,12 +4584,15 @@ export default {
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params, done) {
|
||||
// 搜索区所属组织多选返回的是部门 ID 数组,需转成部门名称逗号串传给后端(与列表接口 deptName 字段一致)
|
||||
// 搜索区所属组织级联返回的是 ID 路径数组,取末级 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(',');
|
||||
const path = params.deptName.filter(
|
||||
item => item !== undefined && item !== null && item !== ''
|
||||
);
|
||||
const deptId = path.length ? path[path.length - 1] : '';
|
||||
params.deptName = deptId
|
||||
? this.deptOptions.find(item => String(item.value) === String(deptId))?.rawLabel || ''
|
||||
: '';
|
||||
}
|
||||
this.query = this.buildQuery(params);
|
||||
this.page.currentPage = 1;
|
||||
|
||||
+13
-13
@@ -47,26 +47,26 @@ export default ({ mode, command }) => {
|
||||
__VUE_I18N_LEGACY_API__: true,
|
||||
__INTLIFY_PROD_DEVTOOLS__: false,
|
||||
},
|
||||
server: {
|
||||
port: 2888,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost',
|
||||
//target: 'https://saber3.bladex.cn/api',
|
||||
changeOrigin: true,
|
||||
rewrite: path => path.replace(/^\/api/, ''),
|
||||
},
|
||||
},
|
||||
},
|
||||
// server: {
|
||||
// port: 2889,
|
||||
// port: 2888,
|
||||
// proxy: {
|
||||
// '/api': {
|
||||
// target: 'http://172.16.203.228:8000',
|
||||
// target: 'http://localhost',
|
||||
// //target: 'https://saber3.bladex.cn/api',
|
||||
// changeOrigin: true,
|
||||
// rewrite: path => path.replace(/^\/api/, ''),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
server: {
|
||||
port: 2889,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://172.16.203.228:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': resolve(__dirname, './'),
|
||||
|
||||
Reference in New Issue
Block a user