|
@@ -1,17 +1,38 @@
|
|
|
<template>
|
|
<template>
|
|
|
<div class="app-container">
|
|
<div class="app-container">
|
|
|
|
|
+ <!-- 审核时间窗倒计时 -->
|
|
|
|
|
+ <el-alert :title="reviewWindowMessage" :type="reviewWindowType" :closable="false" show-icon class="mb16" />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 审核概览区:各部门填报状态汇总卡片 -->
|
|
|
|
|
+ <el-row :gutter="16" class="mb16">
|
|
|
|
|
+ <el-col :span="4" v-for="card in overviewCards" :key="card.deptId">
|
|
|
|
|
+ <el-card shadow="hover" class="overview-card" :class="`overview-card--${card.status}`">
|
|
|
|
|
+ <div class="overview-card__dept">{{ card.deptName }}</div>
|
|
|
|
|
+ <div class="overview-card__count">{{ card.submittedCount }} / {{ card.totalCount }}</div>
|
|
|
|
|
+ <div class="overview-card__label">已提交/总项目</div>
|
|
|
|
|
+ <el-progress
|
|
|
|
|
+ :percentage="card.totalCount > 0 ? Math.round((card.submittedCount / card.totalCount) * 100) : 0"
|
|
|
|
|
+ :color="card.statusColor"
|
|
|
|
|
+ :stroke-width="6"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div class="overview-card__status">
|
|
|
|
|
+ <el-tag :type="card.statusTagType" size="small">{{ card.statusLabel }}</el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 搜索区域 -->
|
|
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
|
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
|
|
|
<el-form-item label="部门" prop="deptId">
|
|
<el-form-item label="部门" prop="deptId">
|
|
|
- <el-tree-select v-model="queryParams.deptId" :data="deptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择部门" clearable check-strictly style="width: 200px" />
|
|
|
|
|
|
|
+ <DeptSelector v-model="queryParams.deptId" :useTree="false" placeholder="请选择部门" width="200px" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="项目名称" prop="projectName">
|
|
<el-form-item label="项目名称" prop="projectName">
|
|
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="审核结果" prop="result">
|
|
<el-form-item label="审核结果" prop="result">
|
|
|
<el-select v-model="queryParams.result" placeholder="审核结果" clearable style="width: 200px">
|
|
<el-select v-model="queryParams.result" placeholder="审核结果" clearable style="width: 200px">
|
|
|
- <el-option label="待审核" value="PENDING" />
|
|
|
|
|
- <el-option label="审核通过" value="APPROVED" />
|
|
|
|
|
- <el-option label="审核不通过" value="REJECTED" />
|
|
|
|
|
|
|
+ <el-option v-for="dict in resultOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
</el-select>
|
|
</el-select>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="填报月份" prop="month">
|
|
<el-form-item label="填报月份" prop="month">
|
|
@@ -23,9 +44,17 @@
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
+ <!-- 工具栏 -->
|
|
|
<el-row :gutter="10" class="mb8">
|
|
<el-row :gutter="10" class="mb8">
|
|
|
<el-col :span="1.5">
|
|
<el-col :span="1.5">
|
|
|
- <el-button type="success" plain icon="CircleCheck" :disabled="multiple" @click="handleBatchApprove" v-hasPermi="['performance:review:approve']">批量审核通过</el-button>
|
|
|
|
|
|
|
+ <el-button type="success" plain icon="CircleCheck" :disabled="selectCount === 0" @click="handleBatchApprove" v-hasPermi="['performance:review:approve']">
|
|
|
|
|
+ 批量通过 ({{ selectCount }})
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button type="danger" plain icon="CircleClose" :disabled="selectCount === 0" @click="handleBatchReject" v-hasPermi="['performance:review:approve']">
|
|
|
|
|
+ 批量不通过 ({{ selectCount }})
|
|
|
|
|
+ </el-button>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="1.5">
|
|
<el-col :span="1.5">
|
|
|
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['performance:review:export']">导出</el-button>
|
|
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['performance:review:export']">导出</el-button>
|
|
@@ -33,40 +62,129 @@
|
|
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
|
|
|
|
|
- <el-table v-loading="loading" :data="reviewList" @selection-change="handleSelectionChange">
|
|
|
|
|
- <el-table-column type="selection" width="50" align="center" />
|
|
|
|
|
|
|
+ <!-- 按部门分组的审核列表 -->
|
|
|
|
|
+ <div v-loading="loading">
|
|
|
|
|
+ <el-collapse v-model="activeDepts" v-if="groupedList.length > 0">
|
|
|
|
|
+ <el-collapse-item
|
|
|
|
|
+ v-for="group in groupedList"
|
|
|
|
|
+ :key="group.deptId"
|
|
|
|
|
+ :name="group.deptId"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #title>
|
|
|
|
|
+ <div class="collapse-title">
|
|
|
|
|
+ <span class="collapse-title__dept">{{ group.deptName }}</span>
|
|
|
|
|
+ <span class="collapse-title__stats">
|
|
|
|
|
+ 已提交 {{ group.submittedCount }} / 共 {{ group.totalCount }} 个项目
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="collapse-title__output">
|
|
|
|
|
+ 产值合计: {{ group.totalOutputSum }} 万元
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table :data="group.items" @selection-change="(sel) => handleGroupSelection(group.deptId, sel)" ref="groupTableRefs">
|
|
|
|
|
+ <el-table-column type="selection" width="50" align="center" />
|
|
|
|
|
+ <el-table-column label="项目名称" align="center" prop="projectName" :show-overflow-tooltip="true" />
|
|
|
|
|
+ <el-table-column label="填报月份" align="center" prop="month" width="120" />
|
|
|
|
|
+ <el-table-column label="当月进度(%)" align="center" prop="currentProgress" width="110" />
|
|
|
|
|
+ <el-table-column label="当月产值(万元)" align="center" prop="currentOutput" width="130" />
|
|
|
|
|
+ <el-table-column label="协作产值(万元)" align="center" prop="coopOutput" width="130" />
|
|
|
|
|
+ <el-table-column label="审核结果" align="center" width="120">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <ReviewStatusTag :status="scope.row.result" :options="resultTagOptions" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="操作" align="center" width="220" class-name="small-padding fixed-width">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button link type="success" icon="CircleCheck" @click="handleApprove(scope.row)" v-if="scope.row.result === 'PENDING'" v-hasPermi="['performance:review:approve']">
|
|
|
|
|
+ 通过
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button link type="danger" icon="CircleClose" @click="handleReject(scope.row)" v-if="scope.row.result === 'PENDING'" v-hasPermi="['performance:review:approve']">
|
|
|
|
|
+ 不通过
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button link type="primary" icon="View" @click="toggleRowExpand(scope.row, scope.$index)">
|
|
|
|
|
+ {{ scope.row.expanded ? '收起' : '详情' }}
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <!-- 行展开:详情 -->
|
|
|
|
|
+ <el-table-column type="expand" width="1">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <div class="expand-detail">
|
|
|
|
|
+ <el-descriptions :column="3" border size="small">
|
|
|
|
|
+ <el-descriptions-item label="项目总产值得(万元)">{{ scope.row.projectTotalOutput || '--' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="已填报产值(万元)">{{ scope.row.filledOutput || '--' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="填报部门">{{ scope.row.deptName }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="协作部门">{{ scope.row.coopDeptName || '无' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="进度说明">{{ scope.row.description || '无' }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="协作信息">
|
|
|
|
|
+ <span v-if="scope.row.coopOutputValue">协作产值 {{ scope.row.coopOutputValue }} 万元,{{ scope.row.coopStatus || '待确认' }}</span>
|
|
|
|
|
+ <span v-else>无</span>
|
|
|
|
|
+ </el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="填报时间">{{ parseTime(scope.row.createTime) }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="最近修改时间">{{ parseTime(scope.row.updateTime) }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="审核意见" v-if="scope.row.reviewComment">{{ scope.row.reviewComment }}</el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+ <div class="expand-detail__history" v-if="scope.row.history && scope.row.history.length > 0">
|
|
|
|
|
+ <div class="expand-detail__history-title">历史填报记录</div>
|
|
|
|
|
+ <el-table :data="scope.row.history" border size="small">
|
|
|
|
|
+ <el-table-column label="月份" prop="month" width="100" />
|
|
|
|
|
+ <el-table-column label="进度(%)" prop="currentProgress" width="80" />
|
|
|
|
|
+ <el-table-column label="产值(万元)" prop="currentOutput" width="120" />
|
|
|
|
|
+ <el-table-column label="审核结果" prop="result" width="100">
|
|
|
|
|
+ <template #default="s">
|
|
|
|
|
+ <ReviewStatusTag :status="s.row.result" :options="resultTagOptions" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </el-collapse-item>
|
|
|
|
|
+ </el-collapse>
|
|
|
|
|
+ <el-empty v-else description="暂无审核数据" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 审核日志 -->
|
|
|
|
|
+ <el-divider content-position="left">审核日志</el-divider>
|
|
|
|
|
+ <el-table :data="reviewLogs" border v-loading="logLoading">
|
|
|
|
|
+ <el-table-column label="审核人" align="center" prop="reviewerName" width="120" />
|
|
|
<el-table-column label="项目名称" align="center" prop="projectName" :show-overflow-tooltip="true" />
|
|
<el-table-column label="项目名称" align="center" prop="projectName" :show-overflow-tooltip="true" />
|
|
|
- <el-table-column label="填报部门" align="center" prop="deptName" />
|
|
|
|
|
- <el-table-column label="填报月份" align="center" prop="month" />
|
|
|
|
|
- <el-table-column label="当月进度(%)" align="center" prop="currentProgress" />
|
|
|
|
|
- <el-table-column label="当月产值(万元)" align="center" prop="currentOutput" />
|
|
|
|
|
- <el-table-column label="协作产值(万元)" align="center" prop="coopOutput" />
|
|
|
|
|
- <el-table-column label="审核结果" align="center" prop="result">
|
|
|
|
|
|
|
+ <el-table-column label="部门" align="center" prop="deptName" width="120" />
|
|
|
|
|
+ <el-table-column label="填报月份" align="center" prop="month" width="100" />
|
|
|
|
|
+ <el-table-column label="审核结果" align="center" prop="result" width="110">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
- <dict-tag :options="resultOptions" :value="scope.row.result" />
|
|
|
|
|
|
|
+ <ReviewStatusTag :status="scope.row.result" :options="resultTagOptions" />
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <el-button link type="success" icon="CircleCheck" @click="handleApprove(scope.row)" v-if="scope.row.result === 'PENDING'" v-hasPermi="['performance:review:approve']">通过</el-button>
|
|
|
|
|
- <el-button link type="danger" icon="CircleClose" @click="handleReject(scope.row)" v-if="scope.row.result === 'PENDING'" v-hasPermi="['performance:review:approve']">不通过</el-button>
|
|
|
|
|
- <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
|
|
+ <el-table-column label="审核意见" align="center" prop="comment" :show-overflow-tooltip="true" />
|
|
|
|
|
+ <el-table-column label="审核时间" align="center" prop="createTime" width="160">
|
|
|
|
|
+ <template #default="scope">{{ parseTime(scope.row.createTime) }}</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
</el-table>
|
|
</el-table>
|
|
|
-
|
|
|
|
|
- <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
|
|
|
|
+ <pagination v-show="logTotal > 0" :total="logTotal" v-model:page="logQuery.pageNum" v-model:limit="logQuery.pageSize" @pagination="getReviewLogs" />
|
|
|
|
|
|
|
|
<!-- 审核不通过对话框 -->
|
|
<!-- 审核不通过对话框 -->
|
|
|
- <el-dialog title="审核不通过" v-model="rejectOpen" width="500px" append-to-body>
|
|
|
|
|
|
|
+ <el-dialog title="审核不通过" v-model="rejectOpen" width="550px" append-to-body @close="rejectOpen = false">
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ <span style="color: #F56C6C; font-weight: bold">审核不通过</span>
|
|
|
|
|
+ <div style="font-size: 13px; color: #909399; margin-top: 4px">审核不通过将自动通知填报部门管理员</div>
|
|
|
|
|
+ </template>
|
|
|
<el-form :model="rejectForm" :rules="rejectRules" ref="rejectRef" label-width="100px">
|
|
<el-form :model="rejectForm" :rules="rejectRules" ref="rejectRef" label-width="100px">
|
|
|
<el-form-item label="不通过原因" prop="reason">
|
|
<el-form-item label="不通过原因" prop="reason">
|
|
|
- <el-input v-model="rejectForm.reason" type="textarea" placeholder="请填写审核不通过的原因" maxlength="500" />
|
|
|
|
|
|
|
+ <el-input v-model="rejectForm.reason" type="textarea" :rows="4" placeholder="请详细填写审核不通过的原因,此意见将反馈给填报部门" maxlength="500" show-word-limit />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <el-alert title="反馈通知" type="warning" :closable="false" show-icon style="margin-top: 8px">
|
|
|
|
|
+ <span>审核不通过后,系统将自动向填报部门管理员发送通知,提醒其在14日前修改后重新提交。</span>
|
|
|
|
|
+ </el-alert>
|
|
|
</el-form>
|
|
</el-form>
|
|
|
<template #footer>
|
|
<template #footer>
|
|
|
<div class="dialog-footer">
|
|
<div class="dialog-footer">
|
|
|
- <el-button type="danger" @click="submitReject">确 定</el-button>
|
|
|
|
|
|
|
+ <el-button type="danger" @click="submitReject">确认不通过并通知</el-button>
|
|
|
<el-button @click="rejectOpen = false">取 消</el-button>
|
|
<el-button @click="rejectOpen = false">取 消</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
@@ -75,53 +193,152 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup name="PerformanceReview">
|
|
<script setup name="PerformanceReview">
|
|
|
-import { listReview, approveReview, rejectReview } from '@/api/performance/review'
|
|
|
|
|
|
|
+import { listReview, approveReview, rejectReview, batchReview, listReviewLog } from '@/api/performance/review'
|
|
|
|
|
+import { ReviewStatusTag, DeptSelector } from '@/components/performance'
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance()
|
|
const { proxy } = getCurrentInstance()
|
|
|
|
|
|
|
|
-const resultOptions = ref([
|
|
|
|
|
|
|
+// 审核时间窗计算 (每月13-16日)
|
|
|
|
|
+const now = new Date()
|
|
|
|
|
+const currentDay = now.getDate()
|
|
|
|
|
+const currentMonth = now.getMonth() + 1
|
|
|
|
|
+const inReviewWindow = currentDay >= 13 && currentDay <= 16
|
|
|
|
|
+const beforeReviewWindow = currentDay < 13
|
|
|
|
|
+const isReviewOverdue = currentDay > 16
|
|
|
|
|
+
|
|
|
|
|
+const reviewWindowMessage = computed(() => {
|
|
|
|
|
+ if (inReviewWindow) {
|
|
|
|
|
+ const remainDays = 16 - currentDay
|
|
|
|
|
+ return `审核进行中(${currentMonth}月13-16日),距离审核截止还有 ${remainDays} 天。请在截止前完成审核评定!`
|
|
|
|
|
+ }
|
|
|
|
|
+ if (beforeReviewWindow) {
|
|
|
|
|
+ return `审核窗口将于${currentMonth}月13日开启(每月13-16日),当前为填报阶段。`
|
|
|
|
|
+ }
|
|
|
|
|
+ return `已超过审核截止日期(${currentMonth}月16日),请尽快完成遗留审核!逾期未审核系统将自动提醒。`
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const reviewWindowType = computed(() => {
|
|
|
|
|
+ if (inReviewWindow) return currentDay >= 15 ? 'warning' : 'success'
|
|
|
|
|
+ if (isReviewOverdue) return 'error'
|
|
|
|
|
+ return 'info'
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 选项
|
|
|
|
|
+const resultOptions = [
|
|
|
{ label: '待审核', value: 'PENDING' },
|
|
{ label: '待审核', value: 'PENDING' },
|
|
|
{ label: '审核通过', value: 'APPROVED' },
|
|
{ label: '审核通过', value: 'APPROVED' },
|
|
|
{ label: '审核不通过', value: 'REJECTED' }
|
|
{ label: '审核不通过', value: 'REJECTED' }
|
|
|
-])
|
|
|
|
|
|
|
+]
|
|
|
|
|
|
|
|
-const reviewList = ref([])
|
|
|
|
|
-const loading = ref(true)
|
|
|
|
|
|
|
+const resultTagOptions = [
|
|
|
|
|
+ { label: '待审核', value: 'PENDING', tagType: 'warning' },
|
|
|
|
|
+ { label: '审核通过', value: 'APPROVED', tagType: 'success' },
|
|
|
|
|
+ { label: '审核不通过', value: 'REJECTED', tagType: 'danger' }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+// 状态变量
|
|
|
|
|
+const loading = ref(false)
|
|
|
const showSearch = ref(true)
|
|
const showSearch = ref(true)
|
|
|
-const ids = ref([])
|
|
|
|
|
-const single = ref(true)
|
|
|
|
|
-const multiple = ref(true)
|
|
|
|
|
const total = ref(0)
|
|
const total = ref(0)
|
|
|
|
|
+const selectCount = ref(0)
|
|
|
const rejectOpen = ref(false)
|
|
const rejectOpen = ref(false)
|
|
|
-const currentRow = ref(null)
|
|
|
|
|
-const deptOptions = ref([])
|
|
|
|
|
|
|
+const activeDepts = ref([])
|
|
|
|
|
+const logLoading = ref(false)
|
|
|
|
|
+const logTotal = ref(0)
|
|
|
|
|
+
|
|
|
|
|
+const reviewList = ref([])
|
|
|
|
|
+const reviewLogs = ref([])
|
|
|
|
|
+const groupSelectionMap = reactive({})
|
|
|
|
|
+const groupedList = computed(() => {
|
|
|
|
|
+ const map = {}
|
|
|
|
|
+ reviewList.value.forEach(item => {
|
|
|
|
|
+ if (!map[item.deptId]) {
|
|
|
|
|
+ map[item.deptId] = {
|
|
|
|
|
+ deptId: item.deptId,
|
|
|
|
|
+ deptName: item.deptName,
|
|
|
|
|
+ items: [],
|
|
|
|
|
+ submittedCount: 0,
|
|
|
|
|
+ totalCount: 0,
|
|
|
|
|
+ totalOutputSum: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ map[item.deptId].items.push({ ...item, expanded: false })
|
|
|
|
|
+ map[item.deptId].totalCount++
|
|
|
|
|
+ if (item.result !== 'DRAFT') {
|
|
|
|
|
+ map[item.deptId].submittedCount++
|
|
|
|
|
+ }
|
|
|
|
|
+ map[item.deptId].totalOutputSum = ((map[item.deptId].totalOutputSum || 0) + (item.currentOutput || 0)).toFixed(4)
|
|
|
|
|
+ })
|
|
|
|
|
+ return Object.values(map)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const overviewCards = computed(() => {
|
|
|
|
|
+ return groupedList.value.map(g => {
|
|
|
|
|
+ const allSubmitted = g.totalCount > 0 && g.submittedCount === g.totalCount
|
|
|
|
|
+ const someSubmitted = g.submittedCount > 0 && g.submittedCount < g.totalCount
|
|
|
|
|
+ let status, statusLabel, statusTagType, statusColor
|
|
|
|
|
+ if (allSubmitted) {
|
|
|
|
|
+ status = 'complete'
|
|
|
|
|
+ statusLabel = '全部已提交'
|
|
|
|
|
+ statusTagType = 'success'
|
|
|
|
|
+ statusColor = '#67C23A'
|
|
|
|
|
+ } else if (someSubmitted) {
|
|
|
|
|
+ status = 'partial'
|
|
|
|
|
+ statusLabel = '部分已提交'
|
|
|
|
|
+ statusTagType = 'warning'
|
|
|
|
|
+ statusColor = '#E6A23C'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ status = 'none'
|
|
|
|
|
+ statusLabel = '尚未提交'
|
|
|
|
|
+ statusTagType = 'danger'
|
|
|
|
|
+ statusColor = '#F56C6C'
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ ...g,
|
|
|
|
|
+ status,
|
|
|
|
|
+ statusLabel,
|
|
|
|
|
+ statusTagType,
|
|
|
|
|
+ statusColor
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
const data = reactive({
|
|
const data = reactive({
|
|
|
queryParams: {
|
|
queryParams: {
|
|
|
pageNum: 1,
|
|
pageNum: 1,
|
|
|
- pageSize: 10,
|
|
|
|
|
|
|
+ pageSize: 20,
|
|
|
deptId: undefined,
|
|
deptId: undefined,
|
|
|
projectName: undefined,
|
|
projectName: undefined,
|
|
|
result: undefined,
|
|
result: undefined,
|
|
|
month: undefined
|
|
month: undefined
|
|
|
},
|
|
},
|
|
|
- rejectForm: {
|
|
|
|
|
- reviewId: undefined,
|
|
|
|
|
- reason: undefined
|
|
|
|
|
- },
|
|
|
|
|
- rejectRules: {
|
|
|
|
|
- reason: [{ required: true, message: '不通过原因不能为空', trigger: 'blur' }]
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ rejectForm: { reviewId: undefined, reason: undefined },
|
|
|
|
|
+ rejectRules: { reason: [{ required: true, message: '不通过原因不能为空', trigger: 'blur' }] },
|
|
|
|
|
+ logQuery: { pageNum: 1, pageSize: 10 }
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const { queryParams, rejectForm, rejectRules } = toRefs(data)
|
|
|
|
|
|
|
+const { queryParams, rejectForm, rejectRules, logQuery } = toRefs(data)
|
|
|
|
|
|
|
|
function getList() {
|
|
function getList() {
|
|
|
loading.value = true
|
|
loading.value = true
|
|
|
|
|
+ selectCount.value = 0
|
|
|
listReview(queryParams.value).then(res => {
|
|
listReview(queryParams.value).then(res => {
|
|
|
- reviewList.value = res.rows
|
|
|
|
|
- total.value = res.total
|
|
|
|
|
|
|
+ reviewList.value = res.rows || []
|
|
|
|
|
+ total.value = res.total || 0
|
|
|
loading.value = false
|
|
loading.value = false
|
|
|
|
|
+ // 默认展开所有有数据的部门
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ activeDepts.value = groupedList.value.filter(g => g.totalCount > 0).map(g => g.deptId)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getReviewLogs() {
|
|
|
|
|
+ logLoading.value = true
|
|
|
|
|
+ listReviewLog(logQuery.value).then(res => {
|
|
|
|
|
+ reviewLogs.value = res.rows || []
|
|
|
|
|
+ logTotal.value = res.total || 0
|
|
|
|
|
+ logLoading.value = false
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -135,25 +352,29 @@ function resetQuery() {
|
|
|
handleQuery()
|
|
handleQuery()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function handleSelectionChange(selection) {
|
|
|
|
|
- ids.value = selection.map(item => item.reviewId)
|
|
|
|
|
- single.value = selection.length !== 1
|
|
|
|
|
- multiple.value = !selection.length
|
|
|
|
|
|
|
+// 分组选择
|
|
|
|
|
+function handleGroupSelection(deptId, selection) {
|
|
|
|
|
+ groupSelectionMap[deptId] = selection
|
|
|
|
|
+
|
|
|
|
|
+ let total = 0
|
|
|
|
|
+ Object.values(groupSelectionMap).forEach(arr => { total += arr.length })
|
|
|
|
|
+ selectCount.value = total
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 单条审核
|
|
|
function handleApprove(row) {
|
|
function handleApprove(row) {
|
|
|
- proxy.$modal.confirm('确认审核通过该条产值填报?').then(() => {
|
|
|
|
|
|
|
+ proxy.$modal.confirm(`确认审核通过「${row.projectName}」的产值填报?`).then(() => {
|
|
|
return approveReview({ reviewId: row.reviewId })
|
|
return approveReview({ reviewId: row.reviewId })
|
|
|
}).then(() => {
|
|
}).then(() => {
|
|
|
proxy.$modal.msgSuccess('审核通过')
|
|
proxy.$modal.msgSuccess('审核通过')
|
|
|
getList()
|
|
getList()
|
|
|
|
|
+ getReviewLogs()
|
|
|
}).catch(() => {})
|
|
}).catch(() => {})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function handleReject(row) {
|
|
function handleReject(row) {
|
|
|
rejectForm.value.reviewId = row.reviewId
|
|
rejectForm.value.reviewId = row.reviewId
|
|
|
rejectForm.value.reason = undefined
|
|
rejectForm.value.reason = undefined
|
|
|
- currentRow.value = row
|
|
|
|
|
rejectOpen.value = true
|
|
rejectOpen.value = true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -161,29 +382,53 @@ function submitReject() {
|
|
|
proxy.$refs.rejectRef.validate(valid => {
|
|
proxy.$refs.rejectRef.validate(valid => {
|
|
|
if (valid) {
|
|
if (valid) {
|
|
|
rejectReview(rejectForm.value).then(() => {
|
|
rejectReview(rejectForm.value).then(() => {
|
|
|
- proxy.$modal.msgSuccess('已提交审核不通过意见')
|
|
|
|
|
|
|
+ proxy.$modal.msgSuccess('审核不通过,已通知填报部门管理员')
|
|
|
rejectOpen.value = false
|
|
rejectOpen.value = false
|
|
|
getList()
|
|
getList()
|
|
|
|
|
+ getReviewLogs()
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 批量审核
|
|
|
function handleBatchApprove() {
|
|
function handleBatchApprove() {
|
|
|
- if (ids.value.length === 0) {
|
|
|
|
|
|
|
+ const allSelectedIds = getAllSelectedIds()
|
|
|
|
|
+ if (allSelectedIds.length === 0) {
|
|
|
proxy.$modal.msgWarning('请选择要审核的记录')
|
|
proxy.$modal.msgWarning('请选择要审核的记录')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- proxy.$modal.confirm('确认批量审核通过选中的记录?').then(() => {
|
|
|
|
|
- return approveReview({ reviewIds: ids.value })
|
|
|
|
|
|
|
+ proxy.$modal.confirm(`确认批量审核通过 ${allSelectedIds.length} 条记录?`).then(() => {
|
|
|
|
|
+ return batchReview({ reviewIds: allSelectedIds, result: 'APPROVED' })
|
|
|
}).then(() => {
|
|
}).then(() => {
|
|
|
proxy.$modal.msgSuccess('批量审核通过')
|
|
proxy.$modal.msgSuccess('批量审核通过')
|
|
|
getList()
|
|
getList()
|
|
|
|
|
+ getReviewLogs()
|
|
|
}).catch(() => {})
|
|
}).catch(() => {})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function handleDetail(row) {
|
|
|
|
|
- // TODO: 查看审核详情
|
|
|
|
|
|
|
+function handleBatchReject() {
|
|
|
|
|
+ const allSelectedIds = getAllSelectedIds()
|
|
|
|
|
+ if (allSelectedIds.length === 0) {
|
|
|
|
|
+ proxy.$modal.msgWarning('请选择要审核的记录')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ rejectForm.value.reviewId = allSelectedIds.join(',')
|
|
|
|
|
+ rejectForm.value.reason = undefined
|
|
|
|
|
+ rejectOpen.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getAllSelectedIds() {
|
|
|
|
|
+ const ids = []
|
|
|
|
|
+ Object.values(groupSelectionMap).forEach(arr => {
|
|
|
|
|
+ arr.forEach(item => ids.push(item.reviewId))
|
|
|
|
|
+ })
|
|
|
|
|
+ return ids
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 行展开/收起
|
|
|
|
|
+function toggleRowExpand(row, index) {
|
|
|
|
|
+ row.expanded = !row.expanded
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function handleExport() {
|
|
function handleExport() {
|
|
@@ -192,5 +437,87 @@ function handleExport() {
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
getList()
|
|
getList()
|
|
|
|
|
+ getReviewLogs()
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.mb16 {
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.overview-card {
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ border-top: 3px solid #DCDFE6;
|
|
|
|
|
+
|
|
|
|
|
+ &--complete { border-top-color: #67C23A; }
|
|
|
|
|
+ &--partial { border-top-color: #E6A23C; }
|
|
|
|
|
+ &--none { border-top-color: #F56C6C; }
|
|
|
|
|
+
|
|
|
|
|
+ &__dept {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__count {
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin: 8px 0 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__label {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__status {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.collapse-title {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 24px;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+
|
|
|
|
|
+ &__dept {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ min-width: 150px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__stats {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__output {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #409EFF;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.expand-detail {
|
|
|
|
|
+ padding: 16px 20px;
|
|
|
|
|
+ background: #F5F7FA;
|
|
|
|
|
+
|
|
|
|
|
+ &__history {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &__history-title {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|