【v3.8.3】用户大改造,取消原职位换成职位字典、原职位改成岗位职级、新增主岗位和兼职岗位

pull/8786/merge
JEECG 2025-09-14 10:24:02 +08:00
parent 862aaa8632
commit adeebee840
7 changed files with 277 additions and 340 deletions

View File

@ -1,71 +0,0 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="用户代理" @ok="handleSubmit" destroyOnClose>
<BasicForm @register="registerForm" />
<template #insertFooter>
<Popconfirm title="确定删除当前配置的代理吗?" @confirm="handleDel">
<a-button v-if="agentData.id"><Icon icon="ant-design:clear-outlined" />删除代理</a-button>
</Popconfirm>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formAgentSchema } from './user.data';
import { deleteAgent, getUserAgent, saveOrUpdateAgent } from './user.api';
import { Popconfirm } from 'ant-design-vue';
// Emits
const emit = defineEmits(['success', 'register']);
//
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
schemas: formAgentSchema,
showActionButtonGroup: false,
});
//
const agentData = ref<any>({});
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//
await resetFields();
setModalProps({ confirmLoading: false });
//
const res = await getUserAgent({ userName: data.userName });
data = res.result ? res.result : data;
//
agentData.value = { ...data };
//
await setFieldsValue({ ...data });
});
//
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
//
await saveOrUpdateAgent(values);
//
closeModal();
//
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
/**
* 删除代理
*/
async function handleDel() {
const reload = async () => {
await resetFields();
await setFieldsValue({ userName: agentData.value.userName });
//
closeModal();
emit('success');
};
if (agentData.value.id) {
await deleteAgent({ id: agentData.value.id }, reload);
}
}
</script>

View File

@ -139,6 +139,26 @@
//update-begin-author:taoyan date:2022-5-24 for: VUEN-1117issue0523
setProps({ disabled: !showFooter.value });
//update-end-author:taoyan date:2022-5-24 for: VUEN-1117issue0523
if(unref(isUpdate)){
updateSchema([
//
{
field: 'mainDepPostId',
componentProps: { params: { departIds: data.record.selecteddeparts, parentId: data.record.selecteddeparts } },
},
{
field: 'otherDepPostId',
componentProps: { params: { departIds: data.record.selecteddeparts, parentId: data.record.selecteddeparts } },
}
]);
}
//
updateSchema([
{
field: 'mainDepPostId',
defaultValue: data?.mainDepPostId || '',
}
])
});
//
const getTitle = computed(() => {

View File

@ -1,81 +0,0 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :width="800" title="离职交接" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup name="user-quit-agent-modal">
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formQuitAgentSchema } from './user.data';
import { getUserAgent, userQuitAgent } from './user.api';
import dayjs from 'dayjs';
// Emits
const emit = defineEmits(['success', 'register']);
//
const [registerForm, { resetFields, setFieldsValue, validate, clearValidate, updateSchema }] = useForm({
schemas: formQuitAgentSchema,
showActionButtonGroup: false,
});
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//
await resetFields();
setModalProps({ confirmLoading: true });
let userId = data.userId;
//
const res = await getUserAgent({ userName: data.userName });
data = res.result ? res.result : data;
let date = new Date();
if (!data.startTime) {
data.startTime = dayjs(date).format('YYYY-MM-DD HH:mm:ss');
}
if (!data.endTime) {
data.endTime = getYear(date);
}
//update-begin---author:wangshuai ---date:20230703 forQQYUN-56855------------
await updateSchema(
[{
field:'agentUserName',
componentProps:{
excludeUserIdList:[userId]
}
}]
)
//update-end---author:wangshuai ---date:20230703 forQQYUN-56855------------
//
await setFieldsValue({ ...data });
setModalProps({ confirmLoading: false });
});
//
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
//
await userQuitAgent(values);
//
closeModal();
//
emit('success',values.userName);
} finally {
setModalProps({ confirmLoading: false });
}
}
/**
* 获取后30年
*/
function getYear(date) {
//update-begin---author:wangshuai ---date:20221207 for[QQYUN-3285] ------------
//
let y = date.getFullYear() + 30;
let m = dayjs(date).format('MM');
let d = dayjs(date).format('DD');
let hour = dayjs(date).format('HH:mm:ss');
console.log('年月日', y + '-' + m + '-' + d);
return dayjs(y + '-' + m + '-' + d + ' ' + hour).format('YYYY-MM-DD HH:mm:ss');
//update-end---author:wangshuai ---date:20221207 for[QQYUN-3285] --------------
}
</script>

View File

@ -0,0 +1,193 @@
<template>
<div>
<a-upload
:headers="headers"
:action="uploadAction"
:showUploadList="false"
:multiple="false"
:maxCount="1"
:data="uploadData"
accept=".xls,.xlsx"
@change="onFileChange"
:beforeUpload="beforeUpload"
ref="uploadRef"
style="width: 100%"
>
<a-button type="primary" preIcon="ant-design:import-outlined" v-if="percent === 0 || percent === 100">{{ btnText }}</a-button>
<a-button @click.prevent.stop="progressOpen = true" type="primary" v-else>
<LoadingOutlined />
导入中
</a-button>
</a-upload>
<a-modal v-model:open="progressOpen" title="文件上传" :body-style="{ padding: '20px' }" :destroyOnClose="true" :footer="null">
<div>
<div style="display: flex">
<div class="label">当前上传文件</div>
<div class="file-name">{{ fileName }}</div>
</div>
<div style="margin-top: 10px; display: flex">
<div class="label">当前文件上传进度:</div>
<a-progress :percent="percent" style="width: 300px; margin-left: 10px" :format="(percent) => `${percent}%`"></a-progress>
</div>
</div>
</a-modal>
</div>
</template>
<script lang="ts">
import { defineComponent, onUnmounted, ref, computed, watch } from 'vue';
import { getTenantId, getToken } from '@/utils/auth';
import { randomString } from '@/utils/common/compUtils';
import { useMessage } from '@/hooks/web/useMessage';
import { useGlobSetting } from '@/hooks/setting';
import { defHttp } from '@/utils/http/axios';
import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'ImportExcelProgress',
components: { LoadingOutlined },
props: {
//
uploadUrl: {
type: String,
required: true,
},
//
btnText: {
type: String,
default: '导入',
},
//
type: {
type: String,
default: 'user',
},
},
emit: ['success', 'register'],
setup(props, { emit }) {
const fileName = ref<string>('');
//
const uploadData = ref({});
//
const headers = ref({});
const { createMessage, createWarningModal } = useMessage();
//
const fileUploadInterval = ref<any>();
const glob = useGlobSetting();
const progressOpen = ref(false);
//
const percent = ref<number>(0);
const uploadRef = ref();
//
const uuid = ref();
const baseApiUrl = glob.domainUrl;
const uploadAction = computed(() => {
return baseApiUrl + props.uploadUrl;
});
//
function onFileChange(info) {
if (info.file.name) {
fileName.value = info.file.name;
}
if (info.file.status === 'done') {
if (info.file.response.success) {
if (info.file.response && info.file.response.code === 201) {
let {
message,
result: { msg, fileUrl, fileName },
} = info.file.response;
let href = glob.uploadUrl + fileUrl;
createWarningModal({
title: message,
centered: false,
content: `<div>
<span>${msg}</span><br/>
<span>具体详情请<a href = ${href} download = ${fileName}> 点击下载 </a> </span>
</div>`,
});
percent.value = 100;
emit('success');
} else {
createMessage.success('文件导入成功!');
percent.value = 100;
emit('success');
}
} else {
createMessage.error(info.file.response.message || `${info.file.name} 导入失败.`);
}
clear();
} else if (info.file.status === 'error' || (info.file.response && info.file.response.code === 500)) {
clear();
createMessage.error(info.file.response?.message || `导入失败,可能原因文件超出上传大小,请查看控制台或服务端日志`);
}
}
/**
* 清除定时器
*/
function clear() {
//
clearInterval(fileUploadInterval.value);
}
/**
* 上传之前的回调
*/
function beforeUpload() {
percent.value = 1;
headers.value = {
'X-Access-Token': getToken(),
'X-Tenant-Id': getTenantId() ? getTenantId() : '0',
};
uuid.value = randomString(16);
uploadData.value['fileKey'] = uuid.value;
progressOpen.value = true;
fileUploadInterval.value = setInterval(() => {
defHttp
.get({ url: '/sys/user/getUploadFileProgress', params: { fileKey: uuid.value, type: props.type } }, { isTransformResponse: false })
.then((res) => {
if (res.success) {
if (res.result) {
percent.value = Math.round(res.result);
if (res.result == 100) {
clear();
}
}
}
})
.catch(() => {
clear();
});
}, 2000);
}
onUnmounted(() => {
clear();
});
return {
fileName,
uploadData,
headers,
onFileChange,
beforeUpload,
progressOpen,
percent,
uploadAction,
uploadRef,
};
},
});
</script>
<style scoped lang="less">
.label {
font-size: 14px;
color: #666;
}
.file-name {
color: #151515;
font-size: 12px;
margin-top: 2px;
}
</style>

View File

@ -5,8 +5,9 @@
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> </a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls" :disabled="isDisabledAuth('system:user:export')"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls"></j-upload-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls" > 导出</a-button>
<!-- <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls" v-auth="'system:user:import'"></j-upload-button>-->
<import-excel-progress :upload-url="getImportUrl" @success="reload"></import-excel-progress>
<a-button type="primary" @click="openModal(true, {})" preIcon="ant-design:hdd-outlined"> 回收站</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
@ -40,12 +41,8 @@
<UserDrawer @register="registerDrawer" @success="handleSuccess" />
<!--修改密码-->
<PasswordModal @register="registerPasswordModal" @success="reload" />
<!--用户代理-->
<UserAgentModal @register="registerAgentModal" @success="reload" />
<!--回收站-->
<UserRecycleBinModal @register="registerModal" @success="reload" />
<!-- 离职受理人弹窗 -->
<UserQuitAgentModal @register="registerQuitAgentModal" @success="reload" />
<!-- 离职人员列弹窗 -->
<UserQuitModal @register="registerQuitModal" @success="reload" />
</div>
@ -58,9 +55,7 @@
import UserDrawer from './UserDrawer.vue';
import UserRecycleBinModal from './UserRecycleBinModal.vue';
import PasswordModal from './PasswordModal.vue';
import UserAgentModal from './UserAgentModal.vue';
import JThirdAppButton from '/@/components/jeecg/thirdApp/JThirdAppButton.vue';
import UserQuitAgentModal from './UserQuitAgentModal.vue';
import UserQuitModal from './UserQuitModal.vue';
import { useDrawer } from '/@/components/Drawer';
import { useListPage } from '/@/hooks/system/useListPage';
@ -68,7 +63,8 @@
import { useMessage } from '/@/hooks/web/useMessage';
import { columns, searchFormSchema } from './user.data';
import { listNoCareTenant, deleteUser, batchDeleteUser, getImportUrl, getExportUrl, frozenBatch } from './user.api';
import {usePermission} from "/@/hooks/web/usePermission";
import { usePermission } from '/@/hooks/web/usePermission';
import ImportExcelProgress from './components/ImportExcelProgress.vue';
const { createMessage, createConfirm } = useMessage();
const { isDisabledAuth } = usePermission();
@ -185,12 +181,6 @@
function handleChangePassword(username) {
openPasswordModal(true, { username });
}
/**
* 打开代理人弹窗
*/
function handleAgentSettings(userName) {
openAgentModal(true, { userName });
}
/**
* 冻结解冻
*/
@ -279,21 +269,9 @@
confirm: handleFrozen.bind(null, record, 1),
},
},
{
label: '代理人',
onClick: handleAgentSettings.bind(null, record.username),
},
];
}
/**
* 离职
* @param userName
*/
function handleQuit(userName) {
//
openQuitAgentModal(true, { userName });
}
</script>
<style scoped></style>

View File

@ -6,9 +6,6 @@ enum Api {
list = '/sys/user/list',
save = '/sys/user/add',
edit = '/sys/user/edit',
agentSave = '/sys/sysUserAgent/add',
agentEdit = '/sys/sysUserAgent/edit',
deleteAgent = '/sys/sysUserAgent/delete',
getUserRole = '/sys/user/queryUserRole',
duplicateCheck = '/sys/duplicate/check',
deleteUser = '/sys/user/delete',
@ -196,36 +193,7 @@ export const frozenBatch = (params, handleSuccess) => {
handleSuccess();
});
};
/**
* 获取用户代理
* @param params
*/
export const getUserAgent = (params) => defHttp.get({ url: Api.getUserAgent, params }, { isTransformResponse: false });
/**
* 保存或者更新用户代理
* @param params
*/
export const saveOrUpdateAgent = (params) => {
let url = params.id ? Api.agentEdit : Api.agentSave;
return defHttp.post({ url: url, params });
};
/**
* 代理删除
* @param params
*/
export const deleteAgent = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteAgent, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
* 用户离职(新增代理人和用户状态变更操作)
* @param params
*/
export const userQuitAgent = (params) => {
return defHttp.put({ url: Api.userQuitAgent, params });
};
/**
* 用户离职列表

View File

@ -49,6 +49,11 @@ export const columns: BasicColumn[] = [
width: 150,
dataIndex: 'departIds_dictText',
},
{
title: '主岗位',
width: 150,
dataIndex: 'mainDepPostId_dictText',
},
{
title: '状态',
dataIndex: 'status_dictText',
@ -177,11 +182,11 @@ export const formSchema: FormSchema[] = [
{
label: '工号',
field: 'workNo',
required: true,
required: false,
component: 'Input',
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_user', 'work_no', model, schema, true),
dynamicRules: ({ model, schema }) => rules.duplicateCheckRule('sys_user', 'work_no', model, schema, false),
},
{
/* {
label: '职务',
field: 'post',
required: false,
@ -189,6 +194,15 @@ export const formSchema: FormSchema[] = [
componentProps: {
labelKey: 'name',
},
},*/
{
label: '职务',
field: 'positionType',
required: false,
component: 'JDictSelectTag',
componentProps: {
dictCode: "user_position"
},
},
{
label: '角色',
@ -220,10 +234,21 @@ export const formSchema: FormSchema[] = [
field: 'departIds',
componentProps: { options },
},
//
{
field: 'mainDepPostId',
componentProps: { params: { departIds: values?values.value.join(","): "" } },
},
{
field: 'otherDepPostId',
componentProps: { params: { departIds: values?values.value.join(","): "" } },
}
]);
//update-begin---author:wangshuai---date:2024-05-11---for:issues/1222---
if(!values){
formModel.departIds = [];
formModel.mainDepPostId = "";
formModel.otherDepPostId = "";
return;
}
//update-end---author:wangshuai---date:2024-05-11---for:issues/1222---
@ -233,6 +258,35 @@ export const formSchema: FormSchema[] = [
};
},
},
{
label: '主岗位',
field: 'mainDepPostId',
component: 'JSelectDepartPost',
componentProps: {
rowKey: 'id',
multiple: false
},
ifShow: ({ values }) => {
if(!values.selecteddeparts){
return false;
}
return !(values.selecteddeparts instanceof Array && values.selecteddeparts.length == 0);
},
},
{
label: '兼职岗位',
field: 'otherDepPostId',
component: 'JSelectDepartPost',
componentProps: {
rowKey: 'id',
},
ifShow: ({ values }) => {
if(!values.selecteddeparts){
return false;
}
return !(values.selecteddeparts instanceof Array && values.selecteddeparts.length == 0);
},
},
{
label: '租户',
field: 'relTenantIds',
@ -296,10 +350,10 @@ export const formSchema: FormSchema[] = [
label: '邮箱',
field: 'email',
component: 'Input',
required: true,
required: false,
dynamicRules: ({ model, schema }) => {
return [
{ ...rules.duplicateCheckRule('sys_user', 'email', model, schema, true)[0], trigger: 'blur' },
{ ...rules.duplicateCheckRule('sys_user', 'email', model, schema, false)[0], trigger: 'blur' },
{ ...rules.rule('email', false)[0], trigger: 'blur' },
];
},
@ -368,131 +422,7 @@ export const formPasswordSchema: FormSchema[] = [
},
];
export const formAgentSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
field: 'userName',
label: '用户名',
component: 'Input',
componentProps: {
readOnly: true,
allowClear: false,
},
},
{
field: 'agentUserName',
label: '代理人用户名',
required: true,
component: 'JSelectUser',
componentProps: {
rowKey: 'username',
labelKey: 'realname',
maxSelectCount: 10,
},
},
{
field: 'startTime',
label: '代理开始时间',
component: 'DatePicker',
required: true,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择代理开始时间',
getPopupContainer: () => document.body,
},
},
{
field: 'endTime',
label: '代理结束时间',
component: 'DatePicker',
required: true,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择代理结束时间',
getPopupContainer: () => document.body,
},
},
{
field: 'status',
label: '状态',
component: 'JDictSelectTag',
defaultValue: '1',
componentProps: {
dictCode: 'valid_status',
type: 'radioButton',
},
},
];
export const formQuitAgentSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
field: 'userName',
label: '用户名',
component: 'Input',
componentProps: {
readOnly: true,
allowClear: false,
},
},
{
field: 'agentUserName',
label: '交接人员',
//required: true,
component: 'JSelectUser',
componentProps: {
rowKey: 'username',
labelKey: 'realname',
maxSelectCount: 1,
},
},
{
field: 'startTime',
label: '交接开始时间',
component: 'DatePicker',
//required: true,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择交接开始时间',
getPopupContainer: () => document.body,
},
},
{
field: 'endTime',
label: '交接结束时间',
component: 'DatePicker',
//required: true,
componentProps: {
showTime: true,
valueFormat: 'YYYY-MM-DD HH:mm:ss',
placeholder: '请选择交接结束时间',
getPopupContainer: () => document.body,
},
},
{
field: 'status',
label: '状态',
component: 'JDictSelectTag',
defaultValue: '1',
componentProps: {
dictCode: 'valid_status',
type: 'radioButton',
},
},
];
//
export const userTenantColumns: BasicColumn[] = [