pull/1/head
z 2024-01-06 17:44:23 +08:00
parent b9f0889fed
commit 3ad49a0620
24 changed files with 2048 additions and 75 deletions
src
components
department
department_tree
import_file
layout/header
select_create
views
confined_space/ledger
enterprise_management
department
industry_qualifications
post

View File

@ -115,7 +115,6 @@
* {
box-sizing: border-box;
font-size: 14px;
&:not(dd,dl,dt) {
margin: 0;

View File

@ -78,6 +78,38 @@ export default [
meta: { title: "组织机构", isSubMenu: false },
component: "enterprise_management/department/index",
},
{
path: "/enterprise_management/post",
meta: { title: "岗位管理", isSubMenu: false },
component: "enterprise_management/post/index",
},
{
path: "/enterprise_management/user",
meta: { title: "用户管理", isSubMenu: false },
component: "children",
children: [
{
path: "",
component: "enterprise_management/user/index",
},
{
path: "/enterprise_management/user/add",
meta: {
title: "新增",
activeMenu: "/enterprise_management/user",
},
component: "enterprise_management/user/add",
},
{
path: "/enterprise_management/user/edit",
meta: {
title: "修改",
activeMenu: "/enterprise_management/user",
},
component: "enterprise_management/user/add",
},
],
},
],
},
{

View File

@ -68,6 +68,76 @@ export const layoutFnGetRiskLevel = async () => {
});
return ref(resData.list);
};
// 部门级别
export const layoutFnGetDepartmentLevel = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "4a661fa8aedc4d158c9cddaa9d2ec47e",
});
return ref(resData.list);
};
// 民族
export const layoutFnGetNation = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "0a0e406f27f74ee698fe9979d25f62dd",
});
return ref(resData.list);
};
// 性别
export const layoutFnGetSex = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "21501808bbc344d593fbf9ccfe6c4531",
});
return ref(resData.list);
};
// 政治面貌
export const layoutFnGetPoliticalLandscape = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "6351efdd12dc4730952e5d195718e252",
});
return ref(resData.list);
};
// 文化程度
export const layoutFnGetDegreeOfEducation = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "d7d80f08d73a4accbccf4fd3d8d1d867",
});
return ref(resData.list);
};
// 人员类型
export const layoutFnGetPersonnelType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "0b62f92b0b624aab8e89a77304a64d5e",
});
return ref(resData.list);
};
// 职务
export const layoutFnGetDuties = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "09e36ac01e9540f8bc84eab1c1a78754",
});
return ref(resData.list);
};
// 职称
export const layoutFnGetProfessionalTitle = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "945a6b10e59946078b500f0fbafa8679",
});
return ref(resData.list);
};
// 工种
export const layoutFnGetJobType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "55484e491a5e442d839c4595380713ec",
});
return ref(resData.list);
};
// 在职情况
export const layoutFnGetEmploymentSituation = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "548764b5d4bf4bd7a18ef88274ef49e4",
});
return ref(resData.list);
};
// 部门树
export const layoutFnGetDepartmentTree = async () => {
const resData = await getDepartmentTree();

View File

@ -3,11 +3,12 @@ import { getDataType } from "@/assets/js/utils.js";
/**
* @param api {Function} 接口函数
* @param options {Object?: {callbackFn, otherParams, immediate, usePagination}} 配置项
* @param options.callbackFn {Function?} 回调函数
* @param options {Object?: {callbackFn, otherParams, immediate, usePagination, key}} 配置项
* @param options.callbackFn {Function?} 回调函数返回值为后台返回的所有数据
* @param options.otherParams {Object?} 其它接口参数
* @param options.immediate {Boolean?} 是否立即执行接口函数默认是
* @param options.usePagination {Boolean?} 是否使用分页默认是
* @param options.key {String?} 返回的存放数组的key默认varList
* @return {Object} 返回对象包含以下属性list 表格数据pagination 分页数据searchForm 搜索表单数据tableRef 表格实例fnGetData 获取数据函数fnResetPagination 重置分页函数
*/
@ -19,8 +20,11 @@ export default function useListData(api, options = {}) {
throw new Error("options.immediate必须是一个布尔值");
if (options.usePagination && getDataType(options.usePagination) !== "Boolean")
throw new Error("options.usePagination必须是一个布尔值");
if (options.key && getDataType(options.key) !== "String")
throw new Error("options.key必须是一个字符串");
const immediate = options.immediate ?? true;
const usePagination = options.usePagination ?? true;
const key = options.key ?? "varList";
if (!immediate && options.otherParams)
throw new Error("options.otherParams只有在immediate为true时才有效");
if (
@ -51,9 +55,9 @@ export default function useListData(api, options = {}) {
...(options.otherParams || {}),
...(getDataType(otherParams) === "Object" ? otherParams : {}),
});
list.value = resData.varList;
list.value = resData[key];
pagination.value.total = resData.page.totalResult;
options.callbackFn && options.callbackFn(list.value);
options.callbackFn && options.callbackFn(resData);
};
immediate && fnGetData().then();
const fnResetPagination = async (otherParams) => {

View File

@ -318,3 +318,40 @@ export function translationStatus(status, list) {
}
}
}
/**
* @description 根据身份证号获取出生日期和性别
* @param {String} idCard 身份证号
* @return {Object} 出生日期和性别 date sex
**/
export function idCardGetDateAndGender(idCard) {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
let sex = "";
let date = "";
if (reg.test(idCard)) {
const org_birthday = idCard.substring(6, 14);
const org_gender = idCard.substring(16, 17);
const birthday =
org_birthday.substring(0, 4) +
"-" +
org_birthday.substring(4, 6) +
"-" +
org_birthday.substring(6, 8);
const birthdays = new Date(birthday.replace(/-/g, "/"));
const Month = birthdays.getMonth() + 1;
let MonthDate;
const DayDate = birthdays.getDate();
let Day;
if (Month < 10) MonthDate = "0" + Month;
else MonthDate = Month;
if (DayDate < 10) Day = "0" + DayDate;
else Day = DayDate;
sex =
org_gender % 2 === 1
? "998f8531a6254c2eab5c825fa2d9896f"
: "fdd532d9def447cdb68af3a938f6f05c";
date = birthdays.getFullYear() + "-" + MonthDate + "-" + Day;
}
return { sex, date };
}

View File

@ -6,6 +6,7 @@
:props="{
children: 'nodes',
label: 'name',
disabled: '',
}"
:render-after-expand="false"
accordion
@ -50,16 +51,10 @@ const props = defineProps({
type: Boolean,
default: true,
},
rootDisable: {
type: String,
default: "Y",
},
});
const emits = defineEmits(["update:modelValue"]);
const modelValue = useVModel(props, "modelValue", emits);
const departmentTree = await layoutFnGetDepartmentTree({
ROOT_DISABLE: props.rootDisable,
});
const departmentTree = await layoutFnGetDepartmentTree();
</script>
<style scoped></style>

View File

@ -0,0 +1,65 @@
<template>
<el-input
v-model="filterText"
placeholder="输入关键字进行过滤"
class="mb-10"
/>
<el-tree
ref="treeRef"
:props="{
children: 'nodes',
label: 'name',
}"
node-key="id"
accordion
:data="treeData"
:filter-node-method="fnFilterNode"
@node-click="nodeClick"
:default-expanded-keys="[departmentId]"
/>
</template>
<script setup>
import { ref, watch, watchEffect } from "vue";
import { layoutFnGetDepartmentTree } from "@/assets/js/data_dictionary.js";
defineOptions({
name: "LayoutDepartmentTree",
});
const props = defineProps({
refresh: {
type: Boolean,
default: false,
},
departmentId: {
type: String,
default: "",
},
});
const emits = defineEmits(["node-click", "throw-data", "update:refresh"]);
const treeRef = ref(null);
const filterText = ref("");
const treeData = ref([]);
watch(filterText, (val) => {
treeRef.value.filter(val);
});
watchEffect(() => {
if (props.refresh) fnGetTreeData();
});
const fnFilterNode = (value, data) => {
if (!value) return true;
return data.name.includes(value);
};
const fnGetTreeData = async () => {
const { value } = await layoutFnGetDepartmentTree();
treeData.value = value;
emits("throw-data", value);
emits("update:refresh", false);
};
fnGetTreeData();
const nodeClick = (data) => {
emits("node-click", data);
};
</script>
<style scoped lang="scss"></style>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog v-model="visible" title="导入" :before-close="fnClose">
<el-dialog v-model="visible" :title="title" :before-close="fnClose">
<el-form ref="formRef" :model="data.form" :rules="rules" label-width="80px">
<el-form-item label="附件" prop="file">
<layout-upload v-model:file-list="data.form.file" accept=".xls" />
@ -16,21 +16,32 @@
<script setup>
import LayoutUpload from "@/components/upload/index.vue";
import { useVModel } from "@vueuse/core";
import { ElMessage, ElMessageBox } from "element-plus";
import { reactive, ref } from "vue";
import { ElMessageBox } from "element-plus";
import { reactive, ref, watchEffect } from "vue";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/assets/js/useFormValidate.js";
import { setLedgerImport } from "@/request/confined_space.js";
const VITE_TEMPLATE_URL = import.meta.env.VITE_TEMPLATE_URL;
defineOptions({
name: "LayoutImportFile",
});
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
title: {
type: String,
default: "导入",
},
templateUrl: {
type: String,
required: true,
default: "",
},
});
const emits = defineEmits(["update:visible", "get-data"]);
const emits = defineEmits(["update:visible", "submit"]);
const visible = useVModel(props, "visible", emits);
const rules = {
file: [{ required: true, message: "附件不能为空", trigger: "change" }],
@ -43,8 +54,13 @@ const data = reactive({
});
const fnExportTemplates = async () => {
await ElMessageBox.confirm("确定要下载excel模板吗", { type: "warning" });
window.open(`${VITE_TEMPLATE_URL}/template/limitSpace.xls`);
window.open(VITE_TEMPLATE_URL + props.templateUrl);
};
watchEffect(() => {
if (!props.visible) {
formRef.value && formRef.value.resetFields();
}
});
const fnClose = () => {
formRef.value.resetFields();
visible.value = false;
@ -56,10 +72,7 @@ const fnSubmit = debounce(
const formData = new FormData();
formData.append("FFILE", data.form.file[0].raw);
formData.append("FFILEName", data.form.file[0].name);
const resData = await setLedgerImport(formData);
ElMessage.success(resData.msg);
fnClose();
emits("get-data");
emits("submit", formData);
},
{ atBegin: true }
);

View File

@ -228,6 +228,7 @@ onBeforeUnmount(() => {
.title {
padding-left: 3px;
font-size: 14px;
}
}
}

View File

@ -0,0 +1,58 @@
<template>
<el-select
v-model="modelValue"
filterable
allow-create
placeholder="请选择,可直接输入创建选项"
>
<el-option
v-for="(item, index) in list"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
>
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
"
>
<div>{{ item.NAME }}</div>
<div>
<el-icon @click="fnDelete(index)">
<circle-close />
</el-icon>
</div>
</div>
</el-option>
</el-select>
</template>
<script setup>
import { CircleClose } from "@element-plus/icons-vue";
import { useVModel } from "@vueuse/core";
defineOptions({
name: "LayoutSelectCreate",
});
const props = defineProps({
modelValue: {
type: String,
required: true,
default: "",
},
list: {
type: Array,
required: true,
default: () => [],
},
});
const emits = defineEmits(["update:modelValue", "delete-option"]);
const modelValue = useVModel(props, "modelValue", emits);
const fnDelete = (index) => {
emits("delete-option", index);
};
</script>
<style scoped lang="scss"></style>

View File

@ -36,3 +36,9 @@ export const getDepartmentTree = (params) =>
loading: false,
...params,
});
// 获取岗位
export const getPostListAll = (params) =>
post("/post/listAll", {
loading: false,
...params,
});

View File

@ -1,5 +0,0 @@
import { post } from "@/request/axios.js";
export const getDepartmentList = (params) => post("/department/list", params); // 组织机构列表
export const setDepartmentDelete = (params) =>
post("/department/delete", params); // 组织机构删除

View File

@ -12,3 +12,30 @@ export const setIndustryQualificationsAdd = (params) =>
post("/qualifications/add", params); // 行业资质添加
export const setIndustryQualificationsEdit = (params) =>
post("/qualifications/edit", params); // 行业资质修改
export const getDepartmentList = (params) => post("/department/list", params); // 组织机构列表
export const getDepartmentView = (params) => post("/department/goEdit", params); // 组织机构查看
export const setDepartmentDelete = (params) =>
post("/department/delete", params); // 组织机构删除
export const setDepartmentAdd = (params) => post("/department/add", params); // 组织机构添加
export const setDepartmentEdit = (params) => post("/department/edit", params); // 组织机构修改
export const getPostList = (params) => post("/post/list", params); // 岗位管理列表
export const getPostView = (params) => post("/post/goEdit", params); // 岗位管理查看
export const setPostDelete = (params) => post("/post/delete", params); // 岗位管理删除
export const setPostAdd = (params) => upload("/post/add", params); // 岗位管理添加
export const setPostEdit = (params) => upload("/post/edit", params); // 岗位管理修改
export const getUserList = (params) => post("/user/list", params); // 用户管理列表
export const setUserDelete = (params) => post("/user/deleteUser", params); // 用户管理删除
export const setUserResetPassword = (params) =>
post("/corpinfo/resetPwd", params); // 用户管理重置密码
export const getUserScheduling = (params) =>
post("/shiftworkrules/listAll", params); // 用户管理获取排班
export const setUserImport = (params) => upload("/user/readExcel2", params); // 用户管理导入
export const setUserLearnersImport = (params) =>
upload("/user/readExcel3", params); // 用户管理在线学习人员导入
export const getUserInfo = (params) => post("/user/goAddUser", params); // 用户管理添加获取信息
export const getUserCurrentShiftList = (params) =>
post("/shiftworkperiod/listPeriods", params); // 用户管理当前班次列表
export const getUserScheduleView = (params) =>
post("/shiftworkperiod/getWorkDate", params); // 用户管理查看排班表
export const setDictionaryDelete = (params) =>
post("/dictionariesCorp/delete", params); // 数据字典删除

View File

@ -65,7 +65,13 @@
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="DESCR">
<el-input v-model="form.DESCR" type="textarea" autosize />
<el-input
v-model="form.DESCR"
type="textarea"
:autosize="{
minRows: 3,
}"
/>
</el-form-item>
</el-col>
<el-col :span="12">

View File

@ -22,9 +22,7 @@
</el-col>
<el-col :span="12">
<el-form-item label-width="10px" class="end">
<el-button @click="data.importDialogVisible = true">
导入
</el-button>
<el-button @click="fnImportDialogChangeShow"> </el-button>
</el-form-item>
</el-col>
</el-row>
@ -81,9 +79,10 @@
</template>
</layout-table>
</layout-card>
<import-file
<layout-import-file
v-model:visible="data.importDialogVisible"
@get-data="fnResetPagination"
template-url="/template/limitSpace.xls"
@submit="fnSubmitImport"
/>
<add
v-model:visible="data.addOrEditDialog.visible"
@ -99,13 +98,17 @@ import { nextTick, reactive } from "vue";
import LayoutTable from "@/components/table/index";
import { serialNumber } from "@/assets/js/utils";
import LayoutCard from "@/components/card/index.vue";
import { getLedgerList, setLedgerDelete } from "@/request/confined_space.js";
import {
getLedgerList,
setLedgerDelete,
setLedgerImport,
} from "@/request/confined_space.js";
import { ElMessage, ElMessageBox } from "element-plus";
import { debounce } from "throttle-debounce";
import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
import { cloneDeep } from "lodash-es";
import useListData from "@/assets/js/useListData.js";
import ImportFile from "./components/import_file.vue";
import LayoutImportFile from "@/components/import_file/index.vue";
import Add from "./components/add.vue";
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
@ -148,6 +151,15 @@ const fnDelete = debounce(
},
{ atBegin: true }
);
const fnImportDialogChangeShow = () => {
data.importDialogVisible = !data.importDialogVisible;
};
const fnSubmitImport = async (formData) => {
const resData = await setLedgerImport(formData);
ElMessage.success(resData.msg);
fnImportDialogChangeShow();
fnResetPagination();
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,168 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'add' ? '新增' : '修改'"
:before-close="fnClose"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="上级部门">
<el-tag>{{ parentName }}</el-tag>
</el-form-item>
<el-form-item label="部门名称" prop="NAME">
<el-input v-model="form.NAME" placeholder="请输入" />
</el-form-item>
<el-form-item label="部门级别" prop="LEVEL">
<el-select v-model="form.LEVEL">
<el-option
v-for="item in departmentLevel"
:key="item.BIANMA"
:label="item.NAME"
:value="item.BIANMA"
/>
</el-select>
</el-form-item>
<el-form-item label="部门排序" prop="DEP_ORDER">
<el-input v-model.number="form.DEP_ORDER" placeholder="请输入" />
</el-form-item>
<el-form-item label="负责人" prop="HEADMAN">
<el-input v-model="form.HEADMAN" placeholder="请输入" />
</el-form-item>
<el-form-item label="电话" prop="TEL">
<el-input v-model="form.TEL" placeholder="请输入" />
</el-form-item>
<el-form-item label="部门职能" prop="FUNCTIONS">
<el-input
:autosize="{
minRows: 3,
}"
v-model="form.FUNCTIONS"
type="textarea"
placeholder="请输入"
/>
</el-form-item>
<el-form-item label="是否监管部门" prop="ISSUPERVISE">
<el-radio-group
v-model="form.ISSUPERVISE"
@change="fnChangeRegulatoryAuthorities"
>
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ISSUPERVISE === '1'"
label="监管部门"
prop="checkedIds"
>
<layout-department v-model="form.checkedIds" multiple show-checkbox />
</el-form-item>
<el-form-item label="备注" prop="BZ">
<el-input
:autosize="{
minRows: 3,
}"
v-model="form.BZ"
type="textarea"
placeholder="请输入"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="fnSubmit"></el-button>
<el-button @click="fnClose"></el-button>
</template>
</el-dialog>
</template>
<script setup>
import { useVModels } from "@vueuse/core";
import { ref } from "vue";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/assets/js/useFormValidate.js";
import { layoutFnGetDepartmentLevel } from "@/assets/js/data_dictionary.js";
import LayoutDepartment from "@/components/department/index.vue";
import { useUserStore } from "@/pinia/user.js";
import {
setDepartmentAdd,
setDepartmentEdit,
} from "@/request/enterprise_management.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
form: {
type: Object,
required: true,
default: () => ({}),
},
type: {
type: String,
required: true,
default: "",
},
parentName: {
type: String,
required: true,
default: "",
},
parentId: {
type: String,
required: true,
default: "",
},
});
const emits = defineEmits(["update:visible", "update:form", "get-data"]);
const { visible, form } = useVModels(props, emits);
const rules = {
NAME: [{ required: true, message: "部门名称不能为空", trigger: "blur" }],
LEVEL: [{ required: true, message: "部门级别不能为空", trigger: "change" }],
DEP_ORDER: [
{ required: true, message: "部门序号不能为空", trigger: "blur" },
{ type: "number", message: "部门序号必须为数字", trigger: "blur" },
],
ISSUPERVISE: [
{ required: true, message: "是否监管部门不能为空", trigger: "blur" },
],
checkedIds: [
{ required: true, message: "是否监管部门不能为空", trigger: "change" },
],
};
const userStore = useUserStore();
const formRef = ref(null);
const departmentLevel = await layoutFnGetDepartmentLevel();
const fnChangeRegulatoryAuthorities = (event) => {
if (event === "1" && form.value.checkedIds.length === 0) {
if (props.type === "add")
form.value.checkedIds = [userStore.getUserInfo.DEPARTMENT_ID];
else form.value.checkedIds = [form.value.DEPARTMENT_ID];
}
};
const fnClose = () => {
formRef.value.resetFields();
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await useFormValidate(formRef);
props.type === "add"
? await setDepartmentAdd({
...form.value,
PARENT_ID: props.parentId,
checkedIds: form.value.checkedIds.join(","),
})
: await setDepartmentEdit({
...form.value,
checkedIds: form.value.checkedIds.join(","),
});
fnClose();
emits("get-data");
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -3,21 +3,19 @@
<layout-card>
<el-row :gutter="12">
<el-col :span="5">
<el-input
v-model="filterText"
placeholder="输入关键字进行过滤"
class="mb-10"
/>
<el-tree
ref="treeRef"
:props="{
children: 'nodes',
label: 'name',
}"
accordion
:data="data.treeData"
:filter-node-method="fnFilterNode"
@node-click="fnResetPagination({ DEPARTMENT_ID: $event.id })"
<layout-department-tree
:department-id="DEPARTMENT_ID"
@throw-data="data.treeData = $event"
@node-click="
router.push({
path: '/enterprise_management/department',
query: {
DEPARTMENT_ID: $event.id,
DEPARTMENT_NAME: $event.name,
},
})
"
v-model:refresh="data.refreshTreeData"
/>
</el-col>
<el-col :span="19">
@ -67,6 +65,7 @@
type="primary"
text
link
v-if="buttonJurisdiction.edit"
@click="fnAddOrEdit(row.DEPARTMENT_ID, 'edit')"
>
编辑
@ -75,6 +74,7 @@
type="primary"
text
link
v-if="buttonJurisdiction.del"
@click="fnDelete(row.DEPARTMENT_ID, row.NAME)"
>
删除
@ -82,10 +82,15 @@
</template>
</el-table-column>
</template>
<template #button>
<el-button type="primary" @click="fnAddOrEdit('', 'add')">
<template #button v-if="DEPARTMENT_ID !== '0'">
<el-button
type="primary"
v-if="buttonJurisdiction.add"
@click="fnAddOrEdit('', 'add')"
>
新增
</el-button>
<el-button @click="router.back"> </el-button>
</template>
</layout-table>
</el-col>
@ -95,6 +100,14 @@
v-model:visible="data.structuralDiagramVisible"
:tree-data="data.treeData"
/>
<add
v-model:visible="data.addOrEditDialog.visible"
v-model:form="data.addOrEditDialog.form"
:type="data.addOrEditDialog.type"
:parent-name="DEPARTMENT_NAME"
:parent-id="DEPARTMENT_ID"
@get-data="fnResetPaginationTransfer"
/>
</div>
</template>
@ -103,15 +116,18 @@ import { serialNumber } from "@/assets/js/utils.js";
import useListData from "@/assets/js/useListData.js";
import {
getDepartmentList,
getDepartmentView,
setDepartmentDelete,
} from "@/request/department.js";
import { layoutFnGetDepartmentTree } from "@/assets/js/data_dictionary.js";
import { reactive, ref, watch } from "vue";
} from "@/request/enterprise_management.js";
import { nextTick, reactive, ref } from "vue";
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
import { ArrowRight } from "@element-plus/icons-vue";
import { debounce } from "throttle-debounce";
import { ElMessage, ElMessageBox } from "element-plus";
import StructuralDiagram from "./components/structural_diagram.vue";
import LayoutDepartmentTree from "@/components/department_tree/index.vue";
import Add from "./components/add.vue";
import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
const router = useRouter();
const route = useRoute();
@ -119,8 +135,6 @@ const parentIdDefault = "0";
const parentNameDefault = "(无)此项为顶级部门";
const DEPARTMENT_ID = ref(route.query.DEPARTMENT_ID || parentIdDefault);
const DEPARTMENT_NAME = ref(route.query.DEPARTMENT_NAME || parentNameDefault);
const treeRef = ref(null);
const filterText = ref("");
const { list, pagination, fnGetData, fnResetPagination } = useListData(
getDepartmentList,
{
@ -131,20 +145,25 @@ const { list, pagination, fnGetData, fnResetPagination } = useListData(
);
const data = reactive({
treeData: [],
refreshTreeData: false,
structuralDiagramVisible: false,
addOrEditDialog: {
visible: false,
type: "",
form: {
NAME: "",
LEVEL: "",
DEP_ORDER: "",
HEADMAN: "",
TEL: "",
FUNCTIONS: "",
BZ: "",
ISSUPERVISE: "0",
checkedIds: [],
},
},
});
watch(filterText, (val) => {
treeRef.value.filter(val);
});
const fnFilterNode = (value, data) => {
if (!value) return true;
return data.name.includes(value);
};
const fnGetTreeData = async () => {
const treeData = await layoutFnGetDepartmentTree();
data.treeData = treeData.value;
};
fnGetTreeData();
const buttonJurisdiction = await useButtonJurisdiction("department");
const fnGetDataTransfer = () => {
fnGetData({
DEPARTMENT_ID: DEPARTMENT_ID.value,
@ -160,13 +179,30 @@ onBeforeRouteUpdate((to) => {
DEPARTMENT_NAME.value = to.query.DEPARTMENT_NAME || parentNameDefault;
fnResetPaginationTransfer();
});
const fnAddOrEdit = async (DEPARTMENT_ID, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.type = type;
if (type === "edit") {
const resData = await getDepartmentView({
DEPARTMENT_ID,
});
data.addOrEditDialog.form = resData.pd;
data.addOrEditDialog.form.checkedIds = resData.varlist.map(
(item) => item.SUB_DEPARTMENT_ID
);
}
};
const fnDelete = debounce(
1000,
async (DEPARTMENT_ID, NAME) => {
await ElMessageBox.confirm(`确定要删除[${NAME}]吗?`, { type: "warning" });
await ElMessageBox.confirm(`确定要删除【${NAME}】吗?`, {
type: "warning",
});
await setDepartmentDelete({ DEPARTMENT_ID });
ElMessage.success("删除成功");
await fnResetPaginationTransfer();
fnResetPaginationTransfer();
data.refreshTreeData = true;
},
{ atBegin: true }
);

View File

@ -20,7 +20,13 @@
<el-input v-model="data.form.NUMBER" />
</el-form-item>
<el-form-item label="备注" prop="DESCR">
<el-input autosize v-model="data.form.DESCR" type="textarea" />
<el-input
:autosize="{
minRows: 3,
}"
v-model="data.form.DESCR"
type="textarea"
/>
</el-form-item>
<el-form-item label="证书图片" prop="file">
<layout-upload

View File

@ -61,10 +61,11 @@
<img
v-for="item in row.imgs"
:key="item.IMGFILES_ID"
:src="FILE_URL + item.FILEPATH"
:src="VITE_FILE_URL + item.FILEPATH"
width="100"
height="100"
alt=""
class="ml-10"
/>
</template>
<span v-else></span>
@ -139,7 +140,7 @@ import {
import { useRouter } from "vue-router";
import useListData from "@/assets/js/useListData.js";
const FILE_URL = import.meta.env.VITE_FILE_URL;
const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
const router = useRouter();
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
useListData(getIndustryQualificationsList);
@ -161,7 +162,7 @@ const fnDelete = debounce(
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
await setIndustryQualificationsDelete({ QUALIFICATIONS_ID });
ElMessage.success("删除成功");
await fnResetPaginationTransfer();
fnResetPaginationTransfer();
},
{ atBegin: true }
);

View File

@ -0,0 +1,117 @@
<template>
<el-dialog
v-model="visible"
:title="type === 'add' ? '新增' : '修改'"
:before-close="fnClose"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="所属部门">
<el-tag>{{ departmentName }}</el-tag>
</el-form-item>
<el-form-item label="岗位名称" prop="NAME">
<el-input v-model="form.NAME" placeholder="请输入" />
</el-form-item>
<el-form-item label="岗位职责" prop="DESCR">
<el-input
:autosize="{
minRows: 3,
}"
v-model="form.DESCR"
type="textarea"
placeholder="请输入"
/>
</el-form-item>
<el-form-item label="状态" prop="STATUS">
<el-radio-group v-model="form.STATUS">
<el-radio label="0">启用</el-radio>
<el-radio label="1">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="告知卡" prop="files">
<layout-upload
v-model:file-list="form.files"
:limit="99"
accept=".jpeg,.png,.jpg"
delete-to-server
list-type="picture-card"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" @click="fnSubmit"></el-button>
<el-button @click="fnClose"></el-button>
</template>
</el-dialog>
</template>
<script setup>
import { useVModels } from "@vueuse/core";
import { ref } from "vue";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/assets/js/useFormValidate.js";
import { setPostAdd, setPostEdit } from "@/request/enterprise_management.js";
import LayoutUpload from "@/components/upload/index.vue";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
form: {
type: Object,
required: true,
default: () => ({}),
},
type: {
type: String,
required: true,
default: "",
},
departmentName: {
type: String,
required: true,
default: "",
},
departmentId: {
type: String,
required: true,
default: "",
},
});
const emits = defineEmits(["update:visible", "update:form", "get-data"]);
const { visible, form } = useVModels(props, emits);
const rules = {
NAME: [{ required: true, message: "岗位名称不能为空", trigger: "blur" }],
DESCR: [{ required: true, message: "岗位职责不能为空", trigger: "blur" }],
};
const formRef = ref(null);
const fnClose = () => {
formRef.value.resetFields();
visible.value = false;
};
const fnSubmit = debounce(
1000,
async () => {
await useFormValidate(formRef);
const formData = new FormData();
Object.keys(form.value).forEach((key) => {
formData.append(key, form.value[key]);
});
formData.delete("files");
for (let i = 0; i < form.value.files.length; i++) {
if (form.value.files[i].raw)
formData.append("FFILE", form.value.files[i].raw);
}
if (props.type === "add") {
formData.append("DEPARTMENT_ID", props.departmentId);
await setPostAdd(formData);
} else await setPostEdit(formData);
fnClose();
emits("get-data");
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,194 @@
<template>
<div>
<layout-card>
<el-row :gutter="12">
<el-col :span="5">
<layout-department-tree
:department-id="DEPARTMENT_ID"
@node-click="
router.push({
path: '/enterprise_management/post',
query: {
DEPARTMENT_ID: $event.id,
DEPARTMENT_NAME: $event.name,
},
})
"
/>
</el-col>
<el-col :span="19">
<layout-table
:data="list"
v-model:pagination="pagination"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="70">
<template v-slot="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column post prop="DEPARTMENT_NAME" label="机构" />
<el-table-column post prop="NAME" label="岗位" />
<el-table-column label="状态" width="100">
<template v-slot="{ row }">
<span v-if="row.STATUS === '0'"></span>
<span v-if="row.STATUS === '1'"></span>
</template>
</el-table-column>
<el-table-column label="照片">
<template v-slot="{ row }">
<el-tooltip placement="top">
<template #content>
<template v-if="row.imgs.length > 0">
<img
v-for="item in row.imgs"
:key="item.IMGFILES_ID"
:src="VITE_FILE_URL + item.FILEPATH"
width="100"
height="100"
alt=""
class="ml-10"
/>
</template>
<span v-else></span>
</template>
<el-tag>预览</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template v-slot="{ row }">
<el-button
type="primary"
text
link
v-if="buttonJurisdiction.edit"
@click="fnAddOrEdit(row.POST_ID, 'edit')"
>
编辑
</el-button>
<el-button
type="primary"
text
link
v-if="buttonJurisdiction.del"
@click="fnDelete(row.POST_ID, row.NAME)"
>
删除
</el-button>
</template>
</el-table-column>
<template #button>
<el-button
type="primary"
v-if="buttonJurisdiction.add"
@click="fnAddOrEdit('', 'add')"
>
新增
</el-button>
</template>
</layout-table>
</el-col>
</el-row>
</layout-card>
<add
v-model:visible="data.addOrEditDialog.visible"
v-model:form="data.addOrEditDialog.form"
:type="data.addOrEditDialog.type"
:department-name="DEPARTMENT_NAME"
:department-id="DEPARTMENT_ID"
@get-data="fnResetPaginationTransfer"
/>
</div>
</template>
<script setup>
import { addingPrefixToFile, serialNumber } from "@/assets/js/utils.js";
import useListData from "@/assets/js/useListData.js";
import {
getPostView,
getPostList,
setPostDelete,
} from "@/request/enterprise_management.js";
import { nextTick, reactive, ref } from "vue";
import LayoutDepartmentTree from "@/components/department_tree/index.vue";
import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
import { useUserStore } from "@/pinia/user.js";
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
import { debounce } from "throttle-debounce";
import { ElMessage, ElMessageBox } from "element-plus";
import Add from "./components/add.vue";
const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
const userStore = useUserStore();
const router = useRouter();
const route = useRoute();
const departmentIdDefault = userStore.getUserInfo.DEPARTMENT_ID;
const departmentNameDefault = userStore.getUserInfo.DEPARTMENT_NAME;
const DEPARTMENT_ID = ref(route.query.DEPARTMENT_ID || departmentIdDefault);
const DEPARTMENT_NAME = ref(
route.query.DEPARTMENT_NAME || departmentNameDefault
);
const { list, pagination, fnGetData, fnResetPagination } = useListData(
getPostList,
{
otherParams: {
DEPARTMENT_ID: DEPARTMENT_ID.value,
},
}
);
const data = reactive({
addOrEditDialog: {
visible: false,
type: "",
form: {
NAME: "",
DESCR: "",
STATUS: "0",
files: [],
},
},
});
const buttonJurisdiction = await useButtonJurisdiction("post");
const fnGetDataTransfer = () => {
fnGetData({
DEPARTMENT_ID: DEPARTMENT_ID.value,
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
DEPARTMENT_ID: DEPARTMENT_ID.value,
});
};
onBeforeRouteUpdate((to) => {
DEPARTMENT_ID.value = to.query.DEPARTMENT_ID || departmentIdDefault;
DEPARTMENT_NAME.value = to.query.DEPARTMENT_NAME || departmentNameDefault;
fnResetPaginationTransfer();
});
const fnAddOrEdit = async (POST_ID, type) => {
data.addOrEditDialog.visible = true;
await nextTick();
data.addOrEditDialog.type = type;
if (type === "edit") {
const resData = await getPostView({
POST_ID,
});
data.addOrEditDialog.form = resData.pd;
data.addOrEditDialog.form.files = addingPrefixToFile(resData.imgs);
}
};
const fnDelete = debounce(
1000,
async (POST_ID, NAME) => {
await ElMessageBox.confirm(`确定要删除【${NAME}】吗?`, {
type: "warning",
});
await setPostDelete({ POST_ID });
ElMessage.success("删除成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,747 @@
<template>
<layout-card>
<el-form
ref="formRef"
:rules="rules"
:model="data.form"
label-width="130px"
>
<el-row :gutter="20">
<el-col :span="12">
<el-row>
<el-col :span="24">
<el-form-item label="角色" prop="ROLE_ID">
<el-select
v-model="data.form.ROLE_ID"
:disabled="data.form.ISMAIN === '1'"
>
<template v-for="item in data.roleList" :key="item.role_ID">
<el-option
v-if="item.ismain !== '1'"
:label="item.role_NAME"
:value="item.role_ID"
/>
</template>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="部门" prop="DEPARTMENT_ID">
<layout-department
v-model="data.form.DEPARTMENT_ID"
@update:model-value="data.form.POST_ID = ''"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="岗位" prop="POST_ID">
<el-select v-model="data.form.POST_ID">
<el-option
v-for="item in data.postList"
:key="item.POST_ID"
:label="item.NAME"
:value="item.POST_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="用户名" prop="USERNAME">
<el-input
:disabled="!!USER_ID"
v-model="data.form.USERNAME"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="姓名" prop="NAME">
<el-input v-model="data.form.NAME" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="部门排序" prop="SORT">
<el-input
v-model.number="data.form.SORT"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="邮箱" prop="EMAIL">
<el-input v-model="data.form.EMAIL" placeholder="请输入" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-row>
<el-col :span="10">
<el-form-item label="排班" prop="SHIFTDUTYONE">
<el-select
v-model="data.form.SHIFTDUTYONE"
@change="fnChangeSchedulingOne"
>
<el-option
v-for="item in data.schedulingList"
:key="item.SHIFTWORKRULES_ID"
:label="item.NAME"
:value="item.SHIFTWORKRULES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label-width="10px" prop="SHIFTDUTYTWO">
<el-select
v-model="data.form.SHIFTDUTYTWO"
@change="fnChangeSchedulingTwo"
>
<el-option
v-for="item in data.schedulingList1"
:key="item.SHIFTWORKRULES_ID"
:label="item.NAME"
:value="item.SHIFTWORKRULES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="10px">
<el-button @click="data.scheduleVisible = true">
当前班状
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col :span="24" v-show="data.periodList.length > 0">
<el-form-item label="当前班次" prop="periodStr">
<el-radio-group
v-model="data.form.periodStr"
@change="fnChangePeriod"
>
<el-radio-button
v-for="(item, index) in data.periodList"
:key="index"
:label="item.periodStr"
>
{{ item.period.WORKSTATUS === "1" ? "上班" : "休班" }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="BZ">
<el-input
:autosize="{
minRows: 3,
}"
v-model="data.form.BZ"
type="textarea"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="是否安全管理人员" prop="IS_SAFETY">
<el-radio-group v-model="data.form.IS_SAFETY">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="是否主要负责人" prop="ISHEAD">
<el-radio-group v-model="data.form.ISHEAD">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="是否部门领导" prop="ISLEADER">
<el-radio-group v-model="data.form.ISLEADER">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
<el-tooltip
class="item"
effect="dark"
content="温馨提示:部门领导可以审核离岗申请,查看同部门清单数据"
placement="top-start"
>
<el-icon color="red" size="16" class="ml-10">
<info-filled />
</el-icon>
</el-tooltip>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="是否在线学习人员">
<el-switch
v-model="data.form.ISSTUDENT"
active-value="true"
inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949"
/>
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col :span="12" v-if="data.form.ISSTUDENT === 'true'">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="身份证号" prop="USER_ID_CARD">
<el-input
v-model="data.form.USER_ID_CARD"
placeholder="请输入"
@blur="fnChangeIdCard"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="民族" prop="NATION">
<el-select v-model="data.form.NATION">
<el-option
v-for="item in data.nationList"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="SEX">
<el-select v-model="data.form.SEX">
<el-option
v-for="item in data.sexList"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="政治面貌" prop="POLITICAL_OUTLOOK">
<el-select v-model="data.form.POLITICAL_OUTLOOK">
<el-option
v-for="item in data.politicalLandscapeList"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出生年月" prop="DATE_OF_BIRTH">
<el-date-picker
v-model="data.form.DATE_OF_BIRTH"
type="date"
placeholder="选择日期"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="文化程度" prop="DEGREE_OF_EDUCATION">
<el-select v-model="data.form.DEGREE_OF_EDUCATION">
<el-option
v-for="item in data.degreeOfEducationList"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="人员类型" prop="PERSONNEL_TYPE">
<el-select v-model="data.form.PERSONNEL_TYPE">
<el-option
v-for="item in data.personnelTypeList"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="职务" prop="DUTIES">
<layout-select-create
v-model="data.form.DUTIES"
:list="data.dutiesList"
@delete-option="fnDictionaryDelete($event, 'POSITION')"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="职称" prop="TITLE">
<layout-select-create
v-model="data.form.TITLE"
:list="data.professionalTitleList"
@delete-option="fnDictionaryDelete($event, 'JOB_TITLE')"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工种" prop="TYPE_OF_WORK">
<layout-select-create
v-model="data.form.TYPE_OF_WORK"
:list="data.jobTypeList"
@delete-option="fnDictionaryDelete($event, 'WORK_TYPE')"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职日期" prop="ENTRY_DATE">
<el-date-picker
v-model="data.form.ENTRY_DATE"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
placeholder="选择日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="参加工作日期" prop="WORKING_DATE">
<el-date-picker
v-model="data.form.WORKING_DATE"
value-format="YYYY-MM-DD"
format="YYYY-MM-DD"
type="date"
placeholder="选择日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="在职情况" prop="INCUMBENCY">
<el-select v-model="data.form.INCUMBENCY">
<el-option
v-for="item in data.employmentSituationList"
:key="item.DICTIONARIES_ID"
:label="item.NAME"
:value="item.DICTIONARIES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="上传人脸照片" prop="faceFile">
<layout-upload
v-model:file-list="data.form.faceFile"
accept=".jpg,.jpeg,.png"
list-type="picture-card"
>
<template #tip>
<div style="line-height: 1.6" class="mt-10">
<div style="color: red; font-size: 12px">
* 图像格式JPEGJPGPNGBMP
</div>
<div style="color: red; font-size: 12px">
* 图像大小不超过3M
</div>
<div style="color: red; font-size: 12px">
*
图像分辨率大于32×32像素小于4096×4096像素人脸占比不低于64×64像素
</div>
<div style="color: red; font-size: 12px">
如无合适照片请该人员登陆秦安APP中进行人脸照片采集
</div>
</div>
</template>
</layout-upload>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="证书信息" prop="userCerFile">
<layout-upload
v-model:file-list="data.form.userCerFile"
accept=".jpg,.jpeg,.png"
list-type="picture-card"
:limit="99"
delete-to-server
/>
</el-form-item>
</el-col>
</el-row>
</el-col>
</el-row>
</el-form>
<div class="tc mt-10">
<el-button type="primary" @click="fnSubmit"></el-button>
</div>
<scheduling
v-model:visible="data.scheduleVisible"
:info="data.scheduleInfo"
:id="data.form.SHIFTDUTYTWO"
/>
</layout-card>
</template>
<script setup>
import { reactive, ref, watch, watchEffect } from "vue";
import {
getUserCurrentShiftList,
getUserInfo,
getUserScheduling,
setDictionaryDelete,
} from "@/request/enterprise_management.js";
import LayoutDepartment from "@/components/department/index.vue";
import LayoutUpload from "@/components/upload/index.vue";
import LayoutSelectCreate from "@/components/select_create/index.vue";
import { getPostListAll } from "@/request/data_dictionary.js";
import { useRoute, useRouter } from "vue-router";
import Scheduling from "./components/scheduling.vue";
import { InfoFilled } from "@element-plus/icons-vue";
import { idCardGetDateAndGender } from "@/assets/js/utils.js";
import {
layoutFnGetDegreeOfEducation,
layoutFnGetDuties,
layoutFnGetEmploymentSituation,
layoutFnGetJobType,
layoutFnGetNation,
layoutFnGetPersonnelType,
layoutFnGetPoliticalLandscape,
layoutFnGetProfessionalTitle,
layoutFnGetSex,
} from "@/assets/js/data_dictionary.js";
import { ElMessageBox, ElMessage } from "element-plus";
import { debounce } from "throttle-debounce";
import {
getVerifyDeduplicationUser,
getVerifyDuplicateEmail,
} from "@/request/api.js";
import useFormValidate from "@/assets/js/useFormValidate.js";
const route = useRoute();
const router = useRouter();
const { USER_ID } = route.query;
const fnHasEmail = async (rule, value, callback) => {
if (value) {
try {
await getVerifyDuplicateEmail({
EMAIL: value,
USERNAME: data.form.USERNAME,
});
callback();
} catch {
callback(new Error("邮箱重复"));
}
} else {
callback();
}
};
const fnHasUser = async (rule, value, callback) => {
if (value) {
const resData = await getVerifyDeduplicationUser({
USERNAME: value,
VERIFYUSER_ID: data.form.USER_ID,
});
if (resData?.pd?.USER_ID) callback(new Error("用户名重复"));
else callback();
} else {
callback();
}
};
const rules = {
NAME: [{ required: true, message: "姓名不能为空", trigger: "blur" }],
USERNAME: [
{ required: true, message: "请输入手机号码", trigger: "blur" },
{ min: 11, max: 11, message: "请输入11位手机号码", trigger: "blur" },
{
pattern: /^(1[3-9][0-9])\d{8}$/,
message: "请输入正确的手机号码",
},
{ validator: fnHasUser, trigger: "blur" },
],
EMAIL: [
{ required: false, message: "请输入邮箱", trigger: "blur" },
{
pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
message: "请输入正确的邮箱",
},
{ validator: fnHasEmail, trigger: "blur" },
],
ROLE_ID: [{ required: true, message: "请选择角色", trigger: "change" }],
DEPARTMENT_ID: [{ required: true, message: "请选择部门", trigger: "change" }],
DUTIES_ID: [{ required: true, message: "请选择岗位", trigger: "change" }],
SORT: [
{ required: true, message: "请输入排序", trigger: "blur" },
{ type: "number", message: "请输入数字类型", trigger: "blur" },
],
NATION: [{ required: true, message: "民族不能为空", trigger: "change" }],
SEX: [{ required: true, message: "性别不能为空", trigger: "change" }],
POLITICAL_OUTLOOK: [
{ required: true, message: "政治面貌不能为空", trigger: "change" },
],
DATE_OF_BIRTH: [
{ required: true, message: "请选择出生年月", trigger: "change" },
],
DEGREE_OF_EDUCATION: [
{ required: true, message: "文化程度不能为空", trigger: "change" },
],
POST: [{ required: true, message: "职务不能为空", trigger: "change" }],
TYPE_OF_WORK: [
{ required: true, message: "工种不能为空", trigger: "change" },
],
ENTRY_DATE: [
{ required: true, message: "请选择入职日期", trigger: "change" },
],
WORKING_DATE: [
{ required: true, message: "请选择参加工作日期", trigger: "change" },
],
INCUMBENCY: [
{ required: true, message: "在职情况不能为空", trigger: "change" },
],
TITLE: [{ required: true, message: "职称不能为空", trigger: "change" }],
DUTIES: [{ required: true, message: "职务不能为空", trigger: "change" }],
USER_ID_CARD: [
{ required: true, message: "身份证号不能为空", trigger: "blur" },
{
pattern:
/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[0-2])(([0-2][1-9])|10|20|30|31)\d{3}(\d|X|x)$/,
message: "请输入正确的身份证号",
trigger: "blur",
},
],
SHIFTDUTYONE: [{ required: true, message: "请选择排班", trigger: "change" }],
SHIFTDUTYTWO: [{ required: true, message: "请选择排班", trigger: "change" }],
periodStr: [
{ required: true, message: "请选择完整的排班类型", trigger: "change" },
],
};
const formRef = ref(null);
const data = reactive({
roleList: [],
postList: [],
schedulingList: [],
schedulingList1: [],
periodList: [],
nationList: [],
sexList: [],
politicalLandscapeList: [],
degreeOfEducationList: [],
personnelTypeList: [],
dutiesList: [],
professionalTitleList: [],
jobTypeList: [],
employmentSituationList: [],
allUser: 0,
USERS_NUM: 0,
form: {
ROLE_ID: "",
DEPARTMENT_ID: "",
POST_ID: "",
USERNAME: "",
NAME: "",
SORT: "",
EMAIL: "",
SHIFTDUTYONE: "",
SHIFTDUTYTWO: "",
periodStr: "",
BZ: "",
IS_SAFETY: 0,
ISHEAD: "0",
ISLEADER: "0",
ISSTUDENT: "false",
USER_ID_CARD: "",
NATION: "",
SEX: "",
POLITICAL_OUTLOOK: "",
DATE_OF_BIRTH: "",
DEGREE_OF_EDUCATION: "",
PERSONNEL_TYPE: "",
DUTIES: "",
TITLE: "",
TYPE_OF_WORK: "",
ENTRY_DATE: "",
WORKING_DATE: "",
INCUMBENCY: "",
faceFile: [],
userCerFile: [],
},
scheduleInfo: {
WORKSTATUS: "",
DURATION: "",
WORKPERIOD: "",
},
scheduleVisible: false,
});
const fnGetUserRole = async () => {
const resData = await getUserInfo();
data.roleList = resData.roleList;
data.allUser = resData.allUser;
data.USERS_NUM = resData.USERS_NUM.USERS_NUM;
};
fnGetUserRole();
const fnGetPost = async (DEPARTMENT_ID) => {
const resData = await getPostListAll({ DEPARTMENT_ID });
data.postList = resData.postList;
};
const fnGetUserScheduling = async (PARENTID, list) => {
const resData = await getUserScheduling({ PARENTID });
data[list] = resData.varList;
};
fnGetUserScheduling("0", "schedulingList");
const fnChangeSchedulingOne = () => {
data.form.SHIFTDUTYTWO = "";
data.periodList = [];
data.scheduleInfo = {
WORKSTATUS: "",
DURATION: "",
WORKPERIOD: "",
};
data.form.periodStr = "";
};
const fnChangeSchedulingTwo = () => {
data.scheduleInfo = {
WORKSTATUS: "",
DURATION: "",
WORKPERIOD: "",
};
data.form.periodStr = "";
};
const fnGetUserCurrentShiftList = async (SHIFTWORKRULES_ID) => {
const resData = await getUserCurrentShiftList({ SHIFTWORKRULES_ID });
data.periodList = resData.varList;
};
const fnChangePeriod = (event) => {
for (let i = 0; i < data.periodList.length; i++) {
if (data.periodList[i].periodStr === event) {
data.scheduleInfo = data.periodList[i].period;
break;
}
}
};
const fnChangeIdCard = () => {
const { sex, date } = idCardGetDateAndGender(data.form.USER_ID_CARD);
data.form.SEX = sex;
data.form.DATE_OF_BIRTH = date;
};
const fnGetNation = async () => {
const { value } = await layoutFnGetNation();
data.nationList = value;
};
const fnGetSex = async () => {
const { value } = await layoutFnGetSex();
data.sexList = value;
};
const fnGetPoliticalLandscape = async () => {
const { value } = await layoutFnGetPoliticalLandscape();
data.politicalLandscapeList = value;
};
const fnGetDegreeOfEducation = async () => {
const { value } = await layoutFnGetDegreeOfEducation();
data.degreeOfEducationList = value;
};
const fnGetPersonnelType = async () => {
const { value } = await layoutFnGetPersonnelType();
data.personnelTypeList = value;
};
const fnGetDuties = async () => {
const { value } = await layoutFnGetDuties();
data.dutiesList = value;
};
const fnGetProfessionalTitle = async () => {
const { value } = await layoutFnGetProfessionalTitle();
data.professionalTitleList = value;
};
const fnGetJobType = async () => {
const { value } = await layoutFnGetJobType();
data.jobTypeList = value;
};
const fnGetEmploymentSituation = async () => {
const { value } = await layoutFnGetEmploymentSituation();
data.employmentSituationList = value;
};
const fnDictionaryDelete = debounce(
1000,
async (index, DICTTYPE) => {
let DICTIONARIES_ID = "";
let msgTitle = "";
if (DICTTYPE === "POSITION") {
DICTIONARIES_ID = data.dutiesList[index].DICTIONARIES_ID;
msgTitle = "职务:" + data.dutiesList[index].NAME;
} else if (DICTTYPE === "JOB_TITLE") {
DICTIONARIES_ID = data.professionalTitleList[index].DICTIONARIES_ID;
msgTitle = "职称:" + data.professionalTitleList[index].NAME;
} else if (DICTTYPE === "WORK_TYPE") {
DICTIONARIES_ID = data.jobTypeList[index].DICTIONARIES_ID;
msgTitle = "工种:" + data.jobTypeList[index].NAME;
}
await ElMessageBox.confirm(
"删除此分类会同时删除其他相关已编辑过的人员信息中的分类设置,确定要删除(" +
msgTitle +
")吗?",
{ type: "warning" }
);
await setDictionaryDelete({ DICTIONARIES_ID, DICTTYPE });
if (DICTTYPE === "POSITION") {
data.dutiesList.splice(index, 1);
} else if (DICTTYPE === "JOB_TITLE") {
data.professionalTitleList.splice(index, 1);
} else if (DICTTYPE === "WORK_TYPE") {
data.jobTypeList.splice(index, 1);
}
},
{
atBegin: true,
}
);
watchEffect(() => {
if (data.form.DEPARTMENT_ID) fnGetPost(data.form.DEPARTMENT_ID);
if (data.form.SHIFTDUTYONE)
fnGetUserScheduling(data.form.SHIFTDUTYONE, "schedulingList1");
if (data.form.SHIFTDUTYTWO) fnGetUserCurrentShiftList(data.form.SHIFTDUTYTWO);
});
const stop = watch(
() => data.form.ISSTUDENT,
(val) => {
if (val === "true") {
fnGetNation();
fnGetSex();
fnGetPoliticalLandscape();
fnGetDegreeOfEducation();
fnGetPersonnelType();
fnGetDuties();
fnGetProfessionalTitle();
fnGetJobType();
fnGetEmploymentSituation();
stop && stop();
}
},
{
immediate: true,
}
);
const fnSubmit = debounce(
1000,
async () => {
await useFormValidate(formRef);
const form = { ...data.form };
form.WORKSTATUS = data.scheduleInfo.WORKSTATUS;
form.DURATION = data.scheduleInfo.DURATION;
form.WORKPERIOD = data.scheduleInfo.WORKPERIOD;
if (!USER_ID) {
//
if (data.USERS_NUM > data.allUser) {
ElMessage.error("已超过可创建用户数量");
return;
}
} else {
//
}
ElMessage.success("操作成功");
router.back();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,93 @@
<template>
<el-dialog v-model="visible" title="排班表">
<el-calendar ref="calendarRef">
<template #header="{ date }">
<span>{{ date }}</span>
<el-button-group>
<el-button size="small" @click="fnGetWorkDate('prev-month')">
上个月
</el-button>
<el-button size="small" @click="fnGetWorkDate('today')">
今天
</el-button>
<el-button size="small" @click="fnGetWorkDate('next-month')">
下个月
</el-button>
</el-button-group>
</template>
<template #date-cell="{ data }">
<div>
<div>
<span>{{ data.day.split("-").slice(2).join() }}</span>
<br />
<span v-for="(item, index) in workDateList" :key="index">
<template v-if="item.DATE === data.day">
<span v-if="item.STATE === '1'" class="text-green"></span>
<span v-else-if="item.STATE === '2'" class="text-red">
休班
</span>
</template>
</span>
</div>
</div>
</template>
</el-calendar>
<template #footer>
<el-button @click="visible = false"> 取消 </el-button>
</template>
</el-dialog>
</template>
<script setup>
import { useVModel } from "@vueuse/core";
import { ref, watch } from "vue";
import { getUserScheduleView } from "@/request/enterprise_management.js";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
info: {
type: Object,
required: true,
default: () => {},
},
id: {
type: String,
required: true,
default: "",
},
});
const emits = defineEmits(["update:visible"]);
const visible = useVModel(props, "visible", emits);
const calendarRef = ref(null);
const MONTHS = ref(0);
const workDateList = ref([]);
const fnGetWorkDate = async (type = "today") => {
if (type === "today") MONTHS.value = 0;
if (type === "prev-month") --MONTHS.value;
if (type === "next-month") ++MONTHS.value;
const resData = await getUserScheduleView({
MONTHS: MONTHS.value,
SHIFTWORKRULES_ID: props.id,
WORKSTATUS: props.info.WORKSTATUS,
DURATION: props.info.DURATION,
WORKPERIOD: props.info.WORKPERIOD,
});
workDateList.value = resData.varList;
calendarRef.value.selectDate(type);
};
watch(
() => props.visible,
(val) => {
if (val) fnGetWorkDate();
},
{
immediate: true,
}
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,291 @@
<template>
<div>
<layout-card>
<el-row :gutter="12">
<el-col :span="5">
<layout-department-tree
:department-id="DEPARTMENT_ID"
@node-click="
router.push({
path: '/enterprise_management/user',
query: {
DEPARTMENT_ID: $event.id,
},
})
"
/>
</el-col>
<el-col :span="19">
<el-form
:model="searchForm"
label-width="50px"
@submit.prevent="fnResetPaginationTransfer"
>
<el-row>
<el-col :span="6">
<el-form-item
label="用户名/姓名"
prop="KEYWORDS"
label-width="100px"
>
<el-input
v-model="searchForm.KEYWORDS"
placeholder="请输入关键字"
/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="排班" prop="SHIFTDUTYONE">
<el-select
v-model="searchForm.SHIFTDUTYONE"
@change="fnGetUserScheduling($event, 'schedulingList1')"
>
<el-option
v-for="item in data.schedulingList"
:key="item.SHIFTWORKRULES_ID"
:label="item.NAME"
:value="item.SHIFTWORKRULES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="0px" prop="SHIFTDUTYTWO">
<el-select v-model="searchForm.SHIFTDUTYTWO">
<el-option
v-for="item in data.schedulingList1"
:key="item.SHIFTWORKRULES_ID"
:label="item.NAME"
:value="item.SHIFTWORKRULES_ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">
搜索
</el-button>
<el-button
native-type="reset"
@click="fnResetPaginationTransfer"
>
重置
</el-button>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px" class="end">
<el-button @click="fnImportDialogChangeShow">
导入
</el-button>
<el-button @click="fnImportLearnersDialogChangeShow">
在线学习人员导入
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<layout-table
:data="list"
v-model:pagination="pagination"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="70">
<template v-slot="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column prop="USERNAME" label="用户名" />
<el-table-column prop="NAME" label="姓名" />
<el-table-column prop="DEPARTMENT_NAME" label="部门" />
<el-table-column
prop="POST_NAME"
label="岗位"
width="150"
show-overflow-tooltip
/>
<el-table-column prop="ROLE_NAME" label="角色" />
<el-table-column label="排班类型">
<template v-slot="{ row }">
<span v-if="row.SHIFTDUTYONENAME && row.SHIFTDUTYTWONAME">
{{ row.SHIFTDUTYONENAME }}-{{ row.SHIFTDUTYTWONAME }}
</span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="操作" width="250">
<template v-slot="{ row }">
<el-button
v-if="buttonJurisdiction.edit"
type="primary"
text
link
@click="fnResetPassword(row.USER_ID, row.NAME)"
>
重置密码
</el-button>
<el-button
v-if="buttonJurisdiction.edit && row.ISMAIN === '0'"
type="primary"
text
link
>
排班表
</el-button>
<el-button
v-if="buttonJurisdiction.edit"
type="primary"
text
link
@click="
router.push({
path: '/enterprise_management/user/edit',
query: { USER_ID: row.USER_ID },
})
"
>
编辑
</el-button>
<el-button
v-if="buttonJurisdiction.del && row.ISMAIN === '0'"
type="primary"
text
link
@click="fnDelete(row.USER_ID, row.NAME)"
>
删除
</el-button>
</template>
</el-table-column>
<template #button>
<el-button
type="primary"
v-if="buttonJurisdiction.add"
@click="
router.push({ path: '/enterprise_management/user/add' })
"
>
新增
</el-button>
</template>
</layout-table>
</el-col>
</el-row>
</layout-card>
<layout-import-file
v-model:visible="data.importDialogVisible"
template-url="template/userExcelTemplate.xls"
@submit="fnSubmitImport"
/>
<layout-import-file
v-model:visible="data.importLearnersDialogVisible"
template-url="template/userstudyExcelTemplate.xls"
title="在线学习人员导入"
@submit="fnSubmitLearnersImport"
/>
</div>
</template>
<script setup>
import { serialNumber } from "@/assets/js/utils.js";
import useListData from "@/assets/js/useListData.js";
import {
getUserList,
getUserScheduling,
setUserDelete,
setUserImport,
setUserLearnersImport,
setUserResetPassword,
} from "@/request/enterprise_management.js";
import { reactive, ref } from "vue";
import LayoutDepartmentTree from "@/components/department_tree/index.vue";
import useButtonJurisdiction from "@/assets/js/useButtonJurisdiction.js";
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
import { debounce } from "throttle-debounce";
import { ElMessage, ElMessageBox } from "element-plus";
import LayoutImportFile from "@/components/import_file/index.vue";
const router = useRouter();
const route = useRoute();
const departmentIdDefault = "";
const DEPARTMENT_ID = ref(route.query.DEPARTMENT_ID || departmentIdDefault);
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
useListData(getUserList, {
otherParams: {
DEPARTMENT_ID: DEPARTMENT_ID.value,
},
key: "userList",
});
const buttonJurisdiction = await useButtonJurisdiction("user");
const data = reactive({
schedulingList: [],
schedulingList1: [],
importDialogVisible: false,
importLearnersDialogVisible: false,
});
const fnGetUserScheduling = async (PARENTID, list) => {
const resData = await getUserScheduling({ PARENTID });
data[list] = resData.varList;
searchForm.value.SHIFTDUTYTWO = "";
};
fnGetUserScheduling("0", "schedulingList");
const fnGetDataTransfer = () => {
fnGetData({
DEPARTMENT_ID: DEPARTMENT_ID.value,
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
DEPARTMENT_ID: DEPARTMENT_ID.value,
});
};
onBeforeRouteUpdate((to) => {
DEPARTMENT_ID.value = to.query.DEPARTMENT_ID || departmentIdDefault;
fnResetPaginationTransfer();
});
const fnResetPassword = debounce(
1000,
async (USER_ID, NAME) => {
await ElMessageBox.confirm(`是否将【${NAME}】重置密码为666666`, {
type: "warning",
});
await setUserResetPassword({ USER_ID });
ElMessage.success("重置成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
const fnDelete = debounce(
1000,
async (USER_ID, NAME) => {
await ElMessageBox.confirm(`确定要删除【${NAME}】吗?`, {
type: "warning",
});
await setUserDelete({ USER_ID });
ElMessage.success("删除成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
const fnImportDialogChangeShow = () => {
data.importDialogVisible = !data.importDialogVisible;
};
const fnSubmitImport = async (formData) => {
const resData = await setUserImport(formData);
ElMessage.success(resData.msg);
fnImportDialogChangeShow();
fnResetPaginationTransfer();
};
const fnImportLearnersDialogChangeShow = () => {
data.importLearnersDialogVisible = !data.importLearnersDialogVisible;
};
const fnSubmitLearnersImport = async (formData) => {
const resData = await setUserLearnersImport(formData);
ElMessage.success(resData.msg);
fnImportLearnersDialogChangeShow();
fnResetPaginationTransfer();
};
</script>
<style scoped lang="scss"></style>