feat: 生产部署 + 会议全链路 (执行方分配/费用结算/签到脱敏/H5相机)
- 生产 nginx 三路径: ry-vue3 /hg/, ry-h5 /camera/, API /hg-api, .env 分环境 - 签约链接/摄像头 base-url 走 yml, 不再硬编码 /hg 与 localhost - 新增 biz_project_executor_assign (镜像 sponsor_assign) 执行方项目级分配 - 会议费用后台汇总 FeeCalcScheduler + 结算回写项目金额 + 状态流转调度 - 签到表拍照高斯模糊脱敏 extra_oss_url, 新增 PosterService/StageDeriver - 清理 biz_support_intent 旧表 (6 Java/XML 删除) - ry-h5 相机黑屏修复: 显式 video.play() + 动态 apiBase 上传 - 资源: simhei 字体 / logo.png / qrcode_1.png / favicon.ico Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -41,12 +41,14 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="toolbar">
|
||||
<!-- SUB 子账号: 仅查看被分配的项目, 不参与重新分配, 隐藏"项目分配"按钮 -->
|
||||
<div v-if="!isSub" class="toolbar">
|
||||
<el-button type="primary" @click="onAssignBatch">项目分配</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="rows" v-loading="loading" stripe border @selection-change="onSelectionChange">
|
||||
<el-table-column type="selection" width="40" />
|
||||
<!-- SUB 子账号无需勾选 (不参与分配) -->
|
||||
<el-table-column v-if="!isSub" type="selection" width="40" />
|
||||
<el-table-column label="项目编号" width="160">
|
||||
<template #default="{ row }">
|
||||
<a class="link">{{ row.projectNo }}</a>
|
||||
@@ -88,7 +90,8 @@
|
||||
<el-table-column label="操作" width="240" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="openSingleScore(row)">项目评价</el-button>
|
||||
<el-button link type="primary" @click="openAssign(row)">分配</el-button>
|
||||
<!-- SUB 子账号仅查看, 不参与分配 -->
|
||||
<el-button v-if="!isSub" link type="primary" @click="openAssign(row)">分配</el-button>
|
||||
<el-button link type="primary" @click="onView(row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -140,39 +143,38 @@
|
||||
</div>
|
||||
<el-form :model="assignForm" label-width="100px" class="modal-form">
|
||||
<el-form-item label="分配给监察员">
|
||||
<el-input v-model="assignForm.monitorInput" placeholder="输入监察员姓名筛选" clearable style="width: 240px" @input="filterMonitors" />
|
||||
</el-form-item>
|
||||
<el-form-item label="候选监察员">
|
||||
<div class="chip-list">
|
||||
<el-tag v-for="m in candidateMonitors" :key="m.userId" class="chip" type="info" effect="plain" @click="pickMonitor(m)">{{ m.name }}</el-tag>
|
||||
<span v-if="!candidateMonitors.length" style="color:#909399">无可分配监察员</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配说明">
|
||||
<el-input v-model="assignForm.assignDesc" type="textarea" :rows="3" placeholder="请输入分配说明" />
|
||||
</el-form-item>
|
||||
<el-form-item label="监察要点">
|
||||
<el-input v-model="assignForm.assignPoints" type="textarea" :rows="3" placeholder="请输入监察要点" />
|
||||
<el-select v-model="assignForm.monitorUserIds" multiple collapse-tags collapse-tags-tooltip filterable placeholder="请选择监察员 (姓名 / 手机号, 可多选)" style="width: 100%">
|
||||
<el-option
|
||||
v-for="m in allMonitors"
|
||||
:key="m.userId"
|
||||
:label="m.name + ' (' + m.phone + ')'"
|
||||
:value="m.userId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="assignVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="assignSubmitting" @click="saveAssign">确定</el-button>
|
||||
<el-button type="primary" :loading="assignSubmitting" :disabled="!assignForm.monitorUserIds.length" @click="saveAssign">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '@/utils/request'
|
||||
import { listSponsorProjects, rateProject, sponsorAssignProject, sponsorAssignBatch } from '@/api/business/project'
|
||||
import { listSponsorProjects, rateProject, sponsorAssignProject, sponsorAssignBatch, getSponsorAssigns } from '@/api/business/project'
|
||||
import { bizUpdate } from '@/api/public'
|
||||
import { listBizPerson } from '@/api/business/person'
|
||||
import { listSponsorPerson } from '@/api/business/person'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
// SUB 子账号: 只能看被分配的项目, 不能修改分配
|
||||
const isSub = computed(() => userStore.isSub)
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref([])
|
||||
@@ -228,7 +230,8 @@ function reset() {
|
||||
function onSelectionChange(val) { selected.value = val }
|
||||
|
||||
function onView(row) {
|
||||
ElMessageBox.alert(`项目编号: ${row.projectNo}\n项目名称: ${row.projectName}`, '项目详情', { confirmButtonText: '确定' })
|
||||
// 跳项目详情页 (复用 manager/ManagerProjectDetail.vue, goBack 自动按角色回退)
|
||||
router.push(`/sponsor/projects/detail/${row.projectId}`)
|
||||
}
|
||||
|
||||
// ========== 执行单位评分 (与 manager/Projects.vue 完全相同的样式和逻辑) ==========
|
||||
@@ -299,26 +302,25 @@ const assignBatchRows = ref([]) // 批量模式下被分配的项目列
|
||||
const assignSubmitting = ref(false)
|
||||
const assignForm = reactive({
|
||||
projectId: null, projectNo: '', projectName: '',
|
||||
monitorInput: '', monitorUserId: null, monitorUserName: '',
|
||||
assignDesc: '', assignPoints: ''
|
||||
monitorUserIds: []
|
||||
})
|
||||
const allMonitors = ref([])
|
||||
const candidateMonitors = ref([])
|
||||
async function loadMonitors() {
|
||||
// 业务隔离: 仅查 login 主账号创建的人员 (parent_user_id = mainUserId)
|
||||
const mainUid = userStore.id
|
||||
const res = await listBizPerson({ parentUserId: mainUid, role: 'supervisor', pageNum: 1, pageSize: 100 })
|
||||
// 跟 sponsor/people (人员管理) 完全一致: 走 sponsorList, SQL 硬编码 unit_type='sponsor'
|
||||
// + (u.parent_user_id = mainUid OR u.user_id = mainUid), 自动取当前主账号
|
||||
// 仅筛 role='supervisor' (监察员候选人)
|
||||
const res = await listSponsorPerson({ role: 'supervisor', pageNum: 1, pageSize: 100 })
|
||||
allMonitors.value = res.rows || []
|
||||
candidateMonitors.value = allMonitors.value.slice()
|
||||
}
|
||||
function filterMonitors() {
|
||||
const kw = assignForm.monitorInput.trim()
|
||||
candidateMonitors.value = kw ? allMonitors.value.filter(m => m.name.includes(kw)) : allMonitors.value.slice()
|
||||
}
|
||||
function pickMonitor(m) {
|
||||
assignForm.monitorUserId = m.userId
|
||||
assignForm.monitorUserName = m.name
|
||||
assignForm.monitorInput = m.name
|
||||
/** 拉项目当前已分配监察员 → 回显 monitorUserIds (单选模式) */
|
||||
async function loadCurrentAssigns(projectId) {
|
||||
try {
|
||||
const resp = await getSponsorAssigns(projectId)
|
||||
const list = (resp && resp.data) || []
|
||||
assignForm.monitorUserIds = (Array.isArray(list) ? list : []).map(x => x.monitorUserId).filter(Boolean)
|
||||
} catch (e) {
|
||||
assignForm.monitorUserIds = []
|
||||
}
|
||||
}
|
||||
function openAssign(row) {
|
||||
assignBatchMode.value = false
|
||||
@@ -326,40 +328,32 @@ function openAssign(row) {
|
||||
assignForm.projectId = row.projectId
|
||||
assignForm.projectNo = row.projectNo
|
||||
assignForm.projectName = row.projectName
|
||||
assignForm.monitorInput = ''
|
||||
assignForm.monitorUserId = null
|
||||
assignForm.monitorUserName = ''
|
||||
assignForm.assignDesc = ''
|
||||
assignForm.assignPoints = ''
|
||||
assignForm.monitorUserIds = []
|
||||
assignVisible.value = true
|
||||
loadMonitors()
|
||||
// 异步回显当前已分配 (等候选加载好, 让 el-select 找得到 option)
|
||||
loadCurrentAssigns(row.projectId)
|
||||
}
|
||||
function openAssignBatch(rows) {
|
||||
assignBatchMode.value = true
|
||||
assignBatchRows.value = rows.map(r => ({ projectId: r.projectId, projectNo: r.projectNo, projectName: r.projectName }))
|
||||
assignForm.monitorInput = ''
|
||||
assignForm.monitorUserId = null
|
||||
assignForm.monitorUserName = ''
|
||||
assignForm.assignDesc = ''
|
||||
assignForm.assignPoints = ''
|
||||
// 批量模式: 不同项目原监察员可能不同, 不回显 (提交时按当前多选覆盖全部)
|
||||
assignForm.monitorUserIds = []
|
||||
assignVisible.value = true
|
||||
loadMonitors()
|
||||
}
|
||||
async function saveAssign() {
|
||||
if (!assignForm.monitorUserId) return ElMessage.warning('请选择监察员')
|
||||
if (!assignForm.assignDesc) return ElMessage.warning('请填写分配说明')
|
||||
if (!assignForm.monitorUserIds.length) return ElMessage.warning('请至少选择一位监察员')
|
||||
assignSubmitting.value = true
|
||||
try {
|
||||
if (assignBatchMode.value) {
|
||||
// 批量模式: 循环逐条 POST (后端先删后插, 每个项目都会被覆盖)
|
||||
// 批量模式: 每个项目都按当前多选覆盖 (后端先删后插 N 条)
|
||||
let ok = 0, errs = []
|
||||
for (const r of assignBatchRows.value) {
|
||||
try {
|
||||
await sponsorAssignProject({
|
||||
projectId: r.projectId,
|
||||
monitorUserId: assignForm.monitorUserId,
|
||||
assignDesc: assignForm.assignDesc,
|
||||
assignPoints: assignForm.assignPoints
|
||||
monitorUserIds: [...assignForm.monitorUserIds]
|
||||
})
|
||||
ok++
|
||||
} catch (e) { errs.push(`${r.projectNo}: ${e?.msg || e.message}`) }
|
||||
@@ -367,12 +361,10 @@ async function saveAssign() {
|
||||
if (errs.length) ElMessageBox.alert(`批量分配: 成功 ${ok}/${assignBatchRows.value.length}\n\n失败:\n${errs.join('\n')}`, '分配结果', { type: 'warning' })
|
||||
else ElMessage.success(`批量分配成功 (${ok}/${assignBatchRows.value.length})`)
|
||||
} else {
|
||||
// 单条模式
|
||||
// 单条模式: 多选一次提交
|
||||
await sponsorAssignProject({
|
||||
projectId: assignForm.projectId,
|
||||
monitorUserId: assignForm.monitorUserId,
|
||||
assignDesc: assignForm.assignDesc,
|
||||
assignPoints: assignForm.assignPoints
|
||||
monitorUserIds: [...assignForm.monitorUserIds]
|
||||
})
|
||||
ElMessage.success('分配成功')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user