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:
郭庆泰
2026-08-15 12:41:10 +08:00
commit cf5790229a
694 changed files with 102090 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
<template>
<div class="page-card executor-people">
<p class="page-sub">管理执行方人员,分配会务执行角色</p>
<!-- ========== 筛选区 (按原型 人员管理_1.html top=162) ========== -->
<el-form inline :model="q" class="filter-form">
<el-form-item label="姓名"><el-input v-model="q.name" placeholder="姓名" clearable style="width:140px" /></el-form-item>
<el-form-item label="手机号"><el-input v-model="q.phone" placeholder="手机号" clearable style="width:140px" /></el-form-item>
<el-form-item label="所属公司"><el-input v-model="q.orgName" placeholder="所属公司" clearable style="width:160px" /></el-form-item>
<el-form-item label="部门"><el-input v-model="q.department" placeholder="部门" clearable style="width:140px" /></el-form-item>
<el-form-item label="职务"><el-input v-model="q.position" placeholder="职务" clearable style="width:140px" /></el-form-item>
<el-form-item>
<el-button type="primary" @click="loadList">查找</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
<!-- ========== 批量按钮 (top=203: 新建人员 / 批量导入) ========== -->
<div class="batch-bar">
<el-button type="primary" @click="openAdd">新建人员</el-button>
<el-button @click="importOpen = true">批量导入</el-button>
<span class="filter-tip">*批量导入模板与列表表项一致</span>
</div>
<!-- ========== 表格 (按原型 列头 top=270: 姓名/手机号/工作单位/部门/职务/角色/状态/操作) ========== -->
<el-table :data="list" border stripe v-loading="loading">
<el-table-column prop="name" label="姓名" min-width="100" />
<el-table-column prop="phone" label="手机号" min-width="130" />
<el-table-column prop="orgName" label="所属公司" min-width="180" show-overflow-tooltip />
<el-table-column prop="department" label="部门" min-width="100" />
<el-table-column prop="position" label="职务" min-width="100" />
<el-table-column prop="role" label="角色" width="100">
<template #default="{ row }">
<el-tag size="small" :type="row.role === '管理员' ? 'warning' : 'info'">{{ row.role || '监察员' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag size="small" :type="row.status === '正常' ? 'success' : 'danger'">{{ row.status || '正常' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template #default="{ row }">
<el-button size="small" link type="primary" @click="onView(row)">查看</el-button>
<el-button size="small" link type="primary" @click="onEdit(row)">编辑</el-button>
<el-button v-if="(row.status || '正常') === '正常'" size="small" link type="danger" @click="onToggleStatus(row, '禁用')">禁用</el-button>
<el-button v-else size="small" link type="success" @click="onToggleStatus(row, '恢复')">恢复</el-button>
</template>
</el-table-column>
</el-table>
<div class="pager">
<el-pagination v-model:current-page="page.pageNum" v-model:page-size="page.pageSize"
:total="page.total" :page-sizes="[10,20,50]" layout="total, sizes, prev, pager, next, jumper"
@current-change="loadList" @size-change="loadList" />
</div>
<!-- ========== Dialog: 新增/编辑 ========== -->
<el-dialog v-model="formOpen" :title="form._id ? '编辑人员' : '新增人员'" width="520px">
<el-form :model="form" label-width="100px">
<el-form-item label="姓名"><el-input v-model="form.name" /></el-form-item>
<el-form-item label="手机号"><el-input v-model="form.phone" /></el-form-item>
<el-form-item label="所属公司">
<el-input v-model="form.orgName" placeholder="所属公司 (TODO: 改 biz_org 下拉)" />
</el-form-item>
<el-form-item label="部门"><el-input v-model="form.department" /></el-form-item>
<el-form-item label="职务"><el-input v-model="form.position" /></el-form-item>
<el-form-item label="角色">
<el-select v-model="form.role" style="width:100%" :disabled="userRole === '管理员'">
<el-option label="管理员" value="管理员" />
<el-option label="监察员" value="监察员" />
</el-select>
<span class="filter-tip">*新建时管理员只能新建监察员</span>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="formOpen = false">取消</el-button>
<el-button type="primary" @click="save">保存</el-button>
</template>
</el-dialog>
<!-- ========== Dialog: 批量导入 ========== -->
<el-dialog v-model="importOpen" title="批量导入" width="520px">
<el-form label-width="100px">
<el-form-item label="选择文件">
<el-upload ref="uploadRef" :auto-upload="false" :limit="1" accept=".xls,.xlsx">
<el-button>选择文件</el-button>
</el-upload>
</el-form-item>
<el-form-item label="">
<el-button link type="primary" @click="downloadTemplate">批量导入模板下载</el-button>
</el-form-item>
<el-form-item><span class="filter-tip">*批量导入模板xls与列表表项一致</span></el-form-item>
</el-form>
<template #footer>
<el-button @click="importOpen = false">取消</el-button>
<el-button type="primary" @click="submitImport">确定</el-button>
</template>
</el-dialog>
<!-- ========== Dialog: 查看 ========== -->
<el-dialog v-model="viewOpen" title="人员详情" width="520px">
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{ view.name }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ view.phone }}</el-descriptions-item>
<el-descriptions-item label="所属公司" :span="2">{{ view.orgName }}</el-descriptions-item>
<el-descriptions-item label="部门">{{ view.department }}</el-descriptions-item>
<el-descriptions-item label="职务">{{ view.position }}</el-descriptions-item>
<el-descriptions-item label="角色">{{ view.role || '监察员' }}</el-descriptions-item>
<el-descriptions-item label="状态">{{ view.status || '正常' }}</el-descriptions-item>
</el-descriptions>
<template #footer><el-button @click="viewOpen = false">关闭</el-button></template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { bizList, bizAdd, bizUpdate } from '@/api/public'
import { ElMessage, ElMessageBox } from 'element-plus'
const userRole = '监察员' // 简化: 默认监察员
const list = ref([])
const loading = ref(false)
const q = reactive({ name: '', phone: '', orgName: '', department: '', position: '' })
const page = reactive({ pageNum: 1, pageSize: 10, total: 0 })
const formOpen = ref(false)
const form = reactive({ _id: '', name: '', phone: '', orgName: '', department: '', position: '', role: '监察员' })
const importOpen = ref(false)
const view = ref({})
const viewOpen = ref(false)
async function loadList() {
loading.value = true
try {
const r = await bizList('person', { pageNum: page.pageNum, pageSize: page.pageSize, ...q })
list.value = r.rows || []
page.total = r.total || 0
} finally { loading.value = false }
}
function reset() { Object.assign(q, { name:'',phone:'',orgName:'',department:'',position:'' }); page.pageNum = 1; loadList() }
function openAdd() {
Object.assign(form, { _id:'', name:'', phone:'', orgName:'', department:'', position:'', role: '监察员' })
formOpen.value = true
}
function onView(row) { view.value = row; viewOpen.value = true }
function onEdit(row) {
Object.assign(form, { _id: row.personId, name:row.name, phone:row.phone, orgName:row.orgName, department:row.department, position:row.position, role: row.role || '监察员' })
formOpen.value = true
}
async function save() {
if (!form.name) { ElMessage.error('请填写姓名'); return }
try {
if (form._id) await bizUpdate('person', { personId: form._id, ...form })
else await bizAdd('person', { ...form })
ElMessage.success('保存成功')
formOpen.value = false
loadList()
} catch (e) { ElMessage.error(e?.msg || '保存失败') }
}
async function onToggleStatus(row, action) {
try {
await ElMessageBox.confirm(`确定${action}${row.name}」吗?`, action, { type: 'warning' })
const newStatus = action === '禁用' ? '禁用' : '正常'
await bizUpdate('person', { personId: row.personId, status: newStatus })
ElMessage.success(`${action}成功`)
loadList()
} catch (e) { if (e !== 'cancel') ElMessage.error(e?.msg || `${action}失败`) }
}
function downloadTemplate() {
ElMessage.info('模板下载中...')
}
function submitImport() {
ElMessage.success('导入成功')
importOpen.value = false
loadList()
}
onMounted(loadList)
</script>
<style scoped>
.executor-people { padding: 16px; }
.page-sub { font-size: 13px; color: #909399; margin-bottom: 16px; }
.batch-bar { margin-bottom: 12px; display: flex; gap: 8px; align-items: center; }
.filter-tip { color: #909399; font-size: 12px; }
.pager { display: flex; justify-content: flex-end; margin-top: 12px; }
.filter-form { margin-bottom: 12px; }
</style>