feat(部门管理): 新增编辑实现
This commit is contained in:
@@ -3,7 +3,7 @@ ENV = 'development'
|
|||||||
|
|
||||||
# 本地环境接口地址
|
# 本地环境接口地址
|
||||||
#`VITE_API_URL = 'https://demo.dvadmin.com/api'
|
#`VITE_API_URL = 'https://demo.dvadmin.com/api'
|
||||||
VITE_API_URL = 'http://192.168.1.160:8003'
|
VITE_API_URL = 'http://192.168.1.94:10000'
|
||||||
|
|
||||||
# 是否启用按钮权限
|
# 是否启用按钮权限
|
||||||
VITE_PM_ENABLED = true
|
VITE_PM_ENABLED = true
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ const route = useRoute();
|
|||||||
const stores = useTagsViewRoutes();
|
const stores = useTagsViewRoutes();
|
||||||
const storesThemeConfig = useThemeConfig();
|
const storesThemeConfig = useThemeConfig();
|
||||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||||
import websocket from "/@/utils/websocket";
|
import websocket from '/@/utils/websocket';
|
||||||
import { ElNotification } from "element-plus";
|
import { ElNotification } from 'element-plus';
|
||||||
// 获取版本号
|
// 获取版本号
|
||||||
const getVersion = computed(() => {
|
const getVersion = computed(() => {
|
||||||
let isVersion = false;
|
let isVersion = false;
|
||||||
@@ -61,11 +61,10 @@ onBeforeMount(() => {
|
|||||||
setIntroduction.jsCdn();
|
setIntroduction.jsCdn();
|
||||||
//websockt 模块
|
//websockt 模块
|
||||||
try {
|
try {
|
||||||
websocket.init(wsReceive)
|
//websocket.init(wsReceive)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("websocket错误")
|
console.log('websocket错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
// 页面加载时
|
// 页面加载时
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -87,7 +86,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
// 页面销毁时,关闭监听布局配置/i18n监听
|
// 页面销毁时,关闭监听布局配置/i18n监听
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
mittBus.off('openSetingsDrawer', () => { });
|
mittBus.off('openSetingsDrawer', () => {});
|
||||||
});
|
});
|
||||||
// 监听路由的变化,设置网站标题
|
// 监听路由的变化,设置网站标题
|
||||||
watch(
|
watch(
|
||||||
@@ -101,11 +100,11 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// websocket相关代码
|
// websocket相关代码
|
||||||
import { messageCenterStore } from "/@/stores/messageCenter";
|
import { messageCenterStore } from '/@/stores/messageCenter';
|
||||||
const wsReceive = (message: any) => {
|
const wsReceive = (message: any) => {
|
||||||
const data = JSON.parse(message.data)
|
const data = JSON.parse(message.data);
|
||||||
const { unread } = data
|
const { unread } = data;
|
||||||
const messageCenter = messageCenterStore()
|
const messageCenter = messageCenterStore();
|
||||||
messageCenter.setUnread(unread);
|
messageCenter.setUnread(unread);
|
||||||
if (data.contentType === 'SYSTEM') {
|
if (data.contentType === 'SYSTEM') {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
@@ -113,14 +112,12 @@ const wsReceive = (message: any) => {
|
|||||||
message: data.content,
|
message: data.content,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
position: 'bottom-right',
|
position: 'bottom-right',
|
||||||
duration: 5000
|
duration: 5000,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
// 关闭连接
|
// 关闭连接
|
||||||
websocket.close()
|
websocket.close();
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form ref="formRef" :rules="rules" :model="deptFormData" label-width="100px" label-position="right" class="dept-form-com">
|
<el-form ref="formRef" :rules="rules" :model="deptFormData" label-width="100px" label-position="right" class="dept-form-com">
|
||||||
<el-form-item label="父级部门" prop="parent">
|
<el-form-item label="父级部门" prop="parent">
|
||||||
<el-select v-model="deptFormData.parent" style="width: 100%">
|
<el-tree-select
|
||||||
<el-option v-for="item in deptAllList" :key="item.id" :label="item.name" :value="item.id" />
|
v-model="deptFormData.parent"
|
||||||
</el-select>
|
:props="defaultTreeProps"
|
||||||
|
:data="deptDefaultList"
|
||||||
|
lazy
|
||||||
|
check-strictly
|
||||||
|
:load="handleTreeLoad"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item required label="部门名称" prop="name">
|
<el-form-item required label="部门名称" prop="name">
|
||||||
<el-input v-model="deptFormData.name" />
|
<el-input v-model="deptFormData.name" />
|
||||||
@@ -11,14 +17,14 @@
|
|||||||
<el-form-item required label="部门标识" prop="key">
|
<el-form-item required label="部门标识" prop="key">
|
||||||
<el-input v-model="deptFormData.key" />
|
<el-input v-model="deptFormData.key" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="负责人" prop="owner">
|
<el-form-item label="负责人">
|
||||||
<el-input v-model="deptFormData.owner" />
|
<el-input v-model="deptFormData.owner" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注">
|
||||||
<el-input v-model="deptFormData.remark" maxlength="200" show-word-limit type="textarea" />
|
<el-input v-model="deptFormData.description" maxlength="200" show-word-limit type="textarea" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="handleUpdateMenu" type="primary" :disabled="deptBtnLoading">
|
<el-button @click="handleUpdateMenu" type="primary" :loading="deptBtnLoading">
|
||||||
{{ deptFormData.id ? '保存' : '新增' }}
|
{{ deptFormData.id ? '保存' : '新增' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="handleClose">取消 </el-button>
|
<el-button @click="handleClose">取消 </el-button>
|
||||||
@@ -29,14 +35,29 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, ref, onMounted } from 'vue';
|
||||||
import { ElForm, FormRules } from 'element-plus';
|
import { ElForm, FormRules } from 'element-plus';
|
||||||
import { getAllDeptList, AddObj, UpdateObj } from '../api';
|
import { lazyLoadDept, AddObj, UpdateObj } from '../api';
|
||||||
import { successNotification } from '../../../../utils/message';
|
import { successNotification } from '../../../../utils/message';
|
||||||
import { DeptFormDataType, TreeItemType, DeptListType } from '../types';
|
import { DeptFormDataType, TreeItemType, APIResponseData } from '../types';
|
||||||
|
import type Node from 'element-plus/es/components/tree/src/model/node';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
initFormData: TreeItemType | null;
|
initFormData: TreeItemType | null;
|
||||||
|
treeData: TreeItemType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultTreeProps: any = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
isLeaf: (data: TreeItemType[], node: Node) => {
|
||||||
|
if (node?.data.hasChild) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const formRef = ref<InstanceType<typeof ElForm>>();
|
const formRef = ref<InstanceType<typeof ElForm>>();
|
||||||
const rules = reactive<FormRules>({
|
const rules = reactive<FormRules>({
|
||||||
name: [{ required: true, message: '部门名称必填', trigger: 'blur' }],
|
name: [{ required: true, message: '部门名称必填', trigger: 'blur' }],
|
||||||
@@ -45,27 +66,20 @@ const rules = reactive<FormRules>({
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<IProps>(), {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
initFormData: () => null,
|
initFormData: () => null,
|
||||||
|
treeData: () => [],
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['drawerClose']);
|
const emit = defineEmits(['drawerClose']);
|
||||||
|
|
||||||
let deptAllList = ref<DeptListType[]>([]);
|
let deptDefaultList = ref<TreeItemType[]>([]);
|
||||||
let deptFormData = reactive<DeptFormDataType>({
|
let deptFormData = reactive<DeptFormDataType>({
|
||||||
key: '',
|
key: '',
|
||||||
parent: '',
|
parent: '',
|
||||||
name: '',
|
name: '',
|
||||||
owner: '',
|
owner: '',
|
||||||
remark: '',
|
description: '',
|
||||||
is_catalog: true,
|
|
||||||
});
|
});
|
||||||
let deptBtnLoading = ref(false);
|
let deptBtnLoading = ref(false);
|
||||||
|
|
||||||
const getData = async () => {
|
|
||||||
const res = await getAllDeptList();
|
|
||||||
if (res?.code === 2000) {
|
|
||||||
deptAllList.value = res.data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setDeptFormData = () => {
|
const setDeptFormData = () => {
|
||||||
if (props.initFormData?.id) {
|
if (props.initFormData?.id) {
|
||||||
deptFormData.id = props.initFormData?.id;
|
deptFormData.id = props.initFormData?.id;
|
||||||
@@ -73,6 +87,15 @@ const setDeptFormData = () => {
|
|||||||
deptFormData.parent = props.initFormData.parent || '';
|
deptFormData.parent = props.initFormData.parent || '';
|
||||||
deptFormData.name = props.initFormData.name || '';
|
deptFormData.name = props.initFormData.name || '';
|
||||||
deptFormData.owner = props.initFormData.owner || '';
|
deptFormData.owner = props.initFormData.owner || '';
|
||||||
|
deptFormData.description = props.initFormData.description || '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTreeLoad = (node: Node, resolve: Function) => {
|
||||||
|
if (node.level !== 0) {
|
||||||
|
lazyLoadDept({ parent: node.data.id }).then((res: APIResponseData) => {
|
||||||
|
resolve(res.data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,7 +126,9 @@ const handleClose = (type: string = '') => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getData();
|
props.treeData.map((item) => {
|
||||||
|
deptDefaultList.value.push(item);
|
||||||
|
});
|
||||||
setDeptFormData();
|
setDeptFormData();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
|
|||||||
* 处理crud警告:Invalid prop: type check failed for prop "name". Expected String with value "2", got Number with value 2.
|
* 处理crud警告:Invalid prop: type check failed for prop "name". Expected String with value "2", got Number with value 2.
|
||||||
*/
|
*/
|
||||||
res.data.forEach((item: any) => {
|
res.data.forEach((item: any) => {
|
||||||
|
item.dept = String(item.dept);
|
||||||
if (item.role && Array.isArray(item.role) && item.role.length > 0) {
|
if (item.role && Array.isArray(item.role) && item.role.length > 0) {
|
||||||
item.role = item.role.map((r: number) => String(r));
|
item.role = item.role.map((r: number) => String(r));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-input v-model="filterVal" :prefix-icon="Search" placeholder="请输入部门名称" />
|
<el-input v-model="filterVal" :prefix-icon="Search" placeholder="请输入部门名称" />
|
||||||
<div class="tree-com">
|
<div class="dept-tree-com">
|
||||||
<div class="tc-head">
|
<div class="tc-head">
|
||||||
<el-icon size="16" color="#606266" class="tc-head-icon">
|
<el-icon size="16" color="#606266" class="tc-head-icon">
|
||||||
<HomeFilled />
|
<HomeFilled />
|
||||||
@@ -15,22 +15,21 @@
|
|||||||
<el-tree
|
<el-tree
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
:data="treeData"
|
:data="treeData"
|
||||||
:props="treeProps"
|
:props="defaultTreeProps"
|
||||||
:filter-node-method="handleFilterTreeNode"
|
:filter-node-method="handleFilterTreeNode"
|
||||||
:load="handleLoadNode"
|
:load="handleLoadNode"
|
||||||
lazy
|
lazy
|
||||||
:indent="38"
|
:indent="38"
|
||||||
@node-click="handleNodeClick"
|
@node-click="handleNodeClick"
|
||||||
highlight-current
|
highlight-current
|
||||||
default-expand-all
|
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<element-tree-line :node="node" :showLabelLine="false" :indent="32">
|
<element-tree-line :node="node" :showLabelLine="false" :indent="32">
|
||||||
<span v-if="data.status" class="text-center font-black font-normal">
|
<span v-if="data.status" class="text-center font-black font-normal">
|
||||||
<SvgIcon name="iconfont icon-shouye" /> {{ node.label }}
|
<SvgIcon name="iconfont icon-shouye" color="var(--el-color-primary)" /> {{ node.label }}
|
||||||
<span v-show="showTotalNum">(10人)</span>
|
<span v-show="showTotalNum">({{ data.dept_user_count }}人)</span>
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="text-center font-black font-normal text-red-700"> <SvgIcon name="iconfont icon-shouye" /> {{ node.label }} </span>
|
<span v-else color="var(--el-color-primary)"> <SvgIcon name="iconfont icon-shouye" /> {{ node.label }} </span>
|
||||||
</element-tree-line>
|
</element-tree-line>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
@@ -85,9 +84,16 @@ interface IProps {
|
|||||||
|
|
||||||
const ElementTreeLine = getElementLabelLine(h);
|
const ElementTreeLine = getElementLabelLine(h);
|
||||||
|
|
||||||
const treeProps = {
|
const defaultTreeProps: any = {
|
||||||
children: 'children',
|
children: 'children',
|
||||||
label: 'name',
|
label: 'name',
|
||||||
|
isLeaf: (data: TreeItemType[], node: Node) => {
|
||||||
|
if (node.data.hasChild) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
withDefaults(defineProps<IProps>(), {
|
withDefaults(defineProps<IProps>(), {
|
||||||
@@ -233,7 +239,7 @@ const handleSort = async (type: string) => {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.tree-com {
|
.dept-tree-com {
|
||||||
height: calc(100% - 60px);
|
height: calc(100% - 60px);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -257,7 +263,7 @@ const handleSort = async (type: string) => {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
margin-left: 18px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tree .el-tree-node__expand-icon.expanded {
|
.el-tree .el-tree-node__expand-icon.expanded {
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
<el-row class="dept-el-row">
|
<el-row class="dept-el-row">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<div class="dept-box dept-left">
|
<div class="dept-box dept-left">
|
||||||
<TreeCom :treeData="deptTreeData" @treeClick="handleTreeClick" @updateDept="handleUpdateMenu"
|
<TreeCom :treeData="deptTreeData" @treeClick="handleTreeClick" @updateDept="handleUpdateMenu" @deleteDept="handleDeleteMenu" />
|
||||||
@deleteDept="handleDeleteMenu" />
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
@@ -15,9 +14,8 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-drawer v-model="drawerVisible" title="部门配置" direction="rtl" size="500px" :close-on-click-modal="false"
|
<el-drawer v-model="drawerVisible" title="部门配置" direction="rtl" size="500px" :close-on-click-modal="false" :before-close="handleDrawerClose">
|
||||||
:before-close="handleDrawerClose">
|
<DeptFormCom v-if="drawerVisible" :initFormData="drawerFormData" :treeData="deptTreeData" @drawerClose="handleDrawerClose" />
|
||||||
<DeptFormCom v-if="drawerVisible" :initFormData="drawerFormData" @drawerClose="handleDrawerClose" />
|
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
</fs-page>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
@@ -34,9 +32,9 @@ import { successNotification } from '../../../utils/message';
|
|||||||
import { APIResponseData, TreeItemType } from './types';
|
import { APIResponseData, TreeItemType } from './types';
|
||||||
|
|
||||||
let deptTreeData = ref([]);
|
let deptTreeData = ref([]);
|
||||||
let drawerVisible = ref(false)
|
let drawerVisible = ref(false);
|
||||||
let drawerFormData = ref<Partial<TreeItemType>>({})
|
let drawerFormData = ref<Partial<TreeItemType>>({});
|
||||||
let deptUserRef = ref<InstanceType<typeof DeptUserCom> | null>(null)
|
let deptUserRef = ref<InstanceType<typeof DeptUserCom> | null>(null);
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
let res: APIResponseData = await GetList({});
|
let res: APIResponseData = await GetList({});
|
||||||
@@ -56,30 +54,26 @@ const getData = async () => {
|
|||||||
* 部门的点击事件
|
* 部门的点击事件
|
||||||
*/
|
*/
|
||||||
const handleTreeClick = (id: string) => {
|
const handleTreeClick = (id: string) => {
|
||||||
deptUserRef.value?.handleDoRefreshUser(id)
|
deptUserRef.value?.handleDoRefreshUser(id);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部门的删除事件
|
* 部门的删除事件
|
||||||
*/
|
*/
|
||||||
const handleDeleteMenu = (id: string, callback: Function) => {
|
const handleDeleteMenu = (id: string, callback: Function) => {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm('您确认删除该部门吗?', '温馨提示', {
|
||||||
'您确认删除该部门吗?',
|
confirmButtonText: '确认',
|
||||||
'温馨提示',
|
cancelButtonText: '取消',
|
||||||
{
|
type: 'warning',
|
||||||
confirmButtonText: '确认',
|
}).then(async () => {
|
||||||
cancelButtonText: '取消',
|
const res: APIResponseData = await DelObj(id);
|
||||||
type: 'warning',
|
callback();
|
||||||
}
|
|
||||||
).then(async () => {
|
|
||||||
const res: APIResponseData = await DelObj(id)
|
|
||||||
callback()
|
|
||||||
if (res?.code === 2000) {
|
if (res?.code === 2000) {
|
||||||
successNotification(res.msg as string);
|
successNotification(res.msg as string);
|
||||||
getData();
|
getData();
|
||||||
deptUserRef.value?.handleDoRefreshUser('')
|
deptUserRef.value?.handleDoRefreshUser('');
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,18 +81,17 @@ const handleDeleteMenu = (id: string, callback: Function) => {
|
|||||||
*/
|
*/
|
||||||
const handleUpdateMenu = (type: string, record?: TreeItemType) => {
|
const handleUpdateMenu = (type: string, record?: TreeItemType) => {
|
||||||
if (type === 'update' && record) {
|
if (type === 'update' && record) {
|
||||||
drawerFormData.value = record
|
drawerFormData.value = record;
|
||||||
}
|
}
|
||||||
drawerVisible.value = true
|
drawerVisible.value = true;
|
||||||
|
|
||||||
};
|
};
|
||||||
const handleDrawerClose = (type?: string) => {
|
const handleDrawerClose = (type?: string) => {
|
||||||
if (type === 'submit') {
|
if (type === 'submit') {
|
||||||
getData()
|
getData();
|
||||||
}
|
}
|
||||||
drawerVisible.value = false
|
drawerVisible.value = false;
|
||||||
drawerFormData.value = {}
|
drawerFormData.value = {};
|
||||||
}
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getData();
|
getData();
|
||||||
@@ -137,4 +130,4 @@ onMounted(() => {
|
|||||||
border-radius: 8px 0 0 8px;
|
border-radius: 8px 0 0 8px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ export interface DeptFormDataType {
|
|||||||
parent: string | number;
|
parent: string | number;
|
||||||
name: string;
|
name: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
remark: string;
|
description: string;
|
||||||
is_catalog?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeptListType {
|
export interface DeptListType {
|
||||||
|
|||||||
Reference in New Issue
Block a user