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:
@@ -25,10 +25,10 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="是否结算:">
|
||||
<el-select v-model="q.isSettled" placeholder="请选择" clearable style="width:120px">
|
||||
<el-option label="已结算" value="1" /><el-option label="未结算" value="0" />
|
||||
<el-option label="已结算" value="Y" /><el-option label="未结算" value="N" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="评价得分:"><el-input v-model="q.ratingScore" placeholder="输入评分" clearable /></el-form-item>
|
||||
<el-form-item label="评价得分:"><el-input v-model="q.managerScore" placeholder="输入评分" clearable /></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="load">查找</el-button><el-button @click="reset">重置</el-button></el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -58,9 +58,15 @@
|
||||
<el-table-column prop="availableAmount" label="可用金额" width="130" align="right" :formatter="fmtMoney" />
|
||||
<el-table-column prop="paidLaborAmount" label="已支付劳务费" width="140" align="right" :formatter="fmtMoney" />
|
||||
<el-table-column prop="paidMeetingAmount" label="已支付会务费" width="140" align="right" :formatter="fmtMoney" />
|
||||
<el-table-column prop="complianceScore" label="执行单位得分(合规)" width="140" align="center">
|
||||
<el-table-column prop="managerScore" label="执行单位得分(合规)" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.ratingScore != null">{{ row.ratingScore }}</span>
|
||||
<span v-if="row.managerScore != null">{{ row.managerScore }}</span>
|
||||
<span v-else style="color:#c0c4cc">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sponsorScore" label="执行单位得分(支持)" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.sponsorScore != null">{{ row.sponsorScore }}</span>
|
||||
<span v-else style="color:#c0c4cc">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -199,10 +205,10 @@
|
||||
<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="batchScoreForm.q1" :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="batchScoreForm.q2" :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="batchScoreForm.q3" :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="batchScoreForm.q4" :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="batchScoreForm.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="batchScoreForm.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="batchScoreForm.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="batchScoreForm.complianceScore" :min="0" :max="5" :step="1" controls-position="right" placeholder="1~5" /></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
@@ -255,10 +261,10 @@
|
||||
<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.q1" :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.q2" :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.q3" :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.q4" :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.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>
|
||||
@@ -277,10 +283,12 @@ import request from '@/utils/request'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import Preview from '@/components/Preview.vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const q = ref({ sponsorOrgName: '', execOrgName: '', projectNo: '', projectName: '', projectForm: '', startTime: '', endTime: '', isFinished: '', isSettled: '', ratingScore: '' })
|
||||
const q = ref({ sponsorOrgName: '', execOrgName: '', projectNo: '', projectName: '', projectForm: '', startTime: '', endTime: '', isFinished: '', isSettled: '', managerScore: '' })
|
||||
const rows = ref([])
|
||||
const loading = ref(false)
|
||||
const page = reactive({ pageNum: 1, pageSize: 10, total: 0 })
|
||||
@@ -333,7 +341,7 @@ async function doDelete(row) {
|
||||
}
|
||||
}
|
||||
|
||||
function reset() { q.value = { sponsorOrgName:'', execOrgName:'', projectNo:'', projectName:'', projectForm:'', startTime:'', endTime:'', isFinished:'', isSettled:'', ratingScore:'' }; page.pageNum = 1; load() }
|
||||
function reset() { q.value = { sponsorOrgName:'', execOrgName:'', projectNo:'', projectName:'', projectForm:'', startTime:'', endTime:'', isFinished:'', isSettled:'', managerScore:'' }; page.pageNum = 1; load() }
|
||||
|
||||
// 导出
|
||||
function exportProjects() { ElMessage.info('导出项目功能开发中') }
|
||||
@@ -413,42 +421,59 @@ function confirmDeleteAnn() {
|
||||
.catch(() => ElMessage.error('删除失败'))
|
||||
}
|
||||
|
||||
function openSingleScore(row) {
|
||||
async function openSingleScore(row) {
|
||||
singleScoreTargetRow.value = row
|
||||
// 反显各维度原值 (从后端 ratingQ1~Q4 字段); 无值才归零
|
||||
singleScoreForm.q1 = row.ratingQ1 != null ? Number(row.ratingQ1) : 0
|
||||
singleScoreForm.q2 = row.ratingQ2 != null ? Number(row.ratingQ2) : 0
|
||||
singleScoreForm.q3 = row.ratingQ3 != null ? Number(row.ratingQ3) : 0
|
||||
singleScoreForm.q4 = row.ratingQ4 != null ? Number(row.ratingQ4) : 0
|
||||
singleScoreForm.qualityScore = 0
|
||||
singleScoreForm.responseScore = 0
|
||||
singleScoreForm.cooperationScore = 0
|
||||
singleScoreForm.complianceScore = 0
|
||||
singleScoreModalOpen.value = true
|
||||
}
|
||||
function confirmSingleScore() {
|
||||
if (!singleScoreTargetRow.value) return
|
||||
const dims = [singleScoreForm.q1, singleScoreForm.q2, singleScoreForm.q3, singleScoreForm.q4]
|
||||
const labels = ['履约质量', '时效响应', '配合度', '合规安全']
|
||||
// 4 个维度全部必填
|
||||
for (let i = 0; i < dims.length; i++) {
|
||||
if (dims[i] === 0 || dims[i] === '' || dims[i] == null) {
|
||||
return ElMessage.error(`请填写「${labels[i]}」评分`)
|
||||
// 反显当前登录管理员的历史明细
|
||||
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 === 'manager'
|
||||
)
|
||||
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
|
||||
}
|
||||
const v = Number(dims[i])
|
||||
if (isNaN(v) || v < 1 || v > 5) return ElMessage.error(`${labels[i]}必须是 1~5 的数字`)
|
||||
} catch (e) { /* 拉取失败不影响打开, 维持默认 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.manager_score
|
||||
await bizUpdate('project', { projectId, managerScore: Number(avg) })
|
||||
// 2. 写 4 维度明细到 biz_project_rating (rater_role='manager', 后端自动填 raterId)
|
||||
await request.post('/business/project/rate', {
|
||||
projectId,
|
||||
raterRole: 'manager',
|
||||
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 || '评分失败')
|
||||
}
|
||||
// 校验通过, 计算平均分
|
||||
const scores = dims.map(v => Number(v))
|
||||
const avg = (scores.reduce((s, v) => s + v, 0) / scores.length).toFixed(1)
|
||||
// 构造 payload: 平均分 + 4 个维度分
|
||||
const payload = {
|
||||
projectId: singleScoreTargetRow.value.projectId,
|
||||
ratingScore: Number(avg),
|
||||
ratingQ1: scores[0],
|
||||
ratingQ2: scores[1],
|
||||
ratingQ3: scores[2],
|
||||
ratingQ4: scores[3]
|
||||
}
|
||||
bizUpdate('project', payload)
|
||||
.then(() => { ElMessage.success(`评分完成,平均分:${avg}`); singleScoreModalOpen.value = false; load() })
|
||||
.catch(() => ElMessage.error('评分失败'))
|
||||
}
|
||||
function viewDetail(row) { router.push(`/manager/projects/detail/${row.projectId}`) }
|
||||
|
||||
@@ -474,7 +499,7 @@ const batchKind = ref('')
|
||||
const batchSubmitting = ref(false)
|
||||
const batchMap = { score: '批量评分', close: '批量结题', activate: '批量开通' }
|
||||
const batchScoreRows = ref([]) // 保留兼容旧引用 (实际不再使用)
|
||||
const batchScoreForm = reactive({ q1: 5, q2: 5, q3: 5, q4: 5 }) // 批量评分: 共享一份 4 维度
|
||||
const batchScoreForm = reactive({ qualityScore: 5, responseScore: 5, cooperationScore: 5, complianceScore: 5 }) // 批量评分: 共享一份 4 维度
|
||||
const activateDeadline = ref('')
|
||||
|
||||
// 5 个单行 modal 状态
|
||||
@@ -486,7 +511,7 @@ const delAnnModalOpen = ref(false)
|
||||
const delAnnTargetRow = ref(null)
|
||||
const singleScoreModalOpen = ref(false)
|
||||
const singleScoreTargetRow = ref(null)
|
||||
const singleScoreForm = reactive({ q1: 0, q2: 0, q3: 0, q4: 0 })
|
||||
const singleScoreForm = reactive({ qualityScore: 0, responseScore: 0, cooperationScore: 0, complianceScore: 0 })
|
||||
|
||||
// 新建项目已迁到独立页面 /manager/projects/new (ManagerProjectsNew.vue)
|
||||
function openNewProject() {
|
||||
@@ -518,7 +543,7 @@ function openBatch(kind) {
|
||||
batchKind.value = kind
|
||||
if (kind === 'score') {
|
||||
// 共享评分: 重置 form 为默认值 (5 分)
|
||||
Object.assign(batchScoreForm, { q1: 5, q2: 5, q3: 5, q4: 5 })
|
||||
Object.assign(batchScoreForm, { qualityScore: 5, responseScore: 5, cooperationScore: 5, complianceScore: 5 })
|
||||
}
|
||||
if (kind === 'activate') {
|
||||
activateDeadline.value = ''
|
||||
@@ -554,24 +579,29 @@ async function confirmBatch() {
|
||||
} catch { ElMessage.error('批量开通失败') }
|
||||
finally { batchSubmitting.value = false }
|
||||
} else if (batchKind.value === 'score') {
|
||||
const q1 = Number(batchScoreForm.q1 || 0)
|
||||
const q2 = Number(batchScoreForm.q2 || 0)
|
||||
const q3 = Number(batchScoreForm.q3 || 0)
|
||||
const q4 = Number(batchScoreForm.q4 || 0)
|
||||
const total = ((q1 + q2 + q3 + q4) / 4).toFixed(1)
|
||||
const f = batchScoreForm
|
||||
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 total = ((f.qualityScore + f.responseScore + f.cooperationScore + f.complianceScore) / 4).toFixed(1)
|
||||
try {
|
||||
batchSubmitting.value = true
|
||||
let ok = 0
|
||||
// 共享评分: 所有项目用同一组 q1..q4
|
||||
// 共享评分: 所有项目用同一组 4 维度
|
||||
for (const r of selection.value) {
|
||||
try {
|
||||
await bizUpdate('project', {
|
||||
projectId: r.projectId,
|
||||
ratingScore: Number(total),
|
||||
ratingQ1: q1,
|
||||
ratingQ2: q2,
|
||||
ratingQ3: q3,
|
||||
ratingQ4: q4
|
||||
managerScore: Number(total)
|
||||
})
|
||||
await request.post('/business/project/rate', {
|
||||
projectId: r.projectId,
|
||||
raterRole: 'manager',
|
||||
qualityScore: f.qualityScore,
|
||||
responseScore: f.responseScore,
|
||||
cooperationScore: f.cooperationScore,
|
||||
complianceScore: f.complianceScore
|
||||
})
|
||||
ok++
|
||||
} catch {}
|
||||
|
||||
Reference in New Issue
Block a user