feat:后台用户关系绑定
This commit is contained in:
1 parent
507bea5469
commit
f125d8d1bf
14 files changed
+2394
-295
No files matched your search
@@ -0,0 +1,70 @@
|
||||
export const DEFAULT_USER_IDENTITY = "DEFAULT"
|
||||
export const STUDENT_USER_IDENTITY = "STUDENT"
|
||||
|
||||
export interface UserStudentInfo {
|
||||
studentNo: string
|
||||
schoolName: string
|
||||
className: string
|
||||
extJson: string
|
||||
}
|
||||
|
||||
export interface UserInfoFormModel {
|
||||
userId: string
|
||||
username: string
|
||||
password?: string
|
||||
salt: string
|
||||
wxOpenid: string
|
||||
qqOpenid: string
|
||||
lockFlag: string
|
||||
phone?: string
|
||||
deptId: string
|
||||
roleList: any[]
|
||||
postList: any[]
|
||||
nickname: string
|
||||
name: string
|
||||
email: string
|
||||
avatar?: string
|
||||
gender: number
|
||||
staffingType: string
|
||||
identityTypes: string[]
|
||||
studentInfo: UserStudentInfo
|
||||
post: Array<string | number>
|
||||
role: Array<string | number>
|
||||
}
|
||||
|
||||
export const createEmptyStudentInfo = (): UserStudentInfo => ({
|
||||
studentNo: "",
|
||||
schoolName: "",
|
||||
className: "",
|
||||
extJson: "",
|
||||
})
|
||||
|
||||
export const createEmptyUserInfo = (deptId = ""): UserInfoFormModel => ({
|
||||
userId: "",
|
||||
username: "",
|
||||
password: "",
|
||||
salt: "",
|
||||
wxOpenid: "",
|
||||
qqOpenid: "",
|
||||
lockFlag: "0",
|
||||
phone: "",
|
||||
deptId,
|
||||
roleList: [],
|
||||
postList: [],
|
||||
nickname: "",
|
||||
name: "",
|
||||
email: "",
|
||||
avatar: "",
|
||||
gender: 0,
|
||||
staffingType: "2",
|
||||
identityTypes: [DEFAULT_USER_IDENTITY],
|
||||
studentInfo: createEmptyStudentInfo(),
|
||||
post: [],
|
||||
role: [],
|
||||
})
|
||||
|
||||
export const getUserIdentityPayload = (model: UserInfoFormModel) => ({
|
||||
identityTypes: [...model.identityTypes],
|
||||
phone: model.identityTypes.includes(DEFAULT_USER_IDENTITY) ? model.phone : undefined,
|
||||
studentInfo: model.identityTypes.includes(STUDENT_USER_IDENTITY) ? { ...model.studentInfo } : undefined,
|
||||
})
|
||||
@@ -0,0 +1,312 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="model" :rules="formRules" label-width="90px" v-loading="loading || optionsLoading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.username')" prop="username">
|
||||
<el-input v-model.trim="model.username" :disabled="isEdit && !auth('user_sysUser_editUsername')"
|
||||
placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.password')" prop="password">
|
||||
<el-input v-model="model.password" clearable show-password placeholder="请输入密码" type="password" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.name')" prop="name">
|
||||
<el-input v-model="model.name" clearable placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-radio-group v-model="model.gender">
|
||||
<el-radio :label="0">未知</el-radio>
|
||||
<el-radio :label="1">男</el-radio>
|
||||
<el-radio :label="2">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="!isDeptAdmin" :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.role')" prop="role">
|
||||
<el-select v-model="model.role" clearable multiple placeholder="请选择角色" class="w100">
|
||||
<el-option v-for="item in roleData" :key="item.roleId" :label="item.roleName"
|
||||
:value="item.roleId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="!isDeptAdmin" :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.post')" prop="post">
|
||||
<el-select v-model="model.post" clearable multiple placeholder="请选择岗位" class="w100">
|
||||
<el-option v-for="item in postData" :key="item.postId" :label="item.postName"
|
||||
:value="item.postId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="!isEdit" :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.dept')" prop="deptId">
|
||||
<el-tree-select v-model="model.deptId" :data="deptData"
|
||||
:props="{ value: 'id', label: 'name', children: 'children', disabled: 'isLock' }" filterable
|
||||
check-strictly class="w100" clearable placeholder="请选择所属部门">
|
||||
<template #default="{ node, data }">
|
||||
<el-tooltip v-if="data.isLock" effect="dark" content="无数据权限" placement="right">
|
||||
<span>
|
||||
{{ node.label }}
|
||||
<SvgIcon name="ele-Lock" />
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.email')" prop="email">
|
||||
<el-input v-model="model.email" clearable placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.nickname')" prop="nickname">
|
||||
<el-input v-model="model.nickname" clearable placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="编制" prop="staffingType">
|
||||
<dict-select v-model="model.staffingType"
|
||||
:disabled="isEdit && !auth('user_sysUser_editStaffingType')" dict-code="user_staffing_type" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item :label="$t('sysuser.lockFlag')" prop="lockFlag">
|
||||
<el-radio-group v-model="model.lockFlag">
|
||||
<el-radio v-for="(item, index) in lock_flag" :key="index" :label="item.value" border>
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-divider>身份信息</el-divider>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<div class="identity-block">
|
||||
<div class="identity-block__title">身份选择</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="用户身份" prop="identityTypes">
|
||||
<dict-select v-model="model.identityTypes" dict-code="user_identity_type" multiple
|
||||
placeholder="请选择用户身份" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col v-if="hasDefaultIdentity" :span="24" class="mb20">
|
||||
<div class="identity-block">
|
||||
<div class="identity-block__title">默认身份</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="手机号" prop="phone" required>
|
||||
<el-input v-model="model.phone" clearable placeholder="请输入手机号" />
|
||||
<div class="identity-field-tip">该手机号与默认身份绑定</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
<template v-if="hasStudentIdentity">
|
||||
<el-col :span="24" class="mb20">
|
||||
<div class="identity-block">
|
||||
<div class="identity-block__title">学生身份</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="学号" prop="studentInfo.studentNo">
|
||||
<el-input v-model.trim="model.studentInfo.studentNo" clearable
|
||||
placeholder="请输入学号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="学校名称" prop="studentInfo.schoolName">
|
||||
<el-input v-model.trim="model.studentInfo.schoolName" clearable
|
||||
placeholder="请输入学校名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="班级名称" prop="studentInfo.className">
|
||||
<el-input v-model.trim="model.studentInfo.className" clearable
|
||||
placeholder="请输入班级名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="UserInfoForm">
|
||||
import type { FormInstance, FormRules } from "element-plus"
|
||||
import { validatePhone, validateUsername } from "/@/api/admin/user"
|
||||
import { deptTree } from "/@/api/admin/dept"
|
||||
import { list as roleList } from "/@/api/admin/role"
|
||||
import { list as postList } from "/@/api/admin/post"
|
||||
import { useDict } from "/@/hooks/dict"
|
||||
import { useMessage } from "/@/hooks/message"
|
||||
import { useUserInfo } from "/@/stores/userInfo"
|
||||
import { auth } from "/@/utils/authFunction"
|
||||
import { rule } from "/@/utils/validate"
|
||||
import { DEFAULT_USER_IDENTITY, STUDENT_USER_IDENTITY, type UserInfoFormModel } from "./user-info-form"
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
model: UserInfoFormModel
|
||||
defaultDeptId?: string
|
||||
loading?: boolean
|
||||
}>(),
|
||||
{
|
||||
defaultDeptId: "",
|
||||
loading: false,
|
||||
},
|
||||
)
|
||||
|
||||
const {
|
||||
userInfos: { roleCodes },
|
||||
} = useUserInfo()
|
||||
// @ts-ignore
|
||||
const { lock_flag } = useDict("lock_flag")
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const optionsLoading = ref(false)
|
||||
const deptData = ref<any[]>([])
|
||||
const roleData = ref<any[]>([])
|
||||
const postData = ref<any[]>([])
|
||||
const model = computed(() => props.model)
|
||||
const isEdit = computed(() => Boolean(model.value.userId))
|
||||
const isDeptAdmin = computed(() => roleCodes.includes("DEPT_ADMIN"))
|
||||
const hasDefaultIdentity = computed(() => model.value.identityTypes.includes(DEFAULT_USER_IDENTITY))
|
||||
const hasStudentIdentity = computed(() => model.value.identityTypes.includes(STUDENT_USER_IDENTITY))
|
||||
|
||||
const validateIdentityPhone = (_currentRule: any, value: string, callback: (error?: Error) => void) => {
|
||||
if (!hasDefaultIdentity.value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
if (!value) {
|
||||
callback(new Error("手机号不能为空"))
|
||||
return
|
||||
}
|
||||
rule.validatePhone(_currentRule, value, callback)
|
||||
}
|
||||
|
||||
const validatePhoneUnique = (currentRule: any, value: string, callback: (error?: Error) => void) => {
|
||||
if (!hasDefaultIdentity.value || !value) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
validatePhone(currentRule, value, callback, isEdit.value)
|
||||
}
|
||||
|
||||
const formRules: FormRules = {
|
||||
username: [
|
||||
{ required: true, message: "用户名不能为空", trigger: "blur" },
|
||||
{ min: 5, max: 60, message: "用户名称长度必须介于5-60个字符之间", trigger: "blur" },
|
||||
{ pattern: /^[^\u4e00-\u9fa5]+$/, message: "用户名称不能包含中文", trigger: "blur" },
|
||||
{
|
||||
validator: (currentRule: any, value: any, callback: any) => {
|
||||
validateUsername(currentRule, value, callback, isEdit.value)
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "密码不能为空", trigger: "blur" },
|
||||
{ min: 6, max: 20, message: "用户密码长度必须介于6-20个字符之间", trigger: "blur" },
|
||||
],
|
||||
name: [
|
||||
{ validator: rule.overLength, trigger: "blur" },
|
||||
{ required: true, message: "姓名不能为空", trigger: "blur" },
|
||||
{ validator: rule.validatorPersonName, trigger: "blur" },
|
||||
],
|
||||
deptId: [{ required: true, message: "部门不能为空", trigger: "change" }],
|
||||
role: isDeptAdmin.value ? [] : [{ required: true, message: "角色不能为空", trigger: "change" }],
|
||||
phone: [
|
||||
{ validator: validateIdentityPhone, trigger: "blur" },
|
||||
{ validator: validatePhoneUnique, trigger: "blur" },
|
||||
],
|
||||
email: [{ validator: rule.overLength, trigger: "blur" }],
|
||||
lockFlag: [{ required: true, message: "状态不能为空", trigger: "change" }],
|
||||
identityTypes: [{ required: true, type: "array", min: 1, message: "用户身份不能为空", trigger: "change" }],
|
||||
"studentInfo.studentNo": [
|
||||
{ required: true, message: "学号不能为空", trigger: "blur" },
|
||||
{ validator: rule.overLength, trigger: "blur" },
|
||||
],
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [...model.value.identityTypes],
|
||||
() => {
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate([
|
||||
"phone",
|
||||
"studentInfo.studentNo",
|
||||
"studentInfo.schoolName",
|
||||
"studentInfo.className",
|
||||
"studentInfo.extJson",
|
||||
])
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
const prepare = async () => {
|
||||
optionsLoading.value = true
|
||||
try {
|
||||
const requests: Promise<any>[] = [roleList(), postList()]
|
||||
if (!isEdit.value) requests.push(deptTree())
|
||||
const [roleRes, postRes, deptRes] = await Promise.all(requests)
|
||||
roleData.value = roleRes.data || []
|
||||
postData.value = postRes.data || []
|
||||
if (deptRes) deptData.value = deptRes.data || []
|
||||
|
||||
if (!isEdit.value) {
|
||||
if (!model.value.deptId) model.value.deptId = props.defaultDeptId
|
||||
if (!isDeptAdmin.value && !model.value.role.length && roleData.value.length) {
|
||||
model.value.role = [roleData.value[0].roleId]
|
||||
}
|
||||
if (!isDeptAdmin.value && !model.value.post.length && postData.value.length) {
|
||||
model.value.post = [postData.value[0].postId]
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
useMessage().error(error?.msg || "加载用户表单选项失败")
|
||||
} finally {
|
||||
optionsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const validate = async () => Boolean(await formRef.value?.validate().catch(() => false))
|
||||
const clearValidate = () => formRef.value?.clearValidate()
|
||||
|
||||
defineExpose({ prepare, validate, clearValidate })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.identity-block {
|
||||
padding: 14px 16px 0;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 4px;
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
}
|
||||
|
||||
.identity-block__title {
|
||||
margin-bottom: 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.identity-field-tip {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user