470 lines
15 KiB
Vue
470 lines
15 KiB
Vue
<template>
|
||
<div class="edit-drawer">
|
||
<el-drawer
|
||
v-model="visible"
|
||
:title="drawerTitle"
|
||
direction="rtl"
|
||
size="780px"
|
||
:before-close="handleClose"
|
||
>
|
||
<el-form ref="formRef" :model="formData" label-width="100px" :rules="formRules">
|
||
<el-divider content-position="left">基本信息</el-divider>
|
||
|
||
<!-- 医助创建诊单数(仅编辑时显示) -->
|
||
<el-form-item v-if="mode === 'edit' && formData.diagnosis_count != null" label="创建诊单数">
|
||
<span class="text-primary font-medium">{{ formData.diagnosis_count }}</span>
|
||
<span class="text-tx-secondary text-sm ml-2">条</span>
|
||
</el-form-item>
|
||
|
||
<!-- 账号输入框 -->
|
||
<el-form-item label="账号" prop="account">
|
||
<el-input
|
||
v-model="formData.account"
|
||
placeholder="请输入账号"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
|
||
<!-- 医生头像 -->
|
||
<el-form-item label="头像">
|
||
<div>
|
||
<div>
|
||
<material-picker v-model="formData.avatar" :limit="1" />
|
||
</div>
|
||
<div class="form-tips">建议尺寸:100*100px,支持jpg,jpeg,png格式</div>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<!-- 姓名输入框 -->
|
||
<el-form-item label="姓名" prop="name">
|
||
<el-input v-model="formData.name" placeholder="请输入姓名" clearable />
|
||
</el-form-item>
|
||
|
||
<!-- 性别 -->
|
||
<el-form-item label="性别" prop="gender">
|
||
<el-radio-group v-model="formData.gender">
|
||
<el-radio :label="1">男</el-radio>
|
||
<el-radio :label="2">女</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<!-- 年龄 -->
|
||
<el-form-item label="年龄" prop="age">
|
||
<el-input-number v-model="formData.age" :min="18" :max="100" placeholder="请输入年龄" />
|
||
</el-form-item>
|
||
|
||
<!-- 手机号输入框 -->
|
||
<el-form-item label="手机号" prop="phone">
|
||
<el-input v-model="formData.phone" placeholder="请输入手机号" clearable />
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">组织信息</el-divider>
|
||
|
||
<!-- 归属部门:数据权限内仅末级可选,父级仅用于展开 -->
|
||
<el-form-item label="归属部门" prop="dept_id">
|
||
<div class="w-full">
|
||
<el-tree-select
|
||
class="flex-1 w-full"
|
||
v-model="formData.dept_id"
|
||
:data="optionsData.dept"
|
||
clearable
|
||
multiple
|
||
node-key="id"
|
||
:props="{
|
||
label: 'name',
|
||
disabled: deptTreeNodeDisabled
|
||
}"
|
||
check-strictly
|
||
:default-expand-all="true"
|
||
placeholder="请选择末级归属部门"
|
||
/>
|
||
<div class="form-tips">仅可选末级部门(含下级的父部门不可选),与数据权限范围一致。</div>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<!-- 岗位 -->
|
||
<el-form-item label="岗位" prop="jobs_id">
|
||
<el-select
|
||
class="flex-1"
|
||
v-model="formData.jobs_id"
|
||
clearable
|
||
multiple
|
||
placeholder="请选择岗位"
|
||
>
|
||
<el-option
|
||
v-for="(item, index) in optionsData.jobs"
|
||
:key="index"
|
||
:label="item.name"
|
||
:value="item.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">专业信息</el-divider>
|
||
|
||
<!-- 职称 -->
|
||
<el-form-item label="职称" prop="title">
|
||
<el-input v-model="formData.title" placeholder="如:主任医师、副主任医师" clearable />
|
||
</el-form-item>
|
||
|
||
<!-- 所属科室 -->
|
||
<el-form-item label="所属科室" prop="department">
|
||
<el-select v-model="formData.department" placeholder="请选择所属科室" clearable class="flex-1">
|
||
<el-option v-for="item in departmentOptions" :key="item.value" :label="item.name" :value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<!-- 擅长领域 -->
|
||
<el-form-item label="擅长领域" prop="specialty">
|
||
<el-input
|
||
v-model="formData.specialty"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入擅长领域"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">履历信息</el-divider>
|
||
|
||
<!-- 教育背景 -->
|
||
<el-form-item label="教育背景" prop="education">
|
||
<el-input
|
||
v-model="formData.education"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入教育背景,如:北京中医药大学 中医学博士"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<!-- 工作经历 -->
|
||
<el-form-item label="工作经历" prop="experience">
|
||
<el-input
|
||
v-model="formData.experience"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入工作经历"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<!-- 荣誉资质 -->
|
||
<el-form-item label="荣誉资质" prop="honors">
|
||
<el-input
|
||
v-model="formData.honors"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入荣誉资质"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">账号设置</el-divider>
|
||
|
||
<!-- 密码输入框 -->
|
||
<el-form-item label="密码" prop="password">
|
||
<el-input
|
||
v-model="formData.password"
|
||
show-password
|
||
clearable
|
||
:placeholder="mode === 'edit' ? '不修改请留空' : '请输入密码'"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<!-- 确认密码输入框 -->
|
||
<el-form-item label="确认密码" prop="password_confirm">
|
||
<el-input
|
||
v-model="formData.password_confirm"
|
||
show-password
|
||
clearable
|
||
:placeholder="mode === 'edit' ? '不修改请留空' : '请输入确认密码'"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<!-- 医生状态 -->
|
||
<el-form-item label="状态">
|
||
<el-switch v-model="formData.disable" :active-value="0" :inactive-value="1" />
|
||
<span class="ml-2 text-gray-500">{{ formData.disable === 0 ? '启用' : '禁用' }}</span>
|
||
</el-form-item>
|
||
|
||
<!-- 多处登录 -->
|
||
<el-form-item label="多处登录">
|
||
<div>
|
||
<el-switch
|
||
v-model="formData.multipoint_login"
|
||
:active-value="1"
|
||
:inactive-value="0"
|
||
/>
|
||
<div class="form-tips">允许多人同时在线登录</div>
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<template #footer>
|
||
<div class="flex justify-end">
|
||
<el-button @click="handleClose">取消</el-button>
|
||
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||
</div>
|
||
</template>
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import type { FormInstance } from 'element-plus'
|
||
import { adminAdd, adminDetail, adminEdit } from '@/api/perms/admin'
|
||
import { deptAll } from '@/api/org/department'
|
||
import { jobsAll } from '@/api/org/post'
|
||
import { getDictData } from '@/api/app'
|
||
import { useDictOptions } from '@/hooks/useDictOptions'
|
||
import feedback from '@/utils/feedback'
|
||
|
||
/** 归属部门:禁止选父级,仅末级可选(status=1 且无子节点) */
|
||
function deptTreeNodeDisabled(data: { status?: number; children?: unknown[] }) {
|
||
if (data.status !== 1) return true
|
||
const ch = data.children
|
||
return Array.isArray(ch) && ch.length > 0
|
||
}
|
||
|
||
const emit = defineEmits(['success', 'close'])
|
||
const formRef = shallowRef<FormInstance>()
|
||
const visible = ref(false)
|
||
const mode = ref('add')
|
||
const loading = ref(false)
|
||
|
||
const departmentOptions = ref<any[]>([])
|
||
const fetchDepartmentOptions = async () => {
|
||
const data = await getDictData({ type: 'department' })
|
||
departmentOptions.value = data?.department || []
|
||
}
|
||
|
||
const drawerTitle = computed(() => {
|
||
return mode.value == 'edit' ? '编辑医助' : '新增医助'
|
||
})
|
||
|
||
const formData = reactive({
|
||
id: '',
|
||
account: '',
|
||
name: '',
|
||
gender: 1,
|
||
age: null as number | null,
|
||
phone: '',
|
||
role_id: [2], // 固定为医助角色
|
||
dept_id: [],
|
||
jobs_id: [],
|
||
avatar: '',
|
||
title: '', // 职称
|
||
department: '', // 所属科室
|
||
specialty: '', // 擅长领域
|
||
education: '', // 教育背景
|
||
experience: '', // 工作经历
|
||
honors: '', // 荣誉资质
|
||
password: '',
|
||
password_confirm: '',
|
||
disable: 0,
|
||
multipoint_login: 1,
|
||
diagnosis_count: null as number | null // 医助创建诊单数(详情接口返回)
|
||
})
|
||
|
||
// 获取部门和岗位选项
|
||
const { optionsData } = useDictOptions<{
|
||
jobs: any[]
|
||
dept: any[]
|
||
}>({
|
||
jobs: {
|
||
api: jobsAll
|
||
},
|
||
dept: {
|
||
api: deptAll,
|
||
params: { apply_data_scope: 1 }
|
||
}
|
||
})
|
||
|
||
// 密码确认验证
|
||
const passwordConfirmValidator = (rule: object, value: string, callback: any) => {
|
||
if (formData.password) {
|
||
if (!value) callback(new Error('请再次输入密码'))
|
||
if (value !== formData.password) callback(new Error('两次输入密码不一致!'))
|
||
}
|
||
callback()
|
||
}
|
||
|
||
// 手机号验证
|
||
const phoneValidator = (rule: object, value: string, callback: any) => {
|
||
if (value && !/^1[3-9]\d{9}$/.test(value)) {
|
||
callback(new Error('请输入正确的手机号'))
|
||
} else {
|
||
callback()
|
||
}
|
||
}
|
||
|
||
const formRules = reactive({
|
||
account: [
|
||
{
|
||
required: true,
|
||
message: '请输入账号',
|
||
trigger: ['blur']
|
||
}
|
||
],
|
||
name: [
|
||
{
|
||
required: true,
|
||
message: '请输入姓名',
|
||
trigger: ['blur']
|
||
}
|
||
],
|
||
gender: [
|
||
{
|
||
required: true,
|
||
message: '请选择性别',
|
||
trigger: ['change']
|
||
}
|
||
],
|
||
dept_id: [
|
||
{
|
||
required: true,
|
||
type: 'array',
|
||
min: 1,
|
||
message: '请选择归属部门',
|
||
trigger: ['change']
|
||
}
|
||
],
|
||
phone: [
|
||
{
|
||
validator: phoneValidator,
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
password: [
|
||
{
|
||
required: true,
|
||
message: '请输入密码',
|
||
trigger: ['blur']
|
||
}
|
||
] as any[],
|
||
password_confirm: [
|
||
{
|
||
required: true,
|
||
message: '请输入确认密码',
|
||
trigger: ['blur']
|
||
},
|
||
{
|
||
validator: passwordConfirmValidator,
|
||
trigger: 'blur'
|
||
}
|
||
] as any[]
|
||
})
|
||
|
||
const handleSubmit = async () => {
|
||
try {
|
||
await formRef.value?.validate()
|
||
loading.value = true
|
||
|
||
mode.value == 'edit' ? await adminEdit(formData) : await adminAdd(formData)
|
||
|
||
feedback.msgSuccess(mode.value == 'edit' ? '编辑成功' : '新增成功')
|
||
visible.value = false
|
||
emit('success')
|
||
} catch (error) {
|
||
console.error('提交失败:', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
const open = (type = 'add') => {
|
||
mode.value = type
|
||
visible.value = true
|
||
fetchDepartmentOptions()
|
||
// 重置表单
|
||
if (type === 'add') {
|
||
Object.assign(formData, {
|
||
id: '',
|
||
account: '',
|
||
name: '',
|
||
gender: 1,
|
||
age: null,
|
||
phone: '',
|
||
role_id: [2],
|
||
dept_id: [],
|
||
jobs_id: [],
|
||
avatar: '',
|
||
title: '',
|
||
department: '',
|
||
specialty: '',
|
||
education: '',
|
||
experience: '',
|
||
honors: '',
|
||
password: '',
|
||
password_confirm: '',
|
||
disable: 0,
|
||
multipoint_login: 1,
|
||
diagnosis_count: null
|
||
})
|
||
|
||
// 新增时密码必填
|
||
formRules.password = [
|
||
{
|
||
required: true,
|
||
message: '请输入密码',
|
||
trigger: ['blur']
|
||
}
|
||
]
|
||
formRules.password_confirm = [
|
||
{
|
||
required: true,
|
||
message: '请输入确认密码',
|
||
trigger: ['blur']
|
||
},
|
||
{
|
||
validator: passwordConfirmValidator,
|
||
trigger: 'blur'
|
||
}
|
||
]
|
||
}
|
||
}
|
||
|
||
const setFormData = async (row: any) => {
|
||
// 编辑时密码不是必填
|
||
formRules.password = []
|
||
formRules.password_confirm = [
|
||
{
|
||
validator: passwordConfirmValidator,
|
||
trigger: 'blur'
|
||
}
|
||
]
|
||
|
||
const data = await adminDetail({
|
||
id: row.id
|
||
})
|
||
|
||
for (const key in formData) {
|
||
if (data[key] != null && data[key] != undefined) {
|
||
//@ts-ignore
|
||
formData[key] = data[key]
|
||
}
|
||
}
|
||
|
||
// 确保role_id包含医助角色
|
||
if (!formData.role_id.includes(2)) {
|
||
formData.role_id.push(2)
|
||
}
|
||
}
|
||
|
||
const handleClose = () => {
|
||
visible.value = false
|
||
emit('close')
|
||
}
|
||
|
||
defineExpose({
|
||
open,
|
||
setFormData
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.form-tips {
|
||
color: #999;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
margin-top: 4px;
|
||
}
|
||
</style>
|