chore: biz_org 合并 / BizProject 重构 / 各角色页面调整 (上次重构遗留)
涵盖改动范围: - 后端 BizOrg / BizProject / BizProjectRating / BizSpecialPlan controller + domain + mapper + service - 前端 admin (BizSpecialPlanAdmin, ProjectCategory) / executor / leader / manager / sponsor 多页调整 - api/dict.js + AdminLayout.vue 同步
This commit is contained in:
@@ -43,7 +43,6 @@
|
||||
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" @click="onAssignBatch">项目分配</el-button>
|
||||
<el-button @click="onEvalBatch">批量评价</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="rows" v-loading="loading" stripe border @selection-change="onSelectionChange">
|
||||
@@ -80,15 +79,15 @@
|
||||
<span>{{ row.isFinished === '1' ? '已结题' : '未结题' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="执行单位评价" width="120" align="center">
|
||||
<el-table-column label="执行单位评价(支持方)" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<a v-if="row.ratingScore" class="link">{{ row.ratingScore }}</a>
|
||||
<a v-if="row.sponsorScore" class="link">{{ row.sponsorScore }}</a>
|
||||
<span v-else style="color:#909399">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="240" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openEval(row)">项目评价</el-button>
|
||||
<el-button link type="primary" @click="openSingleScore(row)">项目评价</el-button>
|
||||
<el-button link type="primary" @click="openAssign(row)">分配</el-button>
|
||||
<el-button link type="primary" @click="onView(row)">查看</el-button>
|
||||
</template>
|
||||
@@ -106,37 +105,29 @@
|
||||
@current-change="load"
|
||||
/>
|
||||
|
||||
<!-- ============ Modal 1: 项目评价 ============ -->
|
||||
<el-dialog v-model="evalVisible" title="执行单位评分" width="600px" append-to-body destroy-on-close>
|
||||
<div class="modal-project-info">
|
||||
<div><span class="lbl">项目编号</span><span>{{ evalForm.projectNo }}</span></div>
|
||||
<div><span class="lbl">项目名称</span><span>{{ evalForm.projectName }}</span></div>
|
||||
</div>
|
||||
<el-form :model="evalForm" label-width="120px" class="modal-form">
|
||||
<el-form-item label="履约质量">
|
||||
<el-rate v-model="evalForm.q1" :max="5" :colors="['#99A9BF','#F7BA2A','#FF9900']" show-text :texts="['1', '2', '3', '4', '5']" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时效响应">
|
||||
<el-rate v-model="evalForm.q2" :max="5" :colors="['#99A9BF','#F7BA2A','#FF9900']" show-text :texts="['1', '2', '3', '4', '5']" />
|
||||
</el-form-item>
|
||||
<el-form-item label="配合度">
|
||||
<el-rate v-model="evalForm.q3" :max="5" :colors="['#99A9BF','#F7BA2A','#FF9900']" show-text :texts="['1', '2', '3', '4', '5']" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合规安全">
|
||||
<el-rate v-model="evalForm.q4" :max="5" :colors="['#99A9BF','#F7BA2A','#FF9900']" show-text :texts="['1', '2', '3', '4', '5']" />
|
||||
</el-form-item>
|
||||
<el-form-item label="评价备注">
|
||||
<el-input v-model="evalForm.remark" type="textarea" :rows="4" placeholder="请输入评价备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- ============ Modal 1: 项目评价 (与 manager/Projects.vue 完全相同的样式) ============ -->
|
||||
<el-dialog v-model="singleScoreModalOpen" title="执行单位评分" width="626px" :show-close="false">
|
||||
<table class="score-table" style="width:100%;border-collapse:collapse;font-size:13px">
|
||||
<thead><tr style="background:#fafafa">
|
||||
<th style="width:110px;padding:10px 12px;text-align:left">评价维度</th>
|
||||
<th style="padding:10px 12px;text-align:left">评价内容(简洁版)</th>
|
||||
<th style="width:130px;padding:10px 12px;text-align:center">评分标准(1~5 分)</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr><td style="padding:10px 12px;border-top:1px solid #f5f5f5">履约质量</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5">服务/活动效果达标度</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5;text-align:center"><el-input-number v-model="singleScoreForm.qualityScore" :min="0" :max="5" :step="1" controls-position="right" placeholder="1~5" /></td></tr>
|
||||
<tr><td style="padding:10px 12px;border-top:1px solid #f5f5f5">时效响应</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5">执行 & 售后响应速度</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5;text-align:center"><el-input-number v-model="singleScoreForm.responseScore" :min="0" :max="5" :step="1" controls-position="right" placeholder="1~5" /></td></tr>
|
||||
<tr><td style="padding:10px 12px;border-top:1px solid #f5f5f5">配合度</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5">沟通配合 & 问题处理</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5;text-align:center"><el-input-number v-model="singleScoreForm.cooperationScore" :min="0" :max="5" :step="1" controls-position="right" placeholder="1~5" /></td></tr>
|
||||
<tr><td style="padding:10px 12px;border-top:1px solid #f5f5f5">合规安全</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5">流程合规 & 无安全事故</td><td style="padding:10px 12px;border-top:1px solid #f5f5f5;text-align:center"><el-input-number v-model="singleScoreForm.complianceScore" :min="0" :max="5" :step="1" controls-position="right" placeholder="1~5" /></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<template #footer>
|
||||
<el-button @click="evalVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveEval">确定</el-button>
|
||||
<el-button type="primary" @click="confirmSingleScore">确定</el-button>
|
||||
<el-button @click="singleScoreModalOpen=false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- ============ Modal 2: 项目分配 (单选 / 批量复用) ============ -->
|
||||
<el-dialog :title="assignBatchMode ? `批量项目分配 (${assignBatchRows.length} 个项目)` : '项目分配'" width="600px" append-to-body destroy-on-close>
|
||||
<el-dialog v-model="assignVisible" :title="assignBatchMode ? `批量项目分配 (${assignBatchRows.length} 个项目)` : '项目分配'" width="600px" append-to-body destroy-on-close>
|
||||
<!-- 单选模式: 显示项目编号 / 名称 -->
|
||||
<div v-if="!assignBatchMode" class="modal-project-info">
|
||||
<div><span class="lbl">项目编号</span><span>{{ assignForm.projectNo }}</span></div>
|
||||
@@ -175,7 +166,9 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '@/utils/request'
|
||||
import { listSponsorProjects, rateProject, sponsorAssignProject, sponsorAssignBatch } from '@/api/business/project'
|
||||
import { bizUpdate } from '@/api/public'
|
||||
import { listBizPerson } from '@/api/business/person'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
@@ -238,39 +231,65 @@ function onView(row) {
|
||||
ElMessageBox.alert(`项目编号: ${row.projectNo}\n项目名称: ${row.projectName}`, '项目详情', { confirmButtonText: '确定' })
|
||||
}
|
||||
|
||||
// ========== Modal 1: 项目评价 ==========
|
||||
const evalVisible = ref(false)
|
||||
const evalForm = reactive({ projectId: null, projectNo: '', projectName: '', q1: 5, q2: 5, q3: 5, q4: 5, remark: '' })
|
||||
function openEval(row) {
|
||||
evalForm.projectId = row.projectId
|
||||
evalForm.projectNo = row.projectNo
|
||||
evalForm.projectName = row.projectName
|
||||
evalForm.q1 = row.sponsorQ1 || 5
|
||||
evalForm.q2 = row.sponsorQ2 || 5
|
||||
evalForm.q3 = row.sponsorQ3 || 5
|
||||
evalForm.q4 = row.sponsorQ4 || 5
|
||||
evalForm.remark = row.sponsorRemark || ''
|
||||
evalVisible.value = true
|
||||
// ========== 执行单位评分 (与 manager/Projects.vue 完全相同的样式和逻辑) ==========
|
||||
const singleScoreModalOpen = ref(false)
|
||||
const singleScoreTargetRow = ref(null)
|
||||
const singleScoreForm = reactive({ qualityScore: 0, responseScore: 0, cooperationScore: 0, complianceScore: 0 })
|
||||
|
||||
async function openSingleScore(row) {
|
||||
singleScoreTargetRow.value = row
|
||||
singleScoreForm.qualityScore = 0
|
||||
singleScoreForm.responseScore = 0
|
||||
singleScoreForm.cooperationScore = 0
|
||||
singleScoreForm.complianceScore = 0
|
||||
singleScoreModalOpen.value = true
|
||||
// 回显当前 sponsor 在该项目上的历史评分
|
||||
const uid = userStore.user?.userId
|
||||
if (!uid) return
|
||||
try {
|
||||
const r = await request({ url: '/business/project/ratings', method: 'get', params: { projectId: row.projectId } })
|
||||
const list = (r.data && (Array.isArray(r.data) ? r.data : r.data.rows)) || r.rows || []
|
||||
const mine = list.find(x =>
|
||||
String(x.projectId) === String(row.projectId) &&
|
||||
String(x.raterId) === String(uid) &&
|
||||
x.raterRole === 'sponsor'
|
||||
)
|
||||
if (mine) {
|
||||
singleScoreForm.qualityScore = Number(mine.qualityScore) || 0
|
||||
singleScoreForm.responseScore = Number(mine.responseScore) || 0
|
||||
singleScoreForm.cooperationScore = Number(mine.cooperationScore) || 0
|
||||
singleScoreForm.complianceScore = Number(mine.complianceScore) || 0
|
||||
}
|
||||
} catch (e) { /* 拉取失败不影响打开, 维持默认 0 */ }
|
||||
}
|
||||
async function saveEval() {
|
||||
const avg = ((evalForm.q1 + evalForm.q2 + evalForm.q3 + evalForm.q4) / 4).toFixed(2)
|
||||
await rateProject({
|
||||
projectId: evalForm.projectId,
|
||||
sponsorRating: avg,
|
||||
sponsorQ1: evalForm.q1,
|
||||
sponsorQ2: evalForm.q2,
|
||||
sponsorQ3: evalForm.q3,
|
||||
sponsorQ4: evalForm.q4,
|
||||
sponsorRemark: evalForm.remark
|
||||
})
|
||||
ElMessage.success('评价已保存')
|
||||
evalVisible.value = false
|
||||
load()
|
||||
}
|
||||
function onEvalBatch() {
|
||||
if (!selected.value.length) return ElMessage.warning('请先选择项目')
|
||||
if (selected.value.length > 1) return ElMessage.warning('批量评价一次仅可评价一个项目')
|
||||
openEval(selected.value[0])
|
||||
|
||||
async function confirmSingleScore() {
|
||||
if (!singleScoreTargetRow.value) return
|
||||
const f = singleScoreForm
|
||||
if (!f.qualityScore || f.qualityScore < 1 || f.qualityScore > 5) return ElMessage.error('履约质量必须是 1~5 的数字')
|
||||
if (!f.responseScore || f.responseScore < 1 || f.responseScore > 5) return ElMessage.error('时效响应必须是 1~5 的数字')
|
||||
if (!f.cooperationScore || f.cooperationScore < 1 || f.cooperationScore > 5) return ElMessage.error('配合度必须是 1~5 的数字')
|
||||
if (!f.complianceScore || f.complianceScore < 1 || f.complianceScore > 5) return ElMessage.error('合规安全必须是 1~5 的数字')
|
||||
const avg = ((f.qualityScore + f.responseScore + f.cooperationScore + f.complianceScore) / 4).toFixed(1)
|
||||
const projectId = singleScoreTargetRow.value.projectId
|
||||
try {
|
||||
// 1. 写聚合分到 biz_project.sponsor_score
|
||||
await bizUpdate('project', { projectId, sponsorScore: Number(avg) })
|
||||
// 2. 写 4 维度明细到 biz_project_rating (rater_role='sponsor', 后端自动填 raterId)
|
||||
await rateProject({
|
||||
projectId,
|
||||
raterRole: 'sponsor',
|
||||
qualityScore: f.qualityScore,
|
||||
responseScore: f.responseScore,
|
||||
cooperationScore: f.cooperationScore,
|
||||
complianceScore: f.complianceScore
|
||||
})
|
||||
ElMessage.success(`评分完成,平均分:${avg}`)
|
||||
singleScoreModalOpen.value = false
|
||||
load()
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.msg || '评分失败')
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Modal 2: 项目分配 (单选 / 批量) ==========
|
||||
|
||||
Reference in New Issue
Block a user