feat: 多处页面改造 + 新增劳务协议模板配置

主要改动:
- biz_meeting_attendee 中间表 + doctor/expert 角色数据隔离
- biz_meeting_attendee 加 handsign/labor_protocol 字段 (劳务协议改造)
- biz_labor_protocol_template 配置表 + admin 页面 (从 HeguiConstants 迁移)
- biz_expert.expertId 改 Long + IdGenerator 生成 (去自增)
- Login.vue / PublicityDetail.vue / Home.vue 等多处 bug 修复 + UI 改进
- 新增 page-tech-review 报告 3 篇 (_self/*.md)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-19 21:06:49 +08:00
co-authored by Claude
parent ec4a8aa8f0
commit e496f34393
44 changed files with 2143 additions and 130 deletions
+26 -13
View File
@@ -5,7 +5,7 @@
<!-- 欢迎栏 -->
<div class="welcome-bar">
<div class="welcome-text">
<h2>下午好,{{ store.user?.userName || '专家' }}专家</h2>
<h2>下午好,{{ displayName }}专家</h2>
<p>欢迎使用项目管理系统</p>
</div>
<div class="welcome-time">
@@ -37,13 +37,11 @@
<a class="more" @click.prevent="$router.push('/doctor/submissions')">更多 </a>
</h2>
<ul class="simple-list">
<li class="simple-item" v-for="s in pendingAgreements" :key="s.planId">
<li class="simple-item" v-for="s in pendingAgreements" :key="s.id">
<div class="item-main">
<span class="item-title">{{ s.planName }}</span>
<span class="item-title">{{ s.meetingName || ('会议 #' + s.meetingId) }}</span>
</div>
<span class="item-status" :class="{ done: s.status === '2' }">
{{ s.status === '2' ? '已通过' : (s.status === '3' ? '已退回' : (s.status === '1' ? '审核中' : '待提交')) }}
</span>
<span class="item-status">待签署</span>
</li>
<li v-if="!pendingAgreements.length" class="empty">暂无待签协议</li>
</ul>
@@ -72,15 +70,23 @@
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useUserStore } from '@/store/user'
import { bizList, listMyMessages } from '@/api/public'
import { getMyExpertProfile } from '@/api/business/expert'
import { listUnsignedMeetingProtocols } from '@/api/business/meetingAttendee'
const store = useUserStore()
const upcomingMeetings = ref([])
const pendingAgreements = ref([])
const notices = ref([])
// 专家真实姓名 (从 biz_expert.name 拿, 不显示 sys_user.userName (登录账号/手机号))
const expertName = ref('')
// 兜底显示名: 优先专家真实姓名 > nickName > userName > '专家'
const displayName = computed(() =>
expertName.value || store.user?.nickName || store.user?.userName || '专家'
)
const nowTime = ref('')
const nowDate = ref('')
@@ -109,13 +115,20 @@ async function load() {
upcomingMeetings.value = (data?.rows || []).slice(0, 5)
} catch (e) { upcomingMeetings.value = [] }
// 待签署协议 (改走 biz_project_plan, 后端 doctor 角色已自动按当前用户过滤)
// 当前用户的专家真实姓名 (用于欢迎栏)
try {
const { data } = await bizList('projectPlan', { pageNum: 1, pageSize: 5 })
pendingAgreements.value = (data?.rows || []).slice(0, 5).map(s => ({
planId: s.planId,
planName: s.planName,
status: s.status
const { data } = await getMyExpertProfile()
expertName.value = data?.name || ''
} catch (e) { expertName.value = '' }
// 待签署协议 (v3: 改走 biz_meeting_attendee, 任一未签即显示)
try {
const { data } = await listUnsignedMeetingProtocols()
pendingAgreements.value = (data || []).slice(0, 5).map(s => ({
id: s.id, // biz_meeting_attendee.id (用于签署接口)
meetingId: s.meetingId,
meetingName: s.meetingName,
startTime: s.startTime
}))
} catch (e) { pendingAgreements.value = [] }