feat(manager): 专家审核页修改按钮跳转独立编辑页
参考 /admin/experts/new 模式:
- 新建 ry-vue3/src/views/manager/ExpertNew.vue (15 字段, 1:1 抄 admin 版, 仅 goBack 跳 /manager/experts)
- router 加 /manager/experts/new 和 /manager/experts/edit/:id 两条路由, 复用 ExpertNew 组件
- manager/Experts.vue 操作列 "修改" 按钮 → router.push('/manager/experts/edit/:id')
- 加 route watcher, 从编辑页返回时自动重载列表
- 新建专家按钮保留原 dialog (按用户当前只要求改修改按钮)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -68,6 +68,8 @@ const routes = [
|
||||
{ path: 'meetings', name: 'manager-meetings', component: () => import('@/views/manager/Meetings.vue'), meta: { title: '会议管理' } },
|
||||
{ path: 'meetings/new', name: 'manager-meetings-new', component: () => import('@/views/manager/MeetingNew.vue'), meta: { title: '新建会议' } },
|
||||
{ path: 'experts', name: 'manager-experts', component: () => import('@/views/manager/Experts.vue'), meta: { title: '专家审核' } },
|
||||
{ path: 'experts/new', name: 'manager-experts-new', component: () => import('@/views/manager/ExpertNew.vue'), meta: { title: '新建专家' } },
|
||||
{ path: 'experts/edit/:id', name: 'manager-experts-edit', component: () => import('@/views/manager/ExpertNew.vue'), meta: { title: '编辑专家' } },
|
||||
{ path: 'sponsor-orgs', name: 'manager-sponsor-orgs', component: () => import('@/views/manager/SponsorOrgs.vue'), meta: { title: '支持单位管理' } },
|
||||
{ path: 'executor-orgs', name: 'manager-executor-orgs', component: () => import('@/views/manager/ExecutorOrgs.vue'), meta: { title: '服务机构管理' } },
|
||||
{ path: 'sponsor-people', name: 'manager-sponsor-people', component: () => import('@/views/manager/SponsorPeople.vue'), meta: { title: '支持单位下人员' } },
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<!--
|
||||
manager 视角专家新建/编辑独立页
|
||||
1:1 抄 admin/ExpertNew.vue, 仅 goBack 跳 /manager/experts
|
||||
通过 route.params.id 区分新建/编辑
|
||||
-->
|
||||
<div class="page-card expert-new">
|
||||
<div class="breadcrumb">
|
||||
<a @click="goBack">专家审核</a> > {{ isEdit ? '编辑专家' : '新建专家' }}
|
||||
</div>
|
||||
|
||||
<el-card v-loading="loadingDetail" shadow="never" class="form-card nested">
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入姓名" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" placeholder="请输入手机号" maxlength="11" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="工作单位" prop="workUnit">
|
||||
<el-input v-model="form.workUnit" placeholder="请输入工作单位" maxlength="200" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="科室" prop="department">
|
||||
<doctor-dept-select
|
||||
v-model="form.department"
|
||||
value-field="label"
|
||||
placeholder="输入关键词搜索"
|
||||
filterable
|
||||
style="width:100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职称" prop="title">
|
||||
<doctor-title-select
|
||||
v-model="form.title"
|
||||
value-field="label"
|
||||
placeholder="输入关键词搜索"
|
||||
filterable
|
||||
style="width:100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="地区">
|
||||
<area-cascader
|
||||
v-model="form.region"
|
||||
format="string"
|
||||
join-sep="/"
|
||||
placeholder="请选择省/市/区"
|
||||
style="width:100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="执业医师证书">
|
||||
<oss-image-uploader
|
||||
v-model="form.practiceCertUrl"
|
||||
dir="ry8080/expert/practice/"
|
||||
placeholder="+"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="职称证明">
|
||||
<oss-image-uploader
|
||||
v-model="form.titleCertUrl"
|
||||
dir="ry8080/expert/title/"
|
||||
placeholder="+"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="身份证件号码">
|
||||
<el-input v-model="form.idCard" placeholder="请输入身份证件号码" maxlength="18" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="身份证件照片">
|
||||
<id-card-uploader
|
||||
v-model:front-url="form.idCardFrontUrl"
|
||||
v-model:back-url="form.idCardBackUrl"
|
||||
dir="ry8080/expert/idcard/"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="银行卡号">
|
||||
<el-input v-model="form.bankCard" placeholder="请输入银行卡号" maxlength="19" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="银行名称">
|
||||
<el-input v-model="form.bankName" placeholder="如: 中国工商银行" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行省份">
|
||||
<el-input v-model="form.bankProvince" placeholder="如: 北京市" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开户行城市">
|
||||
<el-input v-model="form.bankCity" placeholder="如: 北京市" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="开户行地址">
|
||||
<el-input v-model="form.bankAddress" placeholder="请输入开户行详细地址" maxlength="200" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<div class="form-actions">
|
||||
<el-button type="primary" :loading="saving" @click="onSave">保存</el-button>
|
||||
<el-button @click="confirmCancel">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { bizAdd, bizUpdate, bizGet } from '@/api/public'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import DoctorTitleSelect from '@/components/DoctorTitleSelect.vue'
|
||||
import DoctorDeptSelect from '@/components/DoctorDeptSelect.vue'
|
||||
import AreaCascader from '@/components/AreaCascader.vue'
|
||||
import IdCardUploader from '@/components/IdCardUploader.vue'
|
||||
import OssImageUploader from '@/components/OssImageUploader.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const formRef = ref(null)
|
||||
const saving = ref(false)
|
||||
const loadingDetail = ref(false)
|
||||
const isEdit = !!route.params.id
|
||||
const expertId = route.params.id || null
|
||||
|
||||
const form = reactive({
|
||||
expertId: null,
|
||||
name: '',
|
||||
phone: '',
|
||||
workUnit: '',
|
||||
department: '',
|
||||
title: '',
|
||||
region: '',
|
||||
practiceCertUrl: '',
|
||||
titleCertUrl: '',
|
||||
idCard: '',
|
||||
idCardFrontUrl: '',
|
||||
idCardBackUrl: '',
|
||||
bankCard: '',
|
||||
bankName: '',
|
||||
bankProvince: '',
|
||||
bankCity: '',
|
||||
bankAddress: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[0-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' },
|
||||
],
|
||||
workUnit: [{ required: true, message: '请输入工作单位', trigger: 'blur' }],
|
||||
department:[{ required: true, message: '请选择科室', trigger: 'change' }],
|
||||
title: [{ required: true, message: '请选择职称', trigger: 'change' }],
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
router.push('/manager/experts')
|
||||
}
|
||||
|
||||
async function loadDetail() {
|
||||
if (!expertId) return
|
||||
loadingDetail.value = true
|
||||
try {
|
||||
const res = await bizGet('expert', expertId)
|
||||
if (res?.code === 200 && res.data) {
|
||||
const d = res.data
|
||||
Object.assign(form, {
|
||||
expertId: d.expertId,
|
||||
name: d.name || '',
|
||||
phone: d.phone || '',
|
||||
workUnit: d.workUnit || '',
|
||||
department: d.department || '',
|
||||
title: d.title || '',
|
||||
region: d.region || '',
|
||||
practiceCertUrl: d.practiceCertUrl || '',
|
||||
titleCertUrl: d.titleCertUrl || '',
|
||||
idCard: d.idCard || '',
|
||||
idCardFrontUrl: d.idCardFrontUrl || '',
|
||||
idCardBackUrl: d.idCardBackUrl || '',
|
||||
bankCard: d.bankCard || '',
|
||||
bankName: d.bankName || '',
|
||||
bankProvince: d.bankProvince || '',
|
||||
bankCity: d.bankCity || '',
|
||||
bankAddress: d.bankAddress || '',
|
||||
})
|
||||
} else {
|
||||
ElMessage.error(res?.msg || '加载失败')
|
||||
goBack()
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.msg || e?.message || '加载失败')
|
||||
goBack()
|
||||
} finally {
|
||||
loadingDetail.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function confirmCancel() {
|
||||
const hasContent = form.name || form.phone || form.workUnit || form.department || form.title
|
||||
if (!isEdit && !hasContent) { goBack(); return }
|
||||
ElMessageBox.confirm('确定取消?未保存的内容将丢失', '提示', { type: 'warning' })
|
||||
.then(() => goBack())
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function isDuplicateError(msg) {
|
||||
return /已存在|重复|已注册|duplicate|uk_expert_phone/i.test(msg || '')
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
} catch {
|
||||
ElMessage.warning('请填写必填项')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
const payload = { ...form }
|
||||
if (isEdit) {
|
||||
delete payload.expertId
|
||||
} else {
|
||||
// manager 创建专家: 自动通过审核, 状态正常
|
||||
payload.auditStatus = '2'
|
||||
payload.auditBy = userStore.user?.userName || userStore.user?.nickName || 'manager'
|
||||
payload.auditTime = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
||||
payload.status = 'Y'
|
||||
}
|
||||
const res = isEdit
|
||||
? await bizUpdate('expert', payload)
|
||||
: await bizAdd('expert', payload)
|
||||
if (res?.code === 200) {
|
||||
if (!isEdit && res.data) {
|
||||
const u = res.data
|
||||
ElMessageBox.alert(
|
||||
`专家账号已创建\n账号: ${u.userName || form.phone}\n初始密码: ${u.password || form.phone}\n请记录后告知使用人 (密码仅本次显示)`,
|
||||
'创建成功',
|
||||
{ type: 'success', confirmButtonText: '我已记录, 返回' }
|
||||
).then(() => goBack())
|
||||
.catch(() => goBack())
|
||||
} else {
|
||||
ElMessage.success(isEdit ? '修改成功' : '新建成功')
|
||||
goBack()
|
||||
}
|
||||
} else {
|
||||
const msg = res?.msg || '保存失败'
|
||||
if (isDuplicateError(msg)) ElMessage.warning('该手机号已注册,请更换')
|
||||
else ElMessage.error(msg)
|
||||
}
|
||||
} catch (e) {
|
||||
const msg = e?.msg || e?.message || '保存失败'
|
||||
if (isDuplicateError(msg)) ElMessage.warning('该手机号已注册,请更换')
|
||||
else ElMessage.error(msg)
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (isEdit) loadDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.expert-new { max-width: 1200px; }
|
||||
.page-card { background: #fff; padding: 16px 20px; border-radius: 6px; border: 1px solid #f0f0f0; }
|
||||
.breadcrumb { font-size: 13px; color: #8c8c8c; margin-bottom: 16px; }
|
||||
.breadcrumb a { color: var(--brand-primary); cursor: pointer; text-decoration: none; }
|
||||
.breadcrumb a:hover { color: var(--brand-primary-deep, var(--brand-primary)); }
|
||||
|
||||
.form-card.nested { background: transparent; border: none; box-shadow: none; }
|
||||
:deep(.form-card .el-card__body) { padding: 24px 28px; }
|
||||
|
||||
.form-actions { margin-top: 16px; display: flex; gap: 8px; }
|
||||
|
||||
:deep(.el-button--primary) { background: var(--brand-primary); border-color: var(--brand-primary); border-radius: 4px; }
|
||||
:deep(.el-button--primary:hover) { background: var(--brand-primary-deep, var(--brand-primary)); border-color: var(--brand-primary-deep, var(--brand-primary)); }
|
||||
:deep(.el-button) { border-radius: 4px; }
|
||||
:deep(.el-input__wrapper), :deep(.el-textarea__inner) { border-radius: 4px; }
|
||||
</style>
|
||||
@@ -74,7 +74,7 @@
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="onAudit(row)">审核</el-button>
|
||||
<el-button link type="primary" @click="onEdit(row)">修改</el-button>
|
||||
<el-button link type="primary" @click="goEdit(row)">修改</el-button>
|
||||
<el-button link type="primary" @click="onView(row)">查看</el-button>
|
||||
<el-button link :type="row.status === 'N' ? 'success' : 'danger'" @click="row.status === 'N' ? onEnable(row) : onDisable(row)">
|
||||
{{ row.status === 'N' ? '恢复' : '禁用' }}
|
||||
@@ -207,11 +207,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { bizList, bizAdd, bizUpdate } from '@/api/public'
|
||||
import AuditStatusTag from '@/components/AuditStatusTag.vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const q = ref({ name: '', phone: '', workUnit: '', auditStatus: '' })
|
||||
const rows = ref([])
|
||||
const selection = ref([])
|
||||
@@ -261,13 +265,9 @@ function openAdd() {
|
||||
Object.assign(editForm, { expertId: '', name: '', phone: '', workUnit: '', department: '', title: '', practiceCertUrl: '', titleCertUrl: '' })
|
||||
editOpen.value = true
|
||||
}
|
||||
function onEdit(row) {
|
||||
Object.assign(editForm, {
|
||||
expertId: row.expertId, name: row.name, phone: row.phone, workUnit: row.workUnit,
|
||||
department: row.department, title: row.title,
|
||||
practiceCertUrl: row.practiceCertUrl || '', titleCertUrl: row.titleCertUrl || ''
|
||||
})
|
||||
editOpen.value = true
|
||||
// 修改按钮: 跳转独立页 /manager/experts/edit/:id (参考 /admin/experts/new)
|
||||
function goEdit(row) {
|
||||
router.push(`/manager/experts/edit/${row.expertId}`)
|
||||
}
|
||||
async function submitEdit() {
|
||||
await editFormRef.value.validate()
|
||||
@@ -424,6 +424,10 @@ async function submitImportTitleCert() {
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
// 从 ExpertNew 跳转回来时刷新列表
|
||||
watch(() => route.fullPath, (newPath) => {
|
||||
if (newPath === '/manager/experts') load()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user