release: freeze 2026-10-15 validated release baseline
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s

This commit is contained in:
WorkBuddy
2026-10-15 09:47:31 +08:00
parent 827c23db1d
commit 3c2b967274
149 changed files with 4324 additions and 348 deletions
@@ -49,9 +49,9 @@
<el-col :span="12">
<el-card shadow="never" class="inner-card">
<template #header><span>手工签到(特殊情形)</span></template>
<el-input v-model="manualUserId" placeholder="输入用户 ID" />
<el-input v-model="manualUserNumber" placeholder="输入13位用户编号" />
<div class="btn-row">
<el-button type="primary" :loading="saving" :disabled="!manualUserId"
<el-button type="primary" :loading="saving" :disabled="!manualUserNumber"
@click="manual">手工签到</el-button>
</div>
<div class="form-hint">
@@ -65,8 +65,8 @@
<el-card v-if="lastResult" shadow="never" class="inner-card result-card">
<template #header><span>最近一次签到</span></template>
<el-descriptions :column="2" border size="small">
<el-descriptions-item label="用户 ID">{{ lastResult.userId }}</el-descriptions-item>
<el-descriptions-item label="活动 ID">{{ lastResult.activityId }}</el-descriptions-item>
<el-descriptions-item label="用户编号">{{ lastResult.userDisplayNumber }}</el-descriptions-item>
<el-descriptions-item label="活动编号">{{ lastResult.activityDisplayId }}</el-descriptions-item>
<el-descriptions-item label="本次是否为重复签到">
<el-tag :type="lastResult.alreadyCheckedIn ? 'warning' : 'success'" size="small">
{{ lastResult.alreadyCheckedIn ? '是(已幂等,未重复记录)' : '否' }}
@@ -96,7 +96,7 @@ import { get, post } from '@/api/client'
const activities = ref<any[]>([])
const activityId = ref('')
const token = ref('')
const manualUserId = ref('')
const manualUserNumber = ref('')
const stats = ref<any>(null)
const lastResult = ref<any>(null)
const scanning = ref(false)
@@ -151,15 +151,15 @@ async function scan() {
}
async function manual() {
if (!activityId.value || !manualUserId.value.trim()) return
if (!activityId.value || !manualUserNumber.value.trim()) return
saving.value = true
try {
const res: any = await post(
`/activities/operator/${activityId.value}/checkin/manual`,
{ userId: manualUserId.value.trim() },
{ userId: manualUserNumber.value.trim() },
authHeader())
lastResult.value = { ...res.data, userId: manualUserId.value.trim(), activityId: activityId.value }
manualUserId.value = ''
lastResult.value = { ...res.data, userDisplayNumber: res.data?.userDisplayNumber || manualUserNumber.value.trim(), activityDisplayId: activities.value.find((item: any) => item.id === activityId.value)?.displayId }
manualUserNumber.value = ''
ElMessage.success(res.data?.alreadyCheckedIn ? '该用户已签到(未重复记录)' : '手工签到成功')
await loadStats()
} finally {
@@ -4,13 +4,13 @@
<template #header>
<div class="card-header">
<span>活动互选管理</span>
<el-input v-model="searchActivityId" placeholder="活动ID搜索" clearable style="width:260px" />
<el-input v-model="searchActivityNumber" placeholder="活动编号搜索" clearable style="width:260px" />
<el-button type="primary" @click="loadData" :loading="loading">查询</el-button>
</div>
</template>
<el-table :data="tableData" v-loading="loading" border stripe>
<el-table-column prop="activityId" label="活动ID" width="160" />
<el-table-column prop="activityDisplayId" label="活动编号" width="160" />
<el-table-column prop="activityTitle" label="活动名称" min-width="180" />
<el-table-column label="双向互选对数" width="140">
<template #default="{row}">{{ row.mutualPairs }} 对</template>
@@ -31,8 +31,8 @@
<div v-if="currentActivity">
<h4>{{ currentActivity.title }} — 双向互选列表</h4>
<el-table :data="mutualList" border size="small" style="margin-top:16px">
<el-table-column prop="sourceUserId" label="表达者ID" min-width="180" />
<el-table-column prop="targetUserId" label="被表达者ID" min-width="180" />
<el-table-column prop="sourceUserDisplayNumber" label="表达者编号" min-width="180" />
<el-table-column prop="targetUserDisplayNumber" label="被表达者编号" min-width="180" />
<el-table-column prop="confirmedAt" label="确认时间" width="180" />
</el-table>
<el-empty v-if="!mutualList.length" description="暂无双向互选记录" />
@@ -46,7 +46,7 @@ import { getAccessToken } from '@/utils/token'
import { ref, onMounted } from 'vue'
import { get } from '@/api/client'
const searchActivityId = ref('')
const searchActivityNumber = ref('')
const loading = ref(false)
const tableData = ref<any[]>([])
const drawerVisible = ref(false)
@@ -59,8 +59,8 @@ async function loadData() {
const token = getAccessToken()
const res: any = await get('/activities/operator/activity-interest-summary', {}, { Authorization: `Bearer ${token}` })
const rows = res.data || []
tableData.value = searchActivityId.value
? rows.filter((row: any) => row.activityId.includes(searchActivityId.value.trim()))
tableData.value = searchActivityNumber.value
? rows.filter((row: any) => (row.activityDisplayId || '').includes(searchActivityNumber.value.trim()))
: rows
} catch (e) {
console.error(e)
@@ -9,6 +9,7 @@
</template>
<el-table :data="tableData" v-loading="loading" border stripe>
<el-table-column prop="displayId" label="活动编号" min-width="180" />
<el-table-column prop="title" label="活动名称" min-width="180" />
<el-table-column prop="activityType" label="活动类型" width="130">
<template #default="{ row }">
@@ -128,7 +129,6 @@
<el-dialog v-model="orgVisible" title="活动组织与容量分组" width="640px">
<h4>组织关系</h4>
<el-table :data="orgs" border size="small">
<el-table-column prop="organizationId" label="组织ID" min-width="160" />
<el-table-column prop="name" label="名称" min-width="140" />
<el-table-column prop="role" label="角色" width="120">
<template #default="{ row }">{{ roleText(row.role) }}</template>
+2 -2
View File
@@ -16,14 +16,14 @@
</template>
<el-table :data="tableData" v-loading="loading" border stripe>
<el-table-column prop="operatorId" label="操作人ID" width="140" />
<el-table-column prop="operatorDisplayId" label="操作人编号" min-width="180" />
<el-table-column prop="action" label="操作" width="160">
<template #default="{row}">{{ actionText(row.action) }}</template>
</el-table-column>
<el-table-column prop="resourceType" label="资源类型" width="120">
<template #default="{row}">{{ row.resourceType }}</template>
</el-table-column>
<el-table-column prop="resourceId" label="资源ID" width="140" show-overflow-tooltip />
<el-table-column prop="resourceDisplayId" label="业务编号" min-width="180" show-overflow-tooltip><template #default="{ row }">{{ row.resourceDisplayId || '未公开编号(已脱敏)' }}</template></el-table-column>
<el-table-column prop="ip" label="IP地址" width="140" />
<el-table-column prop="createdAt" label="时间" width="180">
<template #default="{row}">{{ formatTime(row.createdAt) }}</template>
@@ -179,7 +179,7 @@ function isExternalLink(value: string) {
function previewTargetText(banner: any) {
const dest = TARGET_TYPE_DEST[banner.targetType] || banner.targetType || '无'
if (!banner.targetValue) return dest
return `${dest} · ${banner.targetValue}`
return `${dest} · 已配置站内目标`
}
function assignForm(source: any) {
form.value = {
@@ -88,7 +88,7 @@
<el-dialog v-model="assignDialogVisible" title="分配咨询师" width="400px">
<el-form label-width="80px">
<el-form-item label="咨询师">
<el-input v-model="assignForm.counselorId" placeholder="请输入咨询师工号 / ID" />
<el-input v-model="assignForm.counselorNumber" placeholder="请输入13位工作人员编号" />
</el-form-item>
</el-form>
<template #footer>
@@ -143,7 +143,7 @@ const assignedServices = ref<any[]>([])
const assignDialogVisible = ref(false)
const followupDialogVisible = ref(false)
const currentId = ref('')
const assignForm = ref({ counselorId: '' })
const assignForm = ref({ counselorNumber: '' })
const followupForm = ref({ type: 'phone_call', content: '' })
const SERVICE_TYPE_LABEL: Record<string, string> = {
@@ -211,19 +211,19 @@ function onTabChange(name: string | number) {
function openAssign(row: any) {
currentId.value = row.id
assignForm.value.counselorId = ''
assignForm.value.counselorNumber = ''
assignDialogVisible.value = true
}
async function doAssign() {
if (!assignForm.value.counselorId) {
ElMessage.warning('请填写咨询师 ID')
if (!assignForm.value.counselorNumber) {
ElMessage.warning('请填写咨询师编号')
return
}
submitting.value = true
try {
await post(`/counseling/services/operator/${currentId.value}/assign`,
{ counselorId: assignForm.value.counselorId })
{ counselorId: assignForm.value.counselorNumber })
ElMessage.success('分配成功')
assignDialogVisible.value = false
loadQueue()
@@ -2,11 +2,24 @@
<div class="matchmaker-management">
<el-card>
<template #header><div class="card-header"><span>公益红娘管理</span></div></template>
<el-tabs v-model="activeTab">
<el-tabs v-model="activeTab" @tab-change="refresh">
<el-tab-pane v-if="canAssign" label="服务进展与跟进" name="management">
<el-table :data="managedServices" stripe v-loading="loading">
<el-table-column prop="userDisplayNumber" label="用户编号" min-width="150" />
<el-table-column prop="operatorDisplayId" label="红娘编号" min-width="150" />
<el-table-column label="服务状态" width="120"><template #default="{ row }">{{ statusLabel(row.status) }}</template></el-table-column>
<el-table-column prop="latestFollowupContent" label="最新跟进" min-width="230" show-overflow-tooltip />
<el-table-column label="操作" width="110"><template #default="{ row }">
<el-button link type="primary" @click="openServiceDetail(row.serviceId, true)">查看记录</el-button>
</template></el-table-column>
</el-table>
</el-tab-pane>
<!-- ZC-MM-002:服务请求队列仅分配权持有者可见(matchmaker.assign 或 matchmaker.team.assign) -->
<el-tab-pane v-if="canAssign" label="服务请求队列" name="services">
<el-table :data="services" stripe v-loading="loading">
<el-table-column prop="userId" label="用户ID" width="120" />
<el-table-column prop="userDisplayNumber" label="用户编号" width="190">
<template #default="{ row }">{{ row.userDisplayNumber || '实名后生成' }}</template>
</el-table-column>
<el-table-column prop="serviceType" label="服务类型" width="160">
<template #default="{ row }">{{ serviceTypeLabel(row.serviceType) }}</template>
</el-table-column>
@@ -21,6 +34,7 @@
<template #default="{ row }">
<el-button v-if="row.status==='submitted'" size="small" type="primary" @click="openAssign(row)">分配</el-button>
<el-button v-if="row.status==='assigned' || row.status==='in_service'" size="small" @click="openRecommendation(row)">创建推荐</el-button>
<el-button v-if="row.status!=='submitted'" size="small" @click="openServiceDetail(row.serviceId || row.id)">查看服务</el-button>
</template>
</el-table-column>
</el-table>
@@ -28,8 +42,8 @@
<el-tab-pane label="推荐列表" name="recommendations">
<el-table :data="recommendations" stripe v-loading="loading">
<el-table-column prop="userId" label="用户" width="110" />
<el-table-column prop="targetUserId" label="推荐对象" width="110" />
<el-table-column prop="userDisplayNumber" label="用户" width="110" />
<el-table-column prop="targetUserDisplayNumber" label="推荐对象" width="110" />
<el-table-column prop="reason" label="推荐理由" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="130">
<template #default="{ row }"><el-tag :type="recStatusTag(row.status)">{{ recStatusLabel(row.status) }}</el-tag></template>
@@ -43,7 +57,9 @@
<!-- ZC-MM-002 新增:服务对象分配(本人名下生效中的分配 + 转分配/撤销/结束) -->
<el-tab-pane label="服务对象分配" name="assignments">
<el-table :data="assignments" stripe v-loading="assignmentsLoading">
<el-table-column prop="userId" label="用户ID" width="120" />
<el-table-column prop="userDisplayNumber" label="用户编号" width="190">
<template #default="{ row }">{{ row.userDisplayNumber || '实名后生成' }}</template>
</el-table-column>
<el-table-column prop="assignmentScope" label="范围" width="90">
<template #default="{ row }">{{ assignmentScopeLabel(row.assignmentScope) }}</template>
</el-table-column>
@@ -59,6 +75,7 @@
</el-table-column>
<el-table-column label="操作" width="220">
<template #default="{ row }">
<el-button v-if="row.serviceId" size="small" @click="openServiceDetail(row.serviceId)">查看服务</el-button>
<el-button size="small" type="primary" @click="openTransfer(row)">转分配</el-button>
<el-button size="small" @click="openTerminate(row, 'revoke')">撤销</el-button>
<el-button size="small" @click="openTerminate(row, 'end')">结束</el-button>
@@ -69,8 +86,8 @@
<!-- ZC-MM-003 / B1 立项:受控联系授权(审批岗队列 + 本人授权/撤销) -->
<el-tab-pane label="联系授权审批" name="contactAuths">
<el-table :data="contactAuths" stripe v-loading="contactAuthsLoading">
<el-table-column prop="operatorId" label="红娘ID" width="120" />
<el-table-column prop="userId" label="用户ID" width="120" />
<el-table-column prop="operatorDisplayId" label="红娘编号" width="120" />
<el-table-column prop="userDisplayNumber" label="用户编号" width="120" />
<el-table-column prop="channel" label="渠道" width="110">
<template #default="{ row }">{{ contactChannelLabel(row.channel) }}</template>
</el-table-column>
@@ -151,8 +168,8 @@
<!-- 创建推荐弹窗 -->
<el-dialog v-model="recDialogVisible" title="创建红娘推荐" width="480px">
<el-form label-width="100px">
<el-form-item label="推荐给用户"><el-input v-model="recForm.userId" disabled /></el-form-item>
<el-form-item label="推荐对象ID"><el-input v-model="recForm.targetUserId" placeholder="输入被推荐对象的用户ID" /></el-form-item>
<el-form-item label="推荐给用户"><el-input v-model="recForm.userDisplayNumber" disabled /></el-form-item>
<el-form-item label="推荐对象编号"><el-input v-model="recForm.targetUserDisplayNumber" placeholder="输入13位用户编号" /></el-form-item>
<el-form-item label="推荐理由"><el-input v-model="recForm.reason" type="textarea" :rows="3" placeholder="说明为什么这个对象适合该用户" /></el-form-item>
</el-form>
<template #footer>
@@ -160,16 +177,67 @@
<el-button type="primary" @click="doCreateRecommendation">发送推荐</el-button>
</template>
</el-dialog>
<el-dialog v-model="serviceDetailVisible" title="服务详情" width="620px">
<el-descriptions v-if="serviceDetail" :column="2" border>
<el-descriptions-item label="服务编号">{{ serviceDetail.serviceDisplayId }}</el-descriptions-item>
<el-descriptions-item label="状态">{{ statusLabel(serviceDetail.status) }}</el-descriptions-item>
<el-descriptions-item label="服务类型">{{ serviceTypeLabel(serviceDetail.serviceType) }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDateTime(serviceDetail.createdAt) }}</el-descriptions-item>
<el-descriptions-item label="服务需求" :span="2">{{ serviceDetail.description || '未填写' }}</el-descriptions-item>
</el-descriptions>
<el-divider content-position="left">服务历史</el-divider>
<el-empty v-if="!serviceDetail?.followups?.length" description="暂无服务记录" />
<el-timeline v-else>
<el-timeline-item v-for="item in serviceDetail.followups" :key="item.id" :timestamp="formatDateTime(item.createdAt)">
{{ item.content }}
</el-timeline-item>
</el-timeline>
<template v-if="serviceDetail && !managementDetail">
<el-divider />
<el-button type="primary" @click="openFollowup">添加跟进记录</el-button>
<el-button
v-if="['completed', 'cancelled'].indexOf(serviceDetail.status) < 0"
type="success"
plain
@click="completeCurrentService"
>完成服务</el-button>
</template>
</el-dialog>
<el-dialog v-model="followupDialogVisible" title="添加跟进记录" width="520px">
<el-form label-width="90px">
<el-form-item label="跟进方式">
<el-select v-model="followupForm.followupType" style="width: 100%">
<el-option label="电话沟通" value="phone_call" />
<el-option label="微信沟通" value="wechat" />
<el-option label="面对面" value="face_to_face" />
<el-option label="其他" value="other" />
</el-select>
</el-form-item>
<el-form-item label="跟进内容" required>
<el-input v-model="followupForm.content" type="textarea" :rows="4" placeholder="记录本次服务进展" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="followupDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitFollowup">保存记录</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ref, computed, onActivated } from 'vue'
import { useRoute } from 'vue-router'
import { get, post } from '@/api/client'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
const authStore = useAuthStore()
const route = useRoute()
const managementDetail = ref(false)
const managedServices = ref<any[]>([])
// ZC-MM-002:分配权 = matchmaker.assign(平台授权)或 matchmaker.team.assign(红娘组长)
const canAssign = computed(() => {
const perms: string[] = authStore.permissions || []
@@ -186,6 +254,7 @@ const assignableMatchmakers = ref<any[]>([])
const assignDialogVisible = ref(false)
const transferDialogVisible = ref(false)
const terminateDialogVisible = ref(false)
const recDialogVisible = ref(false)
const currentServiceId = ref('')
const currentUserId = ref('')
const currentAssignmentId = ref('')
@@ -193,7 +262,11 @@ const terminateAction = ref<'revoke' | 'end'>('revoke')
const assignForm = ref({ matchmakerOperatorId: '', reason: '' })
const transferForm = ref({ newMatchmakerOperatorId: '', reason: '' })
const terminateForm = ref({ reason: '' })
const recForm = ref({ userId: '', targetUserId: '', reason: '' })
const recForm = ref({ userDisplayNumber: '', targetUserDisplayNumber: '', reason: '' })
const serviceDetailVisible = ref(false)
const serviceDetail = ref<any>(null)
const followupDialogVisible = ref(false)
const followupForm = ref({ followupType: 'phone_call', content: '' })
const SERVICE_TYPE_LABEL: Record<string, string> = {
recommendation: '推荐对象', referral_counseling: '转介咨询',
@@ -248,6 +321,49 @@ function contactChannelLabel(s: string) { return CONTACT_CHANNEL_LABEL[s] || s }
function contactStatusLabel(s: string) { return CONTACT_STATUS_LABEL[s] || s }
function contactStatusTag(s: string) { return CONTACT_STATUS_TAG[s] || 'info' }
async function openServiceDetail(serviceId: string, management = false) {
managementDetail.value = management
if (!serviceId) return
try {
const res: any = await get(`/operator/matchmaker/${management ? 'management/' : ''}services/${serviceId}`)
serviceDetail.value = res?.data || null
serviceDetailVisible.value = true
} catch { serviceDetail.value = null }
}
function openFollowup() {
followupForm.value = { followupType: 'phone_call', content: '' }
followupDialogVisible.value = true
}
async function submitFollowup() {
if (!followupForm.value.content.trim() || !serviceDetail.value?.serviceId) {
ElMessage.warning('请填写跟进内容')
return
}
try {
await post(`/operator/matchmaker/services/${serviceDetail.value.serviceId}/followup`, followupForm.value)
ElMessage.success('跟进记录已保存')
followupDialogVisible.value = false
await openServiceDetail(serviceDetail.value.serviceId)
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || '保存跟进记录失败')
}
}
async function completeCurrentService() {
if (!serviceDetail.value?.serviceId) return
try {
await ElMessageBox.confirm('确认将该红娘服务标记为已完成吗?', '完成服务', { type: 'warning' })
await post(`/operator/matchmaker/services/${serviceDetail.value.serviceId}/complete`, {})
ElMessage.success('服务已完成')
await openServiceDetail(serviceDetail.value.serviceId)
await loadAssignments()
} catch (e: any) {
if (e !== 'cancel' && e !== 'close') ElMessage.error(e?.response?.data?.message || '完成服务失败')
}
}
// ===== ZC-MM-003 / B1:受控联系授权 =====
// 审批岗客户端可见性提示(真实判定在 Service 层动态执行:principal 权限 ∈ approver-authorities 配置)
const canApprove = computed(() => {
@@ -259,7 +375,13 @@ const contactAuthsLoading = ref(false)
async function loadContactAuths() {
contactAuthsLoading.value = true
try { const res: any = await get('/operator/matchmaker/contact-authorizations/my'); contactAuths.value = res?.data?.list || [] } finally { contactAuthsLoading.value = false }
try {
const endpoint = canApprove.value
? '/operator/matchmaker/contact-authorizations/pending'
: '/operator/matchmaker/contact-authorizations/my'
const res: any = await get(endpoint)
contactAuths.value = res?.data?.list || []
} finally { contactAuthsLoading.value = false }
}
async function doContactApprove(row: any) {
try {
@@ -306,7 +428,12 @@ async function loadAssignableMatchmakers() {
try { const res: any = await get('/operator/matchmaker/assignments/assignable-matchmakers'); assignableMatchmakers.value = res?.data || [] } catch { assignableMatchmakers.value = [] }
}
async function refresh() {
if (activeTab.value === 'services') await loadServices()
if (activeTab.value === 'management') {
loading.value = true
try { const res: any = await get('/operator/matchmaker/management/services'); managedServices.value = res?.data || [] }
finally { loading.value = false }
}
else if (activeTab.value === 'services') await loadServices()
else if (activeTab.value === 'assignments') await loadAssignments()
else if (activeTab.value === 'contactAuths') await loadContactAuths()
else await loadRecommendations()
@@ -373,21 +500,27 @@ async function doTerminate() {
function openRecommendation(row: any) {
currentServiceId.value = row.id
recForm.value = { userId: row.userId, targetUserId: '', reason: '' }
recForm.value = { userDisplayNumber: row.userDisplayNumber || '实名后生成', targetUserDisplayNumber: '', reason: '' }
recDialogVisible.value = true
}
async function doCreateRecommendation() {
if (!recForm.value.targetUserId || !recForm.value.reason) { ElMessage.warning('请填写推荐对象和理由'); return }
if (!recForm.value.targetUserDisplayNumber || !recForm.value.reason) { ElMessage.warning('请填写推荐对象和理由'); return }
// Phase 13 契约修复:原 POST /matchmaker/services/{id}/recommendation → 后端为 /operator/matchmaker/services/{id}/recommend
try {
await post(`/operator/matchmaker/services/${currentServiceId.value}/recommend`, {
targetUserId: recForm.value.targetUserId, reason: recForm.value.reason
targetUserId: recForm.value.targetUserDisplayNumber, reason: recForm.value.reason
})
ElMessage.success('推荐已发送给用户'); recDialogVisible.value = false; loadRecommendations()
} catch (e: any) { ElMessage.error(e?.response?.data?.message || '操作失败') }
}
onMounted(refresh)
onActivated(async () => {
if (canAssign.value && typeof route.query.serviceId === 'string') {
activeTab.value = 'management'
await openServiceDetail(route.query.serviceId, true)
}
await refresh()
})
</script>
<style scoped>
@@ -10,6 +10,7 @@
<el-table :data="tableData" v-loading="loading" border stripe>
<el-table-column prop="username" label="用户名" width="140" />
<el-table-column prop="displayId" label="人员编号" width="150" />
<el-table-column prop="name" label="姓名" width="120" />
<el-table-column prop="organizationId" label="所属机构" width="160">
<template #default="{row}">{{ orgMap[row.organizationId] || '-' }}</template>
@@ -6,7 +6,7 @@
<el-table-column prop="type" label="类型" width="120">
<template #default="{ row }"><el-tag>{{ typeLabel(row.type) }}</el-tag></template>
</el-table-column>
<el-table-column prop="id" label="关联ID" width="120" />
<el-table-column prop="referralDisplayId" label="转介编号" min-width="180" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }"><el-tag :type="row.status==='confirmed'?'success':'warning'">{{ row.status }}</el-tag></template>
</el-table-column>
+5 -3
View File
@@ -20,7 +20,8 @@
<el-tabs v-model="activeTab">
<el-tab-pane :label="`待审核队列(${pendingTasks.length})`" name="pending">
<el-table :data="pendingTasks" v-loading="loading" row-key="taskId">
<el-table-column prop="userId" label="用户编号" min-width="170" show-overflow-tooltip />
<el-table-column prop="displayId" label="任务编号" min-width="180" />
<el-table-column prop="userDisplayNumber" label="用户编号" min-width="170" show-overflow-tooltip />
<el-table-column prop="reviewType" label="审核类型" min-width="130">
<template #default="{ row }">{{ reviewTypeText(row.reviewType) }}</template>
</el-table-column>
@@ -42,7 +43,8 @@
<el-tab-pane :label="`我的审核(${myTasks.length})`" name="my">
<el-table :data="myTasks" v-loading="loading" row-key="taskId">
<el-table-column prop="userId" label="用户编号" min-width="170" show-overflow-tooltip />
<el-table-column prop="displayId" label="任务编号" min-width="180" />
<el-table-column prop="userDisplayNumber" label="用户编号" min-width="170" show-overflow-tooltip />
<el-table-column prop="reviewType" label="审核类型" min-width="130">
<template #default="{ row }">{{ reviewTypeText(row.reviewType) }}</template>
</el-table-column>
@@ -65,7 +67,7 @@
<div v-if="detailLoading" v-loading="true" class="drawer-loading"></div>
<template v-else-if="currentTask">
<el-descriptions :column="1" border>
<el-descriptions-item label="用户编号">{{ currentTask.userId }}</el-descriptions-item>
<el-descriptions-item label="用户编号">{{ currentTask.userDisplayNumber }}</el-descriptions-item>
<el-descriptions-item label="审核类型">{{ reviewTypeText(currentTask.reviewType) }}</el-descriptions-item>
<el-descriptions-item label="提交时间">{{ formatTime(currentTask.submittedAt) }}</el-descriptions-item>
<el-descriptions-item label="当前状态">
@@ -25,7 +25,8 @@
<el-table :data="tableData" v-loading="loading" border stripe>
<el-table-column prop="reasonText" label="举报原因" width="140" />
<el-table-column prop="reportedUserId" label="被举报人ID" width="140" />
<el-table-column prop="displayId" label="举报编号" min-width="180" />
<el-table-column prop="reportedUserDisplayNumber" label="被举报人编号" width="140" />
<el-table-column prop="description" label="说明" min-width="200" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="100">
<template #default="{row}">
@@ -11,8 +11,8 @@
</el-radio-group>
</div></template>
<el-table :data="appointments" stripe v-loading="loading">
<el-table-column prop="id" label="ID" width="100" />
<el-table-column prop="userId" label="用户ID" width="120" />
<el-table-column prop="displayId" label="预约编号" min-width="180" />
<el-table-column prop="userDisplayNumber" label="用户编号" min-width="180" />
<el-table-column prop="serviceType" label="服务类型" width="150" />
<el-table-column prop="source" label="来源" width="140">
<template #default="{ row }"><el-tag size="small">{{ sourceLabel(row.source) }}</el-tag></template>
@@ -21,7 +21,7 @@
<template #default="{ row }"><el-tag :type="statusTag(row.status)">{{ statusLabel(row.status) }}</el-tag></template>
</el-table-column>
<el-table-column prop="preferredDate" label="预约日期" width="110" />
<el-table-column prop="assignedOperatorId" label="分配人员" width="100" />
<el-table-column prop="assignedOperatorDisplayId" label="分配人员" width="100" />
<el-table-column prop="createdAt" label="创建时间" width="160">
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
</el-table-column>
@@ -74,7 +74,7 @@
<el-descriptions-item label="合计">{{ stats.total }}</el-descriptions-item>
</el-descriptions>
<el-table :data="members" stripe v-loading="memberLoading">
<el-table-column prop="userId" label="用户ID" width="200" show-overflow-tooltip />
<el-table-column prop="userDisplayNumber" label="用户编号" width="200" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="110">
<template #default="{ row }">
<el-tag :type="memberStatusTag(row.status)">{{ memberStatusLabel(row.status) }}</el-tag>
@@ -19,7 +19,7 @@
<section v-else-if="user" class="surface-card detail-card">
<div class="identity-summary">
<el-avatar :size="58">{{ (user.displayName || '用')[0] }}</el-avatar>
<div><h2>{{ user.displayName || '未设置显示名称' }}</h2><span>用户编号:{{ user.id }}</span></div>
<div><h2>{{ user.displayName || '未设置显示名称' }}</h2><span>用户编号:{{ user.displayNumber || '实名后生成' }}</span></div>
<el-progress class="completion" type="dashboard" :percentage="user.profileCompletion || 0" :width="72" :stroke-width="7" color="var(--admin-coral-solid)" />
</div>
<el-tabs v-model="activeTab" class="detail-tabs">
@@ -42,7 +42,9 @@
</el-alert>
<el-table v-else :data="tableData" v-loading="loading" row-key="id">
<el-table-column prop="id" label="用户编号" min-width="170" show-overflow-tooltip />
<el-table-column prop="displayNumber" label="用户编号" min-width="190" show-overflow-tooltip>
<template #default="{ row }">{{ row.displayNumber || '实名后生成' }}</template>
</el-table-column>
<el-table-column prop="displayName" label="显示名称" min-width="110">
<template #default="{ row }">{{ row.displayName || '未设置' }}</template>
</el-table-column>