refactor: 合并 biz_support_unit/biz_execution_unit/biz_service_org 为 biz_org, 删除 2 张孤儿表, 改 biz_person.work_unit → org_id FK
主要改动: - SQL: biz_support_unit → biz_org, 加 org_type, 加 business_nature; 删 biz_execution_unit, biz_service_org - SQL: biz_project.support_unit_id/name + service_org_id/name → org_id/name/type - SQL: biz_meeting.support_unit_name → org_name - SQL: biz_person.work_unit → org_id (FK) - 后端: 新 BizOrg entity/mapper/service/controller - 后端: BizProject/BizMeeting 字段重命名 - 后端: 删 12 个 BizSupportUnit/BizExecutionUnit/BizServiceOrg Java 文件 - 后端: BizPersonImportVO.workUnit → orgName (导入时查 biz_org 取 org_id) - 后端: BizAuthController.registerExecutor 完整实现 (原 registerSupplier stub) - 前端: 新 admin/Orgs.vue + manager/Orgs.vue (原 SupportUnits) - 前端: RegisterExecutor.vue (原 RegisterSupplier, 单页 2 步) - 前端: sponsor/executor/manager 多个文件 workUnit → orgId/orgName 重命名 - 前端: 统一 supplier → executor, 业务命名 sponsor(赞助方) / executor(执行方=供应商) - 前端: 全工程 execution → executor (company type / role / person unit_type)
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<!--
|
||||
1:1 抄 doctor/SubmissionNew.vue
|
||||
表单布局: el-card shadow="never" form-card + el-row :gutter="12" el-col :span="12" 两列
|
||||
编辑/新建复用: 通过 route.params.id 区分
|
||||
-->
|
||||
<div class="page-card">
|
||||
<!-- 面包屑 -->
|
||||
<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 v-if="!isEdit" :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="账号" prop="loginUsername">
|
||||
<el-input v-model="form.loginUsername" placeholder="请输入子账号登录账号" maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="初始密码" prop="loginPassword">
|
||||
<el-input v-model="form.loginPassword" placeholder="请输入初始密码 (≥6 位)" type="password" show-password maxlength="30" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<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="orgName">
|
||||
<el-input v-model="form.orgName" placeholder="请输入所属公司 (TODO: 改 biz_org 下拉)" maxlength="200" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="部门" prop="department">
|
||||
<el-input v-model="form.department" placeholder="请输入部门" maxlength="100" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职务" prop="position">
|
||||
<el-input v-model="form.position" placeholder="请输入职务" maxlength="50" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="角色" prop="role">
|
||||
<el-select v-model="form.role" placeholder="请选择" style="width:100%">
|
||||
<el-option label="监察员" value="监察员" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio value="0">正常</el-radio>
|
||||
<el-radio value="1">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</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'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const formRef = ref(null)
|
||||
const saving = ref(false)
|
||||
const loadingDetail = ref(false)
|
||||
const isEdit = !!route.params.id
|
||||
const personId = route.params.id || null
|
||||
|
||||
const form = reactive({
|
||||
personId: null,
|
||||
name: '',
|
||||
phone: '',
|
||||
orgName: '',
|
||||
department: '',
|
||||
position: '',
|
||||
role: '监察员',
|
||||
unitType: 'sponsor',
|
||||
status: '0',
|
||||
// 子账号登录信息 (后端会创建 sys_user SUB,parent_user_id=主账号)
|
||||
loginUsername: '',
|
||||
loginPassword: '',
|
||||
})
|
||||
|
||||
// 7 字段全必填 + 手机号正则
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[0-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' },
|
||||
],
|
||||
orgName: [{ required: true, message: '请输入所属公司', trigger: 'blur' }],
|
||||
department:[{ required: true, message: '请输入部门', trigger: 'blur' }],
|
||||
position: [{ required: true, message: '请输入职务', trigger: 'blur' }],
|
||||
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
loginUsername: [
|
||||
{ required: true, message: '请输入子账号登录账号', trigger: 'blur' },
|
||||
{ pattern: /^[a-zA-Z][a-zA-Z0-9_]{2,29}$/, message: '账号以字母开头,3-30 位字母数字下划线', trigger: 'blur' },
|
||||
],
|
||||
loginPassword: [
|
||||
{ required: true, message: '请输入初始密码', trigger: 'blur' },
|
||||
{ min: 6, max: 30, message: '密码长度 6-30 位', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
router.push({ path: '/sponsor/people', query: { _t: Date.now() } })
|
||||
}
|
||||
|
||||
async function loadDetail() {
|
||||
if (!personId) return
|
||||
loadingDetail.value = true
|
||||
try {
|
||||
const res = await bizGet('person', personId)
|
||||
if (res?.code === 200 && res.data) {
|
||||
const d = res.data
|
||||
Object.assign(form, {
|
||||
personId: d.personId,
|
||||
name: d.name || '',
|
||||
phone: d.phone || '',
|
||||
orgName: d.orgName || '',
|
||||
department: d.department || '',
|
||||
position: d.position || '',
|
||||
role: d.role || '',
|
||||
unitType: d.unitType || 'sponsor',
|
||||
status: d.status != null ? String(d.status) : '0',
|
||||
})
|
||||
} else {
|
||||
ElMessage.error(res?.msg || '加载失败')
|
||||
goBack()
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.msg || '加载失败')
|
||||
goBack()
|
||||
} finally {
|
||||
loadingDetail.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function confirmCancel() {
|
||||
// 检查表单是否有内容 (新建时只要 form.name 即可)
|
||||
const hasContent = form.name || form.phone || form.orgName || form.department || form.position || form.role
|
||||
if (!isEdit && !hasContent) { goBack(); return }
|
||||
ElMessageBox.confirm('确定取消?未保存的内容将丢失', '提示', { type: 'warning' })
|
||||
.then(() => goBack())
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
// 业务"重复/已存在"提示用 warning 黄 (memory 规则)
|
||||
function isDuplicateError(msg) {
|
||||
return /已存在|重复|duplicate|uk_person_phone/i.test(msg || '')
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
} catch {
|
||||
ElMessage.warning('请填写必填项')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
let payload = { ...form }
|
||||
let res
|
||||
if (isEdit) {
|
||||
delete payload.loginUsername // 编辑时不改登录账号
|
||||
delete payload.loginPassword // 编辑时不改密码 (password 走独立接口)
|
||||
res = await bizUpdate('person', payload)
|
||||
} else {
|
||||
payload.unitType = 'sponsor'
|
||||
res = await bizAdd('person', payload)
|
||||
}
|
||||
if (res?.code === 200) {
|
||||
if (!isEdit && res.data) {
|
||||
// 新建成功: 提示主账号刚创建的子账号信息
|
||||
const u = res.data
|
||||
ElMessageBox.alert(
|
||||
`子账号已创建\n账号: ${u.userName || form.loginUsername}\n初始密码: ${u.password || form.loginPassword}\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>
|
||||
/* 1:1 抄 doctor/SubmissionNew.vue */
|
||||
.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; }
|
||||
.form-hint { font-size: 12px; color: #ff4d4f; line-height: 1.6; margin: 4px 0; }
|
||||
|
||||
: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>
|
||||
Reference in New Issue
Block a user