523 lines
12 KiB
Vue
523 lines
12 KiB
Vue
<template>
|
|
<view class="verify-detail-page">
|
|
<!-- 自定义导航栏 -->
|
|
|
|
|
|
<!-- 内容区域 -->
|
|
<scroll-view class="content-scroll" scroll-y v-if="!loading">
|
|
<view class="content-wrapper">
|
|
<!-- 详情表格 -->
|
|
<view class="info-card">
|
|
<view class="info-row">
|
|
<text class="info-label">所属项目</text>
|
|
<text class="info-value">{{ detailData.projectName || '--' }}</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">任务类型</text>
|
|
<view class="info-value">
|
|
<uv-tags
|
|
:text="getTaskTypeLabel(detailData.task?.type)"
|
|
type="success"
|
|
size="mini"
|
|
:plain="false"
|
|
></uv-tags>
|
|
</view>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">优先级</text>
|
|
<view class="info-value">
|
|
<uv-tags
|
|
:text="getPriorityLabel(detailData.task?.level)"
|
|
:type="getPriorityType(detailData.task?.level)"
|
|
size="mini"
|
|
:plain="false"
|
|
></uv-tags>
|
|
</view>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">任务状态</text>
|
|
<view class="info-value">
|
|
<uv-tags
|
|
:text="getTaskStatusLabel(detailData.task?.status)"
|
|
:type="getTaskStatusType(detailData.task?.status)"
|
|
size="mini"
|
|
:plain="false"
|
|
></uv-tags>
|
|
</view>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">审核状态</text>
|
|
<view class="info-value">
|
|
<uv-tags
|
|
:text="getVerifyStatusLabel(detailData.status)"
|
|
:type="getVerifyStatusType(detailData.status)"
|
|
size="mini"
|
|
:plain="false"
|
|
></uv-tags>
|
|
</view>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">创建人</text>
|
|
<text class="info-value">{{ detailData?.createName || '--' }}</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">创建时间</text>
|
|
<text class="info-value">{{ detailData.createTime || '--' }}</text>
|
|
</view>
|
|
<view class="info-row" v-if="detailData.task?.expireTime">
|
|
<text class="info-label">截止时间</text>
|
|
<text class="info-value">{{ detailData.task.expireTime }}</text>
|
|
</view>
|
|
<view class="info-row" v-if="detailData.task?.passTime">
|
|
<text class="info-label">结束时间</text>
|
|
<text class="info-value">{{ detailData.task.passTime }}</text>
|
|
</view>
|
|
<view class="info-row" v-if="detailData.userName">
|
|
<text class="info-label">审核人</text>
|
|
<text class="info-value">{{ detailData.userName }}</text>
|
|
</view>
|
|
<view class="info-row" v-if="detailData.verifyTime">
|
|
<text class="info-label">审核时间</text>
|
|
<text class="info-value">{{ detailData.verifyTime }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 任务描述 -->
|
|
<view class="info-card" v-if="detailData.task?.description">
|
|
<view class="card-title">任务描述</view>
|
|
<view class="content-text">{{ detailData.task.description }}</view>
|
|
</view>
|
|
|
|
<!-- 备注 -->
|
|
<view class="info-card" v-if="detailData.createRemark">
|
|
<view class="card-title">备注</view>
|
|
<view class="content-text">{{ detailData.createRemark }}</view>
|
|
</view>
|
|
|
|
<!-- 改动值 -->
|
|
<view class="info-card" v-if="changeData">
|
|
<view class="card-title">改动值</view>
|
|
<view class="info-row">
|
|
<text class="info-label">字段</text>
|
|
<text class="info-value">{{ changeData.field || '--' }}</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="info-label">值</text>
|
|
<text class="info-value">{{ changeData.value || '--' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 评论输入框 -->
|
|
<view class="info-card">
|
|
<view class="card-title">审核意见</view>
|
|
<textarea
|
|
v-model="commentText"
|
|
class="comment-textarea"
|
|
placeholder="请输入审核意见"
|
|
:maxlength="500"
|
|
:auto-height="true"
|
|
></textarea>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 加载中 -->
|
|
<view class="loading-wrapper" v-if="loading">
|
|
<text>加载中...</text>
|
|
</view>
|
|
|
|
<!-- 底部操作按钮 -->
|
|
<view class="action-buttons" v-if="!loading && detailData.status === '3' && showPrivateSwitch">
|
|
<view class="btn-wrapper">
|
|
<uv-button type="primary" size="normal" @click="handleApprove">通过</uv-button>
|
|
</view>
|
|
<view class="btn-wrapper">
|
|
<uv-button type="error" size="normal" @click="handleReject">驳回</uv-button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
import { getVerifyDetail, handleVerify } from '@/api/verify'
|
|
import { getDictLabel } from '@/utils/dict'
|
|
import {useUserStore} from "@/store/user";
|
|
import {storeToRefs} from "pinia";
|
|
|
|
const userStore = useUserStore()
|
|
const { userInfo } = storeToRefs(userStore)
|
|
|
|
const showPrivateSwitch = computed(() =>
|
|
userInfo.value?.roles?.some(r => ['admin', 'sys_admin'].includes(r))
|
|
)
|
|
|
|
// 页面参数
|
|
const verifyId = ref('')
|
|
const detailData = ref({})
|
|
const loading = ref(false)
|
|
const commentText = ref('')
|
|
|
|
// 解析改动数据
|
|
const changeData = computed(() => {
|
|
if (!detailData.value.data) return null
|
|
|
|
try {
|
|
const data = typeof detailData.value.data === 'string'
|
|
? JSON.parse(detailData.value.data)
|
|
: detailData.value.data
|
|
|
|
if (data.expireTime) {
|
|
return {
|
|
field: '截止时间',
|
|
value: data.expireTime
|
|
}
|
|
}
|
|
|
|
return null
|
|
} catch (e) {
|
|
console.error('解析改动数据失败:', e)
|
|
return null
|
|
}
|
|
})
|
|
|
|
// 获取任务类型标签
|
|
const getTaskTypeLabel = (type) => {
|
|
if (!type) return '--'
|
|
console.log('getDictLabel', type)
|
|
console.log('getDictLabel',getDictLabel('task_type', type))
|
|
return getDictLabel('task_type', type)
|
|
}
|
|
|
|
// 获取优先级标签
|
|
const getPriorityLabel = (level) => {
|
|
if (!level) return '--'
|
|
return getDictLabel('task_level', level)
|
|
}
|
|
|
|
// 获取优先级类型(用于标签颜色)
|
|
const getPriorityType = (level) => {
|
|
const typeMap = {
|
|
'1': 'error', // 高优先级 - 红色
|
|
'2': 'warning', // 中优先级 - 黄色
|
|
'3': 'success' // 低优先级 - 绿色
|
|
}
|
|
return typeMap[level] || 'primary'
|
|
}
|
|
|
|
// 获取任务状态标签
|
|
const getTaskStatusLabel = (status) => {
|
|
if (!status) return '--'
|
|
return getDictLabel('task_status', status)
|
|
}
|
|
|
|
// 获取任务状态类型(用于标签颜色)
|
|
const getTaskStatusType = (status) => {
|
|
const typeMap = {
|
|
'1': 'primary', // 待接收
|
|
'2': 'warning', // 进行中
|
|
'4': 'success', // 已完成
|
|
'6': 'error' // 已取消
|
|
}
|
|
return typeMap[status] || 'primary'
|
|
}
|
|
|
|
// 获取审核状态标签
|
|
const getVerifyStatusLabel = (status) => {
|
|
if (!status) return '--'
|
|
return getDictLabel('verify_status', status)
|
|
}
|
|
|
|
// 获取审核状态类型(用于标签颜色)
|
|
const getVerifyStatusType = (status) => {
|
|
const typeMap = {
|
|
'1': 'success', // 已通过
|
|
'2': 'error', // 已驳回error
|
|
'3': 'warning' // 待审核
|
|
}
|
|
return typeMap[status] || 'primary'
|
|
}
|
|
|
|
// 加载审核详情
|
|
const loadVerifyDetail = async (id) => {
|
|
if (!id) {
|
|
uni.showToast({
|
|
title: '审核ID不能为空',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
try {
|
|
loading.value = true
|
|
const res = await getVerifyDetail(id)
|
|
console.log('审核详情数据:', res)
|
|
detailData.value = res || {}
|
|
loading.value = false
|
|
} catch (err) {
|
|
console.error('加载审核详情失败:', err)
|
|
loading.value = false
|
|
uni.showToast({
|
|
title: '加载审核详情失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
|
|
// 处理审核通过
|
|
const handleApprove = async () => {
|
|
if (!verifyId.value) {
|
|
uni.showToast({
|
|
title: '审核ID不能为空',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
try {
|
|
uni.showLoading({
|
|
title: '处理中...'
|
|
})
|
|
|
|
await handleVerify(verifyId.value, '1', commentText.value)
|
|
|
|
uni.hideLoading()
|
|
uni.showToast({
|
|
title: '审核通过',
|
|
icon: 'success'
|
|
})
|
|
|
|
// 刷新详情数据
|
|
await loadVerifyDetail(verifyId.value)
|
|
|
|
// 发送刷新列表事件
|
|
uni.$emit('verifyListRefresh')
|
|
|
|
// 延迟返回上一页
|
|
setTimeout(() => {
|
|
uni.navigateBack()
|
|
}, 1500)
|
|
} catch (err) {
|
|
uni.hideLoading()
|
|
console.error('审核通过失败:', err)
|
|
uni.showToast({
|
|
title: err.message || '审核通过失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
|
|
// 处理审核驳回
|
|
const handleReject = async () => {
|
|
if (!verifyId.value) {
|
|
uni.showToast({
|
|
title: '审核ID不能为空',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
// 确认对话框
|
|
uni.showModal({
|
|
title: '确认驳回',
|
|
content: '确定要驳回此审核吗?',
|
|
success: async (res) => {
|
|
if (res.confirm) {
|
|
try {
|
|
uni.showLoading({
|
|
title: '处理中...'
|
|
})
|
|
|
|
await handleVerify(verifyId.value, '2', commentText.value)
|
|
|
|
uni.hideLoading()
|
|
uni.showToast({
|
|
title: '已驳回',
|
|
icon: 'success'
|
|
})
|
|
|
|
// 刷新详情数据
|
|
await loadVerifyDetail(verifyId.value)
|
|
|
|
// 发送刷新列表事件
|
|
uni.$emit('verifyListRefresh')
|
|
|
|
// 延迟返回上一页
|
|
setTimeout(() => {
|
|
uni.navigateBack()
|
|
}, 1500)
|
|
} catch (err) {
|
|
uni.hideLoading()
|
|
console.error('审核驳回失败:', err)
|
|
uni.showToast({
|
|
title: err.message || '审核驳回失败',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 返回上一页
|
|
const handleBack = () => {
|
|
uni.navigateBack()
|
|
}
|
|
|
|
// 页面加载
|
|
onLoad((options) => {
|
|
const id = options.id || options.verifyId
|
|
if (id) {
|
|
verifyId.value = id
|
|
loadVerifyDetail(id)
|
|
} else {
|
|
uni.showToast({
|
|
title: '缺少审核ID',
|
|
icon: 'none'
|
|
})
|
|
setTimeout(() => {
|
|
uni.navigateBack()
|
|
}, 1500)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.verify-detail-page {
|
|
width: 100%;
|
|
height: 100vh;
|
|
background: #f5f5f5;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.custom-navbar {
|
|
background: #fff;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
|
|
.navbar-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 12px 16px;
|
|
height: 44px;
|
|
}
|
|
|
|
.nav-btn {
|
|
font-size: 24px;
|
|
color: #333;
|
|
width: 32px;
|
|
text-align: center;
|
|
line-height: 32px;
|
|
}
|
|
|
|
.nav-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
|
|
.content-scroll {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.content-wrapper {
|
|
padding: 16px;
|
|
}
|
|
|
|
.info-card {
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
padding: 16px;
|
|
margin-bottom: 16px;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.info-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 12px 0;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
}
|
|
|
|
.info-label {
|
|
font-size: 14px;
|
|
color: #666;
|
|
width: 100px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.info-value {
|
|
font-size: 14px;
|
|
color: #333;
|
|
flex: 1;
|
|
text-align: right;
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
margin-bottom: 12px;
|
|
padding-bottom: 12px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.content-text {
|
|
font-size: 14px;
|
|
color: #333;
|
|
line-height: 1.6;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.comment-textarea {
|
|
width: 100%;
|
|
min-height: 100px;
|
|
padding: 12px;
|
|
font-size: 14px;
|
|
color: #333;
|
|
background: #f5f5f5;
|
|
border-radius: 8px;
|
|
border: 1px solid #e0e0e0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.loading-wrapper {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #999;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 12px;
|
|
padding: 16px;
|
|
background: #fff;
|
|
border-top: 1px solid #e0e0e0;
|
|
position: sticky;
|
|
bottom: 0;
|
|
z-index: 100;
|
|
}
|
|
|
|
.btn-wrapper {
|
|
flex: 1;
|
|
}
|
|
</style>
|
|
|