Compare commits

...

2 Commits

  1. 1
      jeecg-boot-master/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/expert/service/impl/ExpertServiceImpl.java
  2. 23
      jeecg-boot-master/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDepartController.java
  3. 6
      jeecg-boot-master/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysDepartService.java
  4. 51
      jeecg-boot-master/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysDepartServiceImpl.java
  5. 4
      jeecgboot-vue3-master/src/views/system/depart/components/DepartLeftTree.vue
  6. 14
      jeecgboot-vue3-master/src/views/system/depart/depart.data.ts
  7. 78
      jeecgboot-vue3-master/src/views/system/departselect/components/DepartDataRuleDrawer.vue
  8. 92
      jeecgboot-vue3-master/src/views/system/departselect/components/DepartFormModal.vue
  9. 135
      jeecgboot-vue3-master/src/views/system/departselect/components/DepartFormTab.vue
  10. 332
      jeecgboot-vue3-master/src/views/system/departselect/components/DepartLeftTree.vue
  11. 170
      jeecgboot-vue3-master/src/views/system/departselect/components/DepartRuleTab.vue
  12. 129
      jeecgboot-vue3-master/src/views/system/departselect/depart.api.ts
  13. 83
      jeecgboot-vue3-master/src/views/system/departselect/depart.data.ts
  14. 14
      jeecgboot-vue3-master/src/views/system/departselect/index.less
  15. 52
      jeecgboot-vue3-master/src/views/system/departselect/index.vue
  16. 4
      jeecgboot-vue3-master/src/views/system/user/index.vue
  17. 13
      jeecgboot-vue3-master/src/views/system/user/user.data.js
  18. 18
      jeecgboot-vue3-master/src/views/system/user/user.data.ts
  19. 6
      jeecgboot-vue3-master/src/views/system/userZJ/user.data.ts
  20. 2
      jeecgboot-vue3-master/src/views/system/userdep/userYX.data.ts
  21. 2
      jeecgboot-vue3-master/src/views/system/userdep/userZJ.data.ts

@ -82,3 +82,4 @@ public class ExpertServiceImpl extends ServiceImpl<ExpertMapper, Expert> impleme
}
}
}

@ -132,15 +132,36 @@ public class SysDepartController {
public Result<List<SysDepartTreeModel>> queryDepartTreeSync(@RequestParam(name = "pid", required = false) String parentId,@RequestParam(name = "ids", required = false) String ids, @RequestParam(name = "primaryKey", required = false) String primaryKey) {
Result<List<SysDepartTreeModel>> result = new Result<>();
try {
List<SysDepartTreeModel> list = sysDepartService.queryTreeListByPid(parentId,ids, primaryKey);
List<SysDepartTreeModel> list = sysDepartService.queryTreeListByPid(parentId, ids, primaryKey);
result.setResult(list);
result.setSuccess(true);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
}
/**
* 异步查询当前用户部门
* @return
*/
@RequestMapping(value = "/queryDepartSync", method = RequestMethod.GET)
public Result<SysDepartTreeModel> queryDepartSync() {
Result<SysDepartTreeModel> result = new Result<>();
try {
List<SysDepartTreeModel> list = sysDepartService.queryUserDep();
result.setResult(list.get(0));
result.setSuccess(true);
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return result;
}
/**
* 获取某个部门的所有父级部门的ID
*

@ -198,4 +198,10 @@ public interface ISysDepartService extends IService<SysDepart>{
* @return
*/
IPage<SysDepart> getMaxCodeDepart(Page<SysDepart> page, String parentId);
/**
* 异步查询当前用户部门
* @return
*/
List<SysDepartTreeModel> queryUserDep();
}

@ -602,6 +602,57 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
return records;
}
/**
* 异步查询当前用户部门
* @return
*/
@Override
public List<SysDepartTreeModel> queryUserDep() {
/* Consumer<LambdaQueryWrapper<SysDepart>> square = i -> {
if (oConvertUtils.isNotEmpty(ids)) {
if (CommonConstant.DEPART_KEY_ORG_CODE.equals(primaryKey)) {
i.in(SysDepart::getOrgCode, ids.split(SymbolConstant.COMMA));
} else {
i.in(SysDepart::getId, ids.split(SymbolConstant.COMMA));
}
} else {
if(oConvertUtils.isEmpty(parentId)){
i.and(q->q.isNull(true,SysDepart::getParentId).or().eq(true,SysDepart::getParentId,""));
}else{
i.eq(true,SysDepart::getParentId,parentId);
}
}
};*/
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
LambdaQueryWrapper<SysDepart> lqw=new LambdaQueryWrapper<>();
//------------------------------------------------------------------------------------------------
//是否开启系统管理模块的 SASS 控制
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
lqw.eq(SysDepart::getTenantId, oConvertUtils.getInt(TenantContext.getTenant(), 0));
}
lqw.eq(SysDepart::getOrgCode, sysUser.getOrgCode());
//------------------------------------------------------------------------------------------------
lqw.eq(true,SysDepart::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
//lqw.func(square);
//update-begin---author:wangshuai ---date:20220527 for:[VUEN-1143]排序不对,vue3和2应该都有问题,应该按照升序排------------
lqw.orderByAsc(SysDepart::getDepartOrder);
//update-end---author:wangshuai ---date:20220527 for:[VUEN-1143]排序不对,vue3和2应该都有问题,应该按照升序排--------------
List<SysDepart> list = list(lqw);
//update-begin---author:wangshuai ---date:20220316 for:[JTC-119]在部门管理菜单下设置部门负责人 创建用户的时候不需要处理
//设置用户id,让前台显示
this.setUserIdsByDepList(list);
//update-end---author:wangshuai ---date:20220316 for:[JTC-119]在部门管理菜单下设置部门负责人 创建用户的时候不需要处理
List<SysDepartTreeModel> records = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
SysDepart depart = list.get(i);
SysDepartTreeModel treeModel = new SysDepartTreeModel(depart);
//TODO 异步树加载key拼接__+时间戳,以便于每次展开节点会刷新数据
//treeModel.setKey(treeModel.getKey()+"__"+System.currentTimeMillis());
records.add(treeModel);
}
return records;
}
@Override
public JSONObject queryAllParentIdByDepartId(String departId) {
JSONObject result = new JSONObject();

@ -7,8 +7,8 @@
<a-button type="primary" preIcon="ant-design:import-outlined">导入</a-button>
</a-upload>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
<a-button type="primary" preIcon="ant-design:sync-outlined">同步企微?</a-button>
<a-button type="primary" preIcon="ant-design:sync-outlined">同步钉钉?</a-button>
<!-- <a-button type="primary" preIcon="ant-design:sync-outlined">同步企微?</a-button>
<a-button type="primary" preIcon="ant-design:sync-outlined">同步钉钉?</a-button>-->
<template v-if="checkedKeys.length > 0">
<a-dropdown>
<template #overlay>

@ -5,12 +5,12 @@ export function useBasicFormSchema() {
const basicFormSchema: FormSchema[] = [
{
field: 'departName',
label: '机构名称',
label: '部门名称',
component: 'Input',
componentProps: {
placeholder: '请输入机构/部门名称',
placeholder: '请输入部门名称',
},
rules: [{ required: true, message: '机构名称不能为空' }],
rules: [{ required: true, message: '部门名称不能为空' }],
},
{
field: 'parentId',
@ -24,18 +24,18 @@ export function useBasicFormSchema() {
},
{
field: 'orgCode',
label: '机构编码',
label: '部门编码',
component: 'Input',
componentProps: {
placeholder: '请输入机构编码',
placeholder: '请输入部门编码',
},
},
{
/* {
field: 'orgCategory',
label: '机构类型',
component: 'RadioButtonGroup',
componentProps: { options: [] },
},
},*/
{
field: 'departOrder',
label: '排序',

@ -0,0 +1,78 @@
<template>
<BasicDrawer title="数据规则/按钮权限配置" :width="365" @close="onClose" @register="registerDrawer">
<a-spin :spinning="loading">
<a-tabs defaultActiveKey="1">
<a-tab-pane tab="数据规则" key="1">
<a-checkbox-group v-model:value="dataRuleChecked" v-if="dataRuleList.length > 0">
<a-row>
<a-col :span="24" v-for="(item, index) in dataRuleList" :key="'dr' + index">
<a-checkbox :value="item.id">{{ item.ruleName }}</a-checkbox>
</a-col>
<a-col :span="24">
<div style="width: 100%; margin-top: 15px">
<a-button type="primary" :loading="loading" :size="'small'" preIcon="ant-design:save-filled" @click="saveDataRuleForRole">
<span>点击保存</span>
</a-button>
</div>
</a-col>
</a-row>
</a-checkbox-group>
<a-empty v-else description="无配置信息" />
</a-tab-pane>
</a-tabs>
</a-spin>
</BasicDrawer>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { queryDepartDataRule, saveDepartDataRule } from '../depart.api';
defineEmits(['register']);
const loading = ref<boolean>(false);
const departId = ref('');
const functionId = ref('');
const dataRuleList = ref<Array<any>>([]);
const dataRuleChecked = ref<Array<any>>([]);
//
const [registerDrawer, { closeDrawer }] = useDrawerInner((data) => {
departId.value = unref(data.departId);
functionId.value = unref(data.functionId);
loadData();
});
async function loadData() {
try {
loading.value = true;
const { datarule, drChecked } = await queryDepartDataRule(functionId, departId);
dataRuleList.value = datarule;
if (drChecked) {
dataRuleChecked.value = drChecked.split(',');
}
} finally {
loading.value = false;
}
}
function saveDataRuleForRole() {
let params = {
departId: departId.value,
permissionId: functionId.value,
dataRuleIds: dataRuleChecked.value.join(','),
};
saveDepartDataRule(params);
}
function onClose() {
doReset();
}
function doReset() {
functionId.value = '';
dataRuleList.value = [];
dataRuleChecked.value = [];
}
</script>

@ -0,0 +1,92 @@
<template>
<BasicModal :title="title" :width="800" v-bind="$attrs" @ok="handleOk" @register="registerModal">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
const emit = defineEmits(['success', 'register']);
const props = defineProps({
rootTreeData: { type: Array, default: () => [] },
});
const prefixCls = inject('prefixCls');
//
const isUpdate = ref<boolean>(false);
//
const model = ref<object>({});
const title = computed(() => (isUpdate.value ? '编辑' : '新增'));
//
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
await resetFields();
isUpdate.value = unref(data?.isUpdate);
//
let isChild = unref(data?.isChild);
let categoryOptions = isChild ? orgCategoryOptions.child : orgCategoryOptions.root;
//
updateSchema([
{
field: 'parentId',
show: isChild,
componentProps: {
//
disabled: isChild,
treeData: props.rootTreeData,
},
},
{
field: 'orgCode',
show: false,
},
{
field: 'orgCategory',
componentProps: { options: categoryOptions },
},
]);
let record = unref(data?.record);
if (typeof record !== 'object') {
record = {};
}
//
record = Object.assign(
{
departOrder: 0,
orgCategory: categoryOptions[0].value,
},
record
);
model.value = record;
await setFieldsValue({ ...record });
});
//
async function handleOk() {
try {
setModalProps({ confirmLoading: true });
let values = await validate();
//
await saveOrUpdateDepart(values, isUpdate.value);
//
closeModal();
//
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

@ -0,0 +1,135 @@
<template>
<a-spin :spinning="loading">
<BasicForm @register="registerForm" />
<!-- <div class="j-box-bottom-button offset-20" style="margin-top: 30px">
<div class="j-box-bottom-button-float" :class="[`${prefixCls}`]">
<a-button preIcon="ant-design:sync-outlined" @click="onReset">重置</a-button>
<a-button type="primary" preIcon="ant-design:save-filled" @click="onSubmit">保存</a-button>
</div>
</div>-->
</a-spin>
</template>
<script lang="ts" setup>
import { watch, computed, inject, ref, unref, onMounted } from 'vue';
import { BasicForm, useForm } from '/@/components/Form/index';
import { saveOrUpdateDepart } from '../depart.api';
import { useBasicFormSchema, orgCategoryOptions } from '../depart.data';
import { Api, queryDepartSync } from '../depart.api';
import { useDesign } from '/@/hooks/web/useDesign';
const { prefixCls } = useDesign('j-depart-form-content');
const emit = defineEmits(['success']);
const props = defineProps({
data: { type: Object, default: () => ({}) },
rootTreeData: { type: Array, default: () => [] },
});
const loading = ref<boolean>(false);
//
const isUpdate = ref<boolean>(true);
//
const model = ref<object>({});
//
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
schemas: useBasicFormSchema().basicFormSchema,
showActionButtonGroup: false,
});
const categoryOptions = computed(() => {
if (!!props?.data?.parentId) {
return orgCategoryOptions.child;
} else {
return orgCategoryOptions.root;
}
});
const setFormValue = async() => {
const res = await queryDepartSync()
await setFieldsValue({ ...res });
}
onMounted(() => {
setFormValue()
//
updateSchema([
{ field: 'parentId', componentProps: { disabled: true } },
{ field: 'orgCode', componentProps: { disabled: true } },
]);
// // data
// watch(
// () => props.data,
// async () => {
// let record = unref(props.data);
// if (typeof record !== 'object') {
// record = {};
// }
//
// model.value = record;
// console.log(record)
// await resetFields();
// await setFieldsValue({ ...record });
// },
// { deep: true, immediate: true }
// );
//
watch(
() => props.rootTreeData,
async () => {
updateSchema([
{
field: 'parentId',
componentProps: { treeData: props.rootTreeData },
},
]);
},
{ deep: true, immediate: true }
);
// orgCategory options
watch(
categoryOptions,
async () => {
updateSchema([
{
field: 'orgCategory',
componentProps: { options: categoryOptions.value },
},
]);
},
{ immediate: true }
);
});
//
async function onReset() {
await resetFields();
await setFieldsValue({ ...model.value });
}
//
async function onSubmit() {
try {
loading.value = true;
let values = await validate();
values = Object.assign({}, model.value, values);
//
await saveOrUpdateDepart(values, isUpdate.value);
//
emit('success');
Object.assign(model.value, values);
} finally {
loading.value = false;
}
}
</script>
<style lang="less">
// update-begin-author:liusq date:20230625 for: [issues/563]
@prefix-cls: ~'@{namespace}-j-depart-form-content';
/*begin 兼容暗夜模式*/
.@{prefix-cls} {
background: @component-background;
border-top: 1px solid @border-color-base;
}
/*end 兼容暗夜模式*/
// update-end-author:liusq date:20230625 for: [issues/563]
</style>

@ -0,0 +1,332 @@
<template>
<a-card :bordered="false" style="height: 100%">
<div class="j-table-operator" style="width: 100%">
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="onAddDepart">新增</a-button>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="onAddChildDepart()">添加下级</a-button>
<a-upload name="file" :showUploadList="false" :customRequest="onImportXls">
<a-button type="primary" preIcon="ant-design:import-outlined">导入</a-button>
</a-upload>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
<a-button type="primary" preIcon="ant-design:sync-outlined">同步企微?</a-button>
<a-button type="primary" preIcon="ant-design:sync-outlined">同步钉钉?</a-button>
<template v-if="checkedKeys.length > 0">
<a-dropdown>
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="onDeleteBatch">
<icon icon="ant-design:delete-outlined" />
<span>删除</span>
</a-menu-item>
</a-menu>
</template>
<a-button>
<span>批量操作 </span>
<icon icon="akar-icons:chevron-down" />
</a-button>
</a-dropdown>
</template>
</div>
<a-alert type="info" show-icon class="alert" style="margin-bottom: 8px">
<template #message>
<template v-if="checkedKeys.length > 0">
<span>已选中 {{ checkedKeys.length }} 条记录</span>
<a-divider type="vertical" />
<a @click="checkedKeys = []">清空</a>
</template>
<template v-else>
<span>未选中任何数据</span>
</template>
</template>
</a-alert>
<a-spin :spinning="loading">
<a-input-search placeholder="按部门名称搜索…" style="margin-bottom: 10px" @search="onSearch" />
<!--组织机构树-->
<template v-if="treeData.length > 0">
<a-tree
v-if="!treeReloading"
checkable
:clickRowToExpand="false"
:treeData="treeData"
:selectedKeys="selectedKeys"
:checkStrictly="checkStrictly"
:load-data="loadChildrenTreeData"
:checkedKeys="checkedKeys"
v-model:expandedKeys="expandedKeys"
@check="onCheck"
@select="onSelect"
>
<template #title="{ key: treeKey, title, dataRef }">
<a-dropdown :trigger="['contextmenu']">
<Popconfirm
:visible="visibleTreeKey === treeKey"
title="确定要删除吗?"
ok-text="确定"
cancel-text="取消"
placement="rightTop"
@confirm="onDelete(dataRef)"
@visibleChange="onVisibleChange"
>
<span>{{ title }}</span>
</Popconfirm>
<template #overlay>
<a-menu @click="">
<a-menu-item key="1" @click="onAddChildDepart(dataRef)">添加子级</a-menu-item>
<a-menu-item key="2" @click="visibleTreeKey = treeKey">
<span style="color: red">删除</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</a-tree>
</template>
<a-empty v-else description="暂无数据" />
</a-spin>
<DepartFormModal :rootTreeData="treeData" @register="registerModal" @success="loadRootTreeData" />
</a-card>
</template>
<script lang="ts" setup>
import { inject, nextTick, ref, unref, defineExpose } from 'vue';
import { useModal } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { useMethods } from '/@/hooks/system/useMethods';
import { Api, deleteBatchDepart, queryDepartTreeSync } from '../depart.api';
import { searchByKeywords } from '/@/views/system/departUser/depart.user.api';
import DepartFormModal from '/@/views/system/depart/components/DepartFormModal.vue';
import { Popconfirm } from 'ant-design-vue';
const prefixCls = inject('prefixCls');
const emit = defineEmits(['select', 'rootTreeData']);
const { createMessage } = useMessage();
const { handleImportXls, handleExportXls } = useMethods();
const loading = ref<boolean>(false);
//
const treeData = ref<any[]>([]);
//
const checkedKeys = ref<any[]>([]);
//
const expandedKeys = ref<any[]>([]);
//
const selectedKeys = ref<any[]>([]);
//
const treeReloading = ref<boolean>(false);
//
const checkStrictly = ref<boolean>(true);
//
const currentDepart = ref<any>(null);
//
const visibleTreeKey = ref<any>(null);
//
const searchKeyword = ref('');
// modal
const [registerModal, { openModal }] = useModal();
//
async function loadRootTreeData() {
try {
loading.value = true;
treeData.value = [];
const result = await queryDepartTreeSync();
if (Array.isArray(result)) {
treeData.value = result;
}
if (expandedKeys.value.length === 0) {
autoExpandParentNode();
} else {
if (selectedKeys.value.length === 0) {
let item = treeData.value[0];
if (item) {
//
setSelectedKey(item.id, item);
}
} else {
emit('select', currentDepart.value);
}
}
emit('rootTreeData', treeData.value);
} finally {
loading.value = false;
}
}
loadRootTreeData();
//
async function loadChildrenTreeData(treeNode) {
try {
const result = await queryDepartTreeSync({
pid: treeNode.dataRef.id,
});
if (result.length == 0) {
treeNode.dataRef.isLeaf = true;
} else {
treeNode.dataRef.children = result;
if (expandedKeys.value.length > 0) {
//
let subKeys: any[] = [];
for (let key of expandedKeys.value) {
if (result.findIndex((item) => item.id === key) !== -1) {
subKeys.push(key);
}
}
if (subKeys.length > 0) {
expandedKeys.value = [...expandedKeys.value];
}
}
}
treeData.value = [...treeData.value];
emit('rootTreeData', treeData.value);
} catch (e) {
console.error(e);
}
return Promise.resolve();
}
//
function autoExpandParentNode() {
let item = treeData.value[0];
if (item) {
if (!item.isLeaf) {
expandedKeys.value = [item.key];
}
//
setSelectedKey(item.id, item);
reloadTree();
} else {
emit('select', null);
}
}
//
async function reloadTree() {
await nextTick();
treeReloading.value = true;
await nextTick();
treeReloading.value = false;
}
/**
* 设置当前选中的行
*/
function setSelectedKey(key: string, data?: object) {
selectedKeys.value = [key];
if (data) {
currentDepart.value = data;
emit('select', data);
}
}
//
function onAddDepart() {
openModal(true, { isUpdate: false, isChild: false });
}
//
function onAddChildDepart(data = currentDepart.value) {
if (data == null) {
createMessage.warning('请先选择一个部门');
return;
}
const record = { parentId: data.id };
openModal(true, { isUpdate: false, isChild: true, record });
}
//
async function onSearch(value: string) {
if (value) {
try {
loading.value = true;
treeData.value = [];
let result = await searchByKeywords({ keyWord: value });
if (Array.isArray(result)) {
treeData.value = result;
}
autoExpandParentNode();
} finally {
loading.value = false;
}
} else {
loadRootTreeData();
}
searchKeyword.value = value;
}
//
function onCheck(e) {
if (Array.isArray(e)) {
checkedKeys.value = e;
} else {
checkedKeys.value = e.checked;
}
}
//
function onSelect(selKeys, event) {
console.log('select: ', selKeys, event);
if (selKeys.length > 0 && selectedKeys.value[0] !== selKeys[0]) {
setSelectedKey(selKeys[0], event.selectedNodes[0]);
} else {
//
setSelectedKey(selectedKeys.value[0]);
}
}
/**
* 根据 ids 删除部门
* @param idListRef array
* @param confirm 是否显示确认提示框
*/
async function doDeleteDepart(idListRef, confirm = true) {
const idList = unref(idListRef);
if (idList.length > 0) {
try {
loading.value = true;
await deleteBatchDepart({ ids: idList.join(',') }, confirm);
await loadRootTreeData();
} finally {
loading.value = false;
}
}
}
//
async function onDelete(data) {
if (data) {
onVisibleChange(false);
doDeleteDepart([data.id], false);
}
}
//
async function onDeleteBatch() {
try {
await doDeleteDepart(checkedKeys);
checkedKeys.value = [];
} finally {
}
}
function onVisibleChange(visible) {
if (!visible) {
visibleTreeKey.value = null;
}
}
function onImportXls(d) {
handleImportXls(d, Api.importExcelUrl, () => {
loadRootTreeData();
});
}
function onExportXls() {
handleExportXls('部门信息', Api.exportXlsUrl);
}
defineExpose({
loadRootTreeData,
});
</script>

@ -0,0 +1,170 @@
<template>
<a-spin :spinning="loading">
<template v-if="treeData.length > 0">
<BasicTree
ref="basicTree"
class="depart-rule-tree"
checkable
:treeData="treeData"
:checkedKeys="checkedKeys"
:selectedKeys="selectedKeys"
:expandedKeys="expandedKeys"
:checkStrictly="checkStrictly"
style="height: 500px; overflow: auto"
@check="onCheck"
@expand="onExpand"
@select="onSelect"
>
<template #title="{ slotTitle, ruleFlag }">
<span>{{ slotTitle }}</span>
<Icon v-if="ruleFlag" icon="ant-design:align-left-outlined" style="margin-left: 5px; color: red" />
</template>
</BasicTree>
</template>
<a-empty v-else description="无可配置部门权限" />
<div class="j-box-bottom-button offset-20" style="margin-top: 30px">
<div class="j-box-bottom-button-float" :class="[`${prefixCls}`]">
<a-dropdown :trigger="['click']" placement="top">
<template #overlay>
<a-menu>
<a-menu-item key="3" @click="toggleCheckALL(true)">全部勾选</a-menu-item>
<a-menu-item key="4" @click="toggleCheckALL(false)">取消全选</a-menu-item>
<a-menu-item key="5" @click="toggleExpandAll(true)">展开所有</a-menu-item>
<a-menu-item key="6" @click="toggleExpandAll(false)">收起所有</a-menu-item>
</a-menu>
</template>
<a-button style="float: left">
树操作
<Icon icon="ant-design:up-outlined" />
</a-button>
</a-dropdown>
<a-button type="primary" preIcon="ant-design:save-filled" @click="onSubmit">保存</a-button>
</div>
</div>
</a-spin>
<DepartDataRuleDrawer @register="registerDataRuleDrawer" />
</template>
<script lang="ts" setup>
import { watch, computed, inject, ref, nextTick } from 'vue';
import { useDrawer } from '/@/components/Drawer';
import { BasicTree } from '/@/components/Tree/index';
import DepartDataRuleDrawer from './DepartDataRuleDrawer.vue';
import { queryRoleTreeList, queryDepartPermission, saveDepartPermission } from '../depart.api';
import { useDesign } from '/@/hooks/web/useDesign';
const { prefixCls } = useDesign('j-depart-form-content');
const props = defineProps({
data: { type: Object, default: () => ({}) },
});
// ID
const departId = computed(() => props.data?.id);
const basicTree = ref();
const loading = ref<boolean>(false);
const treeData = ref<any[]>([]);
const expandedKeys = ref<Array<any>>([]);
const selectedKeys = ref<Array<any>>([]);
const checkedKeys = ref<Array<any>>([]);
const lastCheckedKeys = ref<Array<any>>([]);
const checkStrictly = ref(true);
//
const [registerDataRuleDrawer, dataRuleDrawer] = useDrawer();
// onCreated
loadData();
watch(departId, () => loadDepartPermission(), { immediate: true });
async function loadData() {
try {
loading.value = true;
let { treeList } = await queryRoleTreeList();
treeData.value = treeList;
await nextTick();
toggleExpandAll(true);
} finally {
loading.value = false;
}
}
async function loadDepartPermission() {
if (departId.value) {
try {
loading.value = true;
let keys = await queryDepartPermission({ departId: departId.value });
checkedKeys.value = keys;
lastCheckedKeys.value = [...keys];
} finally {
loading.value = false;
}
}
}
async function onSubmit() {
try {
loading.value = true;
await saveDepartPermission({
departId: departId.value,
permissionIds: checkedKeys.value.join(','),
lastpermissionIds: lastCheckedKeys.value.join(','),
});
await loadData();
await loadDepartPermission();
} finally {
loading.value = false;
}
}
// tree
function onCheck(event) {
if (!Array.isArray(event)) {
checkedKeys.value = event.checked;
} else {
checkedKeys.value = event;
}
}
// tree
function onExpand($expandedKeys) {
expandedKeys.value = $expandedKeys;
}
// tree
function onSelect($selectedKeys, { selectedNodes }) {
if (selectedNodes[0]?.ruleFlag) {
let functionId = $selectedKeys[0];
dataRuleDrawer.openDrawer(true, { departId, functionId });
}
selectedKeys.value = [];
}
//
async function toggleCheckStrictly(flag) {
checkStrictly.value = flag;
await nextTick();
checkedKeys.value = basicTree.value.getCheckedKeys();
}
//
async function toggleExpandAll(flag) {
basicTree.value.expandAll(flag);
await nextTick();
expandedKeys.value = basicTree.value.getExpandedKeys();
}
//
async function toggleCheckALL(flag) {
basicTree.value.checkAll(flag);
await nextTick();
checkedKeys.value = basicTree.value.getCheckedKeys();
}
</script>
<style lang="less" scoped>
// VUEN-188
.depart-rule-tree :deep(.scrollbar__bar) {
pointer-events: none;
}
</style>

@ -0,0 +1,129 @@
import { unref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage';
const { createConfirm } = useMessage();
export enum Api {
queryDepartTreeSync = '/sys/sysDepart/queryDepartTreeSync',
queryDepartSync = '/sys/sysDepart/queryDepartSync',
save = '/sys/sysDepart/add',
edit = '/sys/sysDepart/edit',
delete = '/sys/sysDepart/delete',
deleteBatch = '/sys/sysDepart/deleteBatch',
exportXlsUrl = '/sys/sysDepart/exportXls',
importExcelUrl = '/sys/sysDepart/importExcel',
roleQueryTreeList = '/sys/role/queryTreeList',
queryDepartPermission = '/sys/permission/queryDepartPermission',
saveDepartPermission = '/sys/permission/saveDepartPermission',
dataRule = '/sys/sysDepartPermission/datarule',
getCurrentUserDeparts = '/sys/user/getCurrentUserDeparts',
selectDepart = '/sys/selectDepart',
getUpdateDepartInfo = '/sys/user/getUpdateDepartInfo',
doUpdateDepartInfo = '/sys/user/doUpdateDepartInfo',
changeDepartChargePerson = '/sys/user/changeDepartChargePerson',
}
/**
*
*/
export const queryDepartTreeSync = (params?) => defHttp.get({ url: Api.queryDepartTreeSync, params });
/**
*
*/
export const queryDepartSync = (params?) => defHttp.get({ url: Api.queryDepartSync, params });
/**
*
*/
export const saveOrUpdateDepart = (params, isUpdate) => {
if (isUpdate) {
return defHttp.put({ url: Api.edit, params });
} else {
return defHttp.post({ url: Api.save, params });
}
};
/**
*
*/
export const deleteBatchDepart = (params, confirm = false) => {
return new Promise((resolve, reject) => {
const doDelete = () => {
resolve(defHttp.delete({ url: Api.deleteBatch, params }, { joinParamsToUrl: true }));
};
if (confirm) {
createConfirm({
iconType: 'warning',
title: '删除',
content: '确定要删除吗?',
onOk: () => doDelete(),
onCancel: () => reject(),
});
} else {
doDelete();
}
});
};
/**
*
*/
export const queryRoleTreeList = (params?) => defHttp.get({ url: Api.roleQueryTreeList, params });
/**
*
*/
export const queryDepartPermission = (params?) => defHttp.get({ url: Api.queryDepartPermission, params });
/**
*
*/
export const saveDepartPermission = (params) => defHttp.post({ url: Api.saveDepartPermission, params });
/**
*
*/
export const queryDepartDataRule = (functionId, departId, params?) => {
let url = `${Api.dataRule}/${unref(functionId)}/${unref(departId)}`;
return defHttp.get({ url, params });
};
/**
*
*/
export const saveDepartDataRule = (params) => defHttp.post({ url: Api.dataRule, params });
/**
*
*/
export const getUserDeparts = (params?) => defHttp.get({ url: Api.getCurrentUserDeparts, params });
/**
*
*/
export const selectDepart = (params?) => defHttp.put({ url: Api.selectDepart, params });
/**
*
* @param id
*/
export const getUpdateDepartInfo = (id) => defHttp.get({ url: Api.getUpdateDepartInfo, params: {id} });
/**
*
* @param params
*/
export const doUpdateDepartInfo = (params) => defHttp.put({ url: Api.doUpdateDepartInfo, params });
/**
*
* @param id
*/
export const deleteDepart = (id) => defHttp.delete({ url: Api.delete, params:{ id } }, { joinParamsToUrl: true });
/**
*
* @param params
*/
export const changeDepartChargePerson = (params) => defHttp.put({ url: Api.changeDepartChargePerson, params });

@ -0,0 +1,83 @@
import { FormSchema } from '/@/components/Form';
// 部门基础表单
export function useBasicFormSchema() {
const basicFormSchema: FormSchema[] = [
{
field: 'departName',
label: '部门名称',
component: 'Input',
componentProps: {
placeholder: '部门名称',
},
},
{
field: 'parentId',
label: '上级部门',
component: 'TreeSelect',
componentProps: {
treeData: [],
placeholder: '无',
dropdownStyle: { maxHeight: '200px', overflow: 'auto' },
},
},
{
field: 'orgCode',
label: '部门编码',
component: 'Input',
componentProps: {
placeholder: '请输入部门编码',
},
},
/* {
field: 'orgCategory',
label: '部门类型',
component: 'RadioButtonGroup',
componentProps: { options: [] },
},*/
{
field: 'mobile',
label: '电话',
component: 'Input',
componentProps: {
placeholder: '请输入电话',
},
},
{
field: 'fax',
label: '传真',
component: 'Input',
componentProps: {
placeholder: '请输入传真',
},
},
{
field: 'address',
label: '地址',
component: 'Input',
componentProps: {
placeholder: '请输入地址',
},
},
{
field: 'memo',
label: '备注',
component: 'InputTextArea',
componentProps: {
placeholder: '请输入备注',
},
},
];
return { basicFormSchema };
}
// 部门类型选项
export const orgCategoryOptions = {
// 一级部门
root: [{ value: '1', label: '公司' }],
// 子级部门
child: [
{ value: '2', label: '部门' },
{ value: '3', label: '岗位' },
],
};

@ -0,0 +1,14 @@
//noinspection LessUnresolvedVariable
@prefix-cls: ~'@{namespace}-depart-manage';
.@{prefix-cls} {
// update-begin-author:liusq date:20230625 for: [issues/563]暗色主题部分失效
background: @component-background;
// update-end-author:liusq date:20230625 for: [issues/563]暗色主题部分失效
&--box {
.ant-tabs-nav {
padding: 0 20px;
}
}
}

@ -0,0 +1,52 @@
<template>
<a-row :class="['p-4', `${prefixCls}--box`]" type="flex" :gutter="10">
<a-col :xl="12" :lg="24" :md="24" style="margin-bottom: 10px">
<div style="height: 100%;" :class="[`${prefixCls}`]">
<a-tabs v-show="departData != null" defaultActiveKey="base-info">
<a-tab-pane tab="基本信息" key="base-info" forceRender style="position: relative">
<div style="padding: 20px">
<DepartFormTab :data="departData" :rootTreeData="rootTreeData" @success="onSuccess" />
</div>
</a-tab-pane>
</a-tabs>
</div>
</a-col>
</a-row>
</template>
<script lang="ts" setup name="system-depart">
import { provide, ref } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import DepartLeftTree from './components/DepartLeftTree.vue';
import DepartFormTab from './components/DepartFormTab.vue';
import DepartRuleTab from './components/DepartRuleTab.vue';
const { prefixCls } = useDesign('depart-manage');
provide('prefixCls', prefixCls);
// ref
const leftTree = ref();
//
const departData = ref({});
const rootTreeData = ref<any[]>([]);
//
function onTreeSelect(data) {
console.log('onTreeSelect: ', data);
departData.value = data;
}
// rootTreeData
function onRootTreeData(data) {
rootTreeData.value = data;
}
function onSuccess() {
leftTree.value.loadRootTreeData();
}
</script>
<style lang="less">
@import './index.less';
</style>

@ -5,11 +5,11 @@
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreateZJ"> 新增专家</a-button>
<!-- <a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreateZJ"> 新增专家</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls" >导入</j-upload-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXlsMb"> 导出模板</a-button>
<a-button type="primary" @click="openModal(true, {})" preIcon="ant-design:hdd-outlined"> 回收站</a-button>
<a-button type="primary" @click="openModal(true, {})" preIcon="ant-design:hdd-outlined"> 回收站</a-button>-->
<JThirdAppButton biz-type="user" :selected-row-keys="selectedRowKeys" syncToApp syncToLocal @sync-finally="onSyncFinally" />
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>

@ -168,7 +168,7 @@ export const formSchema = [
component: 'Input',
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_user', 'work_no', model, schema, true),
},
{
/*{
label: '职务',
field: 'post',
required: false,
@ -177,7 +177,7 @@ export const formSchema = [
rowKey: 'code',
labelKey: 'name',
},
},
},*/
{
label: '角色',
field: 'selectedroles',
@ -198,6 +198,7 @@ export const formSchema = [
sync: false,
checkStrictly: true,
defaultExpandLevel: 2,
multiple:false,
onSelect: (options, values) => {
const { updateSchema } = formActionType;
//所属部门修改后更新负责部门下拉框数据
@ -213,7 +214,7 @@ export const formSchema = [
};
},
},
{
/* {
label: '租户',
field: 'relTenantIds',
component: 'ApiSelect',
@ -250,7 +251,7 @@ export const formSchema = [
mode: 'multiple',
},
ifShow: ({ values }) => values.userIdentity == 2,
},
},*/
{
label: '头像',
field: 'avatar',
@ -296,7 +297,7 @@ export const formSchema = [
];
},
},
{
/*{
label: '座机',
field: 'telephone',
component: 'Input',
@ -312,7 +313,7 @@ export const formSchema = [
type: 'radio',
stringToNumber: true,
},
},
},*/
];
export const formSchemaZJ = [
{

@ -398,7 +398,7 @@ export const formSchema: FormSchema[] = [
component: 'Input',
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_user', 'work_no', model, schema, true),
},
{
/*{
label: '职务',
field: 'post',
required: false,
@ -407,7 +407,7 @@ export const formSchema: FormSchema[] = [
rowKey: 'code',
labelKey: 'name',
},
},
},*/
{
label: '角色',
field: 'selectedroles',
@ -428,7 +428,7 @@ export const formSchema: FormSchema[] = [
sync: false,
checkStrictly: true,
defaultExpandLevel: 2,
multiple:false,
onSelect: (options, values) => {
const { updateSchema } = formActionType;
//所属部门修改后更新负责部门下拉框数据
@ -444,7 +444,7 @@ export const formSchema: FormSchema[] = [
};
},
},
{
/*{
label: '租户',
field: 'relTenantIds',
component: 'ApiSelect',
@ -472,8 +472,8 @@ export const formSchema: FormSchema[] = [
},
};
},
},
{
},*/
/* {
label: '负责部门',
field: 'departIds',
component: 'Select',
@ -481,7 +481,7 @@ export const formSchema: FormSchema[] = [
mode: 'multiple',
},
ifShow: ({ values }) => values.userIdentity == 2,
},
},*/
{
label: '头像',
field: 'avatar',
@ -527,12 +527,12 @@ export const formSchema: FormSchema[] = [
];
},
},
{
/* {
label: '座机',
field: 'telephone',
component: 'Input',
rules: [{ pattern: /^0\d{2,3}-[1-9]\d{6,7}$/, message: '请输入正确的座机号码' }],
},
},*/
// {
// label: '工作流引擎',
// field: 'activitiSync',

@ -204,7 +204,7 @@ export const formSchema: FormSchema[] = [
sync: false,
checkStrictly: true,
defaultExpandLevel: 2,
multiple:false,
onSelect: (options, values) => {
const { updateSchema } = formActionType;
//所属部门修改后更新负责部门下拉框数据
@ -343,12 +343,12 @@ export const formSchema: FormSchema[] = [
field: 'expResume',
component: 'InputTextArea',
},
{
/*{
label: '比赛',
field: 'compid',
component: 'Input',
show: false,
},
},*/
/*{
label: '比赛名称',
field: 'compName',

@ -411,7 +411,7 @@ export const formSchemaYX: FormSchema[] = [
sync: false,
checkStrictly: true,
defaultExpandLevel: 2,
multiple:false,
onSelect: (options, values) => {
const { updateSchema } = formActionType;
//所属部门修改后更新负责部门下拉框数据

@ -201,7 +201,7 @@ export const formSchemaZJ: FormSchema[] = [
sync: false,
checkStrictly: true,
defaultExpandLevel: 2,
multiple:false,
onSelect: (options, values) => {
const { updateSchema } = formActionType;
//所属部门修改后更新负责部门下拉框数据

Loading…
Cancel
Save