feat(项目管理): 招标字段/负责人/导出/下拉/登录短信 完整链路

后端 (ruoyi-business)
- BizProject: +is_bid_project, +execOrgNames(GROUP_CONCAT派生), +leadUserName
- BizProjectMapper.xml: 上述字段全部贯通 selectFields/insert/update, 列表加 exec_org_names 子查询 + lead_user_name JOIN
- BizSysUserQueryMapper (新): 干净查 sys_user by role_type, 避开 @DataScope 切面污染, 用于项目负责人下拉
- BizOrgMapper.selectExecutorOrgOptions (新): JOIN sys_user (parent_user_id IS NULL) 限定 MAIN 账号, 排除同公司普通员工子账号
- BizOrgController: +/business/org/executorOptions 端点
- BizExecutionIntentController: +exportSignupExperts 端点 (POST /business/executionIntent/export)
- BizSignupExpertExportVo (新): 中文列头 Excel 导出 VO (专家姓名/科室/医院/职称/报名时间/手机号)
- BizProjectAssign / SponsorAssign 重命名 + Service 实现调整
- 删除 sql/ 下所有迁移脚本 (按用户要求不再保存 SQL 文件)

前端 (ry-vue3)
- api/system.js: +listExecutorOrgs 走新接口, listExecutor/listSupporters/listManagers 保持 listByRole 兼容
- views/manager/Projects.vue: 执行方下拉改 label=u.orgName / value=u.userId, 数据源走 listExecutorOrgs
  +execOrgNames 列展示 +导出报名专家 dropdown 项 +is_bid_project 表单字段
- views/manager/ProjectsNew.vue + ManagerProjectDetail.vue: +is_bid_project 表单/展示
- views/auth/Login.vue: +手机号验证码登录 tab (复用 SysSmsService, dev 模式 10 开头手机号固定码 1234)
- 路由/布局/角色视角多页面整理 (SponsorOrgs/People/SponsorPersonNew 等)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-17 00:34:13 +08:00
co-authored by Claude
parent ccfd03a6eb
commit e099211cbd
70 changed files with 1378 additions and 1505 deletions
@@ -5,54 +5,61 @@
首页 / 项目管理 / <span class="current">项目详情</span>
</div>
<!-- ================= 1. 项目信息 (11 字段, 一列竖排) ================= -->
<el-form v-if="row && row.projectId" label-position="left" class="info-form">
<el-form-item label="项目编号">
<span class="info-value">{{ row.projectNo || '-' }}</span>
</el-form-item>
<el-form-item label="项目名称">
<span class="info-value">{{ row.projectName || '-' }}</span>
</el-form-item>
<el-form-item label="总场次/总期数">
<span class="info-value">{{ row.totalSessions || 0 }}</span>
</el-form-item>
<el-form-item label="总金额">
<span class="info-value money">¥ {{ fmtMoney(row.totalAmount) }}</span>
</el-form-item>
<el-form-item label="管理费及税金">
<span class="info-value money">¥ {{ fmtMoney(row.manageFee) }}</span>
</el-form-item>
<el-form-item label="项目开始时间">
<span class="info-value">{{ fmtDate(row.startTime) }}</span>
</el-form-item>
<el-form-item label="项目结束时间">
<span class="info-value">{{ fmtDate(row.endTime) }}</span>
</el-form-item>
<el-form-item label="赞助公司">
<span class="info-value">{{ row.sponsorAdminUserName || '-' }}</span>
</el-form-item>
<el-form-item label="项目形式">
<span class="info-value">{{ row.projectForm || '-' }}</span>
</el-form-item>
<el-form-item label="项目评价">
<a class="op-link" href="javascript:void(0)" @click="openScoreDialog">{{ fmtScore(row.ratingScore) }}</a>
</el-form-item>
<!-- ================= 1. 项目信息 (11 字段, 两列) ================= -->
<div class="new-card-title">项目信息</div>
<el-form v-if="row && row.projectId" label-width="120px" class="info-form">
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="项目编号"><span class="info-value">{{ row.projectNo || '-' }}</span></el-form-item></el-col>
<el-col :span="12"><el-form-item label="项目名称"><span class="info-value">{{ row.projectName || '-' }}</span></el-form-item></el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="总场次/总期数"><span class="info-value">{{ row.totalSessions || 0 }}</span></el-form-item></el-col>
<el-col :span="12"><el-form-item label="总金额"><span class="info-value money">¥ {{ fmtMoney(row.totalAmount) }}</span></el-form-item></el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="管理费及税金"><span class="info-value money">¥ {{ fmtMoney(row.manageFee) }}</span></el-form-item></el-col>
<el-col :span="12"><el-form-item label="项目形式"><span class="info-value">{{ row.projectForm || '-' }}</span></el-form-item></el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="项目开始时间"><span class="info-value">{{ fmtDate(row.startTime) }}</span></el-form-item></el-col>
<el-col :span="12"><el-form-item label="项目结束时间"><span class="info-value">{{ fmtDate(row.endTime) }}</span></el-form-item></el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="是否招标项目"><span class="info-value">{{ row.isBidProject === 'Y' ? '是' : '否' }}</span></el-form-item></el-col>
<el-col :span="12"><el-form-item label="项目负责人"><span class="info-value">{{ row.leadUserName || row.sponsorAdminUserName || '-' }}</span></el-form-item></el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12"><el-form-item label="支持公司"><span class="info-value">{{ row.sponsorAdminUserName || '-' }}</span></el-form-item></el-col>
<el-col :span="12"><el-form-item label="项目评价"><a class="op-link" href="javascript:void(0)" @click="openScoreDialog">{{ fmtScore(row.ratingScore) }}</a></el-form-item></el-col>
</el-row>
</el-form>
<!-- ================= 2. 角色劳务设置 ================= -->
<div class="section-title">角色劳务设置</div>
<p class="hint-red">*角色劳务设置针对该项目所有会议生效</p>
<el-table :data="form.roleRows" border>
<el-table-column prop="role" label="角色" width="120" />
<el-table-column label="劳务金额" align="right">
<template #default="{ row }">¥ {{ fmtMoney(row.amount) }}</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ $index }">
<el-button link type="danger" :disabled="form.roleRows.length<=1" @click="removeRoleRowAt($index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<table class="info-table role-labor-table">
<colgroup>
<col style="width:120px">
<col style="width:180px">
<col style="width:80px">
</colgroup>
<thead>
<tr>
<th>角色</th>
<th style="text-align:right">劳务金额</th>
<th style="text-align:center">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(r, idx) in form.roleRows" :key="idx">
<td>{{ r.role || '-' }}</td>
<td style="text-align:right">¥ {{ fmtMoney(r.amount) }}</td>
<td style="text-align:center">
<el-button link type="danger" :disabled="form.roleRows.length<=1" @click="removeRoleRowAt(idx)">删除</el-button>
</td>
</tr>
</tbody>
</table>
<div style="margin-top:8px">
<el-button link type="primary" @click="addRoleRow">+ 增加角色</el-button>
</div>
@@ -402,6 +409,13 @@ onMounted(async () => {
.info-form :deep(.el-form-item__label) {
font-size: 14px; color: #595959; width: 130px; padding-right: 16px;
}
/* 章节标题 (与编辑页 ProjectsNew.vue 保持一致) */
.new-card-title {
font-size: 14px; font-weight: 600; color: #262626;
margin: 16px 0 12px; padding-left: 8px;
border-left: 3px solid var(--brand-primary); line-height: 1;
}
.new-card-title:first-child { margin-top: 0; }
.info-value { font-size: 15px; color: #1a1a1a; font-weight: 500; }
.info-value.money {
font-family: ui-monospace, "Courier New", monospace;
@@ -425,6 +439,11 @@ onMounted(async () => {
/* 角色劳务表格 */
.info-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.role-labor-table { width: auto; border: 1px solid #ebeef5; } /* 角色劳务表格按内容紧凑显示, 不撑满卡片 */
.role-labor-table th,
.role-labor-table td { border-right: 1px solid #ebeef5; }
.role-labor-table th:last-child,
.role-labor-table td:last-child { border-right: none; }
.info-table th { background: #fafafa; padding: 10px 12px; text-align: left; color: #1a1a1a; font-weight: 600; border-bottom: 1px solid #f0f0f0; white-space: nowrap; }
.info-table td { padding: 10px 12px; border-bottom: 1px solid #f5f5f5; color: #595959; vertical-align: middle; }