249 lines
10 KiB
Vue
249 lines
10 KiB
Vue
<template>
|
|
<div class="page-card">
|
|
<!-- 面包屑 -->
|
|
<div class="breadcrumb">
|
|
首页 / 项目管理 / <span class="current">项目详情</span>
|
|
</div>
|
|
|
|
<!-- ================= 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 || '-' }}</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="项目评价"><span class="info-value">{{ fmtScore(row.managerScore) }}</span></el-form-item></el-col>
|
|
</el-row>
|
|
</el-form>
|
|
|
|
<!-- ================= 2. 角色劳务设置 (只读) ================= -->
|
|
<div class="new-card-title">角色劳务设置</div>
|
|
<p class="hint-text">*角色劳务设置针对该项目所有会议生效</p>
|
|
<table class="info-table role-labor-table">
|
|
<colgroup>
|
|
<col style="width:120px">
|
|
<col style="width:180px">
|
|
</colgroup>
|
|
<thead>
|
|
<tr>
|
|
<th>角色</th>
|
|
<th style="text-align:right">劳务金额</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>
|
|
</tr>
|
|
<tr v-if="!form.roleRows.length"><td colspan="2" class="empty-row">暂无角色</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- ================= 3. 支持单位监督员 (只读) ================= -->
|
|
<div class="new-card-title">支持单位监督员</div>
|
|
<p class="hint-text">*增加只显示已点支持意向的</p>
|
|
<div class="monitor-list">
|
|
<div v-for="(m, idx) in monitors" :key="idx" class="monitor-row">
|
|
<span class="monitor-num">{{ idx + 1 }}.</span>
|
|
<span class="monitor-name">{{ m.userName || m.name || '-' }}</span>
|
|
</div>
|
|
<div v-if="!monitors.length" class="empty-hint">暂无监督员</div>
|
|
</div>
|
|
|
|
<!-- ================= 4. 已发布公告 (只读, 仅查看) ================= -->
|
|
<div class="new-card-title">已发布公告</div>
|
|
<el-table v-loading="loading" :data="announcements" border>
|
|
<el-table-column prop="title" label="公告标题" min-width="200" show-overflow-tooltip />
|
|
<el-table-column prop="createTime" label="发布时间" width="160" align="center" :formatter="(r)=>fmtDate(r.createTime)" />
|
|
<el-table-column prop="creatorName" label="发布人" width="120" align="center" />
|
|
<el-table-column label="操作" width="100" align="center">
|
|
<template #default="{ row }">
|
|
<el-button link type="primary" @click="openPreview(row.fileUrl || row.url, row.title)">查看</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<!-- 公告预览 (只读) -->
|
|
<Preview v-model="previewOpen" :url="previewUrl" :title="previewTitle" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { bizGet, bizList } from '@/api/public'
|
|
import Preview from '@/components/Preview.vue'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
// ===================== 状态 =====================
|
|
const projectId = ref('')
|
|
const row = ref({})
|
|
const loading = ref(false)
|
|
|
|
// 角色劳务 (从 biz_project.role_labor JSON 字段读)
|
|
const form = reactive({
|
|
roleRows: [
|
|
{ role: '主席', customName: '', amount: 5000 },
|
|
{ role: '主持', customName: '', amount: 3000 }
|
|
]
|
|
})
|
|
// 监督员 (从 biz_project.monitors JSON 字段读)
|
|
const monitors = ref([])
|
|
|
|
// 已发布公告
|
|
const announcements = ref([])
|
|
|
|
// 公告预览
|
|
const previewOpen = ref(false)
|
|
const previewUrl = ref('')
|
|
const previewTitle = ref('公告预览')
|
|
|
|
// ===================== 工具 =====================
|
|
function numOrZero(v) {
|
|
const n = Number(v)
|
|
return isNaN(n) ? 0 : n
|
|
}
|
|
function fmtScore(v) {
|
|
if (v == null || v === '') return '-'
|
|
return Number(v).toFixed(1)
|
|
}
|
|
function fmtMoney(v) {
|
|
if (v == null || v === '' || v === 0) return '0.00'
|
|
return Number(v).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
}
|
|
function fmtDate(v) {
|
|
if (!v) return '-'
|
|
const s = String(v)
|
|
const m = s.match(/^(\d{4})-(\d{2})-(\d{2})/)
|
|
return m ? `${m[1]}.${m[2]}.${m[3]}` : s
|
|
}
|
|
|
|
// ===================== 加载 =====================
|
|
async function loadProject() {
|
|
projectId.value = route.params.projectId
|
|
if (!projectId.value) return
|
|
loading.value = true
|
|
try {
|
|
const resp = await bizGet('project', projectId.value)
|
|
const data = (resp && resp.data) || resp || {}
|
|
row.value = data || {}
|
|
// 角色劳务 (JSON 字段, 缺则用默认)
|
|
try {
|
|
const rl = JSON.parse(row.value.roleLabor || '[]')
|
|
if (Array.isArray(rl) && rl.length) form.roleRows = rl
|
|
} catch (e) { /* keep default */ }
|
|
// 监督员 (JSON 字段)
|
|
try {
|
|
const mn = JSON.parse(row.value.monitors || '[]')
|
|
if (Array.isArray(mn)) monitors.value = mn
|
|
} catch (e) { /* keep empty */ }
|
|
} catch (e) {
|
|
console.error('[project-detail] load failed', e)
|
|
row.value = {}
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function loadAnnouncements() {
|
|
try {
|
|
const resp = await bizList('publicity', { projectId: projectId.value })
|
|
const arr = (resp && resp.rows) || ((resp && resp.data) && (resp && resp.data).rows) || (resp && resp.data) || []
|
|
announcements.value = Array.isArray(arr) ? arr : []
|
|
} catch (e) {
|
|
console.error('[project-detail] announcements load failed', e)
|
|
announcements.value = []
|
|
}
|
|
}
|
|
|
|
// ===================== 导航 =====================
|
|
function goBack() { router.push('/manager/projects') }
|
|
function goEdit() { router.push(`/manager/projects/edit/${projectId.value}`) }
|
|
|
|
// ===================== 预览 =====================
|
|
function openPreview(url, title) {
|
|
if (!url) return
|
|
previewUrl.value = url
|
|
previewTitle.value = title || '公告预览'
|
|
previewOpen.value = true
|
|
}
|
|
|
|
// ===================== 初始化 =====================
|
|
onMounted(async () => {
|
|
await loadProject()
|
|
await loadAnnouncements()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 1:1 抄列表页 (manager/Projects.vue) 风格 */
|
|
.page-card { background: #fff; padding: 16px 20px; border-radius: 6px; border: 1px solid #f0f0f0; max-width: 1200px; }
|
|
.breadcrumb { font-size: 13px; color: #8c8c8c; margin-bottom: 12px; }
|
|
.breadcrumb .current { color: #262626; font-weight: 500; }
|
|
|
|
/* 章节标题 (与编辑页 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; }
|
|
|
|
/* 项目信息: 11 字段, 一列竖排 (el-form + 文本, 无分隔线) */
|
|
.info-form { padding-top: 4px; }
|
|
.info-form :deep(.el-form-item) { margin-bottom: 8px; }
|
|
.info-form :deep(.el-form-item__label) {
|
|
font-size: 14px; color: #595959; width: 130px; padding-right: 16px;
|
|
}
|
|
.info-value { font-size: 15px; color: #1a1a1a; font-weight: 500; }
|
|
.info-value.money {
|
|
font-family: ui-monospace, "Courier New", monospace;
|
|
}
|
|
|
|
/* 监督员 */
|
|
.monitor-list { padding: 4px 0; }
|
|
.monitor-row {
|
|
display: flex; align-items: center; gap: 8px;
|
|
padding: 8px 12px; background: #fafafa; border-radius: 4px;
|
|
margin-bottom: 6px;
|
|
}
|
|
.monitor-num { width: 24px; color: #8c8c8c; font-size: 13px; }
|
|
.monitor-name { flex: 1; font-size: 14px; color: #1a1a1a; }
|
|
.empty-hint { text-align: center; color: #c0c4cc; padding: 12px 0; font-size: 13px; }
|
|
|
|
/* 角色劳务表格 */
|
|
.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; }
|
|
.empty-row { text-align: center; color: #c0c4cc; }
|
|
|
|
/* 提示 (灰色, 跟 ProjectsNew 一致) */
|
|
.hint-text { font-size: 12px; color: #909399; margin: 8px 0; line-height: 1.6; }
|
|
.hint-text p { margin-bottom: 4px; }
|
|
.hint-text p:last-child { margin-bottom: 0; }
|
|
</style> |