You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
shop-admin/src/views/permission/dept/index.vue

511 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="list-container">
<div class="aside">
<div class="aside-header">
<div class="aside-title">组织架构</div>
<div class="aside-action">
<el-button type="text" @click="state.condition1.hiddenDisable = !state.condition1.hiddenDisable">
{{ state.condition1.hiddenDisable ? '隐藏' : '显示' }}禁用部门
</el-button>
</div>
</div>
<div class="aside-body">
<el-tree
:data="list"
default-expand-all
:expand-on-click-node="false"
highlight-current
node-key="id"
:props="{
label: 'name',
value: 'id',
children: 'childDepartment',
disabled: (data) => !data.isEnable,
}"
@current-change="(data) => handleChange(data.id)"
>
<template #default="{ data, node }">
<div class="flex">
<div class="name">
{{ data.name }}
</div>
<div class="btns">
<el-button type="text" @click.stop="handleCreate(null, data)">
<el-icon name="Plus" />
</el-button>
<template v-if="node.level > 1">
<el-button type="text" @click.stop="handleCreate(data)">
<el-icon name="Edit" />
</el-button>
<el-switch v-model="data.isEnable" @click.stop="handleEnabled(data)" />
</template>
</div>
</div>
</template>
</el-tree>
<el-dialog
v-model="formState.formVisible"
:title="(formState.form.id ? '编辑' : '新增') + '部门'"
width="300px"
>
<el-form
ref="refsForm"
v-loading="formState.submitting"
label-width="100px"
:model="formState.form"
:rules="formState.rules"
>
<el-form-item label="上级组织" prop="parentId">
<el-cascader
v-model="formState.form.parentId"
:options="list"
:props="{
label: 'name',
value: 'id',
children: 'childDepartment',
disabled: (data) => !data.isEnable,
checkStrictly: true,
expandTrigger: 'hover',
emitPath: false,
}"
/>
</el-form-item>
<el-form-item label="组织名称" prop="name">
<el-input v-model="formState.form.name" maxlength="10" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button :loading="formState.submitting" type="primary" @click="handleSave">保存</el-button>
</template>
</el-dialog>
</div>
</div>
<div class="content">
<el-tabs v-model="currentTab" type="border-card">
<el-tab-pane label="员工" name="employee">
<table-list
ref="refsTable"
v-loading="loading2"
:code="employeeCode"
:config="employeeConfig"
:data="employeeList"
:operation="['search']"
@search="handleSearchEmployee"
>
<template #search>
<el-form inline :model="state.condition2">
<el-form-item label="姓名" prop="employeeName">
<el-input v-model="state.condition2.employeeName" />
</el-form-item>
<el-form-item label="花名" prop="userName">
<el-input v-model="state.condition2.userName" />
</el-form-item>
<el-form-item label="手机号" prop="phone">
<el-input v-model="state.condition2.phone" />
</el-form-item>
</el-form>
</template>
</table-list>
</el-tab-pane>
<el-tab-pane label="角色" name="role">
<template v-if="state.condition2.departmentId">
<div class="role-list">
<div v-for="(item, index) in opts.system" :key="index" class="system">
<h3 class="system-header">
{{ item.systemName }}
</h3>
<div class="flex">
<el-tag
v-for="(role, j) in store.getters['deptRole/roleList'](item.id)"
:key="index + '' + j"
closable
size="large"
@close="(delRoleId = role.id), (delVisible = true)"
>
{{ role.roleName }}
</el-tag>
<el-button @click="handleAddRole(item.id)">添加角色</el-button>
</div>
</div>
</div>
</template>
<div v-else class="empty">请选择组织架构</div>
<el-dialog v-model="roleVisible" title="添加角色">
<el-form v-loading="loading3">
<el-form-item>
<el-select
v-model="checkedRole"
:config="{
label: 'roleName',
value: 'id',
disabled: (item) =>
store.getters['deptRole/roleList'](currentSystemId).findIndex(
(role) => role.id === item.id
) !== -1,
}"
multiple
:opts="systemRoleList"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="roleVisible = false">取消</el-button>
<el-button :loading="loading3" type="primary" @click="handleSaveRole">确定</el-button>
</template>
</el-dialog>
<el-dialog v-model="delVisible" title="提示">
<h3 style="display: flex; align-items: center">
<el-icon name="Warning" size="20" style="margin-right: 10px" />
确定删除组织中的角色吗?
</h3>
<p>删除角色后,组织下的员工将失去对应角色权限</p>
<el-checkbox
v-model="isDeleteDepartmentOfEmployeeRole"
label="仅删除组织角色不删除组织下员工角色"
/>
<template #footer>
<el-button @click="delVisible = false">取消</el-button>
<el-button :loading="loading4" type="primary" @click="handleDelRole">确定</el-button>
</template>
</el-dialog>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script setup lang="jsx">
import ElButton from '@/components/extra/ElButton.vue';
import ElSwitch from '@/components/extra/ElSwitch.vue';
const router = useRouter();
const store = useStore();
const { proxy } = getCurrentInstance();
const opts = computed(() => store.state.dept.opts);
if (!unref(opts).init) {
store.dispatch('dept/load');
}
/* 查询树结构 */
const loading = ref(false);
const list = computed(() => _.cloneDeep(store.state.dept.list));
const state = reactive({
condition1: { hiddenDisable: false },
condition2: { employeeName: null, userName: null, phone: null, departmentId: null },
condition3: { departmentId: null },
});
const handleSearch = async () => {
loading.value = true;
await store.dispatch('dept/search');
loading.value = false;
};
const handleChange = (id) => {
state.condition2.departmentId = id;
state.condition3.departmentId = id;
};
watch(
() => state.condition1,
(value) => {
store.commit('dept/setCondition', _.cloneDeep(value));
handleSearch();
},
{ immediate: true, deep: true }
);
const currentTab = ref('employee');
/* 员工 */
const loading2 = ref(false);
const refsTable = ref(null);
const employeeCode = computed(() => store.state.deptEmployee.code);
const employeeList = computed(() => _.cloneDeep(store.state.deptEmployee.list));
const handleSearchEmployee = async () => {
if (state.condition2.departmentId) {
loading2.value = true;
await store.dispatch('deptEmployee/search');
loading2.value = false;
} else {
proxy.$message.error('请先选择部门');
}
};
watch(
() => state.condition2,
(value) => {
let search = value?.departmentId && value?.departmentId !== store.state.deptEmployee.condition.departmentId;
store.commit('deptEmployee/setCondition', _.cloneDeep(value));
if (search) {
handleSearchEmployee();
}
},
{ immediate: true, deep: true }
);
const handleUpdateEmployee = (row) => {
router.push({
name: 'UpdateEmployee',
params: {
id: row.id,
},
});
};
const handleTransferEmployee = () => {};
let employeeConfig = reactive({
page: false,
columns: [
{
label: '工号',
prop: 'id',
width: 100,
},
{
label: '姓名',
prop: 'employeeName',
minWidth: 160,
},
{
label: '花名',
prop: 'userName',
minWidth: 160,
},
{
label: '手机号',
prop: 'phone',
minWidth: 160,
},
{
label: '状态',
width: 160,
slots: {
default: ({ row }) => <ElSwitch v-model={row.isEnable} />,
},
},
{
label: '创建人',
prop: 'createUserName',
width: 180,
},
{
label: '创建时间',
prop: 'createTime',
width: 180,
},
{
label: '操作',
width: 300,
slots: {
default: ({ row }) => (
<div>
<ElButton type="text" onClick={() => handleUpdateEmployee(row)}>
编辑
</ElButton>
<ElButton type="text" onClick={() => handleTransferEmployee(row)}>
调动
</ElButton>
</div>
),
},
},
],
});
/* 角色 */
const loading3 = ref(false);
const roleVisible = ref(false);
const systemRoleList = ref([]);
const currentSystemId = ref(null);
const checkedRole = ref([]);
const handleSearchRole = async () => {
if (state.condition3.departmentId) {
loading3.value = true;
await store.dispatch('deptRole/search');
loading3.value = false;
} else {
proxy.$message.error('请先选择部门');
}
};
watch(
() => state.condition3,
(value) => {
let search = value?.departmentId && value?.departmentId !== store.state.deptRole.condition.departmentId;
store.commit('deptRole/setCondition', _.cloneDeep(value));
if (search) {
handleSearchRole();
}
},
{ immediate: true, deep: true }
);
const handleAddRole = async (id) => {
loading3.value = true;
roleVisible.value = true;
currentSystemId.value = id;
systemRoleList.value = await store.dispatch('deptRole/role', id);
loading3.value = false;
};
const handleSaveRole = async () => {
loading3.value = true;
let res =
!unref(checkedRole).length ||
(await store.dispatch('deptRole/save', {
departmentId: state.condition3.departmentId,
isDistributionCurrentDepartmentEmployee: true,
roleIds: unref(checkedRole),
}));
if (res) {
roleVisible.value = false;
handleSearchRole();
}
loading3.value = false;
};
const loading4 = ref(false);
const delVisible = ref(false);
const delRoleId = ref(null);
const isDeleteDepartmentOfEmployeeRole = ref(false);
const handleDelRole = async () => {
loading4.value = true;
await store.dispatch('deptRole/remove', {
departmentId: state.condition3.departmentId,
isDeleteDepartmentOfEmployeeRole: !unref(isDeleteDepartmentOfEmployeeRole),
roleId: unref(delRoleId),
});
loading4.value = false;
delVisible.value = false;
};
/* 表单 */
const refsForm = ref(null);
const formState = reactive({
formVisible: false,
submitting: false,
form: {
id: null,
parentId: null,
name: null,
},
rules: {
name: [{ required: true, message: '请输入组织名称' }],
parentId: [{ required: true, message: '请选择组织类型' }],
},
});
const handleCreate = (row, parent) => {
formState.formVisible = true;
Object.assign(
formState.form,
row || {
id: null,
parentId: null,
name: null,
isEnable: true,
}
);
if (parent) {
formState.form.parentId = parent.id;
}
};
const handleCancel = () => {
formState.formVisible = false;
};
const handleEnabled = (row) => {
store.dispatch('dept/enable', row);
};
const handleSave = async () => {
formState.submitting = true;
try {
await proxy.$validate(refsForm);
let data = _.cloneDeep(formState.form);
await store.dispatch('dept/save', data);
formState.formVisible = false;
} catch (e) {
console.info('取消保存', e);
}
formState.submitting = false;
};
</script>
<style lang="less" scoped>
.list-container {
display: flex;
flex-direction: row;
.aside {
margin-right: @layout-space;
.aside-header {
min-width: max-content;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: @layout-space;
.aside-title {
font-size: @layout-h3;
}
.aside-action {
margin-left: @layout-space;
}
}
.aside-body {
:deep(.el-tree) {
.flex {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.btns {
display: flex;
align-items: center;
margin-left: @layout-space-super;
.el-button {
position: relative;
top: 2px;
+ .el-button {
margin-left: @layout-space-small;
}
}
.el-switch {
height: 100%;
transform: scale(0.6);
}
}
}
}
}
.content {
width: 100%;
flex-shrink: 1;
display: flex;
overflow: hidden;
.el-tabs {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
:deep(.el-tabs__content) {
height: 100%;
.el-tab-pane {
height: 100%;
}
}
}
.common-list {
width: 100%;
overflow: hidden;
}
.role-list {
.system {
& + .system {
margin-top: @layout-space;
}
.system-header {
margin-bottom: @layout-space;
}
.flex {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.el-tag {
margin: @layout-space-small @layout-space @layout-space-small 0;
}
}
}
}
}
</style>