feat: 项目初始化,未病局
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<!--
|
||||
* @Description: 复制
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:25
|
||||
!-->
|
||||
<template>
|
||||
<form-container v-model="visible" title="复制机构" form-size="600px">
|
||||
<el-form ref="orgFormRef" :rules="rules" :model="copyProps" label-width="auto">
|
||||
<s-form-item label="目标机构" prop="targetId">
|
||||
<org-selector v-model:org-value="copyProps.targetId" :org-tree-api="bizOrgApi.tree" :show-all="false" />
|
||||
</s-form-item>
|
||||
<s-form-item label="包含子集" prop="containsChild" :tooltip="{ content: '将组织下的子机构也复制一份到目标组织' }">
|
||||
<s-radio-group v-model="copyProps.containsChild" :options="yesOptions" button />
|
||||
</s-form-item>
|
||||
<s-form-item label="包含职位" prop="containsPosition" :tooltip="{ content: '将组织下的岗位也复制一份到目标组织' }">
|
||||
<s-radio-group v-model="copyProps.containsPosition" :options="yesOptions" button />
|
||||
</s-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
</template>
|
||||
</form-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { bizOrgApi } from "@/api";
|
||||
import { SysDictEnum } from "@/enums";
|
||||
import { required } from "@/utils/formRules";
|
||||
import { FormInstance } from "element-plus";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
const visible = ref(false); //是否显示表单
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
// 是否选项
|
||||
const yesOptions = dictStore.getDictList(SysDictEnum.YES_NO);
|
||||
|
||||
// 表单参数
|
||||
interface copyProps {
|
||||
/** 目标组织id */
|
||||
targetId: string | number;
|
||||
/** 选择的组织id */
|
||||
ids: string[] | number[];
|
||||
/** 是否包含子集 */
|
||||
containsChild: boolean;
|
||||
/** 是否包含职位 */
|
||||
containsPosition: boolean;
|
||||
/** 成功后的回调 */
|
||||
successful?: () => void;
|
||||
}
|
||||
|
||||
// 表单参数
|
||||
const copyProps = reactive<copyProps>({
|
||||
targetId: "",
|
||||
ids: [],
|
||||
containsChild: true,
|
||||
containsPosition: true
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
targetId: [required("请选择目标机构")],
|
||||
containsChild: [required("请选择是否包含子集")]
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param props 表单参数
|
||||
*/
|
||||
function onOpen(ids: string[] | number[], successful: () => void) {
|
||||
visible.value = true; //显示表单
|
||||
copyProps.ids = ids; //设置机构id
|
||||
copyProps.successful = successful;
|
||||
}
|
||||
|
||||
// 提交数据(新增/编辑)
|
||||
const orgFormRef = ref<FormInstance>();
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
orgFormRef.value?.validate(async valid => {
|
||||
if (!valid) return; //表单验证失败
|
||||
//提交表单
|
||||
await bizOrgApi
|
||||
.copy(copyProps)
|
||||
.then(() => {
|
||||
copyProps.successful!(); //调用父组件的successful方法
|
||||
})
|
||||
.finally(() => {
|
||||
onClose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭表单*/
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
onOpen
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,157 @@
|
||||
<!--
|
||||
* @Description: 表单
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:28
|
||||
!-->
|
||||
<template>
|
||||
<div>
|
||||
<form-container v-model="visible" :title="`${orgProps.opt}机构`" form-size="600px">
|
||||
<el-form
|
||||
ref="orgFormRef"
|
||||
:rules="rules"
|
||||
:disabled="orgProps.disabled"
|
||||
:model="orgProps.record"
|
||||
:hide-required-asterisk="orgProps.disabled"
|
||||
label-width="auto"
|
||||
label-suffix=" :"
|
||||
>
|
||||
<s-form-item label="上级机构" prop="parentId">
|
||||
<org-selector v-model:org-value="orgProps.record.parentId!" :org-tree-api="bizOrgApi.tree" :show-all="false" />
|
||||
</s-form-item>
|
||||
<s-form-item label="机构名称" prop="name">
|
||||
<s-input v-model="orgProps.record.name"></s-input>
|
||||
</s-form-item>
|
||||
<s-form-item label="机构编码" prop="code">
|
||||
<s-input v-model="orgProps.record.code" placeholder="请填写机构编码,不填则为随机值" clearable></s-input>
|
||||
</s-form-item>
|
||||
<s-form-item label="状态" prop="status">
|
||||
<s-radio-group v-model="orgProps.record.status" :options="statusOptions" button />
|
||||
</s-form-item>
|
||||
<s-form-item label="指定主管" prop="sortCode">
|
||||
<el-button link type="primary" @click="showSelector">选择</el-button>
|
||||
<el-tag v-if="orgProps.record.directorId" class="ml-3px" type="warning" closable @close="removeDirector">{{
|
||||
orgProps.record.directorInfo?.name
|
||||
}}</el-tag>
|
||||
</s-form-item>
|
||||
<s-form-item label="排序" prop="sortCode">
|
||||
<el-slider v-model="orgProps.record.sortCode" show-input :min="1" />
|
||||
</s-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button v-show="!orgProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
</template>
|
||||
</form-container>
|
||||
<user-selector
|
||||
ref="userSelectorRef"
|
||||
biz
|
||||
:org-tree-api="bizOrgApi.tree"
|
||||
:position-tree-api="bizPositionApi.tree"
|
||||
:role-tree-api="bizRoleApi.tree"
|
||||
:user-selector-api="bizUserApi.selector"
|
||||
@successful="handleChooseUser"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SysOrg, SysUser, bizOrgApi, bizRoleApi, bizPositionApi, bizUserApi } from "@/api";
|
||||
import { FormOptEnum, SysDictEnum } from "@/enums";
|
||||
import { required } from "@/utils/formRules";
|
||||
import { FormInstance } from "element-plus";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import { UserSelectorInstance } from "@/components/Selectors/UserSelector/interface";
|
||||
|
||||
const visible = ref(false); //是否显示表单
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
|
||||
// 通用状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
|
||||
// 表单参数
|
||||
const orgProps = reactive<FormProps.Base<SysOrg.SysOrgInfo>>({
|
||||
opt: FormOptEnum.ADD,
|
||||
record: {},
|
||||
disabled: false
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
name: [required("请输入机构名称")],
|
||||
parentId: [required("请选择上级机构")],
|
||||
category: [required("请选择机构类型")],
|
||||
status: [required("请选择状态")]
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param props 表单参数
|
||||
*/
|
||||
function onOpen(props: FormProps.Base<SysOrg.SysOrgInfo>) {
|
||||
Object.assign(orgProps, props); //合并参数
|
||||
if (props.opt == FormOptEnum.ADD) {
|
||||
//如果是新增,设置默认值
|
||||
orgProps.record.sortCode = 99;
|
||||
orgProps.record.status = statusOptions[0].value;
|
||||
}
|
||||
visible.value = true; //显示表单
|
||||
if (props.record.id) {
|
||||
//如果传了id,就去请求api获取record
|
||||
bizOrgApi.detail({ id: props.record.id }).then(res => {
|
||||
orgProps.record = res.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 提交数据(新增/编辑)
|
||||
const orgFormRef = ref<FormInstance>();
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
orgFormRef.value?.validate(async valid => {
|
||||
if (!valid) return; //表单验证失败
|
||||
//提交表单
|
||||
await bizOrgApi
|
||||
.submitForm(orgProps.record, orgProps.record.id != undefined)
|
||||
.then(() => {
|
||||
orgProps.successful!(); //调用父组件的successful方法
|
||||
})
|
||||
.finally(() => {
|
||||
onClose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭表单*/
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
const userSelectorRef = ref<UserSelectorInstance>(); //用户选择器引用
|
||||
/** 显示用户选择器 */
|
||||
function showSelector() {
|
||||
//将orgProps.record.directorInfo转为 SysUser.SysUserInfo[]类型
|
||||
const directorInfo = orgProps.record.directorInfo ? [orgProps.record.directorInfo] : [];
|
||||
userSelectorRef.value?.showSelector(directorInfo);
|
||||
}
|
||||
|
||||
/** 选择用户 */
|
||||
function handleChooseUser(data: SysUser.SysUserInfo[]) {
|
||||
// 选择用户后,将用户id赋值给orgProps.record.directorId
|
||||
if (data.length > 0) {
|
||||
orgProps.record.directorId = data[0].id;
|
||||
orgProps.record.directorInfo = data[0];
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除主管 */
|
||||
function removeDirector() {
|
||||
orgProps.record.directorId = null;
|
||||
orgProps.record.directorInfo = null;
|
||||
}
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
onOpen
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,162 @@
|
||||
<!--
|
||||
* @Description: 机构管理
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:32
|
||||
!-->
|
||||
<template>
|
||||
<div class="main-box">
|
||||
<TreeFilter
|
||||
ref="treeFilter"
|
||||
label="name"
|
||||
title="机构列表"
|
||||
:show-all="false"
|
||||
:request-api="bizOrgApi.tree"
|
||||
:default-value="initParam.parentId"
|
||||
@change="changeTreeFilter"
|
||||
/>
|
||||
<div class="table-box">
|
||||
<ProTable ref="proTable" title="机构列表" :columns="columns" :request-api="bizOrgApi.page" :init-param="initParam">
|
||||
<!-- 表格 header 按钮 -->
|
||||
<template #tableHeader="scope">
|
||||
<s-button v-auth="bizOrgButtonCode.add" suffix="机构" @click="onOpen(FormOptEnum.ADD)" />
|
||||
<s-button v-auth="bizOrgButtonCode.copy" :disabled="!scope.isSelected" :icon="CopyDocument" @click="onOpenCopy(scope.selectedListIds)">
|
||||
复制机构
|
||||
</s-button>
|
||||
<s-button
|
||||
type="danger"
|
||||
v-auth="bizOrgButtonCode.batchDelete"
|
||||
plain
|
||||
suffix="机构"
|
||||
:opt="FormOptEnum.DELETE"
|
||||
:disabled="!scope.isSelected"
|
||||
@click="onDelete(scope.selectedListIds, '删除所选机构')"
|
||||
/>
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #status="scope">
|
||||
<el-tag v-if="scope.row.status === CommonStatusEnum.ENABLE" type="success">{{
|
||||
dictStore.dictTranslation(SysDictEnum.COMMON_STATUS, scope.row.status)
|
||||
}}</el-tag>
|
||||
<el-tag v-else type="danger">{{ dictStore.dictTranslation(SysDictEnum.COMMON_STATUS, scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #operation="scope">
|
||||
<s-button v-auth="bizOrgButtonCode.edit" link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)" />
|
||||
<s-button
|
||||
v-auth="bizOrgButtonCode.delete"
|
||||
link
|
||||
:opt="FormOptEnum.DELETE"
|
||||
@click="onDelete([scope.row.id], `删除【${scope.row.name}】机构`)"
|
||||
/>
|
||||
</template>
|
||||
</ProTable>
|
||||
<!-- 新增/编辑表单 -->
|
||||
<Form ref="formRef" />
|
||||
<!-- 复制表单 -->
|
||||
<Copy ref="copyRef" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="sysOrg">
|
||||
import { bizOrgApi, SysOrg, bizOrgButtonCode } from "@/api";
|
||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||
import { CopyDocument } from "@element-plus/icons-vue";
|
||||
import { SysDictEnum, FormOptEnum, CommonStatusEnum } from "@/enums";
|
||||
import { useHandleData } from "@/hooks";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
|
||||
import TreeFilter from "@/components/TreeFilter/index.vue";
|
||||
import Form from "./components/form.vue";
|
||||
import Copy from "./components/copy.vue";
|
||||
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
// 机构类型选项
|
||||
const orgCategoryOptions = dictStore.getDictList(SysDictEnum.ORG_CATEGORY);
|
||||
// 状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
const treeFilter = ref<InstanceType<typeof TreeFilter> | null>(null);
|
||||
|
||||
interface InitParam {
|
||||
parentId: number | string;
|
||||
}
|
||||
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
||||
const initParam = reactive<InitParam>({ parentId: 0 });
|
||||
// 获取 ProTable 元素
|
||||
const proTable = ref<ProTableInstance>();
|
||||
|
||||
// 表格配置项
|
||||
const columns: ColumnProps<SysOrg.SysOrgInfo>[] = [
|
||||
{ type: "selection", fixed: "left", width: 50 },
|
||||
{ prop: "name", label: "机构名称", search: { el: "input" } },
|
||||
{ prop: "names", label: "机构全称" },
|
||||
{
|
||||
prop: "category",
|
||||
label: "分类",
|
||||
enum: orgCategoryOptions,
|
||||
search: { el: "select" }
|
||||
},
|
||||
{ prop: "code", label: "编码", search: { el: "input" } },
|
||||
{ prop: "status", label: "状态", enum: statusOptions, search: { el: "select" } },
|
||||
{ prop: "sortCode", label: "排序" },
|
||||
{ prop: "createTime", label: "创建时间" },
|
||||
|
||||
{ prop: "operation", label: "操作", width: 150, fixed: "right" }
|
||||
];
|
||||
|
||||
/** 部门切换 */
|
||||
function changeTreeFilter(val: number | string) {
|
||||
proTable.value!.pageable.pageNum = 1;
|
||||
if (val != "") {
|
||||
// 如果传入的val不为空,则将val赋值给initParam.parentId
|
||||
initParam.parentId = val;
|
||||
} else {
|
||||
// 否则将initParam.parentId赋值为0
|
||||
initParam.parentId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<InstanceType<typeof Form> | null>(null);
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param opt 操作类型
|
||||
* @param record 记录
|
||||
*/
|
||||
function onOpen(opt: FormOptEnum, record: {} | SysOrg.SysOrgInfo = {}) {
|
||||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable });
|
||||
}
|
||||
|
||||
// 表单引用
|
||||
const copyRef = ref<InstanceType<typeof Copy> | null>(null);
|
||||
|
||||
/**
|
||||
* 打开复制表单
|
||||
* @param opt 操作类型
|
||||
* @param record 记录
|
||||
*/
|
||||
function onOpenCopy(ids: string[] | number[]) {
|
||||
copyRef.value?.onOpen(ids, RefreshTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids id数组
|
||||
*/
|
||||
async function onDelete(ids: string[], msg: string) {
|
||||
// 二次确认 => 请求api => 刷新表格
|
||||
await useHandleData(bizOrgApi.delete, { ids }, msg);
|
||||
RefreshTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新表格
|
||||
*/
|
||||
function RefreshTable() {
|
||||
proTable.value?.refresh(); //刷新表格
|
||||
treeFilter.value?.refresh(); //刷新树形筛选器
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,122 @@
|
||||
<!--
|
||||
* @Description: 表单
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:37
|
||||
!-->
|
||||
<template>
|
||||
<div>
|
||||
<form-container v-model="visible" :title="`${positionProps.opt}岗位`" form-size="600px">
|
||||
<el-form
|
||||
ref="positionFormRef"
|
||||
:rules="rules"
|
||||
:disabled="positionProps.disabled"
|
||||
:model="positionProps.record"
|
||||
:hide-required-asterisk="positionProps.disabled"
|
||||
label-width="auto"
|
||||
label-suffix=" :"
|
||||
>
|
||||
<s-form-item label="所属组织" prop="orgId">
|
||||
<org-selector v-model:org-value="positionProps.record.orgId!" :org-tree-api="bizOrgApi.tree" :show-all="false" />
|
||||
</s-form-item>
|
||||
<s-form-item label="岗位名称" prop="name">
|
||||
<s-input v-model="positionProps.record.name"></s-input>
|
||||
</s-form-item>
|
||||
<s-form-item label="岗位分类" prop="category">
|
||||
<s-radio-group v-model="positionProps.record.category" :options="posCategoryOptions" button />
|
||||
</s-form-item>
|
||||
<s-form-item label="状态" prop="status">
|
||||
<s-radio-group v-model="positionProps.record.status" :options="statusOptions" button />
|
||||
</s-form-item>
|
||||
<s-form-item label="排序" prop="sortCode">
|
||||
<el-slider v-model="positionProps.record.sortCode" show-input :min="1" />
|
||||
</s-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button v-show="!positionProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
</template>
|
||||
</form-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { bizOrgApi, SysPosition, bizPositionApi } from "@/api";
|
||||
import { FormOptEnum, SysDictEnum } from "@/enums";
|
||||
import { required } from "@/utils/formRules";
|
||||
import { FormInstance } from "element-plus";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
|
||||
const visible = ref(false); //是否显示表单
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
// 岗位类型选项
|
||||
const posCategoryOptions = dictStore.getDictList(SysDictEnum.POSITION_CATEGORY);
|
||||
// 通用状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
|
||||
// 表单参数
|
||||
const positionProps = reactive<FormProps.Base<SysPosition.SysPositionInfo>>({
|
||||
opt: FormOptEnum.ADD,
|
||||
record: {},
|
||||
disabled: false
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
parentId: [required("请选择所属组织")],
|
||||
name: [required("请输入岗位名称")],
|
||||
orgId: [required("请选择所属岗位")],
|
||||
category: [required("请选择岗位类型")],
|
||||
status: [required("请选择状态")]
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param props 表单参数
|
||||
*/
|
||||
function onOpen(props: FormProps.Base<SysPosition.SysPositionInfo>) {
|
||||
Object.assign(positionProps, props); //合并参数
|
||||
if (props.opt == FormOptEnum.ADD) {
|
||||
//如果是新增,设置默认值
|
||||
positionProps.record.sortCode = 99;
|
||||
positionProps.record.category = posCategoryOptions[0].value;
|
||||
positionProps.record.status = statusOptions[0].value;
|
||||
}
|
||||
visible.value = true; //显示表单
|
||||
if (props.record.id) {
|
||||
//如果传了id,就去请求api获取record
|
||||
bizPositionApi.detail({ id: props.record.id }).then(res => {
|
||||
positionProps.record = res.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 提交数据(新增/编辑)
|
||||
const positionFormRef = ref<FormInstance>();
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
positionFormRef.value?.validate(async valid => {
|
||||
if (!valid) return; //表单验证失败
|
||||
//提交表单
|
||||
await bizPositionApi
|
||||
.submitForm(positionProps.record, positionProps.record.id != undefined)
|
||||
.then(() => {
|
||||
positionProps.successful!(); //调用父组件的successful方法
|
||||
})
|
||||
.finally(() => {
|
||||
onClose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭表单*/
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
onOpen
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,141 @@
|
||||
<!--
|
||||
* @Description: 岗位管理
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:43
|
||||
!-->
|
||||
<template>
|
||||
<div class="main-box">
|
||||
<TreeFilter
|
||||
ref="treeFilter"
|
||||
label="name"
|
||||
title="机构列表"
|
||||
:show-all="false"
|
||||
:request-api="bizOrgApi.tree"
|
||||
:default-value="initParam.orgId"
|
||||
@change="changeTreeFilter"
|
||||
/>
|
||||
<div class="table-box">
|
||||
<ProTable ref="proTable" title="岗位列表" :columns="columns" :request-api="bizPositionApi.page" :init-param="initParam">
|
||||
<!-- 表格 header 按钮 -->
|
||||
<template #tableHeader="scope">
|
||||
<s-button v-auth="bizPositionButtonCode.add" suffix="岗位" @click="onOpen(FormOptEnum.ADD)" />
|
||||
<s-button
|
||||
v-auth="bizPositionButtonCode.batchDelete"
|
||||
type="danger"
|
||||
plain
|
||||
suffix="岗位"
|
||||
:opt="FormOptEnum.DELETE"
|
||||
:disabled="!scope.isSelected"
|
||||
@click="onDelete(scope.selectedListIds, '删除所选岗位')"
|
||||
/>
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #status="scope">
|
||||
<el-tag v-if="scope.row.status === CommonStatusEnum.ENABLE" type="success">{{
|
||||
dictStore.dictTranslation(SysDictEnum.COMMON_STATUS, scope.row.status)
|
||||
}}</el-tag>
|
||||
<el-tag v-else type="danger">{{ dictStore.dictTranslation(SysDictEnum.COMMON_STATUS, scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #operation="scope">
|
||||
<s-button v-auth="bizPositionButtonCode.edit" link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)" />
|
||||
<s-button
|
||||
v-auth="bizPositionButtonCode.delete"
|
||||
link
|
||||
:opt="FormOptEnum.DELETE"
|
||||
@click="onDelete([scope.row.id], `删除【${scope.row.name}】岗位`)"
|
||||
/>
|
||||
</template>
|
||||
</ProTable>
|
||||
<!-- 新增/编辑表单 -->
|
||||
<Form ref="formRef" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="sysPosition">
|
||||
import { bizOrgApi, bizPositionApi, SysPosition, bizPositionButtonCode } from "@/api";
|
||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||
import { SysDictEnum, FormOptEnum, CommonStatusEnum } from "@/enums";
|
||||
import { useHandleData } from "@/hooks/useHandleData";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import TreeFilter from "@/components/TreeFilter/index.vue";
|
||||
import Form from "./components/form.vue";
|
||||
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
// 岗位类型选项
|
||||
const posCategoryOptions = dictStore.getDictList(SysDictEnum.POSITION_CATEGORY);
|
||||
// 状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
const treeFilter = ref<InstanceType<typeof TreeFilter> | null>(null);
|
||||
|
||||
interface InitParam {
|
||||
orgId: number | string;
|
||||
}
|
||||
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
||||
const initParam = reactive<InitParam>({ orgId: 0 });
|
||||
// 获取 ProTable 元素
|
||||
const proTable = ref<ProTableInstance>();
|
||||
|
||||
// 表格配置项
|
||||
const columns: ColumnProps<SysPosition.SysPositionInfo>[] = [
|
||||
{ type: "selection", fixed: "left", width: 50 },
|
||||
{ prop: "name", label: "岗位名称", search: { el: "input" } },
|
||||
{
|
||||
prop: "category",
|
||||
label: "岗位分类",
|
||||
enum: posCategoryOptions,
|
||||
search: { el: "select" }
|
||||
},
|
||||
{ prop: "code", label: "岗位编码", search: { el: "input" } },
|
||||
{ prop: "status", label: "状态", enum: statusOptions, search: { el: "select" } },
|
||||
{ prop: "sortCode", label: "排序" },
|
||||
{ prop: "createTime", label: "创建时间" },
|
||||
|
||||
{ prop: "operation", label: "操作", width: 150, fixed: "right" }
|
||||
];
|
||||
|
||||
/** 部门切换 */
|
||||
function changeTreeFilter(val: number | string) {
|
||||
proTable.value!.pageable.pageNum = 1;
|
||||
if (val != "") {
|
||||
// 如果传入的val不为空,则将val赋值给initParam.parentId
|
||||
initParam.orgId = val;
|
||||
} else {
|
||||
// 否则将initParam.parentId赋值为0
|
||||
initParam.orgId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<InstanceType<typeof Form> | null>(null);
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param opt 操作类型
|
||||
* @param record 记录
|
||||
*/
|
||||
function onOpen(opt: FormOptEnum, record: {} | SysPosition.SysPositionInfo = {}) {
|
||||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids id数组
|
||||
*/
|
||||
async function onDelete(ids: string[], msg: string) {
|
||||
// 二次确认 => 请求api => 刷新表格
|
||||
await useHandleData(bizPositionApi.delete, { ids }, msg);
|
||||
RefreshTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新表格
|
||||
*/
|
||||
function RefreshTable() {
|
||||
proTable.value?.refresh(); //刷新表格
|
||||
treeFilter.value?.refresh(); //刷新树形筛选器
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,118 @@
|
||||
<!--
|
||||
* @Description: 数据范围选择器
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:43:27
|
||||
!-->
|
||||
<template>
|
||||
<div>
|
||||
<el-space>
|
||||
<s-radio-group v-model="defaultLevel" :options="scopeOptions" value-key="level" label-key="title" @change="changeDataScope">
|
||||
<template #radio="{ item }">
|
||||
<el-badge
|
||||
v-if="
|
||||
dataScope.level === defineScope?.level && item.scopeCategory === SysRole.DataScopeEnum.SCOPE_ORG_DEFINE && item.scopeDefineOrgIdList
|
||||
"
|
||||
:value="dataScope.scopeDefineOrgIdList.length"
|
||||
type="success"
|
||||
>
|
||||
<span class="mb-5">{{ item.title }}</span>
|
||||
</el-badge>
|
||||
<span v-else>{{ item.title }}</span>
|
||||
</template>
|
||||
</s-radio-group>
|
||||
<div v-if="dataScope.level === defineScope?.level">
|
||||
<el-button link type="primary" class="ml-15px" @click="handleDefineOrg">选择机构</el-button>
|
||||
</div>
|
||||
</el-space>
|
||||
<el-dialog v-model="dialogVisible" append-to-body title="选择机构" width="400" destroy-on-close>
|
||||
<TreeFilter
|
||||
title="机构列表"
|
||||
multiple
|
||||
check-strictly
|
||||
class="treeFilter"
|
||||
label="name"
|
||||
:request-api="bizOrgApi.tree"
|
||||
:default-value="dataScope.scopeDefineOrgIdList"
|
||||
@change="changeTreeFilter"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="dialogCancel">取消</el-button>
|
||||
<el-button type="primary" @click="chooseDefineOrg"> 确定 </el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SysRole, bizOrgApi } from "@/api";
|
||||
|
||||
const dialogVisible = ref(false); //是否显示弹窗
|
||||
|
||||
// props
|
||||
interface DataScopeProps {
|
||||
modelValue: SysRole.DefaultDataScope | undefined;
|
||||
}
|
||||
const emit = defineEmits(["update:modelValue"]); //定义emit
|
||||
const props = defineProps<DataScopeProps>(); //定义props
|
||||
|
||||
// 用户信息
|
||||
const dataScope = computed({
|
||||
get: () => props.modelValue ?? scopeOptions[0], // 判断是否有值,没有值则取第一个
|
||||
set: val => emit("update:modelValue", val)
|
||||
});
|
||||
|
||||
// 数据范围列表,去掉全部
|
||||
const scopeOptions = SysRole.dataScopeOptions.filter(item => item.scopeCategory !== SysRole.DataScopeEnum.SCOPE_ALL);
|
||||
|
||||
// 默认数据范围
|
||||
const defaultLevel = ref(dataScope.value.level);
|
||||
|
||||
// 自定义数据范围
|
||||
const defineScope = SysRole.dataScopeOptions.find(item => item.scopeCategory === SysRole.DataScopeEnum.SCOPE_ORG_DEFINE);
|
||||
|
||||
// 自定义机构id列表
|
||||
const defineOrgIdList = ref<number[] | string[]>(dataScope.value.scopeDefineOrgIdList);
|
||||
|
||||
/** 修改数据权限 */
|
||||
function changeDataScope(value: any) {
|
||||
let scope = SysRole.dataScopeOptions.find(item => item.level === value);
|
||||
if (scope) {
|
||||
dataScope.value = scope;
|
||||
}
|
||||
}
|
||||
|
||||
// /** 处理自定义 */
|
||||
function handleDefineOrg() {
|
||||
dialogVisible.value = true;
|
||||
console.log("[ dataScope.value ] >", dataScope.value);
|
||||
}
|
||||
|
||||
/** 机构树数据变化 */
|
||||
function changeTreeFilter(val: any[]) {
|
||||
defineOrgIdList.value = val;
|
||||
}
|
||||
|
||||
/** 取消选择机构 */
|
||||
function dialogCancel() {
|
||||
// 关闭弹窗
|
||||
dialogVisible.value = false;
|
||||
}
|
||||
|
||||
/** 选择机构 */
|
||||
function chooseDefineOrg() {
|
||||
// 设置默认数据权限中的机构ID列表
|
||||
// 关闭弹窗
|
||||
dialogVisible.value = false;
|
||||
dataScope.value.scopeDefineOrgIdList = defineOrgIdList.value;
|
||||
console.log("[ dataScope.value ] >", dataScope.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.treeFilter {
|
||||
width: 100% !important;
|
||||
}
|
||||
:deep(.el-badge__content.is-fixed) {
|
||||
transform: translateY(-30%) translateX(100%);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,118 @@
|
||||
<!--
|
||||
* @Description: 表单
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:43:34
|
||||
!-->
|
||||
<template>
|
||||
<form-container v-model="visible" :title="`${sysRoleProps.opt}角色`" form-size="800px" @close="onClose">
|
||||
<el-form
|
||||
ref="sysRoleFormRef"
|
||||
:rules="rules"
|
||||
:disabled="sysRoleProps.disabled"
|
||||
:model="sysRoleProps.record"
|
||||
:hide-required-asterisk="sysRoleProps.disabled"
|
||||
label-width="auto"
|
||||
label-suffix=" :"
|
||||
>
|
||||
<s-form-item label="角色名称" prop="name">
|
||||
<s-input v-model="sysRoleProps.record.name"></s-input>
|
||||
</s-form-item>
|
||||
<s-form-item label="所属组织" prop="orgId">
|
||||
<org-selector v-model:org-value="sysRoleProps.record.orgId!" :org-tree-api="bizOrgApi.tree" :show-all="false" />
|
||||
</s-form-item>
|
||||
<s-form-item label="默认数据范围" prop="code">
|
||||
<DataScopeSelector v-model="sysRoleProps.record.defaultDataScope"></DataScopeSelector>
|
||||
</s-form-item>
|
||||
<s-form-item label="状态" prop="status">
|
||||
<s-radio-group v-model="sysRoleProps.record.status" :options="statusOptions" button />
|
||||
</s-form-item>
|
||||
<s-form-item label="排序" prop="sortCode">
|
||||
<el-slider v-model="sysRoleProps.record.sortCode" show-input :min="1" />
|
||||
</s-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button v-show="!sysRoleProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
</template>
|
||||
</form-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SysRole, bizRoleApi, bizOrgApi } from "@/api";
|
||||
import { FormOptEnum, SysDictEnum } from "@/enums";
|
||||
import { required } from "@/utils/formRules";
|
||||
import { FormInstance } from "element-plus";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import DataScopeSelector from "./dataScopeSelector.vue";
|
||||
|
||||
const visible = ref(false); //是否显示表单
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS); // 通用状态选项
|
||||
|
||||
// 表单参数
|
||||
const sysRoleProps = reactive<FormProps.Base<SysRole.SysRoleInfo>>({
|
||||
opt: FormOptEnum.ADD,
|
||||
record: {},
|
||||
disabled: false
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
name: [required("请输入角色名称")],
|
||||
orgId: [required("请选择所属组织")],
|
||||
category: [required("请选择角色类型")],
|
||||
status: [required("请选择状态")]
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param props 表单参数
|
||||
*/
|
||||
function onOpen(props: FormProps.Base<SysRole.SysRoleInfo>) {
|
||||
Object.assign(sysRoleProps, props); //合并参数
|
||||
if (props.opt == FormOptEnum.ADD) {
|
||||
//如果是新增,设置默认值
|
||||
sysRoleProps.record.sortCode = 99;
|
||||
sysRoleProps.record.status = statusOptions[0].value;
|
||||
sysRoleProps.record.defaultDataScope = SysRole.dataScopeOptions[0];
|
||||
}
|
||||
visible.value = true; //显示表单
|
||||
if (props.record.id) {
|
||||
//如果传了id,就去请求api获取record
|
||||
bizRoleApi.detail({ id: props.record.id }).then(res => {
|
||||
sysRoleProps.record = res.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 提交数据(新增/编辑)
|
||||
const sysRoleFormRef = ref<FormInstance>();
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
sysRoleFormRef.value?.validate(async valid => {
|
||||
if (!valid) return; //表单验证失败
|
||||
//提交表单
|
||||
await bizRoleApi
|
||||
.submitForm(sysRoleProps.record, sysRoleProps.record.id != undefined)
|
||||
.then(() => {
|
||||
sysRoleProps.successful!(); //调用父组件的successful方法
|
||||
})
|
||||
.finally(() => {
|
||||
onClose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭表单*/
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
onOpen
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,414 @@
|
||||
<!--
|
||||
* @Description: 授权资源表单
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:43:49
|
||||
!-->
|
||||
<template>
|
||||
<div>
|
||||
<form-container v-model="visible" :title="`授权资源->${grantProps.name}`" form-size="1200px" @close="onClose">
|
||||
<el-alert title="非超管角色不可被授权系统模块菜单资源" type="warning" show-icon style="margin-bottom: 10px" />
|
||||
<ProTable
|
||||
ref="proTable"
|
||||
:columns="columns"
|
||||
:tool-button="false"
|
||||
:cell-style="{ padding: '0px' }"
|
||||
:row-style="{ height: '0' }"
|
||||
:data="grantProps.tableData"
|
||||
:pagination="false"
|
||||
:span-method="objectSpanMethod"
|
||||
>
|
||||
<!-- 表格 header 按钮 -->
|
||||
<template #tableHeader>
|
||||
<s-radio-group
|
||||
v-model="grantProps.moduleId"
|
||||
:options="grantProps.echoDatalist"
|
||||
value-key="id"
|
||||
label-key="title"
|
||||
button
|
||||
class="mb-2"
|
||||
@change="moduleClick"
|
||||
>
|
||||
<template #radio="{ item }">
|
||||
<el-space>
|
||||
<svg-icon :icon="item.icon" class="h-4 w-4"></svg-icon>
|
||||
{{ item.title }}
|
||||
</el-space>
|
||||
</template>
|
||||
</s-radio-group>
|
||||
</template>
|
||||
<!-- 一级目录 -->
|
||||
<template #parentName="scope">
|
||||
<el-checkbox v-model="scope.row.parentCheck" @change="val => changeParent(scope.row, val)">
|
||||
{{ scope.row.parentName }}
|
||||
</el-checkbox>
|
||||
</template>
|
||||
<!-- 菜单 -->
|
||||
<template #title="scope">
|
||||
<el-checkbox v-model="scope.row.nameCheck" @change="val => changeSub(scope.row, val)">
|
||||
{{ scope.row.title }}
|
||||
</el-checkbox>
|
||||
</template>
|
||||
<!-- 按钮授权 -->
|
||||
<template #button="scope">
|
||||
<template v-if="scope.row.button.length > 0">
|
||||
<template v-for="(item, index) in scope.row.button" :key="item.id">
|
||||
<el-checkbox v-model="item.check" style="margin-right: 5px" @change="val => changeChildCheckBox(scope.row, val)">
|
||||
{{ item.title }}
|
||||
</el-checkbox>
|
||||
<br v-if="(index + 1) % 5 === 0" />
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</ProTable>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
</template>
|
||||
</form-container>
|
||||
<el-dialog v-model="dialogVisible" title="请选择数据范围" width="30%" destroy-on-close>
|
||||
<DataScopeSelector v-model="grantProps.defaultDataScope"></DataScopeSelector>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleUserSubmit"> 确定 </el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SysRole, SysUser, bizRoleApi, bizUserApi } from "@/api";
|
||||
import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
|
||||
import type { TableColumnCtx } from "element-plus";
|
||||
import DataScopeSelector from "./dataScopeSelector.vue";
|
||||
const visible = ref(false); //授权资源表单是否显示
|
||||
const dialogVisible = ref(false); // 数据范围弹窗显示
|
||||
|
||||
/** 授权资源表单参数接口 */
|
||||
interface GrantResourceFormProps {
|
||||
/** id */
|
||||
id: number | string;
|
||||
/** 名称 */
|
||||
name: string;
|
||||
/** 模块id */
|
||||
moduleId: number | string;
|
||||
/** 资源树 */
|
||||
resTree: SysRole.ResTreeSelector[];
|
||||
/** 已拥有的资源 */
|
||||
ownResource: SysRole.RoleOwnResource | null;
|
||||
/** 一级菜单索引 */
|
||||
firstShowMap: Record<string, number[]>;
|
||||
/** 回显数据列表 */
|
||||
echoDatalist: SysRole.ResTreeSelector[];
|
||||
/** 表格数据 */
|
||||
tableData: SysRole.RoleGrantResourceMenu[];
|
||||
//默认数据范围,用户才有
|
||||
defaultDataScope: SysRole.DefaultDataScope;
|
||||
}
|
||||
|
||||
/** 授权资源表单参数 */
|
||||
const grantProps = reactive<GrantResourceFormProps>({
|
||||
id: 0,
|
||||
name: "",
|
||||
moduleId: 0,
|
||||
resTree: [],
|
||||
ownResource: null,
|
||||
firstShowMap: {},
|
||||
echoDatalist: [],
|
||||
tableData: [],
|
||||
defaultDataScope: SysRole.dataScopeOptions[0]
|
||||
});
|
||||
|
||||
/** props */
|
||||
const props = defineProps<{
|
||||
/** 是否是用户 */
|
||||
user?: boolean;
|
||||
}>();
|
||||
|
||||
// 获取 ProTable DOM
|
||||
const proTable = ref<ProTableInstance>();
|
||||
// 表格配置项
|
||||
const columns: ColumnProps<SysRole.ResTreeSelector>[] = [
|
||||
// { type: "selection", width: 80 },
|
||||
{ prop: "parentName", label: "一级目录", width: 150, align: "left" },
|
||||
{ prop: "title", label: "菜单", width: 180, align: "left" },
|
||||
{ prop: "button", label: "按钮授权", align: "left" }
|
||||
];
|
||||
|
||||
/** 列合并 */
|
||||
interface SpanMethodProps {
|
||||
// 行
|
||||
row: SysRole.RoleGrantResourceMenu;
|
||||
// 列
|
||||
column: TableColumnCtx<SysRole.RoleGrantResourceMenu>;
|
||||
// 行索引
|
||||
rowIndex: number;
|
||||
// 列索引
|
||||
columnIndex: number;
|
||||
}
|
||||
|
||||
/** 列合并*/
|
||||
const objectSpanMethod = ({ row, rowIndex, columnIndex }: SpanMethodProps) => {
|
||||
// 如果列索引为0
|
||||
if (columnIndex == 0) {
|
||||
// 获取父名称
|
||||
const parentName = row.parentName;
|
||||
// 获取父名称的索引数组
|
||||
const indexArr = grantProps.firstShowMap[parentName];
|
||||
// 如果行索引等于索引数组的第一个
|
||||
if (rowIndex === indexArr[0]) {
|
||||
return { rowspan: indexArr.length, colspan: 1 };
|
||||
} else {
|
||||
return { rowspan: 0, colspan: 0 };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param props 表单参数
|
||||
*/
|
||||
async function onOpen(props: FormProps.Base<SysRole.SysRoleInfo | SysUser.SysUserInfo>) {
|
||||
grantProps.id = props.record.id || 0;
|
||||
grantProps.name = props.record.name || "";
|
||||
await loadData();
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
/** 加载数据 */
|
||||
async function loadData() {
|
||||
if (grantProps.echoDatalist.length > 0) {
|
||||
let data = grantProps.echoDatalist.find(f => f.id === grantProps.moduleId)!.menu;
|
||||
grantProps.tableData = data;
|
||||
} else {
|
||||
// 获取角色资源树
|
||||
await bizRoleApi.resourceTreeSelector().then(res => {
|
||||
grantProps.resTree = res.data;
|
||||
grantProps.moduleId = res.data[0].id;
|
||||
});
|
||||
await getResources(); // 获取资源
|
||||
// 如果有已拥有的资源
|
||||
if (grantProps.ownResource) {
|
||||
// 回显模块数据
|
||||
// 获取echoDatalist
|
||||
grantProps.echoDatalist = echoModuleData(grantProps.resTree, grantProps.ownResource);
|
||||
// 获取模块id
|
||||
grantProps.moduleId = grantProps.resTree[0].id;
|
||||
// 获取表格数据
|
||||
grantProps.tableData = grantProps.echoDatalist[0].menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取资源 */
|
||||
async function getResources() {
|
||||
if (props.user) {
|
||||
// 获取用户拥有资源
|
||||
await bizUserApi.ownResource({ id: grantProps.id }).then(res => {
|
||||
grantProps.ownResource = res.data;
|
||||
});
|
||||
} else {
|
||||
// 获取角色拥有资源
|
||||
await bizRoleApi.ownResource({ id: grantProps.id }).then(res => {
|
||||
grantProps.ownResource = res.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回显模块数据
|
||||
* @param resTree 资源树
|
||||
* @param ownResource 已拥有的资源
|
||||
*/
|
||||
function echoModuleData(resTree: SysRole.ResTreeSelector[], ownResource: SysRole.RoleOwnResource) {
|
||||
// 通过应用循环
|
||||
resTree.forEach(module => {
|
||||
//如果有菜单
|
||||
if (module.menu) {
|
||||
module.menu.forEach(menu => {
|
||||
const menuCheck = ref(0);
|
||||
// 如果已授权资源信息列表有数据
|
||||
if (ownResource.grantInfoList.length > 0) {
|
||||
// 通过已授权资源信息列表循环
|
||||
ownResource.grantInfoList.forEach(grantInfo => {
|
||||
// 如果已授权资源信息列表中的菜单id等于当前菜单id
|
||||
if (menu.id == grantInfo.menuId) {
|
||||
menuCheck.value++;
|
||||
// 处理按钮
|
||||
if (grantInfo.buttonInfo.length > 0) {
|
||||
// 通过已授权资源信息列表中的按钮循环
|
||||
grantInfo.buttonInfo.forEach(button => {
|
||||
menu.button.forEach(itemButton => {
|
||||
// 如果已授权资源信息列表中的按钮id等于当前按钮id
|
||||
if (button === itemButton.id) {
|
||||
itemButton.check = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// 回显前面的2个
|
||||
if (menuCheck.value > 0) {
|
||||
menu.parentCheck = true;
|
||||
menu.nameCheck = true;
|
||||
}
|
||||
});
|
||||
// 缓存加入索引
|
||||
module.menu.forEach((item: SysRole.RoleGrantResourceMenu, index) => {
|
||||
// 下面就是用来知道不同的一级菜单里面有几个二级菜单,以及他们所在的索引
|
||||
if (grantProps.firstShowMap[item.parentName]) {
|
||||
grantProps.firstShowMap[item.parentName].push(index);
|
||||
} else {
|
||||
grantProps.firstShowMap[item.parentName] = [index];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return resTree;
|
||||
}
|
||||
|
||||
/** 选择模块 */
|
||||
function moduleClick() {
|
||||
loadData();
|
||||
}
|
||||
|
||||
/** 一级目录点击 */
|
||||
function changeParent(row: SysRole.RoleGrantResourceMenu, val: any) {
|
||||
row.parentCheck = val;
|
||||
// 通过这个应用id,找到应用下的所有菜单
|
||||
const moduleMenu = grantProps.echoDatalist.find(f => row.module === f.id);
|
||||
const parentName = row.parentName;
|
||||
// 获取同一级菜单的所有索引
|
||||
const indexArr = grantProps.firstShowMap[parentName];
|
||||
indexArr.forEach(indexItem => {
|
||||
// 获取同一级菜单的所有行
|
||||
const row = moduleMenu?.menu[indexItem];
|
||||
|
||||
if (row)
|
||||
// 给这些菜单的索引去勾选
|
||||
changeSub(row, val);
|
||||
});
|
||||
}
|
||||
|
||||
/** 二级菜单的点击 */
|
||||
function changeSub(row: SysRole.RoleGrantResourceMenu, val: any) {
|
||||
// 选中二级菜单
|
||||
row.nameCheck = val;
|
||||
row.button.forEach(item => {
|
||||
// 选中按钮
|
||||
item.check = val;
|
||||
});
|
||||
}
|
||||
|
||||
/** 权限按钮点击 */
|
||||
function changeChildCheckBox(row: SysRole.RoleGrantResourceMenu, val: any) {
|
||||
// 如果val为false,并且row的子菜单没有选中,则取消所有按钮的选中
|
||||
if (!val && checkAllChildNotChecked(row)) {
|
||||
// 这里注释掉勾选去掉所有按钮,联动去掉菜单
|
||||
/*record.nameCheck = false
|
||||
record.parentCheck = false*/
|
||||
} else if (val) {
|
||||
// 如果val为true,则将row的nameCheck和parentCheck属性设置为true
|
||||
row.nameCheck = val;
|
||||
row.parentCheck = val;
|
||||
}
|
||||
}
|
||||
|
||||
/** 检查所有子级是否都没有勾选 */
|
||||
function checkAllChildNotChecked(record: SysRole.RoleGrantResourceMenu) {
|
||||
// 获取记录的子节点
|
||||
const child = record.button;
|
||||
// 判断子节点是否被选中,如果有一个子节点未被选中,则返回false
|
||||
return child.every(field => !field.check);
|
||||
}
|
||||
|
||||
/** 提交之前转换数据 */
|
||||
function convertData() {
|
||||
const params: SysRole.GrantResourceReq = {
|
||||
id: grantProps.id,
|
||||
grantInfoList: []
|
||||
};
|
||||
// 遍历echoDatalist,获取每一项
|
||||
grantProps.echoDatalist.forEach(data => {
|
||||
// 如果data有menu属性
|
||||
if (data.menu) {
|
||||
// 遍历data.menu,获取每一项
|
||||
data.menu.forEach(item => {
|
||||
// 初始化grantInfo
|
||||
const grantInfo: SysRole.RelationRoleResource = {
|
||||
menuId: 0,
|
||||
buttonInfo: []
|
||||
};
|
||||
// 如果被选中了
|
||||
if (item.nameCheck) {
|
||||
// 获取item的id
|
||||
grantInfo.menuId = item.id;
|
||||
// 遍历item.button,获取每一项
|
||||
item.button.forEach(button => {
|
||||
// 如果button被选中了
|
||||
if (button.check) {
|
||||
// 将button的id添加到grantInfo.buttonInfo中
|
||||
grantInfo.buttonInfo.push(button.id as never);
|
||||
}
|
||||
});
|
||||
// 将grantInfo添加到params.grantInfoList中
|
||||
params.grantInfoList.push(grantInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 返回params
|
||||
return params;
|
||||
}
|
||||
|
||||
/** 提交 */
|
||||
function handleSubmit() {
|
||||
if (props.user) {
|
||||
dialogVisible.value = true;
|
||||
} else {
|
||||
let params = convertData();
|
||||
grantResource(params);
|
||||
}
|
||||
}
|
||||
|
||||
/** 用户授权资源提交 */
|
||||
const handleUserSubmit = () => {
|
||||
const param = convertData();
|
||||
param.defaultDataScope = grantProps.defaultDataScope;
|
||||
grantResource(param);
|
||||
dialogVisible.value = false;
|
||||
};
|
||||
|
||||
/** 授权资源 */
|
||||
function grantResource(params: SysRole.GrantResourceReq) {
|
||||
// 根据用户判断走哪个方法
|
||||
if (props.user) {
|
||||
bizUserApi.grantResource(params).then(() => {
|
||||
onClose();
|
||||
});
|
||||
} else {
|
||||
bizRoleApi.grantResource(params).then(() => {
|
||||
onClose();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** 关闭表单 */
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
grantProps.echoDatalist = [];
|
||||
grantProps.moduleId = 0;
|
||||
grantProps.firstShowMap = {};
|
||||
grantProps.tableData = [];
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
onOpen
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,222 @@
|
||||
<!--
|
||||
* @Description: 角色管理
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:43:53
|
||||
!-->
|
||||
<template>
|
||||
<div class="main-box">
|
||||
<TreeFilter
|
||||
ref="treeFilter"
|
||||
label="name"
|
||||
title="组织列表"
|
||||
:show-all="false"
|
||||
:request-api="bizOrgApi.tree"
|
||||
:default-value="initParam.orgId"
|
||||
@change="changeTreeFilter"
|
||||
/>
|
||||
<div class="table-box">
|
||||
<ProTable ref="proTable" :columns="columns" :request-api="bizRoleApi.page" :init-param="initParam">
|
||||
<!-- 表格 header 按钮 -->
|
||||
<template #tableHeader="scope">
|
||||
<s-button v-auth="bizRoleButtonCode.add" suffix="角色" @click="onOpen(FormOptEnum.ADD)" />
|
||||
<s-button
|
||||
v-auth="bizRoleButtonCode.batchDelete"
|
||||
type="danger"
|
||||
plain
|
||||
suffix="角色"
|
||||
:opt="FormOptEnum.DELETE"
|
||||
:disabled="!scope.isSelected"
|
||||
@click="onDelete(scope.selectedListIds, '删除所选角色')"
|
||||
/>
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #status="scope">
|
||||
<el-tag v-if="scope.row.status === CommonStatusEnum.ENABLE" type="success">{{
|
||||
dictStore.dictTranslation(SysDictEnum.COMMON_STATUS, scope.row.status)
|
||||
}}</el-tag>
|
||||
<el-tag v-else type="danger">{{ dictStore.dictTranslation(SysDictEnum.COMMON_STATUS, scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #operation="scope">
|
||||
<el-space>
|
||||
<s-button v-auth="bizRoleButtonCode.edit" link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)" />
|
||||
<s-button
|
||||
v-auth="bizRoleButtonCode.delete"
|
||||
link
|
||||
:opt="FormOptEnum.DELETE"
|
||||
@click="onDelete([scope.row.id], `删除【${scope.row.name}】角色`)"
|
||||
/>
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-link type="primary" :underline="false" :icon="ArrowDown"> 授权 </el-link>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<div v-auth="bizRoleButtonCode.grantResource">
|
||||
<el-dropdown-item :command="command(scope.row, cmdEnum.GrantResource)">{{ cmdEnum.GrantResource }}</el-dropdown-item>
|
||||
</div>
|
||||
<div v-auth="bizRoleButtonCode.grantUser">
|
||||
<el-dropdown-item :command="command(scope.row, cmdEnum.GrantUser)">{{ cmdEnum.GrantUser }}</el-dropdown-item>
|
||||
</div>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-space>
|
||||
</template>
|
||||
</ProTable>
|
||||
<!-- 新增/编辑表单 -->
|
||||
<Form ref="formRef" />
|
||||
<!-- 授权资源组件 -->
|
||||
<GrantResource ref="grantResourceRef" />
|
||||
<!-- 用户选择器 -->
|
||||
<user-selector
|
||||
ref="userSelectorRef"
|
||||
biz
|
||||
:org-tree-api="bizOrgApi.tree"
|
||||
:position-tree-api="bizPositionApi.tree"
|
||||
:role-tree-api="bizRoleApi.tree"
|
||||
:user-selector-api="bizUserApi.selector"
|
||||
multiple
|
||||
@successful="handleChooseUser"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="sysRole">
|
||||
import { bizOrgApi, bizRoleApi, SysRole, SysUser, bizUserApi, bizPositionApi, bizRoleButtonCode } from "@/api";
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||
import { SysDictEnum, FormOptEnum, CommonStatusEnum } from "@/enums";
|
||||
import { useHandleData } from "@/hooks/useHandleData";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import TreeFilter from "@/components/TreeFilter/index.vue";
|
||||
import Form from "./components/form.vue";
|
||||
import GrantResource from "./components/grantResource.vue";
|
||||
import { UserSelectorInstance } from "@/components/Selectors/UserSelector/interface";
|
||||
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
|
||||
// 状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
// 组织列表组件
|
||||
const treeFilter = ref<InstanceType<typeof TreeFilter> | null>(null);
|
||||
|
||||
interface InitParam {
|
||||
orgId: number | string;
|
||||
}
|
||||
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
||||
const initParam = reactive<InitParam>({ orgId: 0 });
|
||||
// 获取 ProTable 元素
|
||||
const proTable = ref<ProTableInstance>();
|
||||
|
||||
// 表格配置项
|
||||
const columns: ColumnProps<SysRole.SysRoleInfo>[] = [
|
||||
{ type: "selection", fixed: "left", width: 50 },
|
||||
{ prop: "name", label: "角色名称", search: { el: "input" } },
|
||||
{ prop: "code", label: "角色编码", search: { el: "input" } },
|
||||
{ prop: "status", label: "状态", enum: statusOptions, search: { el: "select" } },
|
||||
{ prop: "sortCode", label: "排序" },
|
||||
{ prop: "createTime", label: "创建时间" },
|
||||
{ prop: "operation", label: "操作", width: 230, fixed: "right" }
|
||||
];
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<InstanceType<typeof Form> | null>(null);
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param opt 操作类型
|
||||
* @param record 记录
|
||||
*/
|
||||
function onOpen(opt: FormOptEnum, record: {} | SysRole.SysRoleInfo = {}) {
|
||||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids id数组
|
||||
*/
|
||||
async function onDelete(ids: string[], msg: string) {
|
||||
// 二次确认 => 请求api => 刷新表格
|
||||
await useHandleData(bizRoleApi.delete, { ids }, msg);
|
||||
RefreshTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新表格
|
||||
*/
|
||||
function RefreshTable() {
|
||||
proTable.value?.refresh(); //刷新表格
|
||||
treeFilter.value?.refresh(); //刷新树形筛选器
|
||||
}
|
||||
|
||||
/** 部门切换 */
|
||||
function changeTreeFilter(val: number | string) {
|
||||
proTable.value!.pageable.pageNum = 1;
|
||||
if (val != "") {
|
||||
// 如果传入的val不为空,则将val赋值给initParam.parentId
|
||||
initParam.orgId = val;
|
||||
} else {
|
||||
// 否则将initParam.parentId赋值为0
|
||||
initParam.orgId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** 更多下拉菜单命令枚举 */
|
||||
enum cmdEnum {
|
||||
GrantResource = "授权资源",
|
||||
GrantUser = "授权用户"
|
||||
}
|
||||
|
||||
/** 下拉菜单参数接口 */
|
||||
interface Command {
|
||||
row: SysRole.SysRoleInfo;
|
||||
command: cmdEnum;
|
||||
}
|
||||
|
||||
/**配置command的参数 */
|
||||
function command(row: SysRole.SysRoleInfo, command: cmdEnum): Command {
|
||||
return {
|
||||
row: row,
|
||||
command: command
|
||||
};
|
||||
}
|
||||
|
||||
const grantResourceRef = ref<InstanceType<typeof GrantResource> | null>(null); //授权资源组件引用
|
||||
const userSelectorRef = ref<UserSelectorInstance>(); //用户选择器引用
|
||||
const roleId = ref<number | string>(0); //角色id
|
||||
|
||||
/**
|
||||
* 更多下拉菜单点击事件
|
||||
* @param command
|
||||
*/
|
||||
function handleCommand(command: Command) {
|
||||
switch (command.command) {
|
||||
case cmdEnum.GrantUser:
|
||||
roleId.value = command.row.id; //获取角色id
|
||||
bizRoleApi.ownUser({ id: command.row.id }).then(res => {
|
||||
userSelectorRef.value?.showSelector(res.data); //显示用户选择器
|
||||
});
|
||||
break;
|
||||
case cmdEnum.GrantResource:
|
||||
// 打开授权资源组件
|
||||
grantResourceRef.value?.onOpen({
|
||||
opt: FormOptEnum.EDIT,
|
||||
record: command.row,
|
||||
successful: RefreshTable
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择用户 */
|
||||
function handleChooseUser(data: SysUser.SysUserInfo[]) {
|
||||
//组装参数
|
||||
const grantUser: SysRole.GrantUserReq = {
|
||||
id: roleId.value,
|
||||
grantInfoList: data.map(item => item.id) as number[] | string[]
|
||||
};
|
||||
bizRoleApi.grantUser(grantUser);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,169 @@
|
||||
<!--
|
||||
* @Description: 基本信息
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:50
|
||||
!-->
|
||||
<template>
|
||||
<div>
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="账号" prop="account">
|
||||
<s-input v-model="userInfo.account"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="姓名" prop="name">
|
||||
<s-input v-model="userInfo.name"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="性别" prop="gender">
|
||||
<s-radio-group v-model="userInfo.gender" :options="genderOptions" />
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="昵称" prop="nickname">
|
||||
<s-input v-model="userInfo.nickname"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="手机号" prop="phone">
|
||||
<s-input v-model="userInfo.phone"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="邮箱" prop="email">
|
||||
<s-input v-model="userInfo.email"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="出生日期" prop="birthday">
|
||||
<date-picker v-model="userInfo.birthday"></date-picker>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="入职日期" prop="entryDate">
|
||||
<date-picker v-model="userInfo.entryDate"></date-picker>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="员工编号" prop="empNo">
|
||||
<s-input v-model="userInfo.empNo"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="职级" prop="positionLevel">
|
||||
<s-input v-model="userInfo.positionLevel" clearable></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="部门和职位" prop="positionId">
|
||||
<position-selector v-model="userInfo.orgAndPosIdList" @change="handleChange" :selector-api="bizPositionApi.selector" />
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="主管" prop="directorId">
|
||||
<el-button link type="primary" @click="showSelector">选择</el-button>
|
||||
<el-tag v-if="userInfo.directorId" class="ml-3px" type="warning" closable @close="removeDirector">{{
|
||||
userInfo.directorInfo?.name
|
||||
}}</el-tag>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<user-selector
|
||||
ref="userSelectorRef"
|
||||
biz
|
||||
:org-tree-api="bizOrgApi.tree"
|
||||
:position-tree-api="bizPositionApi.tree"
|
||||
:role-tree-api="bizRoleApi.tree"
|
||||
:user-selector-api="bizUserApi.selector"
|
||||
@successful="handleChooseUser"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SysUser, bizOrgApi, bizPositionApi, bizRoleApi, bizUserApi } from "@/api";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import { SysDictEnum } from "@/enums";
|
||||
import { UserSelectorInstance } from "@/components/Selectors/UserSelector/interface";
|
||||
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
// props
|
||||
interface FormProps {
|
||||
modelValue: Partial<SysUser.SysUserInfo>;
|
||||
}
|
||||
const emit = defineEmits(["update:modelValue"]); //定义emit
|
||||
const props = defineProps<FormProps>(); //定义props
|
||||
// 人员信息
|
||||
const userInfo = computed({
|
||||
get: () => props.modelValue,
|
||||
set: val => emit("update:modelValue", val)
|
||||
});
|
||||
|
||||
// 通用状态选项
|
||||
const genderOptions = dictStore.getDictList(SysDictEnum.GENDER);
|
||||
onMounted(() => {
|
||||
// 初始化
|
||||
userInfo.value.gender = userInfo.value.gender ? userInfo.value.gender : genderOptions[0].value;
|
||||
});
|
||||
|
||||
/** 用于处理职位选择器改变值的情况 */
|
||||
function handleChange(value: any) {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
// 获取改变后的值中最后一位的值,即positionId
|
||||
let positionId = value[value.length - 1];
|
||||
// 获取改变后的值中最后两位的值,即orgId
|
||||
let orgId = value[value.length - 2];
|
||||
// 将orgId赋值给userInfo的value的orgId
|
||||
userInfo.value.orgId = orgId;
|
||||
// 将positionId赋值给userInfo的value的positionId
|
||||
userInfo.value.positionId = positionId;
|
||||
}
|
||||
|
||||
const userSelectorRef = ref<UserSelectorInstance>(); //人员选择器ref
|
||||
/** 显示人员选择器 */
|
||||
function showSelector() {
|
||||
//将userInfo.value.directorInfo转为 SysUser.SysUserInfo[]类型
|
||||
const directorInfo = userInfo.value.directorInfo ? [userInfo.value.directorInfo] : [];
|
||||
userSelectorRef.value?.showSelector(directorInfo);
|
||||
}
|
||||
|
||||
/** 选择人员 */
|
||||
function handleChooseUser(data: SysUser.SysUserInfo[]) {
|
||||
// 选择人员后,将人员id赋值给userInfo.value.directorId
|
||||
if (data.length > 0) {
|
||||
userInfo.value.directorId = data[0].id;
|
||||
userInfo.value.directorInfo = data[0];
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除主管 */
|
||||
function removeDirector() {
|
||||
userInfo.value.directorId = null;
|
||||
userInfo.value.directorInfo = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-input__wrapper) {
|
||||
width: 100% !important;
|
||||
}
|
||||
:deep(.el-date-editor.el-input) {
|
||||
width: 92% !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,145 @@
|
||||
<!--
|
||||
* @Description: 更多信息
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:55
|
||||
!-->
|
||||
<template>
|
||||
<el-tab-pane label="更多信息" name="more">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="民族" prop="nation">
|
||||
<s-select v-model="userInfo.nation" :options="nationOptions"></s-select>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="籍贯" prop="nativePlace">
|
||||
<s-input v-model="userInfo.nativePlace"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="家庭住址" prop="homeAddress">
|
||||
<s-input v-model="userInfo.homeAddress" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="通信地址" prop="mailingAddress">
|
||||
<s-input v-model="userInfo.mailingAddress" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="证件类型" prop="nation">
|
||||
<s-select v-model="userInfo.idCardType" :options="cardTypeOptions"></s-select>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="证件号码" prop="idCardNumber">
|
||||
<s-input v-model="userInfo.idCardNumber"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="文化程度" prop="cultureLevel">
|
||||
<s-select v-model="userInfo.cultureLevel" :options="cultureLevelOptions"></s-select>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="政治面貌" prop="politicalOutlook">
|
||||
<s-input v-model="userInfo.politicalOutlook"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="毕业学校" prop="college">
|
||||
<s-input v-model="userInfo.college"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="学历" prop="education">
|
||||
<s-input v-model="userInfo.education"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="学制" prop="eduLength">
|
||||
<s-input v-model="userInfo.eduLength"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="学位" prop="degree">
|
||||
<s-input v-model="userInfo.degree"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="家庭电话" prop="homeTel">
|
||||
<s-input v-model="userInfo.homeTel"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="办公电话" prop="officeTel">
|
||||
<s-input v-model="userInfo.officeTel"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="紧急联系人" prop="emergencyContact">
|
||||
<s-input v-model="userInfo.emergencyContact"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<s-form-item label="紧急联系电话" prop="emergencyPhone">
|
||||
<s-input v-model="userInfo.emergencyPhone"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<s-form-item label="紧急联系人地址" prop="emergencyAddress">
|
||||
<s-input v-model="userInfo.emergencyAddress"></s-input>
|
||||
</s-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SysUser } from "@/api";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import { SysDictEnum } from "@/enums";
|
||||
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
// props
|
||||
interface FormProps {
|
||||
modelValue: Partial<SysUser.SysUserInfo>;
|
||||
}
|
||||
const emit = defineEmits(["update:modelValue"]); //定义emit
|
||||
const props = defineProps<FormProps>(); //定义props
|
||||
// 人员信息
|
||||
const userInfo = computed({
|
||||
get: () => props.modelValue,
|
||||
set: val => emit("update:modelValue", val)
|
||||
});
|
||||
|
||||
// 通用状态选项
|
||||
const nationOptions = dictStore.getDictList(SysDictEnum.NATION);
|
||||
// 证件类型选项
|
||||
const cardTypeOptions = dictStore.getDictList(SysDictEnum.IDCARD_TYPE);
|
||||
//文化程度选项
|
||||
const cultureLevelOptions = dictStore.getDictList(SysDictEnum.CULTURE_LEVEL);
|
||||
onMounted(() => {
|
||||
// 初始化默认值
|
||||
// userInfo.value.nation = userInfo.value.nation ? userInfo.value.nation : nationOptions[0].value;
|
||||
userInfo.value.idCardType = userInfo.value.idCardType ? userInfo.value.idCardType : cardTypeOptions[0].value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,111 @@
|
||||
<!--
|
||||
* @Description: 表单
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:45:59
|
||||
!-->
|
||||
<template>
|
||||
<div>
|
||||
<form-container v-model="visible" :title="`${sysUserProps.opt}人员`" form-size="800px" @close="onClose">
|
||||
<el-form
|
||||
ref="sysUserFormRef"
|
||||
:rules="rules"
|
||||
:disabled="sysUserProps.disabled"
|
||||
:model="sysUserProps.record"
|
||||
:hide-required-asterisk="sysUserProps.disabled"
|
||||
label-width="auto"
|
||||
label-suffix=" :"
|
||||
>
|
||||
<el-tabs v-model="activeName">
|
||||
<Basic v-model="sysUserProps.record"></Basic>
|
||||
<More v-model="sysUserProps.record"></More>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="onClose"> 取消 </el-button>
|
||||
<el-button v-show="!sysUserProps.disabled" type="primary" @click="handleSubmit"> 确定 </el-button>
|
||||
</template>
|
||||
</form-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="sysUserForm">
|
||||
import { SysUser, bizUserApi } from "@/api";
|
||||
import { FormOptEnum, SysDictEnum } from "@/enums";
|
||||
import { required } from "@/utils/formRules";
|
||||
import { FormInstance } from "element-plus";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import Basic from "./form_basic.vue";
|
||||
import More from "./form_more.vue";
|
||||
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
const visible = ref(false); //是否显示表单
|
||||
const activeName = ref("basic");
|
||||
// 通用状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
|
||||
// 表单参数
|
||||
const sysUserProps = reactive<FormProps.Base<SysUser.SysUserInfo>>({
|
||||
opt: FormOptEnum.ADD,
|
||||
record: {},
|
||||
disabled: false
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
account: [required("请输入账号")],
|
||||
name: [required("请输入姓名")],
|
||||
gender: [required("请选择性别")],
|
||||
positionId: [required("请选择部门和职位")]
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param props 表单参数
|
||||
*/
|
||||
function onOpen(props: FormProps.Base<SysUser.SysUserInfo>) {
|
||||
Object.assign(sysUserProps, props); //合并参数
|
||||
if (props.opt == FormOptEnum.ADD) {
|
||||
//如果是新增,设置默认值
|
||||
sysUserProps.record.sortCode = 99;
|
||||
sysUserProps.record.status = statusOptions[0].value;
|
||||
}
|
||||
visible.value = true; //显示表单
|
||||
if (props.record.id) {
|
||||
//如果传了id,就去请求api获取record
|
||||
bizUserApi.detail({ id: props.record.id }).then(res => {
|
||||
sysUserProps.record = res.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 提交数据(新增/编辑)
|
||||
const sysUserFormRef = ref<FormInstance>();
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
sysUserFormRef.value?.validate(async valid => {
|
||||
if (!valid) return; //表单验证失败
|
||||
//提交表单
|
||||
await bizUserApi
|
||||
.submitForm(sysUserProps.record, sysUserProps.record.id != undefined)
|
||||
.then(() => {
|
||||
sysUserProps.successful!(); //调用父组件的successful方法
|
||||
})
|
||||
.finally(() => {
|
||||
onClose();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 关闭表单*/
|
||||
function onClose() {
|
||||
visible.value = false;
|
||||
activeName.value = "basic";
|
||||
}
|
||||
|
||||
// 暴露给父组件的方法
|
||||
defineExpose({
|
||||
onOpen
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,249 @@
|
||||
<!--
|
||||
* @Description: 人员管理
|
||||
* @Author: huguodong
|
||||
* @Date: 2023-12-15 15:46:03
|
||||
!-->
|
||||
<template>
|
||||
<div class="main-box">
|
||||
<TreeFilter
|
||||
ref="treeFilter"
|
||||
label="name"
|
||||
title="机构列表"
|
||||
:show-all="false"
|
||||
:request-api="bizOrgApi.tree"
|
||||
:default-value="initParam.orgId"
|
||||
@change="changeTreeFilter"
|
||||
/>
|
||||
<div class="table-box">
|
||||
<ProTable ref="proTable" title="人员列表" :columns="columns" :request-api="bizUserApi.page" :init-param="initParam">
|
||||
<!-- 表格 header 按钮 -->
|
||||
<template #tableHeader="scope">
|
||||
<s-button v-auth="bizUserButtonCode.add" suffix="人员" @click="onOpen(FormOptEnum.ADD)" />
|
||||
<s-button
|
||||
v-auth="bizUserButtonCode.batchDelete"
|
||||
type="danger"
|
||||
plain
|
||||
suffix="人员"
|
||||
:opt="FormOptEnum.DELETE"
|
||||
:disabled="!scope.isSelected"
|
||||
@click="onDelete(scope.selectedListIds, '删除所选人员')"
|
||||
/>
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #avatar="scope">
|
||||
<el-avatar :src="scope.row.avatar" :size="30" />
|
||||
<!-- <span>{{ scope.row.avatar }}}</span> -->
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #status="scope">
|
||||
<el-switch :model-value="scope.row.status === CommonStatusEnum.ENABLE" :loading="switchLoading" @change="editStatus(scope.row)" />
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #operation="scope">
|
||||
<el-space>
|
||||
<s-button v-auth="bizUserButtonCode.edit" link :opt="FormOptEnum.EDIT" @click="onOpen(FormOptEnum.EDIT, scope.row)" />
|
||||
<s-button
|
||||
v-auth="bizUserButtonCode.delete"
|
||||
link
|
||||
:opt="FormOptEnum.DELETE"
|
||||
@click="onDelete([scope.row.id], `删除【${scope.row.account}】人员`)"
|
||||
/>
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-link type="primary" :underline="false" :icon="ArrowDown"> 更多 </el-link>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-auth="bizUserButtonCode.pwdReset" :command="command(scope.row, cmdEnum.ResetPassword)">{{
|
||||
cmdEnum.ResetPassword
|
||||
}}</el-dropdown-item>
|
||||
<el-dropdown-item v-auth="bizUserButtonCode.grantRole" :command="command(scope.row, cmdEnum.GrantRole)">{{
|
||||
cmdEnum.GrantRole
|
||||
}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-space>
|
||||
</template>
|
||||
</ProTable>
|
||||
<!-- 新增/编辑表单 -->
|
||||
<Form ref="formRef" />
|
||||
<!-- 角色选择器组件 -->
|
||||
<role-selector
|
||||
ref="roleSelectorRef"
|
||||
multiple
|
||||
:org-tree-api="bizOrgApi.tree"
|
||||
:role-selector-api="bizRoleApi.roleSelector"
|
||||
@successful="handleChooseRole"
|
||||
></role-selector>
|
||||
<!-- 授权资源组件 -->
|
||||
<GrantResource user ref="grantResourceRef" />
|
||||
<!-- 授权权限组件 -->
|
||||
<GrantPermission user ref="grantPermissionRef" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="SysUser">
|
||||
import { bizOrgApi, bizUserApi, SysUser, SysRole, bizRoleApi, bizUserButtonCode } from "@/api";
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
|
||||
import { SysDictEnum, FormOptEnum, CommonStatusEnum } from "@/enums";
|
||||
import { useHandleData } from "@/hooks/useHandleData";
|
||||
import { useDictStore } from "@/stores/modules";
|
||||
import TreeFilter from "@/components/TreeFilter/index.vue";
|
||||
import Form from "./components/form/index.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { RoleSelectorInstance } from "@/components/Selectors/RoleSelector/interface";
|
||||
import GrantResource from "@/views/sys/limit/role/components/grantResource.vue";
|
||||
import GrantPermission from "@/views/sys/limit/role/components/grantPermission.vue";
|
||||
const dictStore = useDictStore(); //字典仓库
|
||||
|
||||
// 状态选项
|
||||
const statusOptions = dictStore.getDictList(SysDictEnum.COMMON_STATUS);
|
||||
const treeFilter = ref<InstanceType<typeof TreeFilter> | null>(null);
|
||||
//状态开关loading
|
||||
const switchLoading = ref(false);
|
||||
|
||||
interface InitParam {
|
||||
orgId: number | string;
|
||||
}
|
||||
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
||||
const initParam = reactive<InitParam>({ orgId: 0 });
|
||||
// 获取 ProTable 元素
|
||||
const proTable = ref<ProTableInstance>();
|
||||
|
||||
// 表格配置项
|
||||
const columns: ColumnProps<SysUser.SysUserInfo>[] = [
|
||||
{ type: "selection", fixed: "left", width: 50 },
|
||||
{ prop: "searchKey", label: "姓名或账号", search: { el: "input" }, isShow: false },
|
||||
{ prop: "avatar", label: "头像", width: 80 },
|
||||
{ prop: "account", label: "账号" },
|
||||
{ prop: "name", label: "姓名" },
|
||||
{ prop: "gender", label: "性别", width: 80 },
|
||||
{ prop: "phone", label: "手机号" },
|
||||
{ prop: "orgName", label: "机构" },
|
||||
{ prop: "positionName", label: "职位" },
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
width: 90,
|
||||
enum: statusOptions,
|
||||
search: { el: "select" }
|
||||
},
|
||||
{ prop: "createTime", label: "创建时间" },
|
||||
{ prop: "operation", label: "操作", width: 230, fixed: "right" }
|
||||
];
|
||||
|
||||
/** 部门切换 */
|
||||
function changeTreeFilter(val: number | string) {
|
||||
proTable.value!.pageable.pageNum = 1;
|
||||
if (val != "") {
|
||||
// 如果传入的val不为空,则将val赋值给initParam.parentId
|
||||
initParam.orgId = val;
|
||||
} else {
|
||||
// 否则将initParam.parentId赋值为0
|
||||
initParam.orgId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 表单引用
|
||||
const formRef = ref<InstanceType<typeof Form> | null>(null);
|
||||
|
||||
/**
|
||||
* 打开表单
|
||||
* @param opt 操作类型
|
||||
* @param record 记录
|
||||
*/
|
||||
function onOpen(opt: FormOptEnum, record: {} | SysUser.SysUserInfo = {}) {
|
||||
formRef.value?.onOpen({ opt: opt, record: record, successful: RefreshTable });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids id数组
|
||||
*/
|
||||
async function onDelete(ids: string[], msg: string) {
|
||||
// 二次确认 => 请求api => 刷新表格
|
||||
await useHandleData(bizUserApi.delete, { ids }, msg);
|
||||
RefreshTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新表格
|
||||
*/
|
||||
function RefreshTable() {
|
||||
proTable.value?.refresh(); //刷新表格
|
||||
treeFilter.value?.refresh(); //刷新树形筛选器
|
||||
}
|
||||
|
||||
/** 更多下拉菜单命令枚举 */
|
||||
enum cmdEnum {
|
||||
ResetPassword = "重置密码",
|
||||
GrantRole = "授权角色"
|
||||
}
|
||||
|
||||
/** 下拉菜单参数接口 */
|
||||
interface Command {
|
||||
row: SysUser.SysUserInfo;
|
||||
command: cmdEnum;
|
||||
}
|
||||
|
||||
/**配置command的参数 */
|
||||
function command(row: SysUser.SysUserInfo, command: cmdEnum): Command {
|
||||
return {
|
||||
row: row,
|
||||
command: command
|
||||
};
|
||||
}
|
||||
|
||||
const roleSelectorRef = ref<RoleSelectorInstance>(); //角色选择器引用
|
||||
const userId = ref<number | string>(0); //人员id
|
||||
const grantResourceRef = ref<InstanceType<typeof GrantResource> | null>(null); //授权资源组件引用
|
||||
const grantPermissionRef = ref<InstanceType<typeof GrantPermission> | null>(null); //授权权限组件引用
|
||||
/**
|
||||
* 更多下拉菜单点击事件
|
||||
* @param command
|
||||
*/
|
||||
function handleCommand(command: Command) {
|
||||
switch (command.command) {
|
||||
case cmdEnum.ResetPassword:
|
||||
useHandleData(bizUserApi.resetPassword, { id: command.row.id }, cmdEnum.ResetPassword);
|
||||
break;
|
||||
case cmdEnum.GrantRole:
|
||||
userId.value = command.row.id; //获取人员id
|
||||
bizUserApi.ownRole({ id: command.row.id }).then(res => {
|
||||
roleSelectorRef.value?.showSelector(res.data); //显示人员选择器
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** 选择角色 */
|
||||
function handleChooseRole(data: SysRole.SysRoleInfo[]) {
|
||||
//组装参数
|
||||
const grantUser: SysUser.GrantRoleReq = {
|
||||
id: userId.value,
|
||||
roleIdList: data.map(item => item.id) as number[] | string[]
|
||||
};
|
||||
bizUserApi.grantRole(grantUser); //授权角色
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
* @param row 当前行数据
|
||||
*/
|
||||
function editStatus(row: SysUser.SysUserInfo) {
|
||||
switchLoading.value = true;
|
||||
const status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE; // 状态取反
|
||||
bizUserApi
|
||||
.updateStatus({ id: row.id }, status)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
row.status = status;
|
||||
})
|
||||
.finally(() => {
|
||||
switchLoading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Reference in New Issue
Block a user