添加管理
This commit is contained in:
parent
968b91a69d
commit
4eaaa6d4bc
@ -72,7 +72,72 @@ const commonRoute = [
|
||||
},
|
||||
component: () => import("@/views/artworkreturn/index.vue"),
|
||||
},
|
||||
{
|
||||
path: "/posimanage/create",
|
||||
name: "PosiManageCre",
|
||||
meta: {
|
||||
title: "岗位管理",
|
||||
},
|
||||
component: () => import("../views/manage/posimanage/create"),
|
||||
},
|
||||
{
|
||||
path: "/orgmanage",
|
||||
name: "OrgManage",
|
||||
meta: {
|
||||
title: "组织管理",
|
||||
},
|
||||
component: () => import("../views/manage/orgmanage/index"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "/posimanage",
|
||||
name: "PosiManage",
|
||||
meta: {
|
||||
title: "岗位管理",
|
||||
},
|
||||
component: () => import("../views/manage/posimanage/index"),
|
||||
},
|
||||
|
||||
{
|
||||
path: "/posirecordlog",
|
||||
name: "PosiRecordLog",
|
||||
meta: {
|
||||
title: "岗位操作记录",
|
||||
},
|
||||
component: () => import("../views/manage/posirecordlog/index"),
|
||||
},
|
||||
{
|
||||
path: "/peomanage",
|
||||
name: "PeoManage",
|
||||
meta: {
|
||||
title: "人员管理",
|
||||
},
|
||||
component: () => import("../views/manage/peomanage/index"),
|
||||
},
|
||||
{
|
||||
path: "/peomanage/personnelManage",
|
||||
name: "personnelManage",
|
||||
meta: {
|
||||
title: "人员管理",
|
||||
},
|
||||
component: () => import("../views/manage/peomanage/personnelManage"),
|
||||
},
|
||||
{
|
||||
path: "/peomanage/create",
|
||||
name: "PeoManageCre",
|
||||
meta: {
|
||||
title: "人员管理",
|
||||
},
|
||||
component: () => import("../views/manage/peomanage/create"),
|
||||
},
|
||||
{
|
||||
path: "/peomanage/editPeo",
|
||||
name: "PeoManageEdi",
|
||||
meta: {
|
||||
title: "人员管理",
|
||||
},
|
||||
component: () => import("../views/manage/peomanage/editPeo"),
|
||||
},
|
||||
];
|
||||
|
||||
export default commonRoute;
|
||||
|
430
src/views/manage/orgmanage/index.vue
Normal file
430
src/views/manage/orgmanage/index.vue
Normal file
@ -0,0 +1,430 @@
|
||||
<template>
|
||||
<div class="row"
|
||||
style="padding: 35px">
|
||||
<div class="col-3 fl-pa-md"
|
||||
style="
|
||||
background: #fff;
|
||||
box-shadow: rgba(188, 188, 188, 0.18) 0px 3px 6px 1px;
|
||||
">
|
||||
<div class="row font-16"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5">
|
||||
<div class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6">
|
||||
组织架构
|
||||
</div>
|
||||
</div>
|
||||
<div class="row"
|
||||
style="height: 76vh; overflow: auto">
|
||||
<fl-tree :data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
:config="{
|
||||
actions: ['edit', 'add', 'subtraction'],
|
||||
addShow: '[%=level%]!==4',
|
||||
}"
|
||||
@triggerTreeAction="handleTreeAction"
|
||||
@triggerTreeClick="handleTreeClick"></fl-tree>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9 row fl-px-md">
|
||||
<div style="background: #fff"
|
||||
class="fl-pa-md">
|
||||
<div class="col-12 row font-16"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5">
|
||||
<div class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6">
|
||||
{{ state.treeSelectData.title || "平台开发管理项目组" }}所有人员
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row fl-mt-sm">
|
||||
<fln-table :config="state.tableConfig"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
:fatherData="state.treeSelectData">
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<n-modal v-model:show="state.dialogAddTree"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card">
|
||||
<template #header>
|
||||
<div class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2">
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
添加组织
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div class="fl-my-lg font-20">
|
||||
<n-input v-model:value="state.dialogAddTreeData"
|
||||
placeholder="请输入组织名称" />
|
||||
</div>
|
||||
<div class="col-12 row justify-center">
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #764cf6; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogAddTreeSave">保存</n-button>
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #eeeaf7; color: #774ff6"
|
||||
@click="state.dialogAddTree = false">返回</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="state.dialogSubtractionTree"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card">
|
||||
<template #header>
|
||||
<div class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2">
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
删除组织
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div class="font-20"
|
||||
style="margin: 115px 0">
|
||||
是否确认删除该组织及其子组织(如有)?
|
||||
</div>
|
||||
<div class="col-12 row justify-center">
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #764cf6; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogSubtractionTreeSave">确定</n-button>
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #eeeaf7; color: #774ff6"
|
||||
@click="state.dialogSubtractionTree = false">返回</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 岗位
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flTree from "@/components/flnlayout/tree/flnindex.vue";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
} from "vue";
|
||||
import { Local } from "@/utils/storage.js";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const state = reactive({
|
||||
dialogData: {},
|
||||
dialogAddTree: false,
|
||||
dialogAddTreeData: "",
|
||||
dialogSubtractionTree: false,
|
||||
treeSelectData: {},
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
|
||||
btnLoading: false,
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
requestbysf: true,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "user/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "departmentID",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
searchConfig: [
|
||||
{
|
||||
type: "text",
|
||||
label: "姓名",
|
||||
field: "nickName",
|
||||
class: "col-4",
|
||||
placeholder: "请输入姓名",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "手机号",
|
||||
field: "telNum",
|
||||
class: "col-4",
|
||||
placeholder: "请输入手机号",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "岗位",
|
||||
field: "positionName",
|
||||
class: "col-4",
|
||||
placeholder: "请输入岗位",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "员工工号",
|
||||
field: "jobNum",
|
||||
class: "col-4",
|
||||
placeholder: "请输入员工工号",
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
title: "员工工号",
|
||||
field: "jobNum",
|
||||
},
|
||||
{
|
||||
title: "姓名",
|
||||
field: "nickName",
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
title: "手机号",
|
||||
field: "telNum",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: "岗位",
|
||||
align: 'left',
|
||||
field: "nowPositions",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
tagField: "name",
|
||||
tagBgColorField: "color",
|
||||
showLength: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
let treeSelectData = Local.get("orgmanage_treeSelectData");
|
||||
if (treeSelectData) {
|
||||
state.treeSelectData = treeSelectData;
|
||||
state.clickKey = treeSelectData.key;
|
||||
}
|
||||
getTreeData();
|
||||
});
|
||||
onMounted(() => { });
|
||||
|
||||
const getTreeData = () => {
|
||||
let url = "/department/v2/tree/my";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
if (
|
||||
state.clickKey === data[0].key &&
|
||||
!state.expandedKeys.includes(data[0].key)
|
||||
) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
if (!state.expandedKeys.includes(state.clickKey)) {
|
||||
state.expandedKeys.push(state.clickKey);
|
||||
}
|
||||
if (data.length === 1 && !state.expandedKeys.includes(data[0].key)) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
state.treeRefreshCount++;
|
||||
state.tableConfig.refreshCount++;
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleCreatePosi = () => {
|
||||
console.log("handleCreatePosi");
|
||||
};
|
||||
|
||||
const handleTreeAction = ({ type, val }) => {
|
||||
state.dialogData = val;
|
||||
if (type === "add") {
|
||||
state.clickKey = val.key;
|
||||
state.treeSelectData = val;
|
||||
state.tableConfig.refreshCount++;
|
||||
state.dialogAddTreeData = "";
|
||||
state.dialogAddTree = true;
|
||||
}
|
||||
if (type === "subtraction") {
|
||||
state.dialogSubtractionTree = true;
|
||||
}
|
||||
if (type === "save") {
|
||||
let url = "/department/v2/update";
|
||||
let params = {
|
||||
name: val.title,
|
||||
ID: val.key,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
getTreeData();
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
}
|
||||
if (type === "cancel") {
|
||||
getTreeData();
|
||||
}
|
||||
};
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
Local.set("orgmanage_treeSelectData", tree);
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
// const handleTreeDefaultClick = () => {
|
||||
// state.treeSelectData = state.treeData[0];
|
||||
// state.clickKey = state.treeData[0].key;
|
||||
// Local.remove("orgmanage_treeSelectData");
|
||||
// state.tableConfig.refreshCount++;
|
||||
// };
|
||||
const handleCloseModal = () => {
|
||||
// emits("triggerCloseModal");
|
||||
};
|
||||
// 树 新增
|
||||
const handleDialogAddTreeSave = () => {
|
||||
let url = "/department/v2/create";
|
||||
let params = {
|
||||
name: state.dialogAddTreeData,
|
||||
pid: state.dialogData.key,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
if (Array.isArray(res.data.levelPathList)) {
|
||||
state.expandedKeys = [];
|
||||
res.data.levelPathList.forEach((treeid) => {
|
||||
if (!state.expandedKeys.includes(treeid)) {
|
||||
state.expandedKeys.push(treeid);
|
||||
}
|
||||
});
|
||||
}
|
||||
getTreeData();
|
||||
state.dialogAddTree = false;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.dialogAddTree = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogAddTree = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
// 树 减
|
||||
const handleDialogSubtractionTreeSave = () => {
|
||||
let url = "/department/v2/remove";
|
||||
let params = {
|
||||
ID: state.dialogData.key,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
getTreeData();
|
||||
state.dialogSubtractionTree = false;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.dialogSubtractionTree = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogSubtractionTree = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.label = item.name;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
893
src/views/manage/peomanage/create.vue
Normal file
893
src/views/manage/peomanage/create.vue
Normal file
@ -0,0 +1,893 @@
|
||||
<template>
|
||||
<div class="row" style="padding: 35px; margin-bottom: 80px">
|
||||
<div
|
||||
class="col-12 row"
|
||||
style="
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
padding: 17px 20px;
|
||||
box-shadow: 0 3px 6px 1px rgba(188, 188, 188, 0.18);
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="col-12 row font-20"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5"
|
||||
>
|
||||
<div
|
||||
v-if="state.pageType === 'create'"
|
||||
class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6"
|
||||
>
|
||||
新增
|
||||
</div>
|
||||
<div
|
||||
v-if="state.pageType === 'edit'"
|
||||
class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6"
|
||||
>
|
||||
修改
|
||||
</div>
|
||||
<div
|
||||
v-if="state.pageType === 'view'"
|
||||
class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6"
|
||||
>
|
||||
查看
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row">
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.nickName"
|
||||
:config="{
|
||||
label: '姓名:',
|
||||
field: 'nickName',
|
||||
placeholder: '请输入姓名',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValBlur="handleValBlur"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.telNum"
|
||||
:config="{
|
||||
label: '手机号:',
|
||||
field: 'telNum',
|
||||
placeholder: '请输入手机号',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.mailAccount"
|
||||
:config="{
|
||||
label: '邮箱:',
|
||||
field: 'mailAccount',
|
||||
placeholder: '自动生成',
|
||||
}"
|
||||
:disable="true"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="date"
|
||||
:val="state.formData.enterDate"
|
||||
:config="{
|
||||
label: '入职时间:',
|
||||
field: 'enterDate',
|
||||
placeholder: '请输入入职时间',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="select"
|
||||
:val="state.formData.status"
|
||||
:config="{
|
||||
placeholder: '请输入状态',
|
||||
label: '状态:',
|
||||
field: 'status',
|
||||
options: [
|
||||
{
|
||||
label: '有效',
|
||||
value: 'notactive',
|
||||
},
|
||||
{
|
||||
label: '无效',
|
||||
value: 'left',
|
||||
},
|
||||
],
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.jobNum"
|
||||
:config="{
|
||||
label: '员工工号:',
|
||||
field: 'jobNum',
|
||||
placeholder: '请输入员工工号',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row col-3">
|
||||
<fln-form-item
|
||||
type="upload"
|
||||
title="图片上传"
|
||||
:config="{
|
||||
limit4096: true,
|
||||
label: '员工近照:',
|
||||
field: 'recentImg',
|
||||
placeholder: '请输入员工近照',
|
||||
style: 'width:80px',
|
||||
}"
|
||||
:val="state.formData.recentImg"
|
||||
:otherParams="{
|
||||
source: 'artwork',
|
||||
mask: '',
|
||||
type: 'image',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mt-md row col-3">
|
||||
<div class="col-12 row items-center">
|
||||
<fln-form-item
|
||||
type="upload"
|
||||
title="图片上传"
|
||||
:config="{
|
||||
limit4096: true,
|
||||
label: '员工头像:',
|
||||
field: 'avatar',
|
||||
placeholder: '请输入员工近照',
|
||||
style: 'width:80px',
|
||||
}"
|
||||
:val="state.formData.avatar"
|
||||
:otherParams="{
|
||||
source: 'artwork',
|
||||
mask: '',
|
||||
type: 'image',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
>
|
||||
<template #uploadRight v-if="!calcPageDisable()">
|
||||
<div class="row" style="width: calc(100% - 84px)">
|
||||
<img
|
||||
v-for="(item, index) in state.defaultImgList"
|
||||
:src="item + '?x-oss-process=image/resize,w_158,h_158'"
|
||||
:key="index"
|
||||
class="img-avatar"
|
||||
:class="
|
||||
state.defaultImgSelect == index ? 'border-active' : ''
|
||||
"
|
||||
@click="handleSetImg(item, index)"
|
||||
/>
|
||||
</div> </template
|
||||
></fln-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="col-12 row fl-mt-md"
|
||||
style="
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
padding: 17px 20px;
|
||||
box-shadow: 0 3px 6px 1px rgba(188, 188, 188, 0.18);
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
Array.isArray(state.depPositions) &&
|
||||
state.depPositions.filter((item) => item.positions?.length > 0)
|
||||
.length > 0
|
||||
"
|
||||
class="col-12 row"
|
||||
>
|
||||
<div
|
||||
class="col-12 fl-mt-md row items-center fl-pb-xs"
|
||||
style="min-height: 40px"
|
||||
>
|
||||
<div
|
||||
class="col-12 row justify-between items-center fl-px-md"
|
||||
style="
|
||||
min-height: 40px;
|
||||
background: #46299dff;
|
||||
border-radius: 3px 3px 0 0;
|
||||
"
|
||||
>
|
||||
<div class="color-white">设置所属部门及岗位</div>
|
||||
</div>
|
||||
<div
|
||||
style="border: 1px solid #c3c3c3; border-radius: 0 0 3px 3px"
|
||||
class="col-12 row fl-pb-xs"
|
||||
>
|
||||
<div
|
||||
class="col-12 row"
|
||||
style="padding: 11px 0; background: #dfd7f2"
|
||||
>
|
||||
<div class="col-3 text-center">所属部门</div>
|
||||
<div class="col-6 text-left">所属岗位</div>
|
||||
</div>
|
||||
<template v-for="(row, rowIdx) in state.depPositions">
|
||||
<div
|
||||
class="col-12 row fl-py-sm"
|
||||
v-if="row.positions?.length > 0"
|
||||
:style="
|
||||
rowIdx !== state.depPositions.length - 1
|
||||
? 'border-bottom:1px solid #c3c3c3'
|
||||
: ''
|
||||
"
|
||||
:key="rowIdx"
|
||||
>
|
||||
<div class="col-3 row items-center justify-center">
|
||||
{{ row.name }}
|
||||
</div>
|
||||
<div class="col-6 row items-center">
|
||||
<div
|
||||
v-for="(tag, idx) in row.positions"
|
||||
:key="idx"
|
||||
class="fl-mr-sm fl-px-sm fl-py-xs row items-center fl-mb-xs"
|
||||
style="border: solid 1px #ded7f1; border-radius: 3px"
|
||||
:style="{
|
||||
borderColor: tag.color,
|
||||
color: tag.color || '#fff',
|
||||
}"
|
||||
>
|
||||
<n-popover :show-arrow="true">
|
||||
<template #trigger>
|
||||
{{ tag.name }}
|
||||
</template>
|
||||
{{ tag.name }}
|
||||
</n-popover>
|
||||
|
||||
<close-circle-outlined
|
||||
v-if="!calcPageDisable()"
|
||||
class="fl-ml-md"
|
||||
@click="handleClearPage(rowIdx, tag)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row">
|
||||
<div
|
||||
class="col-3 row fl-px-md"
|
||||
style="overflow: auto; box-shadow: 0 3px 6px 1px #bcbcbc2e"
|
||||
>
|
||||
<fln-tree
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fln-tree>
|
||||
</div>
|
||||
<div class="col-9 row fl-pl-md" style="align-content: flex-start">
|
||||
<fln-table
|
||||
class="artwork-table"
|
||||
:config="state.tableConfig"
|
||||
:defaultSelectedRows="state.tableDefaultPositions"
|
||||
:clearSelectRowKey="state.tableClearKey"
|
||||
:fatherData="state.treeSelectData"
|
||||
:fatherParams="state.tableSearchData"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
@triggerRowSelect="handleTableRowSelect"
|
||||
@triggerRowSelectAll="handleTableCheckAll"
|
||||
>
|
||||
<template #table-header>
|
||||
<span style="color: #fd0000" class="fl-mx-sm">*</span>
|
||||
请至少勾选一个岗位</template
|
||||
>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="width-100 row fl-pt-md fl-pb-lg"
|
||||
style="position: fixed; bottom: 0px; background: #f0f0f5"
|
||||
>
|
||||
<div class="col-10 row justify-center">
|
||||
<n-button
|
||||
tertiary
|
||||
class="fl-mr-md"
|
||||
style="width: 161px; background: #c7c7c9ff; color: #ffffffff"
|
||||
@click="handleBack"
|
||||
>返回</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="state.pageType === 'edit'"
|
||||
style="width: 161px; height: 34px; background: #46299dff; color: #fff"
|
||||
@click="handleSave"
|
||||
>保存</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="state.pageType === 'create'"
|
||||
style="width: 161px; height: 34px; background: #46299dff; color: #fff"
|
||||
@click="handleCreate"
|
||||
>保存</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flnFormItem from "@/components/flnlayout/form/flnformItem.vue";
|
||||
import flnTree from "@/components/flnlayout/tree/flnindex.vue";
|
||||
|
||||
import { CloseCircleOutlined } from "@ant-design/icons-vue";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onActivated,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const state = reactive({
|
||||
tableClearKey: null,
|
||||
treeSelectData: {},
|
||||
tableDefaultPositions: [],
|
||||
depPositions: [],
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
formData: {
|
||||
nickName: null,
|
||||
telNum: null,
|
||||
status: "notactive",
|
||||
positionName: null,
|
||||
jobNum: null,
|
||||
mailAccount: null,
|
||||
enterDate: null,
|
||||
recentImg: null,
|
||||
avatar: null,
|
||||
},
|
||||
tableSearchData: {},
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
hasSelection: true,
|
||||
requestbysf: true,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/position/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "departmentID",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: "岗位ID",
|
||||
field: "ID",
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
align: "left",
|
||||
title: "岗位名",
|
||||
field: "name",
|
||||
type: "sfBgColor",
|
||||
bgConfig: {
|
||||
bgColorField: "color",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
align: "left",
|
||||
width: 120,
|
||||
title: "岗位描述",
|
||||
field: "remark",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
align: "left",
|
||||
title: "页面权限",
|
||||
field: "menuAuths",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
showLength: 3,
|
||||
bgColor: "#fff",
|
||||
borderRadius: "14px",
|
||||
style: "border-radius: 14px",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultImgList: [
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/image/avart/0/d5f416d5-0b95-4b2c-82c4-6218d4017d17.jpg",
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/image/avart/0/012a339a-53d2-43f7-ae47-84d444d5ca6f.jpg",
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/file/avart/11/48ae18e2-37a4-4eab-bd03-5fe1099892c3.jpg",
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/file/avart/11/0a53fd8a-951e-4a8e-8d71-0198dad5e7ae.jpg",
|
||||
],
|
||||
defaultImgSelect: null,
|
||||
btnLoading: false,
|
||||
pageType: "create",
|
||||
});
|
||||
watch(
|
||||
() => state.clickKey,
|
||||
(newVal, oldVal) => {
|
||||
state.tableDefaultPositions =
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey).length > 0
|
||||
? state.depPositions.filter((item) => item.ID === state.clickKey)[0]
|
||||
.positions
|
||||
: [];
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
getTreeData();
|
||||
if (route.query.type) {
|
||||
state.pageType = route.query.type;
|
||||
}
|
||||
if (route.query.type === "view") {
|
||||
state.tableConfig.hasSelection = false;
|
||||
}
|
||||
});
|
||||
onActivated(() => {
|
||||
getTreeData();
|
||||
if (route.query.type) {
|
||||
state.pageType = route.query.type;
|
||||
}
|
||||
if (route.query.type === "view") {
|
||||
state.tableConfig.hasSelection = false;
|
||||
}
|
||||
});
|
||||
onMounted(() => {});
|
||||
const getPageData = () => {
|
||||
if (route.query.ID) {
|
||||
let url = "/user/v2/detail";
|
||||
let params = {
|
||||
ID: Number(route.query.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
state.formData = {
|
||||
nickName: res.data.nickName,
|
||||
telNum: res.data.telNum,
|
||||
status: res.data.status,
|
||||
positionName: res.data.positionName,
|
||||
jobNum: res.data.jobNum,
|
||||
mailAccount: res.data.mailAccount,
|
||||
enterDate: res.data.enterDate,
|
||||
recentImg: res.data.recentImg,
|
||||
avatar: res.data.avatar,
|
||||
};
|
||||
state.depPositions = res.data.depPositions || [];
|
||||
state.tableDefaultPositions =
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey)
|
||||
.length > 0
|
||||
? state.depPositions.filter(
|
||||
(item) => item.ID === state.clickKey
|
||||
)[0].positions
|
||||
: [];
|
||||
state.depPositions.forEach((item) => {
|
||||
item.isLeader = item.isLeader || false;
|
||||
});
|
||||
} else {
|
||||
processError(res.msg || "获取数据失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
const getTreeData = () => {
|
||||
let url = "/department/v2/tree/all";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
if (!state.expandedKeys.includes(data[0].key)) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
state.treeRefreshCount++;
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
state.tableConfig.refreshCount++;
|
||||
if (route.query.ID) {
|
||||
getPageData();
|
||||
}
|
||||
} else {
|
||||
processError("获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
|
||||
const handleValChange = ({ val, config }) => {
|
||||
state.formData[config.field] = val;
|
||||
};
|
||||
const handleValBlur = ({ val, config }) => {
|
||||
if (config.field === "nickName" && state.formData.nickName) {
|
||||
let url = "/user/mail/account";
|
||||
let params = {
|
||||
nickName: val,
|
||||
ID: route.query.ID ? Number(route.query.ID) : null,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.formData.mailAccount = res.data.mailAccount;
|
||||
} else {
|
||||
processError("生成邮箱错误:" + res.msg);
|
||||
state.formData.mailAccount = "";
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
state.tableSearchData = JSON.parse(JSON.stringify(state.formData));
|
||||
};
|
||||
|
||||
const handleTableRowSelect = ({ record, selected, selectedRows }) => {
|
||||
if (
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey).length === 0
|
||||
) {
|
||||
state.depPositions.push({
|
||||
isLeader: false,
|
||||
ID: state.clickKey,
|
||||
name: state.treeSelectData.title,
|
||||
positions: selectedRows.map((item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
name: item.name,
|
||||
color: item.color,
|
||||
};
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
state.depPositions.forEach((item) => {
|
||||
if (item.ID === state.clickKey) {
|
||||
if (selected) {
|
||||
// 当前部门有其他子岗位时不直接覆盖
|
||||
item.positions = item.positions.filter(
|
||||
(depItem) => depItem.ID !== record.ID
|
||||
);
|
||||
item.positions = item.positions.concat([
|
||||
{
|
||||
ID: record.ID,
|
||||
name: record.name,
|
||||
color: record.color,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
item.positions = item.positions.filter(
|
||||
(depItem) => depItem.ID !== record.ID
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
state.selectedRows = selectedRows;
|
||||
};
|
||||
const handleTableCheckAll = ({ selectedRowKeys, selectedRows }) => {
|
||||
if (
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey).length === 0
|
||||
) {
|
||||
state.depPositions.push({
|
||||
isLeader: false,
|
||||
ID: state.clickKey,
|
||||
name: state.treeSelectData.title,
|
||||
positions: selectedRows.map((item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
name: item.name,
|
||||
color: item.color,
|
||||
};
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
state.depPositions.forEach((item) => {
|
||||
if (item.ID === state.clickKey) {
|
||||
item.positions = selectedRows.map((item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
name: item.name,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
state.selectedRows = selectedRows;
|
||||
};
|
||||
const handleDialogSave = () => {
|
||||
state.dialogDel = false;
|
||||
};
|
||||
const handleDialogBack = () => {
|
||||
state.dialogDel = false;
|
||||
};
|
||||
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
const calcPageDisable = () => {
|
||||
return state.pageType === "view";
|
||||
};
|
||||
const handleSetImg = (item, index) => {
|
||||
state.formData.avatar = "";
|
||||
nextTick(() => {
|
||||
state.formData.avatar = item;
|
||||
state.defaultImgSelect = index;
|
||||
});
|
||||
};
|
||||
const handleCreate = () => {
|
||||
if (
|
||||
state.formData.jobNum === null ||
|
||||
state.formData.jobNum === "" ||
|
||||
state.formData.jobNum === undefined
|
||||
) {
|
||||
processError("请输入员工工号");
|
||||
return;
|
||||
}
|
||||
state.btnLoading = true;
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
let url = "/user/v2/create";
|
||||
let params = {
|
||||
depPositions: state.depPositions,
|
||||
};
|
||||
Object.assign(params, state.formData);
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
processSuccess("操作成功!");
|
||||
router.go(-1);
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleSave = () => {
|
||||
if (
|
||||
state.formData.jobNum === null ||
|
||||
state.formData.jobNum === "" ||
|
||||
state.formData.jobNum === undefined
|
||||
) {
|
||||
processError("请输入员工工号");
|
||||
return;
|
||||
}
|
||||
state.btnLoading = true;
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
let url = "/user/v2/update";
|
||||
let params = {
|
||||
ID: Number(route.query.ID),
|
||||
depPositions: state.depPositions,
|
||||
};
|
||||
Object.assign(params, state.formData);
|
||||
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
processSuccess("操作成功!");
|
||||
getPageData();
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleLink = (path) => {
|
||||
router.push(path);
|
||||
};
|
||||
const handleBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
const calcDepPositions = () => {
|
||||
for (let key in state.depPositions) {
|
||||
let data = state.depPositions[key];
|
||||
if (!data || JSON.stringify(data) === "[]") {
|
||||
delete state.depPositions[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleClearPage = (idx, clearItem) => {
|
||||
state.depPositions[idx].positions.forEach((item, index) => {
|
||||
if (item.name === clearItem.name) {
|
||||
state.depPositions[idx].positions.splice(index, 1);
|
||||
}
|
||||
|
||||
if (state.depPositions[idx].positions.length === 0) {
|
||||
state.depPositions.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
state.tableClearKey = clearItem.ID;
|
||||
};
|
||||
const calcSfBgColorWidth = (val) => {
|
||||
if (val) {
|
||||
return val.length * 15 + "px !important";
|
||||
}
|
||||
return null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.avatar-uploader > .ant-upload) {
|
||||
background: #fafafcff;
|
||||
}
|
||||
:deep(.ant-image) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
:deep(.ant-image-img) {
|
||||
object-fit: cover !important;
|
||||
border-radius: 50%;
|
||||
}
|
||||
:deep(.ant-image-mask) {
|
||||
border-radius: 50%;
|
||||
}
|
||||
:deep(.ant-upload) {
|
||||
color: #c1b2e5;
|
||||
}
|
||||
:deep(.ant-select) {
|
||||
color: #000;
|
||||
}
|
||||
:deep(.ant-select-selector .ant-select-selection-placeholder) {
|
||||
color: #c3c3c3 !important;
|
||||
&::placeholder {
|
||||
color: #c3c3c3 !important;
|
||||
}
|
||||
}
|
||||
:deep(.ant-select:not(.ant-select-customize-input) .ant-select-selector) {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.img-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.border-active {
|
||||
border: 2px solid #764cf6;
|
||||
}
|
||||
</style>
|
893
src/views/manage/peomanage/editPeo.vue
Normal file
893
src/views/manage/peomanage/editPeo.vue
Normal file
@ -0,0 +1,893 @@
|
||||
<template>
|
||||
<div class="row" style="padding: 35px; margin-bottom: 80px">
|
||||
<div
|
||||
class="col-12 row"
|
||||
style="
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
padding: 17px 20px;
|
||||
box-shadow: 0 3px 6px 1px rgba(188, 188, 188, 0.18);
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="col-12 row font-20"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5"
|
||||
>
|
||||
<div
|
||||
v-if="state.pageType === 'create'"
|
||||
class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6"
|
||||
>
|
||||
新增
|
||||
</div>
|
||||
<div
|
||||
v-if="state.pageType === 'edit'"
|
||||
class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6"
|
||||
>
|
||||
修改
|
||||
</div>
|
||||
<div
|
||||
v-if="state.pageType === 'view'"
|
||||
class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6"
|
||||
>
|
||||
查看
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row">
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.nickName"
|
||||
:config="{
|
||||
label: '姓名:',
|
||||
field: 'nickName',
|
||||
placeholder: '请输入姓名',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValBlur="handleValBlur"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.telNum"
|
||||
:config="{
|
||||
label: '手机号:',
|
||||
field: 'telNum',
|
||||
placeholder: '请输入手机号',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.mailAccount"
|
||||
:config="{
|
||||
label: '邮箱:',
|
||||
field: 'mailAccount',
|
||||
placeholder: '自动生成',
|
||||
}"
|
||||
:disable="true"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="date"
|
||||
:val="state.formData.enterDate"
|
||||
:config="{
|
||||
label: '入职时间:',
|
||||
field: 'enterDate',
|
||||
format: 'YYYY-MM-dd',
|
||||
placeholder: '请输入入职时间',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="select"
|
||||
:val="state.formData.status"
|
||||
:config="{
|
||||
placeholder: '请输入状态',
|
||||
label: '状态:',
|
||||
field: 'status',
|
||||
options: [
|
||||
{
|
||||
label: '有效',
|
||||
value: 'notactive',
|
||||
},
|
||||
{
|
||||
label: '无效',
|
||||
value: 'left',
|
||||
},
|
||||
],
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row items-center col-3">
|
||||
<fln-form-item
|
||||
type="text"
|
||||
:val="state.formData.jobNum"
|
||||
:config="{
|
||||
label: '员工工号:',
|
||||
field: 'jobNum',
|
||||
placeholder: '请输入员工工号',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="search-item fl-mt-md row col-3">
|
||||
<fln-form-item
|
||||
type="upload"
|
||||
title="图片上传"
|
||||
:config="{
|
||||
limit4096: true,
|
||||
label: '员工近照:',
|
||||
field: 'recentImg',
|
||||
placeholder: '请输入员工近照',
|
||||
style: 'width:80px',
|
||||
}"
|
||||
:val="state.formData.recentImg"
|
||||
:otherParams="{
|
||||
source: 'artwork',
|
||||
mask: '',
|
||||
type: 'image',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mt-md row col-3">
|
||||
<div class="col-12 row items-center">
|
||||
<fln-form-item
|
||||
type="upload"
|
||||
title="图片上传"
|
||||
:config="{
|
||||
limit4096: true,
|
||||
label: '员工头像:',
|
||||
field: 'avatar',
|
||||
placeholder: '请输入员工近照',
|
||||
style: 'width:80px',
|
||||
}"
|
||||
:val="state.formData.avatar"
|
||||
:otherParams="{
|
||||
source: 'artwork',
|
||||
mask: '',
|
||||
type: 'image',
|
||||
}"
|
||||
:disable="calcPageDisable()"
|
||||
@triggerValChange="handleValChange"
|
||||
>
|
||||
<template #uploadRight v-if="!calcPageDisable()">
|
||||
<div class="row" style="width: calc(100% - 84px)">
|
||||
<img
|
||||
v-for="(item, index) in state.defaultImgList"
|
||||
:src="item + '?x-oss-process=image/resize,w_158,h_158'"
|
||||
:key="index"
|
||||
class="img-avatar"
|
||||
:class="
|
||||
state.defaultImgSelect == index ? 'border-active' : ''
|
||||
"
|
||||
@click="handleSetImg(item, index)"
|
||||
/>
|
||||
</div> </template
|
||||
></fln-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="col-12 row fl-mt-md"
|
||||
style="
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
padding: 17px 20px;
|
||||
box-shadow: 0 3px 6px 1px rgba(188, 188, 188, 0.18);
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
Array.isArray(state.depPositions) &&
|
||||
state.depPositions.filter((item) => item.positions?.length > 0)
|
||||
.length > 0
|
||||
"
|
||||
class="col-12 row"
|
||||
>
|
||||
<div
|
||||
class="col-12 fl-mt-md row items-center fl-pb-xs"
|
||||
style="min-height: 40px"
|
||||
>
|
||||
<div
|
||||
class="col-12 row justify-between items-center fl-px-md"
|
||||
style="
|
||||
min-height: 40px;
|
||||
background: #46299dff;
|
||||
border-radius: 3px 3px 0 0;
|
||||
"
|
||||
>
|
||||
<div class="color-white">设置所属部门及岗位</div>
|
||||
</div>
|
||||
<div
|
||||
style="border: 1px solid #c3c3c3; border-radius: 0 0 3px 3px"
|
||||
class="col-12 row fl-pb-xs"
|
||||
>
|
||||
<div
|
||||
class="col-12 row"
|
||||
style="padding: 11px 0; background: #dfd7f2"
|
||||
>
|
||||
<div class="col-3 text-center">所属部门</div>
|
||||
<div class="col-6 text-left">所属岗位</div>
|
||||
</div>
|
||||
<template v-for="(row, rowIdx) in state.depPositions">
|
||||
<div
|
||||
class="col-12 row fl-py-sm"
|
||||
v-if="row.positions?.length > 0"
|
||||
:style="
|
||||
rowIdx !== state.depPositions.length - 1
|
||||
? 'border-bottom:1px solid #c3c3c3'
|
||||
: ''
|
||||
"
|
||||
:key="rowIdx"
|
||||
>
|
||||
<div class="col-3 row items-center justify-center">
|
||||
{{ row.name }}
|
||||
</div>
|
||||
<div class="col-6 row items-center">
|
||||
<div
|
||||
v-for="(tag, idx) in row.positions"
|
||||
:key="idx"
|
||||
class="fl-mr-sm fl-px-sm fl-py-xs row items-center fl-mb-xs"
|
||||
style="border: solid 1px #ded7f1; border-radius: 3px"
|
||||
:style="{
|
||||
borderColor: tag.color,
|
||||
color: tag.color || '#fff',
|
||||
}"
|
||||
>
|
||||
<n-popover :show-arrow="true">
|
||||
<template #trigger>
|
||||
{{ tag.name }}
|
||||
</template>
|
||||
{{ tag.name }}
|
||||
</n-popover>
|
||||
|
||||
<close-circle-outlined
|
||||
v-if="!calcPageDisable()"
|
||||
class="fl-ml-md"
|
||||
@click="handleClearPage(rowIdx, tag)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row">
|
||||
<div
|
||||
class="col-3 row fl-px-md"
|
||||
style="overflow: auto; box-shadow: 0 3px 6px 1px #bcbcbc2e"
|
||||
>
|
||||
<fln-tree
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fln-tree>
|
||||
</div>
|
||||
<div class="col-9 row fl-pl-md" style="align-content: flex-start">
|
||||
<fln-table
|
||||
class="artwork-table"
|
||||
:config="state.tableConfig"
|
||||
:defaultSelectedRows="state.tableDefaultPositions"
|
||||
:clearSelectRowKey="state.tableClearKey"
|
||||
:fatherData="state.treeSelectData"
|
||||
:fatherParams="state.tableSearchData"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
@triggerRowSelect="handleTableRowSelect"
|
||||
@triggerRowSelectAll="handleTableCheckAll"
|
||||
>
|
||||
<template #table-header>
|
||||
<span style="color: #fd0000" class="fl-mx-sm">*</span>
|
||||
请至少勾选一个岗位</template
|
||||
>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="width-100 row fl-pt-md fl-pb-lg"
|
||||
style="position: fixed; bottom: 0px; background: #f0f0f5"
|
||||
>
|
||||
<div class="col-10 row justify-center">
|
||||
<n-button
|
||||
tertiary
|
||||
class="fl-mr-md"
|
||||
style="width: 161px; background: #c7c7c9ff; color: #ffffffff"
|
||||
@click="handleBack"
|
||||
>返回</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="state.pageType === 'edit'"
|
||||
style="width: 161px; height: 34px; background: #46299dff; color: #fff"
|
||||
@click="handleSave"
|
||||
>保存</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="state.pageType === 'create'"
|
||||
style="width: 161px; height: 34px; background: #46299dff; color: #fff"
|
||||
@click="handleCreate"
|
||||
>保存</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flnFormItem from "@/components/flnlayout/form/flnformItem.vue";
|
||||
import flnTree from "@/components/flnlayout/tree/flnindex.vue";
|
||||
|
||||
import { CloseCircleOutlined } from "@ant-design/icons-vue";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onActivated,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const state = reactive({
|
||||
tableClearKey: null,
|
||||
treeSelectData: {},
|
||||
tableDefaultPositions: [],
|
||||
depPositions: [],
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
formData: {
|
||||
nickName: null,
|
||||
telNum: null,
|
||||
status: "notactive",
|
||||
positionName: null,
|
||||
jobNum: null,
|
||||
mailAccount: null,
|
||||
enterDate: null,
|
||||
recentImg: null,
|
||||
avatar: null,
|
||||
},
|
||||
tableSearchData: {},
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
hasSelection: true,
|
||||
requestbysf: true,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/position/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "departmentID",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: "岗位ID",
|
||||
field: "ID",
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
align: "left",
|
||||
title: "岗位名",
|
||||
field: "name",
|
||||
type: "sfBgColor",
|
||||
bgConfig: {
|
||||
bgColorField: "color",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
align: "left",
|
||||
width: 120,
|
||||
title: "岗位描述",
|
||||
field: "remark",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
align: "left",
|
||||
title: "页面权限",
|
||||
field: "menuAuths",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
showLength: 3,
|
||||
bgColor: "#fff",
|
||||
borderRadius: "14px",
|
||||
style: "border-radius: 14px",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultImgList: [
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/image/avart/0/d5f416d5-0b95-4b2c-82c4-6218d4017d17.jpg",
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/image/avart/0/012a339a-53d2-43f7-ae47-84d444d5ca6f.jpg",
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/file/avart/11/48ae18e2-37a4-4eab-bd03-5fe1099892c3.jpg",
|
||||
"https://cdns.fontree.cn/fonchain-main/prod/runtime/file/avart/11/0a53fd8a-951e-4a8e-8d71-0198dad5e7ae.jpg",
|
||||
],
|
||||
defaultImgSelect: null,
|
||||
btnLoading: false,
|
||||
pageType: "create",
|
||||
});
|
||||
watch(
|
||||
() => state.clickKey,
|
||||
(newVal, oldVal) => {
|
||||
state.tableDefaultPositions =
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey).length > 0
|
||||
? state.depPositions.filter((item) => item.ID === state.clickKey)[0]
|
||||
.positions
|
||||
: [];
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
getTreeData();
|
||||
if (route.query.type) {
|
||||
state.pageType = route.query.type;
|
||||
}
|
||||
if (route.query.type === "view") {
|
||||
state.tableConfig.hasSelection = false;
|
||||
}
|
||||
});
|
||||
onActivated(() => {
|
||||
getTreeData();
|
||||
if (route.query.type) {
|
||||
state.pageType = route.query.type;
|
||||
}
|
||||
if (route.query.type === "view") {
|
||||
state.tableConfig.hasSelection = false;
|
||||
}
|
||||
});
|
||||
onMounted(() => {});
|
||||
const getPageData = () => {
|
||||
if (route.query.ID) {
|
||||
let url = "/user/v2/boss/staff/detail";
|
||||
let params = {
|
||||
ID: Number(route.query.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
state.formData = {
|
||||
nickName: res.data.nickName,
|
||||
telNum: res.data.telNum,
|
||||
status: res.data.status,
|
||||
positionName: res.data.positionName,
|
||||
jobNum: res.data.jobNum,
|
||||
mailAccount: res.data.mailAccount,
|
||||
enterDate: res.data.enterDate,
|
||||
recentImg: res.data.recentImg,
|
||||
avatar: res.data.avatar,
|
||||
};
|
||||
state.depPositions = res.data.depPositions || [];
|
||||
state.tableDefaultPositions =
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey)
|
||||
.length > 0
|
||||
? state.depPositions.filter(
|
||||
(item) => item.ID === state.clickKey
|
||||
)[0].positions
|
||||
: [];
|
||||
state.depPositions.forEach((item) => {
|
||||
item.isLeader = item.isLeader || false;
|
||||
});
|
||||
} else {
|
||||
processError(res.msg || "获取数据失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
const getTreeData = () => {
|
||||
let params = {};
|
||||
$request.HTTP.components.viewMyTree(params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
if (!state.expandedKeys.includes(data[0].key)) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
state.treeRefreshCount++;
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
state.tableConfig.refreshCount++;
|
||||
if (route.query.ID) {
|
||||
getPageData();
|
||||
}
|
||||
} else {
|
||||
processError("获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
|
||||
const handleValChange = ({ val, config }) => {
|
||||
state.formData[config.field] = val;
|
||||
};
|
||||
const handleValBlur = ({ val, config }) => {
|
||||
if (config.field === "nickName" && state.formData.nickName) {
|
||||
let url = "/user/mail/account";
|
||||
let params = {
|
||||
nickName: val,
|
||||
ID: route.query.ID ? Number(route.query.ID) : null,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.formData.mailAccount = res.data.mailAccount;
|
||||
} else {
|
||||
processError("生成邮箱错误:" + res.msg);
|
||||
state.formData.mailAccount = "";
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
() => {}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
state.tableSearchData = JSON.parse(JSON.stringify(state.formData));
|
||||
};
|
||||
|
||||
const handleTableRowSelect = ({ record, selected, selectedRows }) => {
|
||||
if (
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey).length === 0
|
||||
) {
|
||||
state.depPositions.push({
|
||||
isLeader: false,
|
||||
ID: state.clickKey,
|
||||
name: state.treeSelectData.title,
|
||||
positions: selectedRows.map((item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
name: item.name,
|
||||
color: item.color,
|
||||
};
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
state.depPositions.forEach((item) => {
|
||||
if (item.ID === state.clickKey) {
|
||||
if (selected) {
|
||||
// 当前部门有其他子岗位时不直接覆盖
|
||||
item.positions = item.positions.filter(
|
||||
(depItem) => depItem.ID !== record.ID
|
||||
);
|
||||
item.positions = item.positions.concat([
|
||||
{
|
||||
ID: record.ID,
|
||||
name: record.name,
|
||||
color: record.color,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
item.positions = item.positions.filter(
|
||||
(depItem) => depItem.ID !== record.ID
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
state.selectedRows = selectedRows;
|
||||
};
|
||||
const handleTableCheckAll = ({ selectedRowKeys, selectedRows }) => {
|
||||
if (
|
||||
state.depPositions.filter((item) => item.ID === state.clickKey).length === 0
|
||||
) {
|
||||
state.depPositions.push({
|
||||
isLeader: false,
|
||||
ID: state.clickKey,
|
||||
name: state.treeSelectData.title,
|
||||
positions: selectedRows.map((item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
name: item.name,
|
||||
color: item.color,
|
||||
};
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
state.depPositions.forEach((item) => {
|
||||
if (item.ID === state.clickKey) {
|
||||
item.positions = selectedRows.map((item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
name: item.name,
|
||||
color: item.color,
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
state.selectedRows = selectedRows;
|
||||
};
|
||||
const handleDialogSave = () => {
|
||||
state.dialogDel = false;
|
||||
};
|
||||
const handleDialogBack = () => {
|
||||
state.dialogDel = false;
|
||||
};
|
||||
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
const calcPageDisable = () => {
|
||||
return state.pageType === "view";
|
||||
};
|
||||
const handleSetImg = (item, index) => {
|
||||
state.formData.avatar = "";
|
||||
nextTick(() => {
|
||||
state.formData.avatar = item;
|
||||
state.defaultImgSelect = index;
|
||||
});
|
||||
};
|
||||
const handleCreate = () => {
|
||||
if (
|
||||
state.formData.jobNum === null ||
|
||||
state.formData.jobNum === "" ||
|
||||
state.formData.jobNum === undefined
|
||||
) {
|
||||
processError("请输入员工工号");
|
||||
return;
|
||||
}
|
||||
state.btnLoading = true;
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
let url = "/user/v2/create";
|
||||
let params = {
|
||||
depPositions: state.depPositions,
|
||||
};
|
||||
Object.assign(params, state.formData);
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
processSuccess("操作成功!");
|
||||
router.go(-1);
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleSave = () => {
|
||||
if (
|
||||
state.formData.jobNum === null ||
|
||||
state.formData.jobNum === "" ||
|
||||
state.formData.jobNum === undefined
|
||||
) {
|
||||
processError("请输入员工工号");
|
||||
return;
|
||||
}
|
||||
state.btnLoading = true;
|
||||
state.depPositions = state.depPositions.filter((item) => {
|
||||
return item.positions && item.positions.length > 0;
|
||||
});
|
||||
let url = "/user/v2/boss/staff/update";
|
||||
let params = {
|
||||
ID: Number(route.query.ID),
|
||||
depPositions: state.depPositions,
|
||||
};
|
||||
Object.assign(params, state.formData);
|
||||
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
processSuccess("操作成功!");
|
||||
getPageData();
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleLink = (path) => {
|
||||
router.push(path);
|
||||
};
|
||||
const handleBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
const calcDepPositions = () => {
|
||||
for (let key in state.depPositions) {
|
||||
let data = state.depPositions[key];
|
||||
if (!data || JSON.stringify(data) === "[]") {
|
||||
delete state.depPositions[key];
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleClearPage = (idx, clearItem) => {
|
||||
state.depPositions[idx].positions.forEach((item, index) => {
|
||||
if (item.name === clearItem.name) {
|
||||
state.depPositions[idx].positions.splice(index, 1);
|
||||
}
|
||||
|
||||
if (state.depPositions[idx].positions.length === 0) {
|
||||
state.depPositions.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
state.tableClearKey = clearItem.ID;
|
||||
};
|
||||
const calcSfBgColorWidth = (val) => {
|
||||
if (val) {
|
||||
return val.length * 15 + "px !important";
|
||||
}
|
||||
return null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.avatar-uploader > .ant-upload) {
|
||||
background: #fafafcff;
|
||||
}
|
||||
:deep(.ant-image) {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
:deep(.ant-image-img) {
|
||||
object-fit: cover !important;
|
||||
border-radius: 50%;
|
||||
}
|
||||
:deep(.ant-image-mask) {
|
||||
border-radius: 50%;
|
||||
}
|
||||
:deep(.ant-upload) {
|
||||
color: #c1b2e5;
|
||||
}
|
||||
:deep(.ant-select) {
|
||||
color: #000;
|
||||
}
|
||||
:deep(.ant-select-selector .ant-select-selection-placeholder) {
|
||||
color: #c3c3c3 !important;
|
||||
&::placeholder {
|
||||
color: #c3c3c3 !important;
|
||||
}
|
||||
}
|
||||
:deep(.ant-select:not(.ant-select-customize-input) .ant-select-selector) {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.img-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.border-active {
|
||||
border: 2px solid #764cf6;
|
||||
}
|
||||
</style>
|
578
src/views/manage/peomanage/index.vue
Normal file
578
src/views/manage/peomanage/index.vue
Normal file
@ -0,0 +1,578 @@
|
||||
<template>
|
||||
<div class="row" style="padding: 35px">
|
||||
<div
|
||||
class="col-3 fl-pa-md"
|
||||
style="
|
||||
background: #fff;
|
||||
box-shadow: rgba(188, 188, 188, 0.18) 0px 3px 6px 1px;
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="row font-16"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5"
|
||||
>
|
||||
<div class="fl-py-sm" style="border-bottom: 4px solid #764cf6">
|
||||
组织架构
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="height: 76vh; overflow: auto">
|
||||
<fl-tree
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fl-tree>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-9 row fl-px-md" style="align-items: flex-start">
|
||||
<fln-table
|
||||
:config="state.tableConfig"
|
||||
:fatherData="state.treeSelectData"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
@triggerRowActions="handleRowActions"
|
||||
@triggerTableBatchActions="handleTableBatchActions"
|
||||
>
|
||||
<template #search-header>
|
||||
<div
|
||||
class="col-12 row font-18"
|
||||
style="
|
||||
color: #764cf6;
|
||||
border-bottom: 1px solid #c1b2e5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div class="fl-py-sm" style="border-bottom: 4px solid #764cf6">
|
||||
{{ state.treeSelectData.title || "平台开发管理项目组" }}所有人员
|
||||
</div>
|
||||
<div>
|
||||
<n-button @click="goToPeoManage" v-permission="'per-system-btn'">
|
||||
系统所有人员
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flTree from "@/components/flnlayout/tree/flnindex.vue";
|
||||
import { NButton, NPopover } from "naive-ui";
|
||||
import { Local } from "@/utils/storage.js";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request, $hadRule } =
|
||||
currentInstance.appContext.config.globalProperties;
|
||||
const state = reactive({
|
||||
permission: false,
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
btnLoading: false,
|
||||
treeSelectData: {},
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
hasSelection: true,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
|
||||
url: "user/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "fatherDepartmentId",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
searchConfig: [
|
||||
{
|
||||
type: "text",
|
||||
label: "姓名",
|
||||
field: "nickName",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "手机号",
|
||||
field: "telNum",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
label: "状态",
|
||||
field: "status",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
label: "有效",
|
||||
value: "notactive",
|
||||
},
|
||||
{
|
||||
label: "无效",
|
||||
value: "left",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "员工工号",
|
||||
field: "jobNum",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
|
||||
{
|
||||
type: "text",
|
||||
label: "部门",
|
||||
field: "departmentName",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
|
||||
{
|
||||
type: "text",
|
||||
label: "岗位",
|
||||
field: "positionName",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
|
||||
{
|
||||
type: "text",
|
||||
label: "邮箱",
|
||||
field: "mailAccount",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "rangdate",
|
||||
label: "入职时间",
|
||||
class: "col-4",
|
||||
field: ["startEnterDate", "endEnterDate"],
|
||||
placeholder: ["开始时间", "结束时间"],
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
width: 100,
|
||||
title: "员工工号",
|
||||
field: "jobNum",
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
title: "姓名",
|
||||
field: "nickName",
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
title: "手机",
|
||||
field: "telNum",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: "归属部门",
|
||||
align: "left",
|
||||
field: "depPositions",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
tagField: "name",
|
||||
showLength: 3,
|
||||
bgColor: "#E0E0E6FF",
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: $hadRule("posi-edit-btn")
|
||||
? "岗位(可点击岗位修改权限)"
|
||||
: "岗位",
|
||||
align: "left",
|
||||
field: "positions",
|
||||
render(row, index) {
|
||||
const positions = row.positions;
|
||||
if (!positions || positions.length === 0) {
|
||||
return h("span", "No positions available");
|
||||
}
|
||||
// 渲染前3个标签
|
||||
const visibleTags = positions.slice(0, 3).map((position) =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: {
|
||||
marginRight: "2px",
|
||||
borderWidth: "1px",
|
||||
borderStyle: "solid",
|
||||
color: position.color,
|
||||
},
|
||||
onClick: () => {
|
||||
if ($hadRule("posi-edit-btn")) {
|
||||
router.push(
|
||||
`/posimanage/create?ID=${position.ID}&&type=edit`
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => position.name }
|
||||
)
|
||||
);
|
||||
|
||||
// 如果标签数量超过3个,渲染省略号按钮
|
||||
if (positions.length > 3) {
|
||||
const hiddenTags = positions.slice(3);
|
||||
visibleTags.push(
|
||||
h(
|
||||
NPopover,
|
||||
{
|
||||
trigger: "hover",
|
||||
placement: "bottom",
|
||||
},
|
||||
{
|
||||
trigger: () =>
|
||||
h(
|
||||
"span",
|
||||
{
|
||||
style: {
|
||||
marginLeft: "10px",
|
||||
borderWidth: "1px",
|
||||
borderStyle: "solid",
|
||||
color: "#000",
|
||||
cursor: "pointer",
|
||||
backgroundColor: "transparent",
|
||||
padding: "10px 15px",
|
||||
borderRadius: "5px",
|
||||
},
|
||||
},
|
||||
"..."
|
||||
),
|
||||
default: () =>
|
||||
h(
|
||||
"div",
|
||||
{
|
||||
style: {
|
||||
padding: "10px",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: "5px",
|
||||
boxShadow: "0 2px 12px rgba(0, 0, 0, 0.1)",
|
||||
},
|
||||
},
|
||||
hiddenTags.map((position) =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: {
|
||||
marginRight: "2px",
|
||||
borderWidth: "1px",
|
||||
borderStyle: "solid",
|
||||
color: position.color,
|
||||
},
|
||||
onClick: () => {
|
||||
if ($hadRule("posi-edit-btn")) {
|
||||
router.push(
|
||||
`/posimanage/create?ID=${position.ID}&&type=edit`
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ default: () => position.name }
|
||||
)
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return visibleTags;
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
width: 260,
|
||||
title: "邮箱",
|
||||
field: "mailAccount",
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
type: "avatar",
|
||||
title: "员工头像",
|
||||
field: "avatar",
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
type: "avatar",
|
||||
title: "员工近照",
|
||||
field: "recentImg",
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
title: "状态",
|
||||
field: "status",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
label: "有效",
|
||||
value: "notactive",
|
||||
class: "color-primary",
|
||||
},
|
||||
{
|
||||
label: "无效",
|
||||
value: "left",
|
||||
class: "color-draft",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
title: "入职时间",
|
||||
field: "enterDate",
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
title: "最近一次更新时间",
|
||||
field: "updatedAt",
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
fixed: "right",
|
||||
title: "操作人",
|
||||
field: "operatorName",
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
fixed: "right",
|
||||
title: "操作",
|
||||
actions: [
|
||||
{
|
||||
actionType: "act_view",
|
||||
type: "label",
|
||||
label: "查看",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
{
|
||||
actionType: "act_edit",
|
||||
type: "label",
|
||||
label: "修改",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tableBatchActions: [
|
||||
{
|
||||
notLimitSelect: true,
|
||||
actionType: "peo-create",
|
||||
type: "primary",
|
||||
label: "新增人员",
|
||||
},
|
||||
{
|
||||
actionType: "peo-pass-set",
|
||||
style: "background:#F7EFFFFF;color:#8352B4FF",
|
||||
type: "primary",
|
||||
label: "重置密码",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
getTreeData();
|
||||
});
|
||||
onMounted(() => {});
|
||||
|
||||
const handleRowActions = ({ btnConfig, rowData }) => {
|
||||
state.dialogData = rowData;
|
||||
Local.set("orgmanage_treeSelectData", state.treeSelectData);
|
||||
if (btnConfig.actionType === "act_edit") {
|
||||
router.push(`/peomanage/editPeo?ID=${rowData.ID}&&type=edit`);
|
||||
}
|
||||
if (btnConfig.actionType === "act_view") {
|
||||
router.push(`/peomanage/editPeo?ID=${rowData.ID}&&type=view`);
|
||||
}
|
||||
};
|
||||
const handleTableBatchActions = ({ selectedRows, btnConfig, fatherData }) => {
|
||||
state.selectedRows = selectedRows;
|
||||
if (btnConfig.actionType === "peo-create") {
|
||||
handleCreatePeo();
|
||||
}
|
||||
if (btnConfig.actionType === "peo-pass-set") {
|
||||
handleResetPass();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreatePeo = () => {
|
||||
router.push("/peomanage/editPeo");
|
||||
};
|
||||
|
||||
const goToPeoManage = () => {
|
||||
router.push("/peomanage/personnelManage");
|
||||
};
|
||||
|
||||
const handleResetPass = () => {
|
||||
state.btnLoading = true;
|
||||
let url = "/user/v2/reset/pwd";
|
||||
let params = {
|
||||
IDs: state.selectedRows.map((item) => item.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.selectedRows = [];
|
||||
state.tableConfig.refreshCount++;
|
||||
processSuccess("操作成功!");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
//只有自己的树
|
||||
const getTreeData = () => {
|
||||
let params = {};
|
||||
$request.HTTP.components.viewMyTree(params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
|
||||
// 获取最近点击的部门
|
||||
let localSelect = Local.get("orgmanage_treeSelectData");
|
||||
if (localSelect && JSON.stringify(localSelect) !== "{}") {
|
||||
state.treeSelectData = localSelect;
|
||||
state.expandedKeys = localSelect.pathIds;
|
||||
state.clickKey = localSelect.key;
|
||||
} else {
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
if (
|
||||
state.clickKey === data[0].key &&
|
||||
!state.expandedKeys.includes(data[0].key)
|
||||
) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
if (!state.expandedKeys.includes(state.clickKey)) {
|
||||
state.expandedKeys.push(state.clickKey);
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
state.treeRefreshCount++;
|
||||
state.tableConfig.refreshCount++;
|
||||
}, 100);
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.label = item.name;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
Local.set("orgmanage_treeSelectData", state.treeSelectData);
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-select) {
|
||||
color: #000;
|
||||
}
|
||||
:deep(.ant-select-selector .ant-select-selection-placeholder) {
|
||||
color: #c3c3c3 !important;
|
||||
&::placeholder {
|
||||
color: #c3c3c3 !important;
|
||||
}
|
||||
}
|
||||
:deep(.ant-select:not(.ant-select-customize-input) .ant-select-selector) {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
483
src/views/manage/peomanage/personnelManage.vue
Normal file
483
src/views/manage/peomanage/personnelManage.vue
Normal file
@ -0,0 +1,483 @@
|
||||
<template>
|
||||
<div class="row"
|
||||
style="padding: 35px">
|
||||
<div class="col-12 row">
|
||||
<fln-table :config="state.tableConfig"
|
||||
:fatherData="state.treeSelectData"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
@triggerRowActions="handleRowActions"
|
||||
@triggerTableBatchActions="handleTableBatchActions">
|
||||
<template #search-header>
|
||||
<div class="col-12 row font-18"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5; display: flex; justify-content: space-between; align-items: center;">
|
||||
<div class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6">
|
||||
系统所有人员
|
||||
</div>
|
||||
<div>
|
||||
<n-button @click="goToIndex">
|
||||
返回部门人员
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
<n-modal v-model:show="state.dialogDel"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card">
|
||||
<template #header>
|
||||
<div class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2">
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
删除
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div v-if="state.dialogData.type === 'one'"
|
||||
class="font-20"
|
||||
style="color: #1f2225ff; margin: 80px 0">
|
||||
确定删除该人员吗? 删除后将不可恢复!
|
||||
</div>
|
||||
<div v-if="state.dialogData.type === 'batch'"
|
||||
class="font-20"
|
||||
style="color: #1f2225ff; margin: 80px 0">
|
||||
确定删除选中的人员吗? 删除后将不可恢复!
|
||||
</div>
|
||||
<div class="col-12 row justify-center">
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogBack">返回</n-button>
|
||||
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #46299dff; color: #fff"
|
||||
@click="handleDialogSave">确定</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import { NButton } from "naive-ui";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const state = reactive({
|
||||
dialogDel: false,
|
||||
dialogData: {},
|
||||
|
||||
btnLoading: false,
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
hasSelection: true,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "user/v2/list",
|
||||
params: [],
|
||||
},
|
||||
searchConfig: [
|
||||
{
|
||||
type: "text",
|
||||
label: "姓名",
|
||||
field: "nickName",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "手机号",
|
||||
field: "telNum",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
label: "状态",
|
||||
field: "status",
|
||||
placeholder: "",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
label: "有效",
|
||||
value: "notactive",
|
||||
},
|
||||
{
|
||||
label: "无效",
|
||||
value: "left",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "员工工号",
|
||||
field: "jobNum",
|
||||
placeholder: "",
|
||||
},
|
||||
|
||||
{
|
||||
type: "text",
|
||||
label: "部门",
|
||||
field: "departmentName",
|
||||
placeholder: "",
|
||||
},
|
||||
|
||||
{
|
||||
type: "text",
|
||||
label: "岗位",
|
||||
field: "positionName",
|
||||
placeholder: "",
|
||||
},
|
||||
|
||||
{
|
||||
type: "text",
|
||||
label: "邮箱",
|
||||
field: "mailAccount",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "rangdate",
|
||||
label: "入职时间",
|
||||
field: ["startEnterDate", "endEnterDate"],
|
||||
placeholder: ["开始时间", "结束时间"],
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
width: 100,
|
||||
title: "员工工号",
|
||||
field: "jobNum",
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
title: "姓名",
|
||||
field: "nickName",
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
title: "手机",
|
||||
field: "telNum",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: "归属部门",
|
||||
align: 'left',
|
||||
field: "depPositions",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
tagField: "name",
|
||||
showLength: 3,
|
||||
bgColor: "#E0E0E6FF",
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: "岗位",
|
||||
align: 'left',
|
||||
field: "positions",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
tagField: "name",
|
||||
tagBgColorField: "color",
|
||||
showLength: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 260,
|
||||
title: "邮箱",
|
||||
field: "mailAccount",
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
type: "avatar",
|
||||
title: "员工头像",
|
||||
field: "avatar",
|
||||
},
|
||||
{
|
||||
width: 100,
|
||||
type: "avatar",
|
||||
title: "员工近照",
|
||||
field: "recentImg",
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
title: "状态",
|
||||
field: "status",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
label: "有效",
|
||||
value: "notactive",
|
||||
class: "color-primary",
|
||||
},
|
||||
{
|
||||
label: "无效",
|
||||
value: "left",
|
||||
class: "color-draft",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
title: "入职时间",
|
||||
field: "enterDate",
|
||||
},
|
||||
{
|
||||
width: 160,
|
||||
title: "最近一次更新时间",
|
||||
field: "updatedAt",
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
fixed: "right",
|
||||
title: "操作人",
|
||||
field: "operatorName",
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
fixed: "right",
|
||||
title: "操作",
|
||||
actions: [
|
||||
{
|
||||
actionType: "act_view",
|
||||
type: "label",
|
||||
label: "查看",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
{
|
||||
actionType: "act_edit",
|
||||
type: "label",
|
||||
label: "修改",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
{
|
||||
actionType: "act_del",
|
||||
type: "label",
|
||||
label: "删除",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tableBatchActions: [
|
||||
{
|
||||
notLimitSelect: true,
|
||||
actionType: "peo-create",
|
||||
type: "primary",
|
||||
label: "新建人员",
|
||||
},
|
||||
{
|
||||
actionType: "peo-pass-set",
|
||||
style: "background:#F7EFFFFF;color:#8352B4FF",
|
||||
type: "primary",
|
||||
label: "重置密码",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
onBeforeMount(() => { });
|
||||
onMounted(() => { });
|
||||
|
||||
const handleRowActions = ({ btnConfig, rowData }) => {
|
||||
state.dialogData = rowData;
|
||||
if (btnConfig.actionType === "act_del") {
|
||||
if (rowData.status !== "left") {
|
||||
processError("该人员在职,不可删除");
|
||||
return
|
||||
}
|
||||
state.dialogData.type = "one";
|
||||
state.dialogDel = true;
|
||||
}
|
||||
if (btnConfig.actionType === "act_edit") {
|
||||
router.push(`/peomanage/create?ID=${rowData.ID}&&type=edit`);
|
||||
}
|
||||
if (btnConfig.actionType === "act_view") {
|
||||
router.push(`/peomanage/create?ID=${rowData.ID}&&type=view`);
|
||||
}
|
||||
};
|
||||
const handleTableBatchActions = ({ selectedRows, btnConfig, fatherData }) => {
|
||||
state.selectedRows = selectedRows
|
||||
|
||||
if (btnConfig.actionType === "peo-create") {
|
||||
handleCreatePeo();
|
||||
}
|
||||
if (btnConfig.actionType === "peo-pass-set") {
|
||||
handleResetPass();
|
||||
}
|
||||
};
|
||||
|
||||
const goToIndex = () => {
|
||||
router.push("/peomanage");
|
||||
};
|
||||
|
||||
const handleCreatePeo = () => {
|
||||
router.push("/peomanage/create");
|
||||
};
|
||||
const handleResetPass = () => {
|
||||
state.btnLoading = true;
|
||||
let url = "/user/v2/reset/pwd";
|
||||
let params = {
|
||||
IDs: state.selectedRows.map((item) => item.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.selectedRows = [];
|
||||
state.tableConfig.refreshCount++;
|
||||
processSuccess("操作成功!");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleDeleteBatch = () => {
|
||||
state.dialogData.type = "batch";
|
||||
state.dialogDel = true;
|
||||
};
|
||||
const handleDeleteBatchSave = () => {
|
||||
state.btnLoading = true;
|
||||
let url = "/user/v2/batch/remove";
|
||||
let params = {
|
||||
IDs: state.selectedRows.map((item) => item.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.selectedRows = [];
|
||||
state.tableConfig.refreshCount++;
|
||||
processSuccess("操作成功!");
|
||||
state.dialogDel = false;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
state.dialogDel = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
state.dialogDel = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleDialogSave = () => {
|
||||
if (state.dialogData.type === "batch") {
|
||||
handleDeleteBatchSave();
|
||||
return;
|
||||
}
|
||||
state.btnLoading = true;
|
||||
let url = "/user/v2/remove";
|
||||
let params = {
|
||||
ID: state.dialogData.ID,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.tableConfig.refreshCount++;
|
||||
state.dialogDel = false;
|
||||
processSuccess("操作成功!");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
state.dialogDel = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogDel = false;
|
||||
state.btnLoading = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleDialogBack = () => {
|
||||
state.dialogDel = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-select) {
|
||||
color: #000;
|
||||
}
|
||||
:deep(.ant-select-selector .ant-select-selection-placeholder) {
|
||||
color: #c3c3c3 !important;
|
||||
&::placeholder {
|
||||
color: #c3c3c3 !important;
|
||||
}
|
||||
}
|
||||
:deep(.ant-select:not(.ant-select-customize-input) .ant-select-selector) {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
311
src/views/manage/posimanage/aboutApproval.vue
Normal file
311
src/views/manage/posimanage/aboutApproval.vue
Normal file
@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<n-modal v-model:show="state.dialogModal" :mask-closable="false">
|
||||
<n-card
|
||||
style="width: 80vw"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
批量加入审批发起人
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #header-extra>
|
||||
<n-button @click="triggerCalcApproval" text style="font-size: 24px">
|
||||
<n-icon>
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="row justify-center fl-pa-md" style="color: #333639">
|
||||
<div class="row col-12 fl-mt-lg">
|
||||
<div class="col-12">请选择审批类型</div>
|
||||
<div class="col-12 fl-mt-md overflow-auto" style="height: 60vh">
|
||||
<fln-table
|
||||
:config="state.tableConfig"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
@triggerSelectCheck="handleTableSelectCheck"
|
||||
>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="triggerCalcApproval"
|
||||
>上一步</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #46299dff; color: #fff"
|
||||
:disabled="state.selectedRows.length === 0"
|
||||
@click="triggerCalcApproval('submit')"
|
||||
>确定</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
<script setup>
|
||||
// 岗位新建中 选模版时的添加审批
|
||||
import {
|
||||
UpOutlined,
|
||||
DownOutlined,
|
||||
CloseCircleOutlined,
|
||||
PlusOutlined,
|
||||
DragOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import flnFormItem from "@/components/flnlayout/form/flnformItem.vue";
|
||||
import flAboutApproval from "./aboutApproval.vue";
|
||||
import draggable from "vuedraggable";
|
||||
import { Close as CloseIcon } from "@vicons/ionicons5";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
import { ChevronDownOutline, ChevronUpOutline } from "@vicons/ionicons5";
|
||||
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
const emit = defineEmits(["triggerCalcApproval"]);
|
||||
const state = reactive({
|
||||
btnLoading: false,
|
||||
dialogModal: true,
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "GroupList",
|
||||
url: "/approval/v2/only-group",
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: "审批类型分组",
|
||||
field: "Title",
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
field: "",
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
field: "",
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
field: "",
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
field: "",
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
field: "",
|
||||
},
|
||||
],
|
||||
expandConfig: {
|
||||
defaultSelectedRows: [],
|
||||
tableScrollWitdh: 600,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "TypeList",
|
||||
url: "/approval/v2/type/all",
|
||||
params: [
|
||||
{
|
||||
label: "domain",
|
||||
field: "Domain",
|
||||
},
|
||||
{
|
||||
label: "groupId",
|
||||
field: "ID",
|
||||
},
|
||||
],
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
type: "selection",
|
||||
width: 50,
|
||||
disabled(row) {
|
||||
return row.Auths[0].UserID !== 0 || row.Auths[0].IsAll !== 0;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "流程名称",
|
||||
field: "Title",
|
||||
},
|
||||
{
|
||||
title: "发起人",
|
||||
dataIndex: "Auths",
|
||||
key: "Auths",
|
||||
render(row) {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
style:
|
||||
"word-break:break-all;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;",
|
||||
title: `${getAuths(row?.Auths)}`,
|
||||
},
|
||||
`${getAuths(row?.Auths)}`
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "审批节点",
|
||||
key: "ApprovalUsers",
|
||||
dataIndex: "ApprovalUsers",
|
||||
render(row) {
|
||||
return h("div", {}, `${getApprovalUsers(row?.ApprovalUsers)}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "抄送",
|
||||
dataIndex: "CopyUsers",
|
||||
key: "CopyUsers",
|
||||
render(row) {
|
||||
return h(
|
||||
"div",
|
||||
{},
|
||||
`${
|
||||
Array.isArray(row.CopyUsers)
|
||||
? row.CopyUsers.map((item) =>
|
||||
item.ID !== 0
|
||||
? item.Name
|
||||
: `${item.DepartmentName}-${item.PositionName}`
|
||||
).join(",")
|
||||
: "-"
|
||||
}`
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "备注",
|
||||
dataIndex: "Remark",
|
||||
key: "Remark",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
selectedRows: [],
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
state.dialogModal = true;
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
const getAuths = (auths) => {
|
||||
if (auths) {
|
||||
if (auths && auths.length === 1 && auths[0].IsAll === 1) {
|
||||
return "全公司人员";
|
||||
} else {
|
||||
return auths
|
||||
.filter((item) => item.IsAll === 0)
|
||||
.map((item) =>
|
||||
item.Name ? item.Name : item.DepartmentName + item.PositionName
|
||||
)
|
||||
.filter(Boolean)
|
||||
.join("—");
|
||||
}
|
||||
}
|
||||
};
|
||||
const getApprovalUsers = (userList) => {
|
||||
let user = "";
|
||||
if (userList) {
|
||||
user = userList
|
||||
.filter((item) => item.DepartmentUID !== "undefined")
|
||||
.map((item) => {
|
||||
return {
|
||||
flow:
|
||||
item.IsDirect === 1 && item.IsDesignate === 1
|
||||
? `直属第${item.Level}级`
|
||||
: item.IsDirect === 1
|
||||
? `直属${item.Level}级`
|
||||
: item.Name
|
||||
? item.Name
|
||||
: item.DepartmentName + item.PositionName,
|
||||
};
|
||||
})
|
||||
.map((item) => item.flow)
|
||||
.join("—");
|
||||
} else {
|
||||
user = "";
|
||||
}
|
||||
return user;
|
||||
};
|
||||
const triggerCalcApproval = (type) => {
|
||||
if (type === "submit") {
|
||||
emit("triggerCalcApproval", state.selectedRows);
|
||||
} else {
|
||||
emit("triggerCalcApproval", []);
|
||||
}
|
||||
};
|
||||
const handleTableSelectCheck = (data) => {
|
||||
if (data && Array.isArray(data.selectedRow)) {
|
||||
data.selectedRow = data.selectedRow.filter((item) => {
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (data.action === "check" || data.action === "checkAll") {
|
||||
state.selectedRows = state.selectedRows.concat(data.selectedRow);
|
||||
state.selectedRows = state.selectedRows.filter((item, index, arr) => {
|
||||
return (
|
||||
arr.findIndex((obj) => JSON.stringify(obj) === JSON.stringify(item)) ===
|
||||
index
|
||||
);
|
||||
});
|
||||
}
|
||||
if (data.action === "uncheck") {
|
||||
state.selectedRows = state.selectedRows.filter((item) => {
|
||||
return item.ID !== data.selectedRow.ID;
|
||||
});
|
||||
}
|
||||
if (data.action === "uncheckAll") {
|
||||
state.selectedRows = state.selectedRows.filter((item) => {
|
||||
return (
|
||||
data.selectedRow.findIndex((obj) => {
|
||||
return obj.ID === item.ID;
|
||||
}) === -1
|
||||
);
|
||||
});
|
||||
}
|
||||
state.tableConfig.expandConfig.defaultSelectedRows = state.selectedRows;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(
|
||||
.fln-table
|
||||
.n-data-table-table
|
||||
.n-checkbox.n-checkbox--disabled
|
||||
.n-checkbox-box
|
||||
) {
|
||||
background-color: #d5d5d5 !important;
|
||||
}
|
||||
</style>
|
156
src/views/manage/posimanage/components/flnindex.vue
Normal file
156
src/views/manage/posimanage/components/flnindex.vue
Normal file
@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div class="fl-tree width-100 fl-mt-md">
|
||||
<div class="row justify-end">
|
||||
<div style="margin-right: 220px">是否为销售部门</div>
|
||||
</div>
|
||||
<n-tree
|
||||
v-if="state.treeLoading"
|
||||
block-line
|
||||
:default-expanded-keys="state.expandedKeys"
|
||||
:default-selected-keys="state.clickKey"
|
||||
label-field="name"
|
||||
key-field="key"
|
||||
:expand-on-click="true"
|
||||
:render-label="renderLabel"
|
||||
:data="state.treeData"
|
||||
@update:selected-keys="handleSelectTree"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
defineEmits,
|
||||
watch,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
|
||||
import treeLabel from "./treelabel.vue";
|
||||
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
let props = defineProps({
|
||||
data: Object,
|
||||
refreshCount: Number,
|
||||
config: Object,
|
||||
expandedKeys: Array,
|
||||
clickKey: [String, Number],
|
||||
});
|
||||
const state = reactive({
|
||||
expandedKeys: [],
|
||||
editTitle: "",
|
||||
treeData: [],
|
||||
clickKey: [],
|
||||
treeLoading: true,
|
||||
selectOptions: [],
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.refreshCount,
|
||||
() => {
|
||||
state.clickKey = [props.clickKey];
|
||||
state.treeLoading = false;
|
||||
nextTick(() => {
|
||||
state.treeData = props.data;
|
||||
calcDefaultConfig(state.treeData, 1);
|
||||
state.treeLoading = true;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.expandedKeys,
|
||||
() => {
|
||||
state.clickKey = [props.clickKey];
|
||||
state.expandedKeys = props.expandedKeys;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
state.clickKey = [props.clickKey];
|
||||
state.treeData = props.data;
|
||||
calcDefaultConfig(state.treeData, 1);
|
||||
state.expandedKeys = state.treeData.map((item) => item.key);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getSelectOptions();
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
"triggerTreeAction",
|
||||
"triggerTreeClick",
|
||||
"triggerTreeDefaultClick",
|
||||
]);
|
||||
const handleSelectTree = (keys, option, meta) => {
|
||||
if (keys.length === 1) {
|
||||
emit("triggerTreeClick", { selectedKey: keys[0], tree: option[0] });
|
||||
} else {
|
||||
emit("triggerTreeDefaultClick");
|
||||
}
|
||||
};
|
||||
const renderLabel = (option, checked) => {
|
||||
return h(
|
||||
treeLabel,
|
||||
{
|
||||
dataRef: option,
|
||||
checked: checked,
|
||||
config: props.config,
|
||||
clickKey: props.clickKey,
|
||||
options: state.selectOptions,
|
||||
onTriggerTreeAction: handleTreeAction,
|
||||
},
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
const calcDefaultConfig = (data, level) => {
|
||||
for (let item of data) {
|
||||
if (!item.key) {
|
||||
item.key = item.title + "_" + level;
|
||||
}
|
||||
item.edit = false;
|
||||
if (item.children) {
|
||||
calcDefaultConfig(item.children, level + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const override = ({ option }) => {
|
||||
if (option.children) {
|
||||
return "toggleExpand";
|
||||
}
|
||||
return "default";
|
||||
};
|
||||
const handleTreeAction = ({ type, val }) => {
|
||||
emit("triggerTreeAction", { type, val });
|
||||
};
|
||||
|
||||
const getSelectOptions = () => {
|
||||
let url = "department/head/list";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
let resData = res.data.list || [];
|
||||
state.selectOptions = resData.filter((item) => item.alias);
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
240
src/views/manage/posimanage/components/treelabel.vue
Normal file
240
src/views/manage/posimanage/components/treelabel.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div class="row items-center">
|
||||
<div v-if="state.treeData.edit">
|
||||
<n-input v-model:value="state.editTitle" style="width: 120px" />
|
||||
</div>
|
||||
|
||||
<n-popover trigger="hover" v-else>
|
||||
<template #trigger>
|
||||
<div style="width: 120px" class="fl-px-sm sf-text-ellipsis">
|
||||
{{ state.treeData.title }}
|
||||
</div>
|
||||
</template>
|
||||
<div>{{ state.treeData.title }}</div>
|
||||
</n-popover>
|
||||
<div class="row" style="width: calc(100% - 120px)">
|
||||
<div class="col-4 row items-center">
|
||||
<n-icon
|
||||
:component="CreateOutline"
|
||||
class="fl-ml-sm"
|
||||
size="20"
|
||||
v-if="config?.actions.includes('edit') && !state.treeData.edit"
|
||||
@click.stop="handleTreeEdit(state.treeData)"
|
||||
/>
|
||||
<n-icon
|
||||
:component="Remove"
|
||||
size="20"
|
||||
v-if="
|
||||
config?.actions.includes('subtraction') &&
|
||||
!state.treeData.edit &&
|
||||
visibleFormItem(config.subtractionShow, state.treeData)
|
||||
"
|
||||
class="fl-ml-sm"
|
||||
@click.stop="handleTreeSubtraction(state.treeData)"
|
||||
/>
|
||||
<n-icon
|
||||
:component="Add"
|
||||
size="20"
|
||||
v-if="
|
||||
config?.actions.includes('add') &&
|
||||
!state.treeData.edit &&
|
||||
visibleFormItem(config.addShow, state.treeData)
|
||||
"
|
||||
class="fl-ml-sm"
|
||||
@click.stop="handleTreeAdd(state.treeData)"
|
||||
/>
|
||||
<drag-outlined
|
||||
v-if="
|
||||
config?.actions.includes('move') &&
|
||||
!state.treeData.edit &&
|
||||
visibleFormItem(config.moveShow, state.treeData)
|
||||
"
|
||||
class="fl-ml-sm"
|
||||
@click.stop="handleTreeMove(state.treeData)"
|
||||
/>
|
||||
|
||||
<!-- <n-icon :component="MoveOutline"
|
||||
size="20"
|
||||
v-if="config?.actions.includes('move')&&!state.treeData.edit&&visibleFormItem(config.moveShow, state.treeData)"
|
||||
class="fl-ml-sm"
|
||||
@click.stop="handleTreeMove(state.treeData)" /> -->
|
||||
|
||||
<n-icon
|
||||
:component="Checkmark"
|
||||
size="20"
|
||||
v-if="state.treeData.edit"
|
||||
class="fl-ml-sm"
|
||||
@click.stop="handleTreeSave(state.treeData)"
|
||||
/>
|
||||
<n-icon
|
||||
:component="Close"
|
||||
size="20"
|
||||
v-if="state.treeData.edit"
|
||||
class="fl-ml-md"
|
||||
@click.stop="handleTreeNotSave(state.treeData)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-8 row items-center justify-end" @click.stop>
|
||||
<div>
|
||||
<n-radio-group
|
||||
v-model:value="state.radioVal"
|
||||
@update:value="handleRadioChange"
|
||||
>
|
||||
<n-space>
|
||||
<n-radio :value="true"> 是 </n-radio>
|
||||
<n-radio :value="false"> 否 </n-radio>
|
||||
</n-space>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
<div class="fl-ml-md" style="width: 200px" @click="handleSelectClick">
|
||||
<n-select
|
||||
v-model:value="state.selectVal"
|
||||
size="small"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择对应部门"
|
||||
label-field="alias"
|
||||
value-field="storeId"
|
||||
:options="options"
|
||||
@update:value="handleSelectChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeMount, onMounted, watch, getCurrentInstance } from "vue";
|
||||
import { visibleFormItem } from "@/utils/helper/form";
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
import {
|
||||
UpOutlined,
|
||||
DownOutlined,
|
||||
CloseCircleOutlined,
|
||||
PlusOutlined,
|
||||
DragOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import {
|
||||
Add,
|
||||
Checkmark,
|
||||
Close,
|
||||
CreateOutline,
|
||||
Remove,
|
||||
MoveOutline,
|
||||
} from "@vicons/ionicons5";
|
||||
let props = defineProps({
|
||||
dataRef: Object,
|
||||
checked: Boolean,
|
||||
config: Object,
|
||||
clickKey: [String, Number],
|
||||
options: Array,
|
||||
});
|
||||
const state = reactive({
|
||||
expandedKeys: [],
|
||||
editTitle: "",
|
||||
treeData: [],
|
||||
radioVal: null,
|
||||
selectVal: null,
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
state.treeData = props.dataRef.option;
|
||||
state.radioVal = state.treeData.sync;
|
||||
if (state.treeData.syncId) {
|
||||
state.selectVal = state.treeData.syncId * 1;
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => props.dataRef.option,
|
||||
(val) => {
|
||||
state.treeData = props.dataRef.option;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onMounted(() => {});
|
||||
const emit = defineEmits(["triggerTreeAction", "triggerTreeClick"]);
|
||||
|
||||
// const myComponentRef = ref(null);
|
||||
const handleTreeEdit = () => {
|
||||
state.editTitle = state.treeData.title;
|
||||
state.treeData.edit = true;
|
||||
// myComponentRef.value.$forceUpdate();
|
||||
};
|
||||
const handleTreeAdd = () => {
|
||||
emit("triggerTreeAction", { type: "add", val: state.treeData });
|
||||
};
|
||||
const handleTreeMove = () => {
|
||||
emit("triggerTreeAction", { type: "move", val: state.treeData });
|
||||
};
|
||||
|
||||
const handleTreeSubtraction = () => {
|
||||
emit("triggerTreeAction", { type: "subtraction", val: state.treeData });
|
||||
};
|
||||
const handleTreeSave = () => {
|
||||
state.treeData.title = state.editTitle;
|
||||
emit("triggerTreeAction", { type: "save", val: state.treeData });
|
||||
};
|
||||
const handleTreeNotSave = () => {
|
||||
state.editTitle = "";
|
||||
emit("triggerTreeAction", { type: "cancel", val: state.treeData });
|
||||
};
|
||||
const handleRadioChange = (val) => {
|
||||
let url = "department/v2/update";
|
||||
let params = {
|
||||
ID: state.treeData.key,
|
||||
name: state.treeData.title,
|
||||
sync: val,
|
||||
syncID: state.treeData.syncId,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
state.treeData.sync = val;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleSelectChange = (val) => {
|
||||
let syncID = "";
|
||||
if (val === null || val === undefined || val === "") {
|
||||
syncID = "";
|
||||
} else {
|
||||
syncID = val + "";
|
||||
}
|
||||
let url = "department/v2/update";
|
||||
let params = {
|
||||
ID: state.treeData.key,
|
||||
name: state.treeData.title,
|
||||
sync: state.treeData.sync,
|
||||
syncID: syncID,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
state.treeData.syncId = syncID;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleSelectClick = () => {
|
||||
console.log(props.options);
|
||||
};
|
||||
</script>
|
1324
src/views/manage/posimanage/create.vue
Normal file
1324
src/views/manage/posimanage/create.vue
Normal file
File diff suppressed because it is too large
Load Diff
942
src/views/manage/posimanage/dialogCreate.vue
Normal file
942
src/views/manage/posimanage/dialogCreate.vue
Normal file
@ -0,0 +1,942 @@
|
||||
<template>
|
||||
<n-modal v-model:show="state.dialogModal" :mask-closable="false">
|
||||
<n-card
|
||||
style="width: 1050px"
|
||||
:bordered="false"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header>
|
||||
<div class="col-12 row justify-center relative">
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
新增岗位
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #header-extra>
|
||||
<n-button @click="handleDialogBack" text style="font-size: 24px">
|
||||
<n-icon>
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-pb-md" style="color: #333639">
|
||||
<div class="col-12 row">
|
||||
<div class="search-item fl-mb-sm row items-center">
|
||||
<div>*岗位名</div>
|
||||
<fln-form-item
|
||||
style="width: 180px; margin-left: 45px"
|
||||
type="text"
|
||||
:config="{
|
||||
placeholder: '请输入岗位名',
|
||||
field: 'name',
|
||||
}"
|
||||
:val="state.formData.name"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mb-sm row items-center">
|
||||
<div style="margin-left: 16px">岗位描述</div>
|
||||
<fln-form-item
|
||||
style="width: 180px; margin-left: 38px"
|
||||
type="text"
|
||||
:config="{
|
||||
placeholder: '请输入岗位描述',
|
||||
field: 'remark',
|
||||
}"
|
||||
:val="state.formData.remark"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mb-sm row items-center">
|
||||
<div style="margin-left: 16px">标签颜色</div>
|
||||
<fln-form-item
|
||||
style="width: 122px; margin-left: 38px"
|
||||
type="colorpicker"
|
||||
:val="state.formData.color"
|
||||
:config="{
|
||||
field: 'color',
|
||||
}"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="search-item fl-mb-sm row items-center">
|
||||
<fln-form-item
|
||||
style="margin-left: 38px"
|
||||
type="checkbox"
|
||||
:val="state.formData.isOwner"
|
||||
:config="{
|
||||
field: 'isOwner',
|
||||
options: [
|
||||
{
|
||||
label: '设为部门负责人',
|
||||
value: 'true',
|
||||
},
|
||||
],
|
||||
}"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row col-12 fl-mt-sm">
|
||||
<div class="col-12 row justify-between fl-mt-md fl-mb-sm">
|
||||
<div>请选择以下模板进行新增岗位:</div>
|
||||
<div>
|
||||
可将该岗位模板加入到审批中,<span
|
||||
class="cursor"
|
||||
style="color: #6c00ff"
|
||||
@click="state.dialogApproval = true"
|
||||
>去试试</span
|
||||
>
|
||||
</div>
|
||||
<fl-about-approval
|
||||
:show="state.dialogApproval"
|
||||
@triggerCalcApproval="handleApproval"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="col-12 row">
|
||||
<div
|
||||
v-for="(temp, tempidx) in state.tempList"
|
||||
:key="tempidx"
|
||||
class="fl-py-xs text-center cursor fl-mr-sm fl-mb-sm row justify-center items-center"
|
||||
:style="[
|
||||
state.selectTemplate === temp.name
|
||||
? { border: '1px solid #46299d' }
|
||||
: {},
|
||||
]"
|
||||
style="
|
||||
border-radius: 3px;
|
||||
min-width: 176px;
|
||||
background: #f7efff;
|
||||
color: #8352b4;
|
||||
"
|
||||
@click="handleTempClick(temp)"
|
||||
>
|
||||
{{ temp.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fl-my-lg row col-12">
|
||||
<div class="col-2 font-14 fl-mb-md">已选模版:</div>
|
||||
<div class="col-10 row items-center fl-mb-md">
|
||||
<div
|
||||
class="fl-py-xs text-center row justify-center items-center"
|
||||
style="
|
||||
border-radius: 3px;
|
||||
min-width: 193px;
|
||||
background: #f7f7f7ff;
|
||||
border: solid 1px #e0e0e5ff;
|
||||
"
|
||||
>
|
||||
{{ state.selectTemplate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-2 font-14">
|
||||
*所选页面
|
||||
<span style="color: #1f2225ff">{{
|
||||
state.selectedRows.length
|
||||
}}</span>
|
||||
个:
|
||||
</div>
|
||||
<n-spin
|
||||
class="fl-ml-md"
|
||||
v-if="state.dataLoading"
|
||||
v-show="state.dataLoading"
|
||||
description="获取中..."
|
||||
style="color: #1f2225ff"
|
||||
/>
|
||||
<div v-else class="col-10 row">
|
||||
<div
|
||||
v-for="(select, idx) in state.selectedRows"
|
||||
:key="idx"
|
||||
class="fl-py-xs text-center fl-mr-sm fl-mb-sm row justify-center items-center"
|
||||
style="
|
||||
border-radius: 3px;
|
||||
min-width: 193px;
|
||||
background: #f7f7f7ff;
|
||||
border: solid 1px #e0e0e5ff;
|
||||
"
|
||||
>
|
||||
<n-tooltip trigger="hover">
|
||||
<template #trigger>
|
||||
{{ select.name }}
|
||||
</template>
|
||||
{{ select.name }}
|
||||
</n-tooltip>
|
||||
<close-circle-outlined
|
||||
class="fl-ml-md"
|
||||
@click="handleClearPage(select)"
|
||||
/>
|
||||
</div>
|
||||
<div class="row items-center fl-mr-sm fl-mb-sm">
|
||||
<div
|
||||
style="
|
||||
border-radius: 50%;
|
||||
background: #e0e0e5;
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
"
|
||||
class="row items-center justify-center fl-mr-sm cursor"
|
||||
@click="handleEditSelectTemp"
|
||||
>
|
||||
<n-icon>
|
||||
<ChevronUpOutline v-if="state.showTempRuleListData" />
|
||||
<ChevronDownOutline v-else />
|
||||
</n-icon>
|
||||
</div>
|
||||
|
||||
<span style="color: rgba(0, 0, 0, 0.3)">点击展开可进行编辑</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="state.showTempRuleListData"
|
||||
style="max-height: 40vh"
|
||||
class="overflow-auto col-12 row"
|
||||
>
|
||||
<div
|
||||
v-for="(row, rowIdx) in state.listData"
|
||||
:key="rowIdx"
|
||||
class="col-12 fl-mb-md row items-center fl-pb-xs"
|
||||
style="min-height: 40px"
|
||||
>
|
||||
<div
|
||||
class="col-12 row justify-between items-center fl-px-md"
|
||||
style="min-height: 40px; background: #46299dff"
|
||||
:style="{ borderRadius: row.showChild ? '3px 3px 0 0' : '3px' }"
|
||||
>
|
||||
<div class="color-white">
|
||||
<n-checkbox
|
||||
v-model:checked="row.selected"
|
||||
@update:checked="(e) => handlePageSelectChange(e)"
|
||||
>
|
||||
</n-checkbox>
|
||||
{{ row.name }}
|
||||
</div>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="!row.showChild"
|
||||
style="background: #ffffffff; color: #46299dff"
|
||||
icon-placement="right"
|
||||
round
|
||||
secondary
|
||||
strong
|
||||
size="small"
|
||||
@click="row.showChild = true"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ChevronDownOutline />
|
||||
</n-icon>
|
||||
</template>
|
||||
展开
|
||||
</n-button>
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="row.showChild"
|
||||
style="background: #ffffffff; color: #46299dff"
|
||||
icon-placement="right"
|
||||
round
|
||||
secondary
|
||||
strong
|
||||
size="small"
|
||||
@click="row.showChild = false"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<ChevronUpOutline />
|
||||
</n-icon>
|
||||
</template>
|
||||
收起
|
||||
</n-button>
|
||||
</div>
|
||||
<div
|
||||
v-if="row.showChild"
|
||||
style="border: 1px solid #1f2225ff; border-radius: 0 0 3px 3px"
|
||||
class="col-12 row"
|
||||
>
|
||||
<div
|
||||
class="col-12 row"
|
||||
style="padding: 11px 0; background: #dfd7f2"
|
||||
>
|
||||
<div class="col-3 text-center">按钮权限</div>
|
||||
<div class="col-3 text-center">列表权限</div>
|
||||
<div class="col-3 text-center">数据范围权限</div>
|
||||
<div class="col-3 text-center">列表字段权限</div>
|
||||
</div>
|
||||
<div class="col-12 row">
|
||||
<div class="col-3 fl-py-md">
|
||||
<div class="" style="margin-left: 30%">
|
||||
<div
|
||||
v-if="
|
||||
Array.isArray(row.buttonList) &&
|
||||
row.buttonList.length > 1
|
||||
"
|
||||
class="row"
|
||||
>
|
||||
<n-checkbox
|
||||
v-model:checked="row.btnSelectedAll"
|
||||
:indeterminate="row.btnIndeterminate"
|
||||
@update:checked="
|
||||
(e) => handleBtnSelectAllChange(e, rowIdx)
|
||||
"
|
||||
>
|
||||
全选
|
||||
</n-checkbox>
|
||||
</div>
|
||||
<n-checkbox-group
|
||||
v-model:value="row.btnSelected"
|
||||
@update:value="(e) => handleBtnSelectChange(e, rowIdx)"
|
||||
>
|
||||
<div
|
||||
class="col-12"
|
||||
v-for="(btn, btnIdx) in row.buttonList"
|
||||
:key="btnIdx"
|
||||
>
|
||||
<n-checkbox :value="btn.ID">
|
||||
{{ btn.name }}
|
||||
</n-checkbox>
|
||||
</div>
|
||||
</n-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-9 row">
|
||||
<div
|
||||
v-for="(list, listIdx) in row.interfaceList"
|
||||
:key="listIdx"
|
||||
class="col-12 row"
|
||||
:style="{
|
||||
'border-left':
|
||||
row.interfaceList.length > 1 ? '1px solid #C1B2E5' : '',
|
||||
'border-bottom':
|
||||
listIdx !== row.interfaceList.length - 1
|
||||
? '1px solid #C1B2E5'
|
||||
: '',
|
||||
}"
|
||||
>
|
||||
<div class="col-4 row fl-pa-md justify-center">
|
||||
<n-checkbox
|
||||
v-model:checked="list.selected"
|
||||
@update:checked="
|
||||
(e) => handleListSelectChange(e, rowIdx, listIdx)
|
||||
"
|
||||
>
|
||||
{{ list.name }}
|
||||
</n-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="col-4 row fl-pa-md justify-center">
|
||||
<n-radio-group v-model:value="list.limitSelected">
|
||||
<n-radio
|
||||
:style="radioStyle"
|
||||
v-for="(limit, limitIdx) in list.limit"
|
||||
:key="limitIdx"
|
||||
:value="limit.value"
|
||||
>
|
||||
<div class="relative">
|
||||
{{ limit.label }}
|
||||
<n-button
|
||||
tertiary
|
||||
v-if="
|
||||
list.limitSelected === 5 && limit.value === 5
|
||||
"
|
||||
size="small"
|
||||
style="
|
||||
position: absolute;
|
||||
right: -120px;
|
||||
width: 70px;
|
||||
background: #ffffff;
|
||||
color: #6d5c9c;
|
||||
border: 1px solid #8a67ef;
|
||||
"
|
||||
@click.stop="
|
||||
handleChangeSelfLimitData(rowIdx, listIdx, list)
|
||||
"
|
||||
>设置</n-button
|
||||
>
|
||||
</div>
|
||||
</n-radio>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
|
||||
<div class="col-4 row fl-pa-md justify-center">
|
||||
<n-checkbox-group
|
||||
v-if="Array.isArray(list.positionRuleFields)"
|
||||
v-model:value="list.fieldsSelected"
|
||||
>
|
||||
<div class="col-12">
|
||||
<n-button
|
||||
tertiary
|
||||
class="fl-ml-md"
|
||||
size="small"
|
||||
style="
|
||||
width: 70px;
|
||||
background: #ffffff;
|
||||
color: #6d5c9c;
|
||||
border: 1px solid #8a67ef;
|
||||
"
|
||||
@click="handleEditField(rowIdx, listIdx)"
|
||||
>设置</n-button
|
||||
>
|
||||
<n-button
|
||||
tertiary
|
||||
class="fl-ml-md"
|
||||
size="small"
|
||||
style="
|
||||
width: 70px;
|
||||
background: #ffffff;
|
||||
color: #6d5c9c;
|
||||
border: 1px solid #8a67ef;
|
||||
"
|
||||
@click="handleEditFieldSort(rowIdx, listIdx)"
|
||||
>排序</n-button
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="col-12"
|
||||
v-for="(field, fieldIdx) in list.positionRuleFields"
|
||||
:key="fieldIdx"
|
||||
>
|
||||
<n-checkbox :value="field.ID">
|
||||
{{ field.fieldCnName }}
|
||||
</n-checkbox>
|
||||
</div>
|
||||
</n-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogBack"
|
||||
>返回</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #46299dff; color: #fff"
|
||||
:loading="state.btnLoading"
|
||||
@click="handleDialogSubmit"
|
||||
>保存</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
<fl-limit-data
|
||||
v-if="state.dialogSelfLimtiData"
|
||||
:fatherData="state.dataDepartmentIds"
|
||||
@triggerLimitData="handleLimitData"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
// 岗位新建方式选择 弹窗
|
||||
import {
|
||||
UpOutlined,
|
||||
DownOutlined,
|
||||
CloseCircleOutlined,
|
||||
PlusOutlined,
|
||||
DragOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import { Close as CloseIcon } from "@vicons/ionicons5";
|
||||
import flnFormItem from "@/components/flnlayout/form/flnformItem.vue";
|
||||
import flAboutApproval from "./aboutApproval.vue";
|
||||
import draggable from "vuedraggable";
|
||||
import flLimitData from "./limitData.vue";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
import { ChevronDownOutline, ChevronUpOutline } from "@vicons/ionicons5";
|
||||
import { Local } from "@/utils/storage.js";
|
||||
|
||||
const emits = defineEmits(["triggerClose"]);
|
||||
const radioStyle = reactive({
|
||||
display: "flex",
|
||||
height: "30px",
|
||||
lineHeight: "30px",
|
||||
});
|
||||
let props = defineProps({
|
||||
fatherData: {
|
||||
type: Object,
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
selectedRows: [],
|
||||
dialogField: false,
|
||||
dialogData: {
|
||||
fieldList: [],
|
||||
},
|
||||
listData: [],
|
||||
formData: {
|
||||
name: "",
|
||||
remark: "",
|
||||
color: "#1F2225",
|
||||
isOwner: [],
|
||||
},
|
||||
btnLoading: false,
|
||||
pageType: "create",
|
||||
enabled: true,
|
||||
dialogFieldSort: false,
|
||||
dataLoading: false,
|
||||
dialogModal: true,
|
||||
tempList: [],
|
||||
selectTemplate: "",
|
||||
showTempRuleListData: false,
|
||||
dialogApproval: false,
|
||||
dialogSelfLimtiData: false,
|
||||
dialogLimitData: {},
|
||||
dataDepartmentIds: [],
|
||||
approvalData: [],
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
console.log("onBeforeMount");
|
||||
state.dialogModal = true;
|
||||
getTempData();
|
||||
if (route.query.type) {
|
||||
state.pageType = route.query.type;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
const getTempData = () => {
|
||||
let url = "/position/v2/model/list";
|
||||
$request.HTTP.components.postDataByParams(url, {}).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
state.tempList = res.data || [];
|
||||
state.selectTemplate = state.tempList[0].name || "无数据";
|
||||
if (state.tempList.length > 0) {
|
||||
getRulesData(state.tempList[0]);
|
||||
}
|
||||
} else {
|
||||
processError(res.msg || "获取数据失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const getRulesData = (temp) => {
|
||||
let url = "/position/v2/model/detail";
|
||||
$request.HTTP.components.postDataByParams(url, { uuid: temp.uuid }).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
let data = res.data.positionTreeRule;
|
||||
data.map((item) => {
|
||||
item.selected = false;
|
||||
item.showChild = false;
|
||||
|
||||
item.btnSelected = [];
|
||||
item.btnSelectedAll = false;
|
||||
item.btnIndeterminate = false;
|
||||
if (Array.isArray(item.interfaceList)) {
|
||||
item.interfaceList.map((listItem) => {
|
||||
listItem.selected = false;
|
||||
listItem.limit = [
|
||||
{
|
||||
label: "仅自己",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "自己及下属",
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: "本部门",
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: "所有",
|
||||
value: 4,
|
||||
},
|
||||
];
|
||||
listItem.limitSelected = 4;
|
||||
listItem.positionRuleFields = listItem.positionRuleFields || [];
|
||||
listItem.fieldsSelected = listItem.positionRuleFields.map(
|
||||
(item) => item.ID
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
state.listData = data;
|
||||
setRulesDataDefaultVal(data, state.listData);
|
||||
state.selectedRows = state.listData.filter((item) => item.selected);
|
||||
} else {
|
||||
processError(res.msg || "获取数据失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取数据失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleValChange = ({ val, config }) => {
|
||||
state.formData[config.field] = val;
|
||||
};
|
||||
const handleDialogSubmit = () => {
|
||||
if (state.formData.name === "") {
|
||||
processError("请填写岗位名称");
|
||||
return;
|
||||
}
|
||||
let url = "/position/v2/create";
|
||||
let params = {
|
||||
name: state.formData.name,
|
||||
remark: state.formData.remark,
|
||||
color: state.formData.color,
|
||||
positionTreeRule: [],
|
||||
departmentID: Number(props.fatherData.key),
|
||||
isLeader: state.formData.isOwner.length > 0,
|
||||
};
|
||||
let data = state.listData;
|
||||
data
|
||||
.filter((item) => item.selected)
|
||||
.map((item) => {
|
||||
let obj = {
|
||||
ID: item.ID,
|
||||
buttonList: Array.isArray(item.buttonList)
|
||||
? item.buttonList.filter((btnItem) =>
|
||||
item.btnSelected.includes(btnItem.ID)
|
||||
)
|
||||
: [],
|
||||
interfaceList: Array.isArray(item.interfaceList)
|
||||
? item.interfaceList
|
||||
.filter((listItem) => listItem.selected)
|
||||
.map((listItem) => {
|
||||
return {
|
||||
ID: listItem.ID,
|
||||
dataRange: listItem.limitSelected,
|
||||
positionRuleFields: Array.isArray(listItem.positionRuleFields)
|
||||
? listItem.positionRuleFields.filter((fieldItem) =>
|
||||
listItem.fieldsSelected.includes(fieldItem.ID)
|
||||
)
|
||||
: [],
|
||||
};
|
||||
})
|
||||
: [],
|
||||
};
|
||||
params.positionTreeRule.push(obj);
|
||||
});
|
||||
state.btnLoading = true;
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
// 岗位绑定审批发起人
|
||||
if (state.approvalData.length > 0) {
|
||||
let url = "/approval/v2/create-auth";
|
||||
let params = {
|
||||
typeId: state.approvalData.map((item) => item.ID),
|
||||
departmentUID: res.data.DepartmentID.toString(),
|
||||
departmentName: res.data.DepartmentName,
|
||||
positionUID: res.data.ID.toString(),
|
||||
positionName: res.data.Name,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
processSuccess("操作成功!");
|
||||
emits("triggerClose");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
state.btnLoading = false;
|
||||
processError("操作失败!");
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
} else {
|
||||
state.btnLoading = false;
|
||||
processSuccess("操作成功!");
|
||||
emits("triggerClose");
|
||||
}
|
||||
} else {
|
||||
state.btnLoading = false;
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleBack = () => {
|
||||
router.go(-1);
|
||||
};
|
||||
const handleEditField = (rowIdx, listIdx) => {
|
||||
state.dialogData.clickData = state.listData[rowIdx];
|
||||
state.dialogData.ID = state.listData[rowIdx].interfaceList[listIdx].ID;
|
||||
state.dialogData.rowIdx = rowIdx;
|
||||
state.dialogData.listIdx = listIdx;
|
||||
state.dialogData.fieldList = JSON.parse(
|
||||
JSON.stringify(
|
||||
state.listData[rowIdx].interfaceList[listIdx].positionRuleFields.map(
|
||||
(item) => {
|
||||
return {
|
||||
ID: item.ID,
|
||||
field: item.fieldKey,
|
||||
fieldCnName: item.fieldCnName,
|
||||
};
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
state.dialogField = true;
|
||||
};
|
||||
const handleBtnSelectAllChange = (checked, idx) => {
|
||||
if (checked) {
|
||||
state.listData[idx].btnSelected = state.listData[idx].buttonList.map(
|
||||
(item) => item.ID
|
||||
);
|
||||
state.listData[idx].btnIndeterminate = false;
|
||||
} else {
|
||||
state.listData[idx].btnSelected = [];
|
||||
state.listData[idx].btnIndeterminate = false;
|
||||
}
|
||||
};
|
||||
const handleBtnSelectChange = (e, idx) => {
|
||||
if (e.length === 0) {
|
||||
state.listData[idx].btnSelectedAll = false;
|
||||
state.listData[idx].btnIndeterminate = false;
|
||||
} else if (e.length === state.listData[idx].buttonList.length) {
|
||||
state.listData[idx].btnSelectedAll = true;
|
||||
state.listData[idx].btnIndeterminate = false;
|
||||
} else {
|
||||
state.listData[idx].btnSelectedAll = false;
|
||||
state.listData[idx].btnIndeterminate = true;
|
||||
}
|
||||
};
|
||||
const handleListSelectChange = (e, rowIdx, listIdx) => {
|
||||
state.listData[rowIdx].listSelected = state.listData[
|
||||
rowIdx
|
||||
].interfaceList.filter((item) => item.selected);
|
||||
};
|
||||
const handlePageSelectChange = (e) => {
|
||||
state.selectedRows = state.listData.filter((item) => item.selected);
|
||||
};
|
||||
const handleClearPage = (select) => {
|
||||
state.listData.forEach((item) => {
|
||||
if (item.name === select.name) {
|
||||
item.selected = false;
|
||||
}
|
||||
});
|
||||
state.selectedRows = state.listData.filter((item) => item.selected);
|
||||
};
|
||||
|
||||
const handleDelDialogFieldList = (delIdx) => {
|
||||
state.dialogData.fieldList.splice(delIdx, 1);
|
||||
};
|
||||
const handleCreDialogFieldList = () => {
|
||||
state.dialogData.fieldList.push({
|
||||
field: "",
|
||||
fieldCnName: "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleDialogBack = () => {
|
||||
state.dialogField = false;
|
||||
emits("triggerClose");
|
||||
};
|
||||
const handleLink = (path) => {
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
const setRulesDataDefaultVal = (pageData, listData) => {
|
||||
pageData.map((pageDataItem) => {
|
||||
listData.map((listItem) => {
|
||||
if (pageDataItem.ID === listItem.ID) {
|
||||
listItem.selected = true;
|
||||
listItem.showChild = false;
|
||||
|
||||
listItem.btnSelected = Array.isArray(pageDataItem.buttonList)
|
||||
? pageDataItem.buttonList.map((btnItem) => btnItem.ID)
|
||||
: [];
|
||||
if (
|
||||
Array.isArray(listItem.interfaceList) &&
|
||||
Array.isArray(pageDataItem.interfaceList)
|
||||
) {
|
||||
listItem.interfaceList.map((interfaceItem) => {
|
||||
pageDataItem.interfaceList.map((pageDataInterfaceItem) => {
|
||||
if (interfaceItem.ID === pageDataInterfaceItem.ID) {
|
||||
interfaceItem.selected = true;
|
||||
interfaceItem.limitSelected = pageDataInterfaceItem.dataRange;
|
||||
pageDataInterfaceItem.positionRuleFields =
|
||||
pageDataInterfaceItem.positionRuleFields || [];
|
||||
interfaceItem.fieldsSelected =
|
||||
pageDataInterfaceItem.positionRuleFields.map(
|
||||
(fieldItem) => fieldItem.ID
|
||||
);
|
||||
interfaceItem.positionRuleFields.map((fieldItem) => {
|
||||
pageDataInterfaceItem.positionRuleFields.map(
|
||||
(pageDataFieldItem) => {
|
||||
if (fieldItem.ID === pageDataFieldItem.ID) {
|
||||
fieldItem.index = pageDataFieldItem.index || 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleEditFieldSort = (rowIdx, listIdx) => {
|
||||
state.dialogData.clickData = state.listData[rowIdx];
|
||||
state.dialogData.ID = state.listData[rowIdx].interfaceList[listIdx].ID;
|
||||
state.dialogData.rowIdx = rowIdx;
|
||||
state.dialogData.listIdx = listIdx;
|
||||
let fieldsSelected =
|
||||
state.listData[rowIdx].interfaceList[listIdx].fieldsSelected;
|
||||
let fieldList = [];
|
||||
state.listData[rowIdx].interfaceList[listIdx].positionRuleFields.map(
|
||||
(item) => {
|
||||
if (fieldsSelected.includes(item.ID)) {
|
||||
fieldList.push({
|
||||
ID: item.ID,
|
||||
field: item.fieldKey,
|
||||
fieldCnName: item.fieldCnName,
|
||||
index: item.index || 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
fieldList.sort((a, b) => {
|
||||
return a.index - b.index;
|
||||
});
|
||||
state.dialogData.fieldList = fieldList;
|
||||
state.dialogFieldSort = true;
|
||||
};
|
||||
const handleDialogSortSave = () => {
|
||||
let rowIdx = state.dialogData.rowIdx;
|
||||
let listIdx = state.dialogData.listIdx;
|
||||
|
||||
state.dialogData.fieldList.forEach((item, idx) => {
|
||||
item.index = idx + 1;
|
||||
});
|
||||
|
||||
state.listData[rowIdx].interfaceList[listIdx].positionRuleFields.map(
|
||||
(item) => {
|
||||
state.dialogData.fieldList.map((fieldItem) => {
|
||||
if (item.ID === fieldItem.ID) {
|
||||
item.index = fieldItem.index;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
state.dialogFieldSort = false;
|
||||
};
|
||||
const handleDialogSortBack = () => {
|
||||
state.dialogFieldSort = false;
|
||||
};
|
||||
|
||||
const handleTempClick = (temp) => {
|
||||
state.selectTemplate = temp.name;
|
||||
getRulesData(temp);
|
||||
};
|
||||
const handleEditSelectTemp = () => {
|
||||
state.showTempRuleListData = !state.showTempRuleListData;
|
||||
};
|
||||
const handleApproval = (approvalData) => {
|
||||
state.approvalData = approvalData;
|
||||
state.dialogApproval = false;
|
||||
};
|
||||
const handleChangeSelfLimitData = (rowIdx, listIdx, list) => {
|
||||
state.dialogLimitData = {
|
||||
rowIdx,
|
||||
listIdx,
|
||||
};
|
||||
state.dataDepartmentIds = list.dataDepartmentIds;
|
||||
state.dialogSelfLimtiData = true;
|
||||
};
|
||||
const handleLimitData = (data) => {
|
||||
let rowIdx = state.dialogLimitData.rowIdx;
|
||||
let listIdx = state.dialogLimitData.listIdx;
|
||||
state.listData[rowIdx].interfaceList[listIdx].dataDepartmentIds = data;
|
||||
state.dialogSelfLimtiData = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-card-header {
|
||||
}
|
||||
</style>
|
628
src/views/manage/posimanage/editAuth.vue
Normal file
628
src/views/manage/posimanage/editAuth.vue
Normal file
@ -0,0 +1,628 @@
|
||||
<template>
|
||||
<div ref="posimanageeditAuth"></div>
|
||||
<n-modal v-model:show="state.showModal" :mask-closable="false">
|
||||
<n-card
|
||||
style="width: 80vw"
|
||||
:bordered="false"
|
||||
class="create-contianer"
|
||||
size="huge"
|
||||
width="1200px"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header>
|
||||
<div class="title-visible">{{ props.title }}</div>
|
||||
</template>
|
||||
<template #header-extra>
|
||||
<img
|
||||
src="@/assets/image/icon/close-tabs.png"
|
||||
class="close-icon"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div class="center-content">
|
||||
<div class="left-box">
|
||||
<div class="title">基础设置</div>
|
||||
<div class="content" ref="modalSelect">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
name="custom-validation"
|
||||
:model="formState"
|
||||
:rules="rules"
|
||||
v-bind="layout"
|
||||
:hideRequiredMark="true"
|
||||
>
|
||||
<a-form-item label="权限类型" name="type">
|
||||
<a-radio-group
|
||||
v-model:value="formState.type"
|
||||
@change="changeType"
|
||||
name="radioGroup"
|
||||
>
|
||||
<a-radio :value="'menu'">菜单权限</a-radio>
|
||||
<a-radio :value="'button'">按钮权限</a-radio>
|
||||
<a-radio :value="'interface'">列表字段权限</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="菜单管理"
|
||||
name="menuType"
|
||||
v-if="formState.type == 'menu'"
|
||||
>
|
||||
<a-radio-group
|
||||
v-model:value="formState.menuType"
|
||||
name="radioGroup"
|
||||
>
|
||||
<a-radio :value="'1'">父级菜单</a-radio>
|
||||
<a-radio :value="'2'">子级菜单</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="上级菜单"
|
||||
name="pid"
|
||||
v-if="formState.menuType == '2'"
|
||||
>
|
||||
<n-select
|
||||
v-model:value="formState.pid"
|
||||
:options="state.menuList"
|
||||
label-field="Title"
|
||||
value-field="ID"
|
||||
filterable
|
||||
/>
|
||||
<!-- <a-select ref="select"
|
||||
v-model:value="formState.pid"
|
||||
style="width: 200px">
|
||||
<a-select-option v-for="(item,index) in state.menuList"
|
||||
:key="index"
|
||||
:value="item.ID">{{item.Title}}</a-select-option>
|
||||
</a-select> -->
|
||||
</a-form-item>
|
||||
<a-form-item label="权限名称" name="title">
|
||||
<n-input
|
||||
v-model:value="formState.title"
|
||||
autocomplete="off"
|
||||
maxLength="12"
|
||||
placeholder="最长输入12个字符"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="上级权限"
|
||||
name="pid"
|
||||
v-if="formState.type !== 'menu'"
|
||||
>
|
||||
<n-tree-select
|
||||
filterable
|
||||
v-model:value="formState.pid"
|
||||
:options="state.menuTreeList"
|
||||
@update:value="handleUpdatemenuTreeValue"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由/按钮名/接口名" name="url">
|
||||
<n-input
|
||||
v-model:value="formState.url"
|
||||
autocomplete="off"
|
||||
:placeholder="
|
||||
formState.type == 'menu' ? '不做路由跳转可为空' : ''
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="图标"
|
||||
name="icon"
|
||||
v-if="formState.type == 'menu' && formState.menuType == '1'"
|
||||
>
|
||||
<Upload
|
||||
:maxCount="1"
|
||||
v-model:uploadVal="formState.icon"
|
||||
:zIndex="9000"
|
||||
class="upload"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="选中图标"
|
||||
name="grayIcon"
|
||||
v-if="formState.type == 'menu' && formState.menuType == '1'"
|
||||
>
|
||||
<Upload
|
||||
:maxCount="1"
|
||||
v-model:uploadVal="formState.grayIcon"
|
||||
:zIndex="9000"
|
||||
class="upload"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="排序"
|
||||
name="weigh"
|
||||
v-if="formState.type == 'menu'"
|
||||
>
|
||||
<n-input-number
|
||||
:show-button="false"
|
||||
v-model:value="formState.weigh"
|
||||
autocomplete="off"
|
||||
placeholder="1-100,越大排序越靠前"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div v-show="formState.type == 'interface'" class="row">
|
||||
<div class="col-4 text-right">
|
||||
列表字段<span style="margin: 0 8px 0 2px">:</span>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div
|
||||
v-for="(item, index) in formState.ruleFields"
|
||||
:key="index"
|
||||
style="margin-bottom: 10px"
|
||||
>
|
||||
<div class="flex">
|
||||
<n-input
|
||||
v-model:value="item.fieldCnName"
|
||||
autocomplete="off"
|
||||
placeholder="列表字段中文名称"
|
||||
style="margin-bottom: 10px; width: 200px"
|
||||
/>
|
||||
<span
|
||||
class="cancel-btn"
|
||||
@click="cancelRule(index)"
|
||||
v-if="formState.ruleFields.length > 1"
|
||||
>移除</span
|
||||
>
|
||||
</div>
|
||||
<n-input
|
||||
type="textarea"
|
||||
v-model:value="item.fieldKey"
|
||||
placeholder="列表字段对应value值"
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 row"></div>
|
||||
<div class="col-8 row">
|
||||
<a-button @click="addInput" style="width: 120px">
|
||||
<PlusOutlined />
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="row justify-end">
|
||||
<div class="footer-content" v-if="state.isEdit">
|
||||
<!-- <a-popconfirm title="是否确认删除该权限,删除后不可恢复?"
|
||||
cancel-text="取消"
|
||||
ok-text="确定"
|
||||
@confirm="handleDel"
|
||||
@cancel="departmentCel">
|
||||
<a-button class="basic-btn">删除该权限</a-button>
|
||||
</a-popconfirm> -->
|
||||
<a-button
|
||||
class="primary-btn"
|
||||
:loading="state.btnLoading"
|
||||
@click="handleUpdate"
|
||||
>完成修改</a-button
|
||||
>
|
||||
</div>
|
||||
<div class="footer-content" v-else>
|
||||
<a-button class="basic-btn" @click="resetForm">清空</a-button>
|
||||
<a-button
|
||||
class="primary-btn"
|
||||
:loading="state.btnLoading"
|
||||
@click="handleCreate"
|
||||
>完成设置</a-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 权限新建弹窗 旧代码
|
||||
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons-vue";
|
||||
import {
|
||||
defineProps,
|
||||
defineEmits,
|
||||
watch,
|
||||
reactive,
|
||||
ref,
|
||||
unref,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
} from "vue";
|
||||
import Upload from "@/components/upload.vue";
|
||||
import { Form, message } from "ant-design-vue";
|
||||
import { processSuccess, processError } from "@/utils/helper/message";
|
||||
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
|
||||
const emits = defineEmits(["submit", "triggerClose"]);
|
||||
let props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
require: true,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
btnLoading: false,
|
||||
menuList: [],
|
||||
menuTreeList: [],
|
||||
isEdit: false,
|
||||
showModal: true,
|
||||
});
|
||||
onMounted(() => {
|
||||
state.showModal = true;
|
||||
getMenu();
|
||||
getMenuTree();
|
||||
if (props.data?.id) {
|
||||
state.isEdit = true;
|
||||
getDetail();
|
||||
} else {
|
||||
state.isEdit = false;
|
||||
formState.dataOpen = true;
|
||||
formState.method = "*";
|
||||
formState.type = "";
|
||||
formState.icon = "";
|
||||
formState.pid = null;
|
||||
formState.title = "";
|
||||
formState.menuType = "";
|
||||
formState.url = "";
|
||||
formState.weigh = "";
|
||||
formState.ruleFields = [{ fieldKey: "", fieldCnName: "" }];
|
||||
}
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
emits("triggerClose");
|
||||
resetForm();
|
||||
state.isEdit = false;
|
||||
};
|
||||
const layout = {
|
||||
labelCol: { span: 8 },
|
||||
wrapperCol: { span: 16 },
|
||||
};
|
||||
const formRef = ref();
|
||||
const formState = reactive({
|
||||
grayIcon: "",
|
||||
dataOpen: true,
|
||||
method: "*",
|
||||
type: "", // 'menu'菜单,'button'按钮组件,'interface'列表字段权限
|
||||
icon: "",
|
||||
pid: null,
|
||||
title: "",
|
||||
menuType: "",
|
||||
url: "",
|
||||
weigh: "",
|
||||
ruleFields: [{ fieldKey: "", fieldCnName: "" }],
|
||||
});
|
||||
const filedJsonV = async (_rule, value) => {
|
||||
// 验空
|
||||
if (
|
||||
formState.type === "interface" &&
|
||||
formState.ruleFields.some((val) => val.fieldKey == "" || val.fieldKey == "")
|
||||
) {
|
||||
return Promise.reject("请补充完整");
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
const urlV = async (_rule, value) => {
|
||||
// 验空
|
||||
if (formState.type === "menu" && formState.menuType === "1") {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
if (value == "") {
|
||||
return Promise.reject("请输入路由/唯一标识");
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
const rules = {
|
||||
type: [{ required: true, message: "请选择权限类型", trigger: "change" }],
|
||||
icon: [{ required: true, message: "请上传Icon", trigger: "change" }],
|
||||
grayIcon: [{ required: true, message: "请上传选中Icon", trigger: "change" }],
|
||||
menuType: [{ required: true, message: "请选择菜单等级", trigger: "change" }],
|
||||
pid: [
|
||||
{ required: true, message: "请选择上级权限/上级菜单", trigger: "change" },
|
||||
],
|
||||
title: [{ required: true, message: "请输入权限名称", trigger: "change" }],
|
||||
url: [{ required: true, validator: urlV, trigger: "change" }], // message: '请输入路由/唯一标识',
|
||||
weigh: [{ required: true, message: "请输入排序", trigger: "change" }],
|
||||
ruleFields: [
|
||||
{
|
||||
required: true,
|
||||
validator: filedJsonV,
|
||||
trigger: ["blur, change"],
|
||||
type: "array",
|
||||
},
|
||||
],
|
||||
};
|
||||
const resetForm = () => {
|
||||
formRef.value.resetFields();
|
||||
};
|
||||
const addInput = () => {
|
||||
formState.ruleFields.push({ fieldKey: "", fieldCnName: "" });
|
||||
};
|
||||
const cancelRule = (index) => {
|
||||
formState.ruleFields.splice(index, 1);
|
||||
};
|
||||
const changeType = () => {
|
||||
formState.dataOpen = true;
|
||||
formState.method = "*";
|
||||
// formState.type = ''
|
||||
formState.icon = "";
|
||||
formState.pid = null;
|
||||
formState.title = "";
|
||||
formState.url = "";
|
||||
formState.weigh = "";
|
||||
formState.menuType = "";
|
||||
formState.ruleFields = [{ fieldKey: "", fieldCnName: "" }];
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
formState.pid = formState.pid ? Number(formState.pid) : 0;
|
||||
formState.weigh = formState.weigh ? Number(formState.weigh) : 1;
|
||||
|
||||
state.btnLoading = true;
|
||||
let url = "/rule/create";
|
||||
let params = formState;
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
processSuccess("添加成功");
|
||||
handleClose();
|
||||
emits("triggerClose");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
state.btnLoading = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch((error) => {});
|
||||
};
|
||||
const handleDel = () => {
|
||||
$request.HTTP.permission
|
||||
.ruleRemove({ ID: formState.ID })
|
||||
.then((res) => {
|
||||
if (res.status == 0) {
|
||||
message.success({
|
||||
content: "删除成功",
|
||||
duration: 2,
|
||||
onClose() {
|
||||
handleClose();
|
||||
emits("triggerClose");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.response?.data?.msg || "操作失败,请稍后再试");
|
||||
});
|
||||
};
|
||||
const departmentCel = () => {};
|
||||
const handleUpdate = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
formState.pid = formState.pid ? Number(formState.pid) : 0;
|
||||
formState.weigh = formState.weigh ? Number(formState.weigh) : 1;
|
||||
|
||||
state.btnLoading = true;
|
||||
let url = "/rule/v2/update";
|
||||
let params = formState;
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
message.success({
|
||||
content: "修改成功",
|
||||
duration: 2,
|
||||
onClose() {
|
||||
handleClose();
|
||||
emits("triggerClose");
|
||||
},
|
||||
});
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
state.btnLoading = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const getMenu = () => {
|
||||
$request.HTTP.permission
|
||||
.menuFList()
|
||||
.then((res) => {
|
||||
if (res.status == 0) {
|
||||
state.menuList = res.data.data;
|
||||
} else {
|
||||
message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.response?.data?.msg || "操作失败,请稍后再试");
|
||||
});
|
||||
};
|
||||
|
||||
const getMenuTree = () => {
|
||||
$request.HTTP.permission
|
||||
.menuSList()
|
||||
.then((res) => {
|
||||
if (res.status == 0) {
|
||||
state.menuTreeList = res.data.data.map((item) => {
|
||||
let obj = {
|
||||
label: item.Title,
|
||||
key: item.ID,
|
||||
children: [],
|
||||
};
|
||||
if (item.Son) {
|
||||
obj.children = item.Son.map((i) => {
|
||||
return {
|
||||
key: i.ID,
|
||||
label: i.Title,
|
||||
};
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
} else {
|
||||
message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.response?.data?.msg || "操作失败,请稍后再试");
|
||||
});
|
||||
};
|
||||
const handleUpdatemenuTreeValue = (val) => {
|
||||
formState.pid = val;
|
||||
};
|
||||
const getDetail = () => {
|
||||
let url = "/rule/v2/detail";
|
||||
let params = {
|
||||
ID: props.data.id,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
formState.ID = res.data.ID;
|
||||
formState.type = res.data.type;
|
||||
formState.title = res.data.title;
|
||||
formState.url = res.data.url;
|
||||
if (res.data.type == "button") {
|
||||
formState.pid = res.data.pid;
|
||||
}
|
||||
if (res.data.type == "menu") {
|
||||
formState.icon = res.data.icon;
|
||||
formState.grayIcon = res.data.grayIcon;
|
||||
formState.weigh = res.data.weigh;
|
||||
formState.pid = res.data.pid;
|
||||
formState.menuType = res.data.menuType;
|
||||
}
|
||||
if (res.data.type == "interface") {
|
||||
formState.pid = res.data.pid;
|
||||
formState.ruleFields = res.data.ruleFields;
|
||||
}
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title-visible {
|
||||
text-align: center;
|
||||
color: $theme-primary;
|
||||
font-size: 22px;
|
||||
}
|
||||
:deep(.ant-modal-body) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
.create-contianer {
|
||||
.ant-input {
|
||||
color: #6d5c9c;
|
||||
}
|
||||
.center-content {
|
||||
height: 65vh;
|
||||
display: flex;
|
||||
.title {
|
||||
width: fit-content;
|
||||
padding-left: 10px;
|
||||
border-left: 4px solid $theme-primary;
|
||||
color: $text-theme;
|
||||
margin: 19px 0;
|
||||
}
|
||||
|
||||
.left-box {
|
||||
width: 55%;
|
||||
padding-right: 30px;
|
||||
.content {
|
||||
height: 54vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
div {
|
||||
color: $text-theme;
|
||||
}
|
||||
:deep(.ant-form-item-label > label) {
|
||||
color: $text-theme;
|
||||
}
|
||||
.ant-form-item input[type="text"],
|
||||
input,
|
||||
textarea {
|
||||
background: #e3dfea;
|
||||
}
|
||||
|
||||
.ant-select :deep(.ant-select-selector) {
|
||||
background: #e3dfea;
|
||||
}
|
||||
.control-item {
|
||||
:deep(.ant-form-item-control) {
|
||||
margin-left: 0px !important;
|
||||
}
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.cancel-btn {
|
||||
color: #999999;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: $text-theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.upload {
|
||||
:deep(.ant-image) {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
:deep(.ant-image-img) {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
.footer-content {
|
||||
margin: 10px 0;
|
||||
button {
|
||||
width: 130px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
padding: 0;
|
||||
margin: 0 10px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
674
src/views/manage/posimanage/index.vue
Normal file
674
src/views/manage/posimanage/index.vue
Normal file
@ -0,0 +1,674 @@
|
||||
<template>
|
||||
<div class="row" style="padding: 35px">
|
||||
<div
|
||||
class="col-3 fl-pa-md"
|
||||
style="
|
||||
background: #fff;
|
||||
box-shadow: rgba(188, 188, 188, 0.18) 0px 3px 6px 1px;
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="row font-16"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5"
|
||||
>
|
||||
<div class="fl-py-sm" style="border-bottom: 4px solid #764cf6">
|
||||
组织架构
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="height: 76vh; overflow: auto">
|
||||
<fl-tree
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fl-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-9 fl-px-md">
|
||||
<div style="background: #fff" class="fl-pa-md">
|
||||
<div
|
||||
class="col-12 row font-18"
|
||||
style="
|
||||
color: #764cf6;
|
||||
border-bottom: 1px solid #c1b2e5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<div class="fl-py-sm" style="border-bottom: 4px solid #764cf6">
|
||||
岗位管理
|
||||
</div>
|
||||
<div>
|
||||
<n-button @click="goToOrgManage" v-permission="'org-manage-btn'">
|
||||
组织管理
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row fl-mt-sm">
|
||||
<fln-table
|
||||
:config="state.tableConfig"
|
||||
:fatherData="state.treeSelectData"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
@triggerRowActions="handleRowActions"
|
||||
@triggerTableBatchActions="handleTableBatchActions"
|
||||
>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #39109c; color: #fff"
|
||||
@click="handlePermissMgm"
|
||||
>权限列表</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-modal
|
||||
v-model:show="state.dialogDel"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
删除
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div
|
||||
v-if="state.dialogData.type === 'one'"
|
||||
class="font-20"
|
||||
style="color: #6d5c9c; margin: 80px 0"
|
||||
>
|
||||
确定删除该岗位吗?
|
||||
</div>
|
||||
<div
|
||||
v-if="state.dialogData.type === 'batch'"
|
||||
class="font-20"
|
||||
style="color: #6d5c9c; margin: 80px 0"
|
||||
>
|
||||
确定删除选中的岗位吗?
|
||||
</div>
|
||||
<div class="col-12 row justify-center">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogBack"
|
||||
>返回</n-button
|
||||
>
|
||||
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #46299dff; color: #fff"
|
||||
@click="handleDialogSave"
|
||||
>确定</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
|
||||
<fl-posi-permiss-mgm
|
||||
v-if="state.dialogPermissMgm"
|
||||
@triggerClose="state.dialogPermissMgm = false"
|
||||
/>
|
||||
|
||||
<orgManage
|
||||
:orgVisible="state.org.orgVisible"
|
||||
:title="'组织管理'"
|
||||
@orgHandleOk="orgHandleOk"
|
||||
></orgManage>
|
||||
|
||||
<fl-posi-dialog-create
|
||||
v-if="state.dialogPosiCreate"
|
||||
:fatherData="state.treeSelectData"
|
||||
@triggerClose="handleDialogCreateClose"
|
||||
></fl-posi-dialog-create>
|
||||
|
||||
<n-modal
|
||||
v-model:show="state.dialogLinkerNum"
|
||||
style="width: 800px"
|
||||
:mask-closable="true"
|
||||
preset="card"
|
||||
>
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<fln-table
|
||||
:config="state.linkerNumConfig"
|
||||
:fatherData="state.dialogData"
|
||||
:refreshCount="state.linkerNumConfig.refreshCount"
|
||||
>
|
||||
</fln-table>
|
||||
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="state.dialogLinkerNum = false"
|
||||
>关闭</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 岗位
|
||||
import flPosiPermissMgm from "./permissionDialog.vue";
|
||||
import { NButton } from "naive-ui";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
h,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
import login from "@/api/login";
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flTree from "@/components/flnlayout/tree/flnindex.vue";
|
||||
import flPosiDialogCreate from "./dialogCreate.vue";
|
||||
|
||||
import { Local } from "@/utils/storage.js";
|
||||
import orgManage from "./orgManage.vue";
|
||||
const router = useRouter();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const title = ref("");
|
||||
const state = reactive({
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
treeSelectData: {},
|
||||
dialogPermissMgm: false,
|
||||
dialogDel: false,
|
||||
dialogData: {},
|
||||
btnLoading: false,
|
||||
selectedRows: [],
|
||||
title: "",
|
||||
org: {
|
||||
orgVisible: false,
|
||||
},
|
||||
dialogLinkerNum: false,
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
rowKey: "ID",
|
||||
hasSelection: true,
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/position/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "departmentID",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
searchConfig: [
|
||||
{
|
||||
type: "text",
|
||||
label: "岗位ID",
|
||||
field: "ID",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "岗位名",
|
||||
field: "name",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "页面权限",
|
||||
field: "menuAuth",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "操作人",
|
||||
field: "operatorName",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "rangdate",
|
||||
label: "最近一次更新时间",
|
||||
field: ["updateStartAt", "updateEndAt"],
|
||||
class: "col-4",
|
||||
placeholder: ["开始", "结束"],
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
title: "岗位ID",
|
||||
field: "ID",
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
title: "岗位名",
|
||||
field: "name",
|
||||
type: "sfBgColor",
|
||||
bgConfig: {
|
||||
bgColorField: "color",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
// {
|
||||
// width: 120,
|
||||
// type: "select",
|
||||
// title: "部门负责人",
|
||||
// field: "",
|
||||
// config: {
|
||||
// options: [
|
||||
// {
|
||||
// label: "是",
|
||||
// value: 1,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
{
|
||||
width: 120,
|
||||
type: "tooltip",
|
||||
title: "岗位描述",
|
||||
field: "remark",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: "页面权限",
|
||||
field: "menuAuths",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
showLength: 3,
|
||||
bgColor: "#fff",
|
||||
borderRadius: "14px",
|
||||
style: "border-radius: 14px",
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
sorter: true,
|
||||
title: "被关联人数",
|
||||
field: "linkerNum",
|
||||
render(row) {
|
||||
return h(
|
||||
"div",
|
||||
{
|
||||
style: "color: #409eff; cursor: pointer",
|
||||
onClick: () => {
|
||||
handleLinkerNum(row);
|
||||
},
|
||||
},
|
||||
row.linkerNum
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
title: "操作人",
|
||||
field: "operatorName",
|
||||
},
|
||||
{
|
||||
width: 160,
|
||||
title: "最近一次更新时间",
|
||||
field: "updatedAt",
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
title: "操作",
|
||||
fixed: "right",
|
||||
actions: [
|
||||
{
|
||||
actionType: "act_view",
|
||||
type: "label",
|
||||
label: "查看",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
{
|
||||
permission: "r_manage_posimanage_btn_edit",
|
||||
actionType: "act_edit",
|
||||
type: "label",
|
||||
label: "修改",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
{
|
||||
permission: "r_manage_posimanage_btn_delete",
|
||||
actionType: "act_del",
|
||||
type: "label",
|
||||
label: "删除",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
tableBatchActions: [
|
||||
{
|
||||
permission: "r_manage_posimanage_btn_create",
|
||||
notLimitSelect: true,
|
||||
style: "background:#F7EFFFFF;color:#8352B4FF",
|
||||
actionType: "posi-direct-create",
|
||||
type: "primary",
|
||||
label: "新建岗位",
|
||||
},
|
||||
{
|
||||
permission: "r_manage_posimanage_btn_create",
|
||||
notLimitSelect: true,
|
||||
style: "background:#F7EFFFFF;color:#8352B4FF",
|
||||
actionType: "posi-create",
|
||||
type: "primary",
|
||||
label: "模版新建岗位",
|
||||
},
|
||||
],
|
||||
},
|
||||
linkerNumConfig: {
|
||||
tableScrollWitdh: 300,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/user/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "positionId",
|
||||
field: "ID",
|
||||
},
|
||||
],
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: "员工工号",
|
||||
field: "jobNum",
|
||||
},
|
||||
{
|
||||
title: "姓名",
|
||||
field: "nickName",
|
||||
},
|
||||
{
|
||||
title: "手机",
|
||||
field: "telNum",
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
title: "状态",
|
||||
field: "status",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
label: "有效",
|
||||
value: "notactive",
|
||||
},
|
||||
{
|
||||
label: "无效",
|
||||
value: "left",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
dialogPosiCreate: false,
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
getTreeData();
|
||||
});
|
||||
onMounted(() => {});
|
||||
|
||||
const getTreeData = () => {
|
||||
let url = "/department/v2/tree/filter";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
// 获取最近点击的部门
|
||||
let localSelect = Local.get("posimanage_treeSelectData");
|
||||
if (localSelect && JSON.stringify(localSelect) !== "{}") {
|
||||
state.treeSelectData = localSelect;
|
||||
state.expandedKeys = localSelect.pathIds;
|
||||
state.clickKey = localSelect.key;
|
||||
} else {
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
if (
|
||||
state.clickKey === data[0].key &&
|
||||
!state.expandedKeys.includes(data[0].key)
|
||||
) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
if (!state.expandedKeys.includes(state.clickKey)) {
|
||||
state.expandedKeys.push(state.clickKey);
|
||||
}
|
||||
}
|
||||
state.treeRefreshCount++;
|
||||
state.tableConfig.refreshCount++;
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.label = item.name;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
state.tableConfig.refreshCount++;
|
||||
Local.set("posimanage_treeSelectData", state.treeSelectData);
|
||||
};
|
||||
|
||||
const handleRowActions = ({ btnConfig, rowData }) => {
|
||||
state.dialogData = rowData;
|
||||
Local.set("posimanage_treeSelectData", state.treeSelectData);
|
||||
if (btnConfig.actionType === "act_del") {
|
||||
state.dialogData.type = "one";
|
||||
state.dialogDel = true;
|
||||
}
|
||||
if (btnConfig.actionType === "act_edit") {
|
||||
router.push(
|
||||
`/posimanage/create?departmentID=${state.clickKey}&&ID=${rowData.ID}&&type=edit`
|
||||
);
|
||||
}
|
||||
if (btnConfig.actionType === "act_view") {
|
||||
router.push(
|
||||
`/posimanage/create?departmentID=${state.clickKey}&&ID=${rowData.ID}&&type=view`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTableBatchActions = ({ selectedRows, btnConfig, fatherData }) => {
|
||||
state.selectedRows = selectedRows;
|
||||
if (btnConfig.actionType === "posi-create") {
|
||||
Local.set("posimanage_treeSelectData", state.treeSelectData);
|
||||
state.dialogPosiCreate = true;
|
||||
// router.push(
|
||||
// `/posimanage/create?departmentID=${state.clickKey}&&type=create`
|
||||
// );
|
||||
}
|
||||
if (btnConfig.actionType === "posi-delete-bat") {
|
||||
handleDeleteBatch();
|
||||
}
|
||||
if (btnConfig.actionType === "posi-direct-create") {
|
||||
router.push(
|
||||
`/posimanage/create?departmentID=${
|
||||
Local.get("posimanage_treeSelectData").key
|
||||
}&&type=create`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteBatch = () => {
|
||||
state.dialogData.type = "batch";
|
||||
state.dialogDel = true;
|
||||
};
|
||||
const handleDeleteBatchSave = () => {
|
||||
state.btnLoading = true;
|
||||
let url = "/position/v2/batch/remove";
|
||||
let params = {
|
||||
IDs: state.selectedRows.map((item) => item.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.selectedRows = [];
|
||||
state.tableConfig.refreshCount++;
|
||||
processSuccess("操作成功!");
|
||||
state.dialogDel = false;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// 单个删除
|
||||
const handleDialogSave = () => {
|
||||
if (state.dialogData.type === "batch") {
|
||||
handleDeleteBatchSave();
|
||||
return;
|
||||
}
|
||||
state.btnLoading = true;
|
||||
let url = "/position/v2/batch/remove";
|
||||
let params = {
|
||||
IDs: [state.dialogData.ID],
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.tableConfig.refreshCount++;
|
||||
state.dialogDel = false;
|
||||
processSuccess("操作成功!");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.btnLoading = false;
|
||||
state.dialogDel = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogDel = false;
|
||||
state.btnLoading = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleDialogBack = () => {
|
||||
state.dialogDel = false;
|
||||
};
|
||||
const handlePermissMgm = () => {
|
||||
state.dialogPermissMgm = true;
|
||||
};
|
||||
|
||||
// 组织管理
|
||||
const goToOrgManage = () => {
|
||||
state.org = { orgVisible: true };
|
||||
};
|
||||
const orgHandleOk = () => {
|
||||
state.org.orgVisible = false;
|
||||
getTreeData();
|
||||
};
|
||||
const handleDialogCreateClose = () => {
|
||||
state.dialogPosiCreate = false;
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
const handleLinkerNum = (row) => {
|
||||
console.log(row);
|
||||
state.dialogData = row;
|
||||
state.dialogLinkerNum = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
134
src/views/manage/posimanage/limitData.vue
Normal file
134
src/views/manage/posimanage/limitData.vue
Normal file
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<n-modal v-model:show="state.dialogModal"
|
||||
:mask-closable="false">
|
||||
<n-card style="width: 600px"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<template #header>
|
||||
<div class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2">
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225">
|
||||
自定义数据范围权限
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #header-extra>
|
||||
<n-button @click="handleLimitData"
|
||||
text
|
||||
style="font-size: 24px">
|
||||
<n-icon>
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
<div class="row justify-center fl-pa-md"
|
||||
style="color: #333639">
|
||||
<div class="col-12 text-center font-14"
|
||||
style="color: #333639">
|
||||
被勾选的部门相关权限可被查看
|
||||
</div>
|
||||
<n-tree block-line
|
||||
checkable
|
||||
:selectable="false"
|
||||
:data="state.treeData"
|
||||
:default-checked-keys="props.fatherData"
|
||||
@update:checked-keys="updateCheckedKeys" />
|
||||
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleLimitData">返回</n-button>
|
||||
|
||||
<n-button tertiary
|
||||
style="width: 161px; background: #46299dff; color: #fff"
|
||||
@click="handleLimitData('submit')">保存</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 权限 自定义数据范围
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
import { Close as CloseIcon } from "@vicons/ionicons5";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
let props = defineProps({
|
||||
fatherData: {
|
||||
type: Array,
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
treeData: [],
|
||||
dialogModal: true,
|
||||
selectedKeys: []
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
state.dialogModal = true;
|
||||
getTreeData()
|
||||
if (props.fatherData) {
|
||||
state.selectedKeys = props.fatherData;
|
||||
}
|
||||
});
|
||||
const getTreeData = () => {
|
||||
let url = "/department/v2/tree/my";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.label = item.name;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
const emits = defineEmits(["triggerLimitData"]);
|
||||
const handleLimitData = (type) => {
|
||||
if (type === 'submit') {
|
||||
emits("triggerLimitData", state.selectedKeys);
|
||||
} else {
|
||||
emits("triggerLimitData", []);
|
||||
}
|
||||
};
|
||||
|
||||
const updateCheckedKeys = (keys) => {
|
||||
state.selectedKeys = keys;
|
||||
};
|
||||
</script>
|
576
src/views/manage/posimanage/orgManage.vue
Normal file
576
src/views/manage/posimanage/orgManage.vue
Normal file
@ -0,0 +1,576 @@
|
||||
<template>
|
||||
<div class="org-dialog">
|
||||
<n-modal
|
||||
class="org-dialog"
|
||||
v-model:show="props.orgVisible"
|
||||
:z-index="1999"
|
||||
:mask-closable="true"
|
||||
style="min-width: 1000px; max-width: 1200px"
|
||||
:to="app"
|
||||
>
|
||||
<n-card
|
||||
:bordered="false"
|
||||
:title="props.title"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header-extra>
|
||||
<div class="close-box">
|
||||
<n-button @click="closeFn" text style="font-size: 24px">
|
||||
<n-icon>
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
<div class="item-box">
|
||||
<fl-tree
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
:config="{
|
||||
actions: ['edit', 'add', 'subtraction', 'move'],
|
||||
moveShow: '[%=level%]>0',
|
||||
subtractionShow: '[%=level%]>0',
|
||||
}"
|
||||
@triggerTreeAction="handleTreeAction"
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fl-tree>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div
|
||||
style="text-align: center; padding-left: 20px; padding-right: 10px"
|
||||
>
|
||||
<n-button class="button-box" @click="closeFn"> 返回 </n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal
|
||||
v-model:show="state.dialogAddTree"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
添加组织
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div class="fl-my-lg font-20">
|
||||
<n-input
|
||||
v-model:value="state.dialogAddTreeData"
|
||||
placeholder="请输入组织名称"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12 row justify-center">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #764cf6; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogAddTreeSave"
|
||||
>保存</n-button
|
||||
>
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #eeeaf7; color: #774ff6"
|
||||
@click="state.dialogAddTree = false"
|
||||
>返回</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
<n-modal
|
||||
v-model:show="state.dialogSubtractionTree"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
删除组织
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div class="font-20" style="margin: 115px 0">
|
||||
是否确认删除该组织及其子组织(如有)?
|
||||
</div>
|
||||
<div class="col-12 row justify-center">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #764cf6; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogSubtractionTreeSave"
|
||||
>确定</n-button
|
||||
>
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #eeeaf7; color: #774ff6"
|
||||
@click="state.dialogSubtractionTree = false"
|
||||
>返回</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
<n-modal
|
||||
v-model:show="state.dialogMoveTree"
|
||||
style="width: 600px"
|
||||
:mask-closable="false"
|
||||
preset="card"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
移动
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-pa-md">
|
||||
<div class="fl-my-lg font-20">
|
||||
<div class="font-16 fl-mb-md">请选择部门节点</div>
|
||||
<fln-form-item
|
||||
type="select"
|
||||
:val="null"
|
||||
:fatherData="state.dialogData"
|
||||
:config="{
|
||||
placeholder: '选择归属组织',
|
||||
config: {
|
||||
optionConfig: {
|
||||
resDataField: 'list',
|
||||
url: '/department/v2/base/all/list',
|
||||
params: [
|
||||
{
|
||||
label: 'notDepartmentIds',
|
||||
field: 'key',
|
||||
type: 'Array',
|
||||
},
|
||||
],
|
||||
labelField: 'name',
|
||||
valueField: 'ID',
|
||||
},
|
||||
},
|
||||
}"
|
||||
:clearable="true"
|
||||
@triggerValChange="handleValChange"
|
||||
/>
|
||||
|
||||
<div class="font-14 fl-mt-sm" style="color: #ef0000">
|
||||
确认后,所选部门及其所有子部门将统一归属于新选定的上级部门之下,且保持原有子部门层级关系不变。
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #eeeaf7; color: #774ff6"
|
||||
class="fl-mr-md"
|
||||
@click="state.dialogMoveTree = false"
|
||||
>取消</n-button
|
||||
>
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #764cf6; color: #fff"
|
||||
@click="handleDialogMoveTreeSave"
|
||||
>确定</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 组织管理
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flTree from "./components/flnindex.vue";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
defineProps,
|
||||
defineEmits,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
} from "vue";
|
||||
import { Close as CloseIcon } from "@vicons/ionicons5";
|
||||
import { Local } from "@/utils/storage.js";
|
||||
import {
|
||||
processError,
|
||||
processSuccess,
|
||||
processWarning,
|
||||
} from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
import flnFormItem from "@/components/flnlayout/form/flnformItem.vue";
|
||||
|
||||
const router = useRouter();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const app = document.getElementById("app");
|
||||
const state = reactive({
|
||||
dialogData: {},
|
||||
dialogMoveTree: false,
|
||||
dialogMoveTreeData: null,
|
||||
departmentOptions: [],
|
||||
dialogAddTree: false,
|
||||
dialogAddTreeData: "",
|
||||
dialogSubtractionTree: false,
|
||||
treeSelectData: {},
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
|
||||
btnLoading: false,
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
requestbysf: true,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "user/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "departmentID",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
const props = defineProps({
|
||||
orgVisible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
require: true,
|
||||
},
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
let treeSelectData = Local.get("orgmanage_treeSelectData");
|
||||
if (treeSelectData) {
|
||||
state.treeSelectData = treeSelectData;
|
||||
state.clickKey = treeSelectData.key;
|
||||
}
|
||||
getTreeData();
|
||||
});
|
||||
onMounted(() => {});
|
||||
|
||||
const getTreeData = () => {
|
||||
let url = "/department/v2/tree/my";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
|
||||
if (
|
||||
state.clickKey === data[0].key &&
|
||||
!state.expandedKeys.includes(data[0].key)
|
||||
) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
if (!state.expandedKeys.includes(state.clickKey)) {
|
||||
state.expandedKeys.push(state.clickKey);
|
||||
}
|
||||
if (data.length === 1 && !state.expandedKeys.includes(data[0].key)) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
state.treeRefreshCount++;
|
||||
state.tableConfig.refreshCount++;
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleCreatePosi = () => {};
|
||||
const handleValChange = ({ val, config, selectOpt }) => {
|
||||
state.dialogMoveTreeData = selectOpt;
|
||||
};
|
||||
const handleTreeAction = ({ type, val }) => {
|
||||
state.dialogData = val;
|
||||
if (type === "add") {
|
||||
state.clickKey = val.key;
|
||||
state.treeSelectData = val;
|
||||
state.tableConfig.refreshCount++;
|
||||
state.dialogAddTreeData = "";
|
||||
state.dialogAddTree = true;
|
||||
}
|
||||
if (type === "move") {
|
||||
state.clickKey = val.key;
|
||||
state.treeSelectData = val;
|
||||
state.tableConfig.refreshCount++;
|
||||
state.dialogMoveTreeData = {};
|
||||
state.dialogMoveTree = true;
|
||||
}
|
||||
if (type === "subtraction") {
|
||||
state.dialogSubtractionTree = true;
|
||||
}
|
||||
if (type === "save") {
|
||||
let url = "/department/v2/update";
|
||||
let params = {
|
||||
name: val.title,
|
||||
ID: val.key,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
getTreeData();
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
}
|
||||
if (type === "cancel") {
|
||||
getTreeData();
|
||||
}
|
||||
};
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
Local.set("orgmanage_treeSelectData", tree);
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
// const handleTreeDefaultClick = () => {
|
||||
// state.treeSelectData = state.treeData[0];
|
||||
// state.clickKey = state.treeData[0].key;
|
||||
// Local.remove("orgmanage_treeSelectData");
|
||||
// state.tableConfig.refreshCount++;
|
||||
// };
|
||||
const handleCloseModal = () => {
|
||||
// emits("triggerCloseModal");
|
||||
};
|
||||
const handleDialogMoveTreeSave = () => {
|
||||
let url = "/department/v2/update";
|
||||
let params = {
|
||||
ID: state.dialogData.key,
|
||||
name: state.dialogData.label,
|
||||
pid: state.dialogMoveTreeData.value,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
if (Array.isArray(res.data.levelPathList)) {
|
||||
state.expandedKeys = [];
|
||||
res.data.levelPathList.forEach((treeid) => {
|
||||
if (!state.expandedKeys.includes(treeid)) {
|
||||
state.expandedKeys.push(treeid);
|
||||
}
|
||||
});
|
||||
}
|
||||
getTreeData();
|
||||
state.dialogMoveTree = false;
|
||||
if (
|
||||
(state.dialogMoveTreeData.level || 0) +
|
||||
1(state.dialogData.sonMaxDepth || 0) +
|
||||
1 >
|
||||
8
|
||||
) {
|
||||
processWarning("组织数已超过8级,建议不要再增加高度");
|
||||
} else {
|
||||
processSuccess("操作成功!");
|
||||
}
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.dialogAddTree = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogAddTree = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
// 树 新增
|
||||
const handleDialogAddTreeSave = () => {
|
||||
let url = "/department/v2/create";
|
||||
let params = {
|
||||
name: state.dialogAddTreeData,
|
||||
pid: state.dialogData.key,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
if (Array.isArray(res.data.levelPathList)) {
|
||||
state.expandedKeys = [];
|
||||
res.data.levelPathList.forEach((treeid) => {
|
||||
if (!state.expandedKeys.includes(treeid)) {
|
||||
state.expandedKeys.push(treeid);
|
||||
}
|
||||
});
|
||||
}
|
||||
getTreeData();
|
||||
state.dialogAddTree = false;
|
||||
if (state.dialogData.level > 7) {
|
||||
processWarning("组织数已超过8级,建议不要再增加高度");
|
||||
}
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.dialogAddTree = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogAddTree = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
// 树 减
|
||||
const handleDialogSubtractionTreeSave = () => {
|
||||
let url = "/department/v2/remove";
|
||||
let params = {
|
||||
ID: state.dialogData.key,
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0) {
|
||||
getTreeData();
|
||||
state.dialogSubtractionTree = false;
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
state.dialogSubtractionTree = false;
|
||||
},
|
||||
() => {
|
||||
state.dialogSubtractionTree = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.label = item.name;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
const emit = defineEmits(["update:orgVisible", "orgHandleOk"]);
|
||||
const closeFn = () => {
|
||||
emit("orgHandleOk");
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.orgVisible,
|
||||
(val) => {
|
||||
let treeSelectData = Local.get("orgmanage_treeSelectData");
|
||||
if (treeSelectData) {
|
||||
state.treeSelectData = treeSelectData;
|
||||
state.clickKey = treeSelectData.key;
|
||||
}
|
||||
getTreeData();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.org-dialog {
|
||||
text-align: center;
|
||||
.naive-modal-body {
|
||||
height: 750px !important;
|
||||
}
|
||||
}
|
||||
.item-box {
|
||||
height: 50vh;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
padding-left: 100px;
|
||||
padding-right: 100px;
|
||||
}
|
||||
.button-box {
|
||||
width: 150px;
|
||||
background-color: rgba(199, 199, 201, 1);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.search-item {
|
||||
:deep(.ant-input-affix-wrapper) {
|
||||
background: #fff;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
:deep(.ant-input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
:deep(.ant-picker) {
|
||||
width: 100%;
|
||||
border-radius: 20px;
|
||||
background: #fff !important;
|
||||
color: #c3c3c3;
|
||||
border: none;
|
||||
}
|
||||
:deep(.ant-picker-input > input) {
|
||||
color: #000;
|
||||
&::placeholder {
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
311
src/views/manage/posimanage/permissionDialog.vue
Normal file
311
src/views/manage/posimanage/permissionDialog.vue
Normal file
@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<n-modal v-model:show="state.dialogModal" :mask-closable="false">
|
||||
<n-card
|
||||
style="width: 80vw"
|
||||
:bordered="false"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header>
|
||||
<div class="col-12 row justify-center relative">
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
权限列表
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #header-extra>
|
||||
<n-button @click="handleDialogBack" text style="font-size: 24px">
|
||||
<n-icon>
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<div class="row justify-center fl-px-md fl-pb-md">
|
||||
<div class="col-12 overflow-auto" style="height: 60vh">
|
||||
<fln-table
|
||||
:config="state.tableConfig"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
:defaultSelectedRows="state.selectedRows"
|
||||
@triggerRowActions="handleRowActions"
|
||||
@triggerSelectCheck="handleTableSelectCheck"
|
||||
>
|
||||
<template #table-header-box>
|
||||
<div class="col-12 row fl-mb-sm justify-between">
|
||||
<div class="row items-center">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #eff3ff; color: #6b69a3"
|
||||
class="fl-mr-md"
|
||||
:disabled="state.selectedRows.length === 0"
|
||||
@click="handleQuickSelectPosi"
|
||||
>快速添加到岗位</n-button
|
||||
>
|
||||
<div v-if="state.selectedRows.length > 0">
|
||||
已选 {{ state.selectedRows.length }} 个
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #9c9ad3; color: #ffffff"
|
||||
@click="handleCreatePermiss"
|
||||
>新增权限</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</fln-table>
|
||||
</div>
|
||||
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogBack"
|
||||
>返回</n-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<Auth
|
||||
v-if="state.showModal"
|
||||
:title="state.modalAuth.title"
|
||||
:data="state.modalAuth.data"
|
||||
@triggerClose="handleCloseEditAuth"
|
||||
/>
|
||||
<fl-permission-bind-posi
|
||||
v-if="state.diallogPosi"
|
||||
:fatherData="state.selectedRows"
|
||||
@triggerClose="handleQuickSelectPosiClose"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
// 权限列表 管理
|
||||
import {
|
||||
UpOutlined,
|
||||
DownOutlined,
|
||||
CloseCircleOutlined,
|
||||
PlusOutlined,
|
||||
DragOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
import { Close as CloseIcon } from "@vicons/ionicons5";
|
||||
import flnFormItem from "@/components/flnlayout/form/flnformItem.vue";
|
||||
import flAboutApproval from "./aboutApproval.vue";
|
||||
import flPermissionBindPosi from "./permissionbindposi.vue";
|
||||
import draggable from "vuedraggable";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
import { ChevronDownOutline, ChevronUpOutline } from "@vicons/ionicons5";
|
||||
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import Auth from "./editAuth.vue";
|
||||
const emits = defineEmits(["triggerClose"]);
|
||||
|
||||
const state = reactive({
|
||||
selectedRows: [],
|
||||
btnLoading: false,
|
||||
dialogModal: true,
|
||||
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
rowKey: "ID",
|
||||
hasSelection: true,
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/rule/list",
|
||||
},
|
||||
searchStyle: "padding-top:0px !important;",
|
||||
searchConfig: [
|
||||
{
|
||||
type: "select",
|
||||
label: "权限类型",
|
||||
field: "Type",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
value: "menu",
|
||||
label: "菜单权限",
|
||||
},
|
||||
{
|
||||
value: "button",
|
||||
label: "按钮权限",
|
||||
},
|
||||
{
|
||||
value: "interface",
|
||||
label: "接口权限",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "权限名称",
|
||||
field: "Title",
|
||||
class: "col-4",
|
||||
placeholder: "",
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
title: "权限编号",
|
||||
field: "ID",
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
title: "权限名",
|
||||
field: "Title",
|
||||
maxWidth: "100%",
|
||||
},
|
||||
{
|
||||
type: "select",
|
||||
title: "权限类型",
|
||||
field: "Type",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
value: "menu",
|
||||
label: "菜单权限",
|
||||
},
|
||||
{
|
||||
value: "button",
|
||||
label: "按钮权限",
|
||||
},
|
||||
{
|
||||
value: "interface",
|
||||
label: "接口权限",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
title: "操作",
|
||||
fixed: "right",
|
||||
actions: [
|
||||
{
|
||||
actionType: "act_edit",
|
||||
type: "label",
|
||||
label: "修改",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
{
|
||||
actionType: "act_del",
|
||||
type: "label",
|
||||
label: "删除",
|
||||
shape: "round",
|
||||
class: "fl-mx-xs fl-mt-xs",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
showModal: false,
|
||||
modalAuth: { title: "新增权限", data: null },
|
||||
diallogPosi: false,
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
state.dialogModal = true;
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
const handleDialogBack = () => {
|
||||
state.dialogField = false;
|
||||
emits("triggerClose");
|
||||
};
|
||||
|
||||
const handleRowActions = ({ btnConfig, rowData }) => {
|
||||
if (btnConfig.actionType === "act_edit") {
|
||||
state.modalAuth = { title: "修改与删除-权限", data: { id: rowData.ID } };
|
||||
state.showModal = true;
|
||||
}
|
||||
if (btnConfig.actionType === "act_del") {
|
||||
$request.HTTP.permission
|
||||
.ruleRemove({ ID: rowData.ID })
|
||||
.then((res) => {
|
||||
if (res.status == 0) {
|
||||
processSuccess("删除成功");
|
||||
state.tableConfig.refreshCount++;
|
||||
} else {
|
||||
processError(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
processError(e.response?.data?.msg || "操作失败");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleTableSelectCheck = (data) => {
|
||||
if (data && Array.isArray(data.selectedRow)) {
|
||||
data.selectedRow = data.selectedRow.filter((item) => {
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (data.action === "check" || data.action === "checkAll") {
|
||||
state.selectedRows = state.selectedRows.concat(data.selectedRow);
|
||||
state.selectedRows = state.selectedRows.filter((item, index, arr) => {
|
||||
return (
|
||||
arr.findIndex((obj) => JSON.stringify(obj) === JSON.stringify(item)) ===
|
||||
index
|
||||
);
|
||||
});
|
||||
}
|
||||
if (data.action === "uncheck") {
|
||||
state.selectedRows = state.selectedRows.filter((item) => {
|
||||
return item.ID !== data.selectedRow.ID;
|
||||
});
|
||||
}
|
||||
if (data.action === "uncheckAll") {
|
||||
state.selectedRows = state.selectedRows.filter((item) => {
|
||||
return (
|
||||
data.selectedRow.findIndex((obj) => {
|
||||
return obj.ID === item.ID;
|
||||
}) === -1
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleCreatePermiss = () => {
|
||||
state.modalAuth = { title: "新增权限", data: null };
|
||||
state.showModal = true;
|
||||
};
|
||||
|
||||
const handleQuickSelectPosi = () => {
|
||||
state.diallogPosi = true;
|
||||
};
|
||||
const handleQuickSelectPosiClose = () => {
|
||||
state.selectedRows = [];
|
||||
state.diallogPosi = false;
|
||||
};
|
||||
const handleCloseEditAuth = (val) => {
|
||||
state.showModal = false;
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
308
src/views/manage/posimanage/permissionbindposi.vue
Normal file
308
src/views/manage/posimanage/permissionbindposi.vue
Normal file
@ -0,0 +1,308 @@
|
||||
<template>
|
||||
<n-modal v-model:show="state.dialogModal" :mask-closable="false">
|
||||
<n-card
|
||||
style="width: 80vw"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
class="col-12 row justify-center relative"
|
||||
style="border-bottom: 1px solid #dfd7f2"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold; color: #1f2225ff">
|
||||
选择需要所选权限的岗位
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #header-extra>
|
||||
<n-button @click="handleDialogBack" text style="font-size: 24px">
|
||||
<n-icon>
|
||||
<close-icon />
|
||||
</n-icon>
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="row justify-center fl-pa-md overflow-auto"
|
||||
style="max-height: 60vh"
|
||||
>
|
||||
<div
|
||||
class="col-3 fl-pa-md row"
|
||||
style="
|
||||
background: #fff;
|
||||
box-shadow: rgba(188, 188, 188, 0.18) 0px 3px 6px 1px;
|
||||
"
|
||||
>
|
||||
<fl-tree
|
||||
:data="state.treeData"
|
||||
:expandedKeys="state.expandedKeys"
|
||||
:refreshCount="state.treeRefreshCount"
|
||||
:clickKey="state.clickKey"
|
||||
@triggerTreeClick="handleTreeClick"
|
||||
></fl-tree>
|
||||
</div>
|
||||
|
||||
<div class="col-9 fl-px-md">
|
||||
<fln-table
|
||||
:config="state.tableConfig"
|
||||
:fatherData="state.treeSelectData"
|
||||
:refreshCount="state.tableConfig.refreshCount"
|
||||
:defaultSelectedRows="state.selectedRows"
|
||||
@triggerRowActions="handleRowActions"
|
||||
@triggerSelectCheck="handleTableSelectCheck"
|
||||
>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row justify-center fl-mt-md">
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #c7c7c9ff; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogBack"
|
||||
>上一步</n-button
|
||||
>
|
||||
<n-button
|
||||
tertiary
|
||||
style="width: 161px; background: #46299d; color: #fff"
|
||||
class="fl-mr-md"
|
||||
@click="handleDialogSave"
|
||||
>保存</n-button
|
||||
>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 岗位
|
||||
import { NButton } from "naive-ui";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
nextTick,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
import login from "@/api/login";
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
import flTree from "@/components/flnlayout/tree/flnindex.vue";
|
||||
import { Close as CloseIcon } from "@vicons/ionicons5";
|
||||
import { Local } from "@/utils/storage.js";
|
||||
const router = useRouter();
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const emits = defineEmits(["triggerClose"]);
|
||||
let props = defineProps({
|
||||
fatherData: {
|
||||
type: Array,
|
||||
},
|
||||
});
|
||||
const state = reactive({
|
||||
clickKey: "",
|
||||
treeRefreshCount: 0,
|
||||
expandedKeys: [],
|
||||
treeData: [],
|
||||
treeSelectData: {},
|
||||
btnLoading: false,
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
rowKey: "ID",
|
||||
hasSelection: true,
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "data",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/position/v2/list",
|
||||
params: [
|
||||
{
|
||||
label: "departmentID",
|
||||
field: "key",
|
||||
},
|
||||
],
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: "岗位ID",
|
||||
field: "ID",
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
title: "岗位名",
|
||||
field: "name",
|
||||
type: "sfBgColor",
|
||||
bgConfig: {
|
||||
bgColorField: "color",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 120,
|
||||
type: "tooltip",
|
||||
title: "岗位描述",
|
||||
field: "remark",
|
||||
},
|
||||
{
|
||||
width: 360,
|
||||
title: "页面权限",
|
||||
field: "menuAuths",
|
||||
type: "tags",
|
||||
tagConfig: {
|
||||
showLength: 3,
|
||||
bgColor: "#fff",
|
||||
borderRadius: "14px",
|
||||
style: "border-radius: 14px",
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 140,
|
||||
sorter: true,
|
||||
title: "被关联人数",
|
||||
field: "linkerNum",
|
||||
},
|
||||
],
|
||||
},
|
||||
dialogModal: true,
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
state.dialogModal = true;
|
||||
getTreeData();
|
||||
});
|
||||
onMounted(() => {});
|
||||
const getTreeData = () => {
|
||||
let url = "/department/v2/tree/my";
|
||||
let params = {};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
if (res.status === 0 && Array.isArray(res.data.nodes)) {
|
||||
let data = res.data.nodes;
|
||||
calcTreeData(data);
|
||||
state.treeData = data;
|
||||
if (JSON.stringify(state.treeSelectData) === "{}") {
|
||||
state.treeSelectData = data[0];
|
||||
state.clickKey = data[0].key;
|
||||
}
|
||||
if (
|
||||
state.clickKey === data[0].key &&
|
||||
!state.expandedKeys.includes(data[0].key)
|
||||
) {
|
||||
state.expandedKeys.push(data[0].key);
|
||||
}
|
||||
if (!state.expandedKeys.includes(state.clickKey)) {
|
||||
state.expandedKeys.push(state.clickKey);
|
||||
}
|
||||
state.treeRefreshCount++;
|
||||
state.tableConfig.refreshCount++;
|
||||
} else {
|
||||
processError(res.msg || "获取失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
},
|
||||
() => {
|
||||
processError("获取失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const calcTreeData = (data) => {
|
||||
for (let item of data) {
|
||||
item.key = item.ID;
|
||||
item.label = item.name;
|
||||
item.title = item.name;
|
||||
if (item.sons) {
|
||||
item.children = item.sons;
|
||||
calcTreeData(item.children);
|
||||
}
|
||||
delete item.ID;
|
||||
delete item.name;
|
||||
delete item.sons;
|
||||
}
|
||||
};
|
||||
const handleTreeClick = ({ selectedKey, tree }) => {
|
||||
state.clickKey = tree.key;
|
||||
state.treeSelectData = tree;
|
||||
state.tableConfig.refreshCount++;
|
||||
};
|
||||
|
||||
const handleRowActions = ({ btnConfig, rowData }) => {
|
||||
Local.set("posimanage_treeSelectData", state.treeSelectData);
|
||||
};
|
||||
|
||||
const handleDialogSave = () => {
|
||||
if (state.selectedRows.length > 50) {
|
||||
return processError("最多选择50个岗位!");
|
||||
}
|
||||
state.btnLoading = true;
|
||||
let url = "/position/v2/batch/bind/auth";
|
||||
let params = {
|
||||
ids: state.selectedRows.map((item) => item.ID),
|
||||
ruleIds: props.fatherData.map((item) => item.ID),
|
||||
};
|
||||
$request.HTTP.components.postDataByParams(url, params).then(
|
||||
(res) => {
|
||||
state.btnLoading = false;
|
||||
if (res.status === 0) {
|
||||
state.tableConfig.refreshCount++;
|
||||
processSuccess("操作成功!");
|
||||
emits("triggerClose");
|
||||
} else {
|
||||
processError(res.msg || "操作失败!");
|
||||
}
|
||||
},
|
||||
() => {
|
||||
processError("操作失败!");
|
||||
},
|
||||
() => {
|
||||
state.btnLoading = false;
|
||||
processError("操作失败!");
|
||||
}
|
||||
);
|
||||
};
|
||||
const handleDialogBack = () => {
|
||||
emits("triggerClose");
|
||||
};
|
||||
const handleTableSelectCheck = (data) => {
|
||||
if (data && Array.isArray(data.selectedRow)) {
|
||||
data.selectedRow = data.selectedRow.filter((item) => {
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (data.action === "check" || data.action === "checkAll") {
|
||||
state.selectedRows = state.selectedRows.concat(data.selectedRow);
|
||||
state.selectedRows = state.selectedRows.filter((item, index, arr) => {
|
||||
return (
|
||||
arr.findIndex((obj) => JSON.stringify(obj) === JSON.stringify(item)) ===
|
||||
index
|
||||
);
|
||||
});
|
||||
}
|
||||
if (data.action === "uncheck") {
|
||||
state.selectedRows = state.selectedRows.filter((item) => {
|
||||
return item.ID !== data.selectedRow.ID;
|
||||
});
|
||||
}
|
||||
if (data.action === "uncheckAll") {
|
||||
state.selectedRows = state.selectedRows.filter((item) => {
|
||||
return (
|
||||
data.selectedRow.findIndex((obj) => {
|
||||
return obj.ID === item.ID;
|
||||
}) === -1
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
149
src/views/manage/posirecordlog/index.vue
Normal file
149
src/views/manage/posirecordlog/index.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="row"
|
||||
style="padding: 35px">
|
||||
<div class="col-12 row">
|
||||
<fln-table :config="state.tableConfig"
|
||||
:refreshCount="state.tableConfig.refreshCount">
|
||||
<template #search-header>
|
||||
<div class="col-12 row font-18"
|
||||
style="color: #764cf6; border-bottom: 1px solid #c1b2e5;">
|
||||
<div class="fl-py-sm"
|
||||
style="border-bottom: 4px solid #764cf6;">
|
||||
岗位操作记录
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</fln-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 岗位
|
||||
import { NButton } from "naive-ui";
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onBeforeMount,
|
||||
onMounted,
|
||||
getCurrentInstance,
|
||||
computed,
|
||||
} from "vue";
|
||||
import { processError, processSuccess } from "@/utils/helper/message";
|
||||
import { useRouter } from "vue-router";
|
||||
import flnTable from "@/components/flnlayout/table/flntable.vue";
|
||||
|
||||
const currentInstance = getCurrentInstance();
|
||||
const { $request } = currentInstance.appContext.config.globalProperties;
|
||||
const title = ref("");
|
||||
const state = reactive({
|
||||
btnLoading: false,
|
||||
selectedRows: [],
|
||||
tableConfig: {
|
||||
tableScrollWitdh: 600,
|
||||
rowKey: "ID",
|
||||
refreshCount: 0,
|
||||
listUrl: {
|
||||
resDataField: "list",
|
||||
pageField: "page",
|
||||
pageSizeField: "pageSize",
|
||||
url: "/position/v2/log/list",
|
||||
},
|
||||
searchConfig: [
|
||||
{
|
||||
type: "select",
|
||||
label: "操作类型",
|
||||
field: "operateType",
|
||||
options: [
|
||||
{
|
||||
label: "新增",
|
||||
value: "add",
|
||||
},
|
||||
{
|
||||
label: "修改",
|
||||
value: "edit",
|
||||
},
|
||||
{
|
||||
label: "删除",
|
||||
value: "del",
|
||||
},
|
||||
{
|
||||
label: "查看",
|
||||
value: "detail",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "操作人",
|
||||
field: "operatorName",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
label: "操作人账号",
|
||||
field: "OperatorTel",
|
||||
placeholder: "",
|
||||
},
|
||||
{
|
||||
type: "rangdate",
|
||||
label: "操作时间",
|
||||
field: ["StartCreatedAt", "EndCreatedAt"],
|
||||
placeholder: ["开始", "结束"],
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
type: 'select',
|
||||
title: "操作类型",
|
||||
field: "operateType",
|
||||
config: {
|
||||
options: [
|
||||
{
|
||||
label: "新增",
|
||||
value: "add",
|
||||
},
|
||||
{
|
||||
label: "修改",
|
||||
value: "edit",
|
||||
},
|
||||
{
|
||||
label: "删除",
|
||||
value: "del",
|
||||
},
|
||||
{
|
||||
label: "查看",
|
||||
value: "detail",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
title: "操作详情",
|
||||
field: "info",
|
||||
},
|
||||
{
|
||||
type: "tooltip",
|
||||
title: "操作人",
|
||||
field: "operatorName",
|
||||
},
|
||||
{
|
||||
title: "操作人账号",
|
||||
field: "OperatorTel",
|
||||
},
|
||||
{
|
||||
title: "操作时间",
|
||||
field: "createdAt",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
onBeforeMount(() => {
|
||||
});
|
||||
onMounted(() => { });
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user