b5d70ee4 by lihua

组织架构管理

1 parent d7b78de0
import request from "@/utils/request";
/** 获取租户列表(分页) */
export const getTenantSinglePage = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/single-page`,
method: 'post',
data: params
})
/** 删除租户 */
export const removeTenant = (data) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/remove`,
method: 'delete',
data
})
export const addTenant = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/update`,
method: 'post',
data: params
})
export const updateTenant = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/update`,
method: 'put',
data: params
})
/** 更新租户状态 */
export const updateTenantState = (guid, state: string = 'Y') => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/tenant/update-state?tenantGuid=${guid}&state=${state}`,
method: 'get'
})
/** 获取会员进度列表 */
export const getMemberGressList = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/page-list`,
method: 'post',
data: params
})
/** 获取任务执行日志 */
export const getTaskExecutionLog = (guid) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/task-info?guid=${guid}`,
method: 'get'
})
/** 任务重启 */
export const getTaskRestart = (guid) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/pending-task/restart?guid=${guid}`,
method: 'get'
})
/**
* 获取部门tree列表
* @param param
* @returns
*/
export const getOrganisationTreeList = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/get-tree-list`,
method: 'post',
data: params
})
export const removeOrganisation = (guids) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/removeListByGuids`,
method: 'delete',
data: guids
})
/**
* 修改部门关系
* @param param
* @returns
*/
export const updateOrganisation= (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/update`,
method: 'put',
data: params
});
/**
* 新增部门
* @param param
* @returns
*/
export const addOrganisation = (params) => request({
url: `${import.meta.env.VITE_APP_PERSONAL_URL}/organisation/save`,
method: 'post',
data: params
});
......@@ -330,6 +330,53 @@ export const useValidator = () => {
};
};
/**
* 中文名字限制校验(表单验证函数)
* @param {*} message 错误提示
* @param {*} minChineseCount 最小中文长度
*/
const minChineseCount = (message?, minChineseCount = 2) => {
const chinesePattern = new RegExp(`^[\u4e00-\u9fa5]{${minChineseCount},}$`);
return {
validator: (_, val, callback) => {
let trimmedInput = val ? val.replace(/\s+/g, '') : '';
if (!chinesePattern.test(trimmedInput) && val) {
callback(new Error(message || `输入必须包含至少${minChineseCount}个中文字符!`))
} else {
callback()
}
},
trigger: 'blur'
}
}
const email = (message?: string): FormItemRule => {
return {
validator: (_, val, callback) => {
if (val && !/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/.test(val)) {
callback(new Error(message || '邮箱格式不正确!'))
} else {
callback()
}
},
trigger: 'blur'
}
}
const mobileNumber = (message?: string): FormItemRule => {
return {
validator: (_, val, callback) => {
if (val && !/^1[3-9]\d{9}$/.test(val)) {
callback(new Error(message || '手机号码格式不正确!'))
} else {
callback()
}
},
trigger: 'blur'
}
}
return {
required,
regexpValidate,
......@@ -343,6 +390,9 @@ export const useValidator = () => {
isUSCCCode,
idCode,
validateIPList,
validateDomainList
validateDomainList,
minChineseCount,
email,
mobileNumber
}
}
......
import type { RouteRecordRaw } from 'vue-router'
function Layout() {
return import('@/layouts/index.vue')
}
const routes: RouteRecordRaw[] = [
{
path: '/data-basic/department-manage',
component: Layout,
meta: {
title: '组织架构管理',
icon: 'sidebar-videos',
},
children: [{
path: '',
name: 'departmentInfoList',
component: () => import('@/views/data_basic/departmentInfoList.vue'),
meta: {
title: '组织架构管理',
sidebar: false,
breadcrumb: false,
cache: true
},
}]
},
]
export default routes
\ No newline at end of file
......@@ -8,6 +8,7 @@ import DataFacilitator from './modules/dataFacilitator';
import HomeIndex from './modules/homeIndex';
import DataDelivery from './modules/dataDelivery';
import DataAnonymization from './modules/dataAnonymization';
import DataBasic from './modules/dataBasic';
import useSettingsStore from '@/store/modules/settings'
......@@ -101,6 +102,7 @@ const asyncRoutes: RouteRecordRaw[] = [
...HomeIndex,
...DataDelivery,
...DataAnonymization,
...DataBasic,
// ...DataAssetRegistry,
]
......
<route lang="yaml">
name: departmentInfoList
</route>
<template>
<div class="container_wrap">
<div class="table_tool_wrap">
<!-- 头部搜索 -->
<TableTools :searchItems="searchItemList" :searchId="'organize-manage-search'" @search="toSearch" :init="false" />
<div class="tools_btns">
<el-button type="primary" @click="handleCreate">新增部门</el-button>
</div>
</div>
<div class="table_panel_wrap">
<!-- 部门信息表格 -->
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
</div>
<!-- 部门信息抽屉 -->
<Drawer ref="drawerRef" :drawerInfo="drawerInfo" @drawerBtnClick="drawerBtnClick">
</Drawer>
</div>
</template>
<script lang="ts" setup name="departmentInfoList">
import TableTools from "@/components/Tools/table_tools.vue";
import { commonPageConfig } from '@/components/PageNav/index';
import { TableColumnWidth } from "@/utils/enum";
import {
getOrganisationTreeList,
removeOrganisation,
updateOrganisation,
addOrganisation,
} from "@/api/modules/dataBasic";
import { useValidator } from '@/hooks/useValidator';
import useUserStore from "@/store/modules/user";
import Moment from 'moment';
import { cloneDeep } from "lodash-es";
const userStore = useUserStore();
const userData = JSON.parse(userStore.userData)
const { proxy } = getCurrentInstance() as any;
const router = useRouter()
const formItem1 = ref()
const drawerRef = ref()
const { required } = useValidator()
const searchItemList = ref([{
type: "input",
label: "",
field: "organisationName",
default: "",
placeholder: "部门名称/部门编码/部门负责人",
clearable: true,
}, {
type: "select",
label: "",
field: "bizState",
default: "Y",
options: [{
value: 'Y',
label: '启用'
}, {
value: 'S',
label: '停用'
}],
placeholder: "状态",
clearable: true,
filterable: true,
}])
/** 分页及搜索传参信息配置。 */
const page = ref({
...commonPageConfig,
organisationName: '',
bizState: 'Y'
});
const currTableData: any = ref({});
const tableInfo = ref({
id: 'department-table',
rowKey: 'guid',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "部门名称", field: "organisationName", width: 170 },
{ label: "部门编号", field: "organisationCode", width: 100 },
{ label: "部门负责人", field: "chargeStaffName", width: 120 },
{
label: "状态",
field: "bizState",
width: TableColumnWidth.STATE,
align: 'center',
type: "tag",
getName: (scope) => {
return scope.row.bizState === 'Y' ? '启用' : '停用';
}
},
{ label: "界面排序", field: "orderNum", width: 100, align: 'center' },
{
label: "创建时间", field: "createTime", width: TableColumnWidth.DATETIME, getName: (scope) => {
return scope.row.createTime ? Moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') : '--';
}
},
],
data: [],
// page: {
// type: "normal",
// rows: 0,
// ...page.value,
// },
showPage: false,
loading: false,
actionInfo: {
label: "操作",
type: "btn",
width: 150,
fixed: 'right',
btns: (scope) => {
return [
{ label: "详情", value: "detail", click: toDetail },
{ label: "编辑", value: "edit", click: toEdit },
{ label: "删除", value: "delete", click: toDelete }
];
}
}
})
const toSearch = (val: any, clear: boolean = false) => {
if (clear) {
searchItemList.value.map((item) => (item.default = ""));
page.value.organisationName = '';
page.value.bizState = 'Y';
} else {
page.value.organisationName = val.organisationName;
page.value.bizState = val.bizState;
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true;
getOrganisationTreeList({
pageIndex: page.value.curr,
pageSize: page.value.limit,
organisationName: page.value.organisationName,
bizState: page.value.bizState,
tenantGuid: userData.tenantGuid,
}).then((res: any) => {
if (res?.code == proxy.$passCode) {
const data = res.data || [];
tableInfo.value.data = data;
// tableInfo.value.page.limit = data.pageSize
// tableInfo.value.page.curr = data.pageIndex
// tableInfo.value.page.rows = data.totalRows
} else {
proxy.$ElMessage({
type: 'error',
message: res.msg,
})
}
tableInfo.value.loading = false
})
}
const tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
// tableInfo.value.page.curr = page.value.curr;
// tableInfo.value.page.limit = page.value.limit;
getTableData();
};
const toEdit = (scope) => {
currTableData.value = scope.row;
drawerInfo.value.visible = true;
drawerInfo.value.header.title = '编辑部门'
drawerInfo.value.type = 'edit';
drawerInfo.value.footer.visible = true;
drawerInfo.value.container.contents[0].formInfo.readonly = false;
depEditFormItems.value.forEach(item => {
item.default = scope.row[item.field] || '';
});
// depEditFormItems.value[1].options = cloneDeep(tableInfo.value.data);
// TODO,禁用下级。
selectParentEdit(scope.row.guid);
}
// 这里有个需求就是编辑时,不能选择自己和自己的子类children作为上级分类,每条数据加上disabled属性true or false
const selectParentEdit = (guid: string) => {
const cloneData = cloneDeep(tableInfo.value.data); // 深拷贝数据,避免直接修改原始数据
const disableNodeAndChildren = (node: any, disabled: boolean) => {
node.disabled = disabled;
if (node.children && node.children.length > 0) {
node.children.forEach((child: any) => disableNodeAndChildren(child, disabled));
}
};
const updateDisabledStatus = (nodes: any[], guid: string) => {
nodes.forEach((node) => {
if (node.guid === guid) {
// 禁用当前节点及其所有子节点
disableNodeAndChildren(node, true);
} else {
// 其他节点保持启用状态
node.disabled = false;
if (node.children && node.children.length > 0) {
updateDisabledStatus(node.children, guid);
}
}
});
};
updateDisabledStatus(cloneData, guid);
depEditFormItems.value[1].options = cloneData; // 更新选项
};
const toDetail = (scope) => {
drawerInfo.value.visible = true;
drawerInfo.value.header.title = '详情'
drawerInfo.value.footer.visible = false;
drawerInfo.value.container.contents[0].formInfo.readonly = true;
depEditFormItems.value.forEach(item => {
item.default = scope.row[item.field] || '';
});
depEditFormItems.value[1].options = tableInfo.value.data;
}
const toDelete = (scope) => {
proxy.$openMessageBox("数据删除后不可恢复,确定是否删除?", "warning", () => {
removeOrganisation([scope.row.guid]).then((res: any) => {
if (res.data.code === proxy.$passCode) {
proxy.$ElMessage({
type: 'success',
message: '删除成功'
})
page.value.curr = 1;
getTableData();
} else {
proxy.$ElMessage({
type: 'error',
message: res.msg,
})
}
})
})
}
const handleCreate = () => {
drawerInfo.value.visible = true;
drawerInfo.value.header.title = '新增部门';
drawerInfo.value.type = 'add';
drawerInfo.value.footer.visible = true;
depEditFormItems.value.forEach(item => {
item.default = '';
item.field == 'bizState' && (item.default = 'Y');
});
depEditFormItems.value[1].options = tableInfo.value.data;
drawerInfo.value.container.contents[0].formInfo.readonly = false;
}
const depEditFormItems = ref([
{
field: 'organisationName',
label: '部门名称',
type: 'input',
placeholder: '请输入',
visible: true,
maxlength: 32,
required: true,
default: '',
clearable: true,
},
{
label: "上级部门",
type: "tree-select",
placeholder: "请选择",
field: "parentGuid",
options: [],
lazy: false,
expandKeys: [],
props: {
label: 'organisationName',
value: 'guid',
children: 'children',
isLeaf: 'isLeaf'
},
filterable: true,
clearable: true,
default: '',
required: false,
},
{
label: '界面排序',
type: 'input',
maxlength: 10,
placeholder: '请输入',
field: 'orderNum',
regexp: /[^\d]/g,
default: '',
clearable: true,
required: false,
},
{
label: '状态',
type: 'switch',
field: 'bizState',
default: 'Y',
placeholder: '请选择',
activeValue: 'Y',
inactiveValue: 'S',
switchWidth: 32,
},
])
const depEditFormRules = ref({
organisationName: [required()]
})
/** 新增分类的form */
const depEditFormInfo = ref({
type: "form",
title: "",
col: "span",
formInfo: {
id: "add-dep-form",
readonly: false,
items: depEditFormItems.value,
rules: depEditFormRules.value,
},
});
const drawerInfo = ref({
visible: false,
direction: "rtl",
size: 520,
header: {
title: "新增部门",
},
type: "",
container: {
contents: [depEditFormInfo.value],
},
footer: {
visible: true,
btns: [
{ type: "default", label: "取消", value: "cancel" },
{ type: "primary", label: "确认", value: "save", loading: false },
],
},
})
const drawerBtnClick = (btn, info) => {
if (btn.value == "cancel") {
drawerInfo.value.visible = false;
} else if (btn.value == "save") {
let isAdd = drawerInfo.value.type == 'add';
let message = !isAdd ? '修改成功' : '新增成功'
if (!isAdd) {
info.guid = currTableData.value.guid;
}
drawerInfo.value.footer.btns[1].loading = true;
let ps: any = null;
if (isAdd) {
ps = addOrganisation({
...info,
tenantGuid: userData.tenantGuid
})
} else {
ps = updateOrganisation({
...info,
tenantGuid: userData.tenantGuid
})
}
ps.then(res => {
drawerInfo.value.footer.btns[1].loading = false;
if (res?.code === proxy.$passCode) {
proxy.$ElMessage({
type: 'success',
message: message
})
drawerInfo.value.visible = false;
getTableData();
} else {
res?.msg && proxy.$ElMessage({
type: 'error',
message: res.msg,
})
}
})
}
}
onBeforeMount(() => {
toSearch({});
})
</script>
<style lang="scss" scoped>
.table_tool_wrap {
width: 100%;
height: 84px !important;
padding: 0 8px;
.tools_btns {
padding: 0px 0 0;
}
}
.table_panel_wrap {
width: 100%;
height: calc(100% - 84px);
padding: 0px 8px 8px;
}
</style>
\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!