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
+307
View File
@@ -0,0 +1,307 @@
<!DOCTYPE html>
<!-- 角色:赞助方 父级:sponsor.html 原型:原型/会议列表.html
补全(2026-08-08 测试反馈):
筛选区(6 字段):
- 项目编号(u9704 label 左:281 + u9706 input 左:348)
- 会议名称(u9707 label 左:515 + u9709 input 左:582)
- 第X期(u9714「第」左:758 + u9713 input 左:783 + u9715「期」左:946)
- 会议时间(u9863 label 左:984 + u9895 input 左:1051)
- 当前阶段(u9716 label 左:1228 + u9722 select 选项:请选择/已执行/未执行)
- 查找按钮(u9710 左:1377)
表格列(10 列,按 left 升序):
- u9694 项目编号 左:302
- u9846 会议ID 左:429
- u9848 项目形式 左:521 (线上/线下/线上+线下/其他)
- u9844 会议名称 左:644
- u9850 会议开始时间 左:794
- u9852 会议结束时间 左:940
- u9854 总期数 左:1070
- u9858 期数 左:1147
- u9716 已执行未传材料 左:1232
- u9702 操作 左:1377
红色注释:
- u9768 *会议详情页,增加学会已审核及审核意见...
- u9770 *支持方状态(当前阶段)未执行/已执行未传材料/待监管/监管通过/待整改/待结算/已结算
- u9776 *支持方点击监察意见保存确认后,按钮变为查看监察意见 -->
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>会议列表</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; color: #333; background: #fafafa; min-height: 100vh; }
.content { padding: 24px 32px; }
.page-title { font-size: 22px; font-weight: 600; color: #1a1a1a; margin-bottom: 20px; }
.card { background: #fff; border-radius: 8px; padding: 20px 24px; margin-bottom: 16px; border: 1px solid #f0f0f0; }
.filter-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.filter-field { display: flex; align-items: center; gap: 8px; }
.filter-label { font-size: 14px; color: #595959; white-space: nowrap; }
.filter-input,.filter-select,.filter-date { width: 180px; height: 32px; padding: 0 12px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 13px; color: #333; background: #fff; outline: none; }
.filter-date { width: 140px; }
.filter-range { display: flex; align-items: center; gap: 6px; }
.filter-range-sep { color: #8c8c8c; font-size: 13px; }
.filter-prefix,.filter-suffix { font-size: 13px; color: #595959; }
.filter-select { cursor: pointer; appearance: none; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") no-repeat right 10px center / 14px; padding-right: 28px; }
.filter-input:focus,.filter-select:focus,.filter-date:focus { border-color: #1890ff; box-shadow: 0 0 0 3px rgba(24,144,255,0.1); }
.filter-btn { height: 32px; padding: 0 20px; background: #1890ff; color: #fff; border: none; border-radius: 4px; font-size: 13px; cursor: pointer; }
.filter-btn:hover { background: #096dd9; }
.hint-red { font-size: 12px; color: #ff0000; line-height: 1.7; }
.hint-red-block { background: #fff5f5; border-left: 3px solid #ff0000; padding: 10px 14px; border-radius: 4px; margin-bottom: 12px; }
.hint-red-block .hint-red-title { font-weight: 600; margin-bottom: 4px; }
.data-card { padding: 0; overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th { background: #fafafa; padding: 10px 12px; text-align: left; color: #1a1a1a; font-weight: 600; border-bottom: 1px solid #f0f0f0; white-space: nowrap; }
.data-table td { padding: 10px 12px; border-bottom: 1px solid #f5f5f5; color: #595959; vertical-align: middle; white-space: nowrap; }
.data-table tr:hover { background: #fafcff; }
.data-table input[type="checkbox"] { cursor: pointer; width: 14px; height: 14px; accent-color: #1890ff; }
.col-code { color: #1890ff; font-family: ui-monospace, "Courier New", monospace; }
.col-name { color: #1a1a1a; font-weight: 500; }
.col-datetime { font-family: ui-monospace, "Courier New", monospace; color: #595959; font-size: 12px; }
.col-period { color: #722ed1; font-weight: 500; cursor: pointer; }
.col-period:hover { color: #1890ff; text-decoration: underline; }
.status-tag { display: inline-block; padding: 2px 10px; border-radius: 10px; font-size: 12px; }
.status-pending { background: #fff7e6; color: #fa8c16; border: 1px solid #ffd591; }
.status-done { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
.status-warn { background: #fffbe6; color: #faad14; border: 1px solid #ffe58f; }
.status-process { background: #e6f4ff; color: #1890ff; border: 1px solid #91d5ff; }
.status-purple { background: #f9f0ff; color: #722ed1; border: 1px solid #d3adf7; }
.form-tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; }
.form-online { background: #e6f4ff; color: #1890ff; border: 1px solid #91d5ff; }
.form-offline { background: #fff7e6; color: #fa8c16; border: 1px solid #ffd591; }
.form-mixed { background: #f9f0ff; color: #722ed1; border: 1px solid #d3adf7; }
.op-link { color: #1890ff; text-decoration: none; font-size: 13px; cursor: pointer; }
.op-link:hover { text-decoration: underline; }
/* Modal */
.modal-mask { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 1000; align-items: center; justify-content: center; }
.modal-mask.show { display: flex; }
.modal { background: #fff; border-radius: 8px; width: 520px; max-width: 90vw; max-height: 90vh; overflow-y: auto; box-shadow: 0 10px 40px rgba(0,0,0,.2); }
.modal-header { padding: 20px 24px; border-bottom: 1px solid #f0f0f0; display: flex; align-items: center; justify-content: space-between; }
.modal-title { font-size: 16px; font-weight: 600; color: #1a1a1a; }
.modal-close { cursor: pointer; color: #8c8c8c; font-size: 20px; background: none; border: none; padding: 4px; line-height: 1; }
.modal-close:hover { color: #1a1a1a; }
.modal-body { padding: 24px; }
.modal-row { margin-bottom: 14px; }
.modal-row-label { font-size: 13px; color: #595959; margin-bottom: 4px; }
.modal-row-value { font-size: 14px; color: #1a1a1a; }
.opinion-box { background: #fafafa; border-left: 3px solid #1890ff; padding: 12px; border-radius: 4px; color: #595959; font-size: 13px; line-height: 1.7; }
.modal-footer { padding: 16px 24px; border-top: 1px solid #f0f0f0; text-align: right; }
.btn-default { height: 32px; padding: 0 20px; background: #fff; color: #333; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 13px; cursor: pointer; }
.btn-primary { height: 32px; padding: 0 20px; background: #1890ff; color: #fff; border: 1px solid #1890ff; border-radius: 4px; font-size: 13px; cursor: pointer; margin-left: 8px; }
.btn-primary:hover { background: #096dd9; }
@media (max-width:768px) { .content { padding: 16px; } .data-table { font-size: 12px; } .modal { width: 96vw; } }
</style>
</head>
<body>
<main class="content">
<h1 class="page-title">会议列表</h1>
<div class="card">
<div class="filter-row">
<div class="filter-field"><span class="filter-label">项目编号:</span><input type="text" class="filter-input" placeholder="输入项目编号"></div>
<div class="filter-field"><span class="filter-label">会议名称:</span><input type="text" class="filter-input" placeholder="输入会议名称"></div>
<div class="filter-field">
<span class="filter-label"></span>
<input type="text" class="filter-input" placeholder="期数" style="width:80px">
<span class="filter-suffix"></span>
</div>
<div class="filter-field">
<span class="filter-label">会议时间:</span>
<div class="filter-range">
<input type="date" class="filter-date" value="2026-01-01">
<span class="filter-range-sep"></span>
<input type="date" class="filter-date" value="2026-12-31">
</div>
</div>
<div class="filter-field"><span class="filter-label">当前阶段:</span>
<select class="filter-select">
<option value="">请选择</option>
<option>已执行</option>
<option>未执行</option>
</select>
</div>
<button class="filter-btn">查找</button>
</div>
</div>
<div class="card data-card">
<table class="data-table">
<thead>
<tr>
<th style="width:40px"><input type="checkbox" id="selectAll"></th>
<th>项目编号</th>
<th>会议ID</th>
<th>项目形式</th>
<th>会议名称</th>
<th>会议开始时间</th>
<th>会议结束时间</th>
<th>总期数</th>
<th>期数</th>
<th>当前阶段</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr><td><input type="checkbox" class="row-check"></td><td><a class="col-code" href="javascript:void(0)">ZH-2026-658</a></td><td>5643145673</td><td><span class="form-tag form-online">线上</span></td><td><a class="col-name" href="javascript:void(0)">小牛血清创新应用研讨会</a></td><td class="col-datetime">2026-05-11 11:30</td><td class="col-datetime">2026-05-11 14:30</td><td style="text-align:center">26</td><td style="text-align:center"><a class="col-period" title="点击可筛选该系列会所有会议">第 3 期</a></td><td><span class="status-tag status-pending">待监管</span></td><td><a class="op-link" href="sponsor-meeting-detail.html">查看</a><a class="op-link" onclick="openOpinion(this)">监察意见</a></td></tr>
<tr><td><input type="checkbox" class="row-check"></td><td><a class="col-code" href="javascript:void(0)">ZH-2026-650</a></td><td>5643145674</td><td><span class="form-tag form-offline">线下</span></td><td><a class="col-name" href="javascript:void(0)">整合医学学会项目评审会</a></td><td class="col-datetime">2026-04-01 09:00</td><td class="col-datetime">2026-04-01 17:00</td><td style="text-align:center">18</td><td style="text-align:center"><a class="col-period" title="点击可筛选该系列会所有会议">第 18 期</a></td><td><span class="status-tag status-done">监管通过</span></td><td><a class="op-link" href="sponsor-meeting-detail.html">查看</a><a class="op-link" onclick="openOpinion(this)">查看监察意见</a></td></tr>
<tr><td><input type="checkbox" class="row-check"></td><td><a class="col-code" href="javascript:void(0)">ZH-2026-645</a></td><td>5643145675</td><td><span class="form-tag form-mixed">线上+线下</span></td><td><a class="col-name" href="javascript:void(0)">智慧医院建设项目方案论证会</a></td><td class="col-datetime">2026-03-15 14:00</td><td class="col-datetime">2026-03-15 16:30</td><td style="text-align:center">12</td><td style="text-align:center"><a class="col-period" title="点击可筛选该系列会所有会议">第 7 期</a></td><td><span class="status-tag status-warn">待整改</span></td><td><a class="op-link" href="sponsor-meeting-detail.html">查看</a><a class="op-link" onclick="openOpinion(this)">监察意见</a></td></tr>
<tr><td><input type="checkbox" class="row-check"></td><td><a class="col-code" href="javascript:void(0)">ZH-2026-640</a></td><td>5643145676</td><td><span class="form-tag form-online">线上</span></td><td><a class="col-name" href="javascript:void(0)">基层医疗改革试点中期评估会</a></td><td class="col-datetime">2026-02-01 09:30</td><td class="col-datetime">2026-02-01 12:00</td><td style="text-align:center">20</td><td style="text-align:center"><a class="col-period" title="点击可筛选该系列会所有会议">第 14 期</a></td><td><span class="status-tag status-purple">已结算</span></td><td><a class="op-link" href="sponsor-meeting-detail.html">查看</a><a class="op-link" onclick="openOpinion(this)">查看监察意见</a></td></tr>
<tr><td><input type="checkbox" class="row-check"></td><td><a class="col-code" href="javascript:void(0)">ZH-2026-635</a></td><td>5643145677</td><td><span class="form-tag form-offline">线下</span></td><td><a class="col-name" href="javascript:void(0)">数字化医疗转型方案评审会</a></td><td class="col-datetime">2026-01-10 10:00</td><td class="col-datetime">2026-01-10 17:00</td><td style="text-align:center">22</td><td style="text-align:center"><a class="col-period" title="点击可筛选该系列会所有会议">第 15 期</a></td><td><span class="status-tag status-purple">已结题</span></td><td><a class="op-link" href="sponsor-meeting-detail.html">查看</a><a class="op-link" onclick="openOpinion(this)">监察意见</a></td></tr>
</tbody>
</table>
</div>
<p class="hint-red" style="margin-top:12px">*支持方点击监察意见保存确认后,按钮变为查看监察意见</p>
<div class="hint-red-block" style="margin-top:16px">
<p class="hint-red">*会议详情页,增加学会已审核及审核意见,增加支持方监督意见详情,两个意见,合规人员能看到,执行方也可看到</p>
</div>
<div class="hint-red-block">
<p class="hint-red hint-red-title">*支持方状态(当前阶段):</p>
<p class="hint-red">未执行:会议开始时间前,显示为未执行;</p>
<p class="hint-red">已执行未传材料:合规人员未审核通过前,一直显示该状态;</p>
<p class="hint-red">待监管:支持方未填写监管意见时,显示该状态;</p>
<p class="hint-red">监管通过:支持方选择确认无误后,则显示该状态;监管通过默认只显示 1 个工作日,之后显示为待结算</p>
<p class="hint-red">待整改:监管未通过后,则显示该状态;</p>
<p class="hint-red">待结算:支持方监管通过后,1 个工作日后变为待结算,显示该状态;</p>
<p class="hint-red">已结算:合规人员当操作结算后,状态变为已结算;</p>
</div>
</main>
<!-- 监督意见 Modal (原型 u9942) -->
<div class="modal-mask" id="opinionModal">
<div class="modal">
<div class="modal-header">
<span class="modal-title" id="modalTitle">监督意见</span>
<button class="modal-close" onclick="closeModal()">×</button>
</div>
<div class="modal-body">
<div class="modal-row">
<div class="modal-row-label">会议名称</div>
<div class="modal-row-value" id="modalName"></div>
</div>
<!-- 编辑态:单选 + 文本框 + 字数 -->
<div id="editView">
<div class="modal-row">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
<input type="radio" name="opinionType" value="ok" checked onchange="toggleTextarea()">
<span>确认无误</span>
</label>
</div>
<div class="modal-row">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
<input type="radio" name="opinionType" value="fix" onchange="toggleTextarea()">
<span>存在需整改的问题</span>
</label>
</div>
<div class="modal-row" id="textareaWrap" style="display:none">
<div style="display:flex;align-items:center;gap:4px;margin-bottom:4px">
<span style="color:#ff4d4f">*</span>
<span class="modal-row-label" style="margin:0">详细意见</span>
<span style="flex:1"></span>
<span style="font-size:12px;color:#8c8c8c"><span id="charCount">0</span>/500</span>
</div>
<textarea class="modal-textarea" id="opinionText" maxlength="500" placeholder="请输入详细意见..." style="width:100%;min-height:90px;padding:8px 12px;border:1px solid #d9d9d9;border-radius:4px;font-size:13px;resize:vertical;outline:none;box-sizing:border-box;font-family:inherit" oninput="document.getElementById('charCount').textContent=this.value.length"></textarea>
</div>
<p class="hint-red">*3个工作日之内如果未填写,则默认为确认无误</p>
<p class="hint-red">*点击存在需整改的问题时,才显示详细意见</p>
</div>
<!-- 查看态:已保存的意见 -->
<div id="viewView" style="display:none">
<div class="modal-row">
<div class="modal-row-label">监督结果</div>
<div class="modal-row-value" id="viewResult"></div>
</div>
<div class="modal-row" id="viewOpinionWrap" style="display:none">
<div class="modal-row-label">详细意见</div>
<div class="opinion-box" id="viewOpinion"></div>
</div>
</div>
</div>
<div class="modal-footer" id="modalFooter">
<button class="btn-default" onclick="closeModal()">取消</button>
<button class="btn-primary" id="saveBtn" onclick="saveOpinion()">保存</button>
</div>
</div>
</div>
<script>
document.getElementById('selectAll').addEventListener('change', function () { document.querySelectorAll('.row-check').forEach(c => c.checked = this.checked); });
// 当前操作的会议数据(简化:从行内取)
function getRowData(btn) {
var tr = btn.closest('tr');
return {
name: tr.querySelector('.col-name').textContent,
stage: tr.querySelector('.status-tag').textContent,
tr: tr
};
}
function openOpinion(btn) {
var data = getRowData(btn);
var isView = btn.textContent.trim() === '查看监察意见' || data.stage !== '待监管';
document.getElementById('modalName').textContent = data.name;
if (isView) {
// 查看态(读取 data-tr 上的 data-opinion 属性,无则给默认值)
var saved = btn.getAttribute('data-opinion') || '已确认无误';
var savedFix = btn.getAttribute('data-opinion-fix') || '';
document.getElementById('viewResult').textContent = saved;
if (savedFix) {
document.getElementById('viewOpinionWrap').style.display = 'block';
document.getElementById('viewOpinion').textContent = savedFix;
}
document.getElementById('editView').style.display = 'none';
document.getElementById('viewView').style.display = 'block';
document.getElementById('modalFooter').innerHTML = '<button class="btn-default" onclick="closeModal()">关闭</button>';
} else {
// 编辑态
document.getElementById('editView').style.display = 'block';
document.getElementById('viewView').style.display = 'none';
document.getElementById('modalFooter').innerHTML = '<button class="btn-default" onclick="closeModal()">取消</button><button class="btn-primary" onclick="saveOpinion()">保存</button>';
document.querySelector('input[name="opinionType"][value="ok"]').checked = true;
toggleTextarea();
document.getElementById('opinionText').value = '';
document.getElementById('charCount').textContent = '0';
}
document.getElementById('opinionModal').classList.add('show');
}
function toggleTextarea() {
var isFix = document.querySelector('input[name="opinionType"]:checked').value === 'fix';
document.getElementById('textareaWrap').style.display = isFix ? 'block' : 'none';
}
function saveOpinion() {
var type = document.querySelector('input[name="opinionType"]:checked').value;
var text = document.getElementById('opinionText').value.trim();
if (type === 'fix' && !text) {
alert('请填写详细意见');
return;
}
// 找出当前行(简化:从 modal 反查)
var name = document.getElementById('modalName').textContent;
var rows = document.querySelectorAll('.data-table tbody tr');
for (var i = 0; i < rows.length; i++) {
var tr = rows[i];
if (tr.querySelector('.col-name').textContent === name) {
var btn = tr.querySelector('.op-link[onclick*="openOpinion"]');
if (btn) {
btn.setAttribute('data-opinion', type === 'ok' ? '已确认无误' : '存在需整改的问题');
if (type === 'fix') btn.setAttribute('data-opinion-fix', text);
btn.textContent = '查看监察意见';
}
var stage = tr.querySelector('.status-tag');
stage.className = 'status-tag status-done';
stage.textContent = '监管通过';
break;
}
}
alert('监督意见已保存');
closeModal();
}
function closeModal() { document.getElementById('opinionModal').classList.remove('show'); }
document.getElementById('opinionModal').addEventListener('click', function (e) { if (e.target === this) closeModal(); });
</script>
</body>
</html>