【v3.8.3】部门大改造关联修改

pull/8786/merge
JEECG 2025-09-14 10:37:05 +08:00
parent 1158977826
commit fa98817aeb
13 changed files with 422 additions and 32 deletions

View File

@ -16,6 +16,12 @@ enum Api {
getCategoryData = '/sys/category/loadAllData', getCategoryData = '/sys/category/loadAllData',
refreshDragCache = '/drag/page/refreshCache', refreshDragCache = '/drag/page/refreshCache',
refreshDefaultIndexCache = '/sys/sysRoleIndex/cleanDefaultIndexCache', refreshDefaultIndexCache = '/sys/sysRoleIndex/cleanDefaultIndexCache',
//
queryDepartAndPostTreeSync = '/sys/sysDepart/queryDepartAndPostTreeSync',
//
queryDepartPostUserPageList = '/sys/user/queryDepartPostUserPageList',
//ID
queryAllParentId = '/sys/sysDepart/queryAllParentId',
} }
/** /**
@ -53,6 +59,13 @@ export const getRoleList = (params) => {
export const queryDepartTreeSync = (params?) => { export const queryDepartTreeSync = (params?) => {
return defHttp.get({ url: Api.queryDepartTreeSync, params }); return defHttp.get({ url: Api.queryDepartTreeSync, params });
}; };
/**
* 异步获取部门职位树列表
*/
export const queryDepartAndPostTreeSync = (params?) => {
return defHttp.get({ url: Api.queryDepartAndPostTreeSync, params });
};
/** /**
* 获取部门树列表 * 获取部门树列表
*/ */
@ -86,6 +99,21 @@ export const getDictItems = (dictCode) => {
export const getTableList = (params) => { export const getTableList = (params) => {
return defHttp.get({ url: Api.getTableList, params }); return defHttp.get({ url: Api.getTableList, params });
}; };
/**
* 部门岗位用户modal查询部门岗位下的用户
*/
export const queryDepartPostUserPageList = (params) => {
return defHttp.get({ url: Api.queryDepartPostUserPageList, params });
};
/**
* 查询所选部门的所有父节点ID
*/
export const queryAllParentId = (params) => {
return defHttp.get({ url: Api.queryAllParentId, params });
};
/** /**
* 加载全部分类字典数据 * 加载全部分类字典数据
*/ */

View File

@ -3,6 +3,8 @@ import { inject, reactive, ref, computed, unref, watch, nextTick } from 'vue';
import { TreeActionType } from '/@/components/Tree'; import { TreeActionType } from '/@/components/Tree';
import { listToTree } from '/@/utils/common/compUtils'; import { listToTree } from '/@/utils/common/compUtils';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import { defHttp } from "@/utils/http/axios";
import { queryAllParentId } from "/@/api/common/api";
export function useTreeBiz(treeRef, getList, props, realProps, emit) { export function useTreeBiz(treeRef, getList, props, realProps, emit) {
// //
@ -24,6 +26,10 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
// loading // loading
let isFirstLoadEcho = true; let isFirstLoadEcho = true;
let prevSelectValues = []; let prevSelectValues = [];
// ID
const expandedKeys = ref<Array<string | number>>([]);
// props
const enableAutoExpand = props.enableAutoExpand !== false;
/** /**
* 监听selectValues变化 * 监听selectValues变化
*/ */
@ -55,11 +61,49 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
function getTree() { function getTree() {
const tree = unref(treeRef); const tree = unref(treeRef);
if (!tree) { if (!tree) {
throw new Error('tree is null!'); //throw new Error('tree is null!');
return null;
} }
return tree; return tree;
} }
/**
* 获取需要展开的父节点ID
*/
async function getParentIdsToExpand(selectedIds) {
if (!selectedIds || selectedIds.length === 0) return [];
try {
const result = await queryAllParentId({
departId: selectedIds.join(','),
orgCode: props.params?.orgCode
});
if (result) {
const allParentIds = [];
// Map Object
const valuesToProcess = result instanceof Map
? Array.from(result.values())
: Object.values(result);
//
valuesToProcess.forEach((nodeData: any) => {
if (nodeData && nodeData.parentIds && Array.isArray(nodeData.parentIds)) {
// ID
const parentIds = nodeData.parentIds.filter(id => !selectedIds.includes(id));
allParentIds.push(...parentIds);
}
});
return [...new Set(allParentIds)]; //
}
return [];
} catch (error) {
console.warn('获取父节点ID失败:', error);
return [];
}
}
/** /**
* 设置树展开级别 * 设置树展开级别
*/ */
@ -70,7 +114,19 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
} }
// //
checkedKeys.value = selectValues['value']; checkedKeys.value = selectValues['value'];
}).then();
//
if (expandedKeys.value.length > 0) {
getTree().setExpandedKeys(expandedKeys.value);
}
}).then(() => {
//
if (expandedKeys.value.length > 0) {
setTimeout(() => {
getTree().setExpandedKeys(expandedKeys.value);
}, 100);
}
});
} }
/** /**
@ -171,8 +227,20 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
startPid = ''; startPid = '';
params['ids'] = ids; params['ids'] = ids;
} }
if(props.params?.departIds){
params['departIds'] = props.params.departIds;
}
let record = await getList(params); let record = await getList(params);
let optionData = record; let optionData = record;
//+
if(props.onlyShowCompany){
record = getCompanyData(record)
}
//
if (props.izOnlySelectDepartPost) {
setCompanyDepartCheckable(record);
}
if (!props.serverTreeData) { if (!props.serverTreeData) {
//tree //tree
record = listToTree(record, props, startPid); record = listToTree(record, props, startPid);
@ -212,6 +280,14 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
} }
} }
/**
* 获取到公司/子公司数据
* @param record
*/
function getCompanyData(record){
const companyData = record.filter(item=>item.orgCategory && ['1','4'].includes(item.orgCategory));
return companyData
}
/** /**
* 异步加载时检测是否含有下级节点 * 异步加载时检测是否含有下级节点
* @param pid 父节点 * @param pid 父节点
@ -256,6 +332,57 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
// //
openModal.value = true; openModal.value = true;
await onLoadData(null, null); await onLoadData(null, null);
//
if (enableAutoExpand && selectValues.value && selectValues.value.length > 0) {
try {
const selectedIds = selectValues.value;
const parentIds = await getParentIdsToExpand(selectedIds);
if (parentIds.length > 0) {
expandedKeys.value = parentIds;
//
nextTick(() => {
try {
const tree = getTree();
if (tree) {
tree.setExpandedKeys(parentIds);
//
setTimeout(() => {
try {
const tree = getTree();
if (tree) {
tree.setExpandedKeys(parentIds);
console.log('父节点已展开:', parentIds);
// 使
setTimeout(() => {
try {
const tree = getTree();
if (tree) {
tree.setExpandedKeys(parentIds);
}
} catch (error) {
console.warn('展开父节点失败:', error);
}
}, 500);
}
} catch (error) {
console.warn('展开父节点失败:', error);
}
}, 200);
}
} catch (error) {
console.warn('展开父节点失败:', error);
}
});
}
} catch (error) {
console.warn('获取父节点ID失败:', error);
}
}
} else { } else {
openModal.value = false; openModal.value = false;
// update-begin--author:liaozhiyang---date:20240527---forTV360X-414(JSelectUser) // update-begin--author:liaozhiyang---date:20240527---forTV360X-414(JSelectUser)
@ -264,6 +391,46 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
} }
} }
/**
* 设置公司部门复选框显示
* @param record
*/
function setCompanyDepartCheckable(record) {
if (record && record.length > 0) {
for (const item of record) {
if (item.orgCategory !== '3') {
item.checkable = false;
item.selectable = false;
} else {
item.checkable = true;
item.selectable = true;
}
if (item.isLeaf) {
setCompanyDepartCheckable(item.children);
}
}
}
}
/**
* 岗位搜索
*
* @param value
*/
async function onSearch(value) {
if(value){
let result = await defHttp.get({ url: "/sys/sysDepart/searchBy", params: { keyWord: value, orgCategory: "3",...props.params } });
if (Array.isArray(result)) {
treeData.value = result;
} else {
treeData.value = [];
}
} else {
treeData.value = [];
await onLoadData(null, null)
}
}
return [ return [
{ {
visibleChange, visibleChange,
@ -279,6 +446,8 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
treeData, treeData,
getCheckStrictly, getCheckStrictly,
getSelectTreeData, getSelectTreeData,
onSearch,
expandedKeys,
}, },
]; ];
} }

View File

@ -50,7 +50,7 @@ export const treeProps = {
// //
defaultExpandLevel: { defaultExpandLevel: {
type: [Number], type: [Number],
default: 0, default: 1,
}, },
//pid //pid
startPid: { startPid: {
@ -78,10 +78,14 @@ export const treeProps = {
sync: propTypes.bool.def(true), sync: propTypes.bool.def(true),
// //
showButton: propTypes.bool.def(true), showButton: propTypes.bool.def(true),
//
onlyShowCompany: propTypes.bool.def(false),
// //
checkable: propTypes.bool.def(true), checkable: propTypes.bool.def(true),
//checkable //checkable
checkStrictly: propTypes.bool.def(false), checkStrictly: propTypes.bool.def(false),
// true // true
multiple: propTypes.bool.def(true), multiple: propTypes.bool.def(true),
//
izOnlySelectDepartPost: propTypes.bool.def(false),
}; };

View File

@ -219,6 +219,22 @@ export const schemas: FormSchema[] = [
label: '选中部门', label: '选中部门',
colProps: { span: 12 }, colProps: { span: 12 },
}, },
{
field: 'depart4',
component: 'JSelectDepartPost',
label: '选择岗位',
helpMessage: ['component模式'],
componentProps: { showButton: false },
colProps: {
span: 12,
},
},
{
field: 'depart4',
component: 'JEllipsis',
label: '选择岗位',
colProps: { span: 12 },
},
{ {
field: 'user2', field: 'user2',
component: 'JSelectUser', component: 'JSelectUser',
@ -258,6 +274,25 @@ export const schemas: FormSchema[] = [
label: '选中用户', label: '选中用户',
colProps: { span: 12 }, colProps: { span: 12 },
}, },
{
field: 'userPost1',
component: 'JSelectUserByDeptPost',
label: '部门岗位选择用户',
helpMessage: ['component模式'],
componentProps: {
labelKey: 'realname',
rowKey: 'username',
},
colProps: {
span: 12,
},
},
{
field: 'userPost1',
component: 'JEllipsis',
label: '选中用户',
colProps: { span: 12 },
},
{ {
field: 'user4', field: 'user4',
component: 'JSelectUserByDepartment', component: 'JSelectUserByDepartment',

View File

@ -3,7 +3,7 @@ import { defHttp } from '/@/utils/http/axios';
export enum Api { export enum Api {
list = '/sys/user/queryByOrgCodeForAddressList', list = '/sys/user/queryByOrgCodeForAddressList',
positionList = '/sys/position/list', positionList = '/sys/position/list',
queryDepartTreeSync = '/sys/sysDepart/queryDepartTreeSync', queryDepartTreeSync = '/sys/sysDepart/queryDepartAndPostTreeSync',
} }
/** /**
* 获取部门树列表 * 获取部门树列表

View File

@ -14,7 +14,12 @@ export const columns: BasicColumn[] = [
}, },
{ {
title: '部门', title: '部门',
dataIndex: 'departName', dataIndex: 'orgCodeTxt',
width: 200,
},
{
title: '主岗位',
dataIndex: 'mainDepPostId_dictText',
width: 200, width: 200,
}, },
{ {

View File

@ -14,7 +14,11 @@
:load-data="loadChildrenTreeData" :load-data="loadChildrenTreeData"
v-model:expandedKeys="expandedKeys" v-model:expandedKeys="expandedKeys"
@select="onSelect" @select="onSelect"
></a-tree> >
<template #title="{ orgCategory, title }">
<TreeIcon :orgCategory="orgCategory" :title="title"></TreeIcon>
</template>
</a-tree>
</template> </template>
<a-empty v-else description="暂无数据" /> <a-empty v-else description="暂无数据" />
</a-spin> </a-spin>
@ -26,6 +30,7 @@
import { queryDepartTreeSync } from '../address.api'; import { queryDepartTreeSync } from '../address.api';
import { searchByKeywords } from '/@/views/system/departUser/depart.user.api'; import { searchByKeywords } from '/@/views/system/departUser/depart.user.api';
import { Popconfirm } from 'ant-design-vue'; import { Popconfirm } from 'ant-design-vue';
import TreeIcon from "@/components/Form/src/jeecg/components/TreeIcon/TreeIcon.vue";
const prefixCls = inject('prefixCls'); const prefixCls = inject('prefixCls');
// props // props
@ -142,7 +147,7 @@
try { try {
loading.value = true; loading.value = true;
treeData.value = []; treeData.value = [];
let result = await searchByKeywords({ keyWord: value }); let result = await searchByKeywords({ keyWord: value, orgCategory: '1,2,3,4' });
if (Array.isArray(result)) { if (Array.isArray(result)) {
treeData.value = result; treeData.value = result;
} }

View File

@ -25,10 +25,12 @@
import { provide, ref, unref } from 'vue'; import { provide, ref, unref } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign'; import { useDesign } from '/@/hooks/web/useDesign';
import DepartLeftTree from './components/DepartLeftTree.vue'; import DepartLeftTree from './components/DepartLeftTree.vue';
import { BasicTable } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './address.data'; import { columns, searchFormSchema } from './address.data';
import { list, positionList } from './address.api'; import { list, positionList } from './address.api';
import { getCacheByDynKey } from "@/utils/auth";
import { JEECG_CHAT_UID } from "@/enums/cacheEnum";
const { prefixCls } = useDesign('address-list'); const { prefixCls } = useDesign('address-list');
provide('prefixCls', prefixCls); provide('prefixCls', prefixCls);
@ -46,14 +48,13 @@
api: list, api: list,
columns, columns,
//update-begin---author:wangshuai ---date:20220629 for[VUEN-1485]-------------- //update-begin---author:wangshuai ---date:20220629 for[VUEN-1485]--------------
rowKey: 'userId', rowKey: 'id',
//update-end---author:wangshuai ---date:20220629 for[VUEN-1485]---------------- //update-end---author:wangshuai ---date:20220629 for[VUEN-1485]----------------
showIndexColumn: true, showIndexColumn: true,
formConfig: { formConfig: {
schemas: searchFormSchema, schemas: searchFormSchema,
}, },
canResize: false, canResize: false,
actionColumn: null,
showTableSetting: false, showTableSetting: false,
// //
beforeFetch(params) { beforeFetch(params) {

View File

@ -168,6 +168,8 @@ export function useBaseInfoForm(treeData: Ref<any[]>) {
return '部门'; return '部门';
} else if (val === '3') { } else if (val === '3') {
return '岗位'; return '岗位';
} else if(val === '4'){
return '子公司';
} }
return val; return val;
}, },

View File

@ -34,7 +34,7 @@
} }
}); });
// //
const getTitle = computed(() => (!unref(isUpdate) ? '新增职务' : '编辑职务')); const getTitle = computed(() => (!unref(isUpdate) ? '新增职务级别' : '编辑职务级别'));
// //
async function handleSubmit() { async function handleSubmit() {
try { try {
@ -51,3 +51,8 @@
} }
} }
</script> </script>
<style lang="less" scoped>
:deep(.ant-input-number){
width: 100%;
}
</style>

View File

@ -0,0 +1,133 @@
<template>
<div>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleAdd"></a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button
>批量操作
<Icon icon="ant-design:down-outlined"></Icon>
</a-button>
</a-dropdown>
</template>
<template #action="{ record }">
<TableAction :actions="getActions(record)" />
</template>
</BasicTable>
<PositionModal @register="registerModal" @success="reload" />
</div>
</template>
<script lang="ts" name="TenantPositionList" setup>
import { onMounted } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { getPositionList, deletePosition, batchDeletePosition, getExportUrl, getImportUrl } from './position.api';
import { columns, searchFormSchema } from './position.data';
import PositionModal from './PositionModal.vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { useListPage } from '/@/hooks/system/useListPage';
import { tenantSaasMessage } from '@/utils/common/compUtils';
import { getTenantId } from '@/utils/auth';
const [registerModal, { openModal }] = useModal();
//
const { onExportXls, onImportXls, tableContext } = useListPage({
tableProps: {
title: '租户职务列表',
api: getPositionList,
columns: columns,
formConfig: {
schemas: searchFormSchema,
},
actionColumn: {
width: 180,
},
showIndexColumn: true,
defSort: {
column: '',
order: '',
},
beforeFetch(params) {
return Object.assign({ tenantId: getTenantId() }, params);
},
},
exportConfig: {
name: '租户职务列表',
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 操作列定义
* @param record
*/
function getActions(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑事件
*/
function handleEdit(record) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deletePosition({ id: record.id }, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDeletePosition({ ids: selectedRowKeys.value }, () => {
selectedRowKeys.value = [];
reload();
});
}
onMounted(() => {
//
tenantSaasMessage('租户职务');
});
</script>

View File

@ -53,6 +53,10 @@
width: 180, width: 180,
}, },
showIndexColumn: true, showIndexColumn: true,
defSort: {
column: "",
order: ""
}
}, },
exportConfig: { exportConfig: {
name: '职务列表', name: '职务列表',

View File

@ -9,22 +9,21 @@ export const columns: BasicColumn[] = [
// align: 'left', // align: 'left',
// }, // },
{ {
title: '职务名称', title: '职务级别名称',
dataIndex: 'name', dataIndex: 'name',
align: 'left' align: 'left'
// width: 200, // width: 200,
}, },
// { {
// title: '', title: '职务级别(越小级别越高)',
// dataIndex: 'postRank_dictText', dataIndex: 'postLevel',
// width: 100, },
// },
]; ];
export const searchFormSchema: FormSchema[] = [ export const searchFormSchema: FormSchema[] = [
{ {
field: 'name', field: 'name',
label: '职务名称', label: '职务级别名称',
component: 'Input', component: 'Input',
colProps: { span: 8 }, colProps: { span: 8 },
}, },
@ -37,25 +36,25 @@ export const formSchema: FormSchema[] = [
component: 'Input', component: 'Input',
show: false, show: false,
}, },
// {
// label: '',
// field: 'postRank',
// component: 'JDictSelectTag',
// required: true,
// componentProps: {
// dictCode: 'position_rank',
// dropdownStyle: {
// maxHeight: '100vh',
// },
// getPopupContainer: () => document.body,
// },
// },
{ {
field: 'name', field: 'name',
label: '职务名称', label: '职务级别名称',
component: 'Input', component: 'Input',
required: true, required: true,
}, },
{
label: '职务级别',
field: 'postLevel',
component: 'InputNumber',
required: true,
componentProps: {
min: 1,
max: 99
},
dynamicRules: ({ model, schema }) => {
return [{ required: true, message: '请输入职务级别!' }];
},
},
// { // {
// field: 'code', // field: 'code',
// label: '', // label: '',