成功引入字典到pinia,应用字典实现审核详细页面
This commit is contained in:
parent
240f9fa966
commit
ff177972d3
|
|
@ -30,19 +30,35 @@ export const getRegionTree = () => {
|
|||
* 获取数据字典列表
|
||||
* @param {Object} params 请求参数(可选)
|
||||
* @param {string} params.dictType 字典类型(可选,用于筛选特定类型的字典)
|
||||
* @param {number} params.pageNum 页码(可选,默认1)
|
||||
* @param {number} params.pageSize 每页大小(可选,默认200)
|
||||
* @returns {Promise} 返回字典数据列表
|
||||
*/
|
||||
export const getDictDataList = (params = {}) => {
|
||||
const queryParams = []
|
||||
if (params.dictType) {
|
||||
queryParams.push(`dictType=${encodeURIComponent(params.dictType)}`)
|
||||
}
|
||||
const queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : ''
|
||||
|
||||
return uni.$uv.http.get(`/system/dict/data/list${queryString}`, {
|
||||
// 设置默认参数
|
||||
const defaultParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 200
|
||||
};
|
||||
|
||||
// 合并参数
|
||||
const requestParams = { ...defaultParams, ...params };
|
||||
|
||||
// 构建查询字符串
|
||||
const searchParams = new URLSearchParams();
|
||||
|
||||
Object.entries(requestParams).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
searchParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
|
||||
const queryString = searchParams.toString();
|
||||
const url = `/system/dict/data/list${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
return uni.$uv.http.get(url, {
|
||||
custom: {
|
||||
auth: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import {convertArrayParamsToQuery} from "@/api/utils";
|
||||
// 审批相关 API
|
||||
|
||||
/**
|
||||
* 获取审核列表
|
||||
* @param {Object} params 请求参数
|
||||
* @returns {Promise} 返回审核列表
|
||||
*/
|
||||
export const getVerifyList = (params = {}) => {
|
||||
// 使用通用函数处理数组参数
|
||||
const { params: processedParams, queryString } = convertArrayParamsToQuery(params);
|
||||
|
|
@ -22,4 +28,49 @@ export const getVerifyList = (params = {}) => {
|
|||
auth: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取审核详情
|
||||
* @param {string|number} id 审核ID
|
||||
* @returns {Promise} 返回审核详情
|
||||
*/
|
||||
export const getVerifyDetail = (id) => {
|
||||
return uni.$uv.http.get(`/bst/verify/${id}`, {
|
||||
custom: {
|
||||
auth: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核通过
|
||||
* @param {string|number} id 审核ID
|
||||
* @param {string} remark 审核备注(可选)
|
||||
* @returns {Promise} 返回接口响应
|
||||
*/
|
||||
export const approveVerify = (id, remark = '') => {
|
||||
return uni.$uv.http.put(`/bst/verify/${id}/approve`, {
|
||||
remark: remark
|
||||
}, {
|
||||
custom: {
|
||||
auth: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核驳回
|
||||
* @param {string|number} id 审核ID
|
||||
* @param {string} remark 审核备注(可选)
|
||||
* @returns {Promise} 返回接口响应
|
||||
*/
|
||||
export const rejectVerify = (id, remark = '') => {
|
||||
return uni.$uv.http.put(`/bst/verify/${id}/reject`, {
|
||||
remark: remark
|
||||
}, {
|
||||
custom: {
|
||||
auth: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -21,6 +21,13 @@
|
|||
"navigationBarTitleText": "审批管理"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/verify/detail/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "审核详情",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
|
|
|
|||
513
pages/verify/detail/index.vue
Normal file
513
pages/verify/detail/index.vue
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
<template>
|
||||
<view class="verify-detail-page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar">
|
||||
<view class="navbar-content">
|
||||
<text class="nav-btn" @click="handleBack">‹</text>
|
||||
<text class="nav-title">详情</text>
|
||||
<text class="nav-btn" @click="handleBack">×</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<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.task?.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'">
|
||||
<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, approveVerify, rejectVerify } from '@/api/verify'
|
||||
import { getDictLabel } from '@/utils/dict'
|
||||
|
||||
// 页面参数
|
||||
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', // 进行中
|
||||
'3': 'success', // 已完成
|
||||
'4': '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', // 已驳回
|
||||
'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 approveVerify(verifyId.value, commentText.value)
|
||||
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '审核通过',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 刷新详情数据
|
||||
await loadVerifyDetail(verifyId.value)
|
||||
|
||||
// 延迟返回上一页
|
||||
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 rejectVerify(verifyId.value, commentText.value)
|
||||
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '已驳回',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 刷新详情数据
|
||||
await loadVerifyDetail(verifyId.value)
|
||||
|
||||
// 延迟返回上一页
|
||||
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>
|
||||
|
||||
|
|
@ -359,12 +359,17 @@ const refreshList = () => {
|
|||
};
|
||||
|
||||
const goHandle = (item) => {
|
||||
// 预留处理入口
|
||||
uni.showToast({ title: '去处理', icon: 'none' });
|
||||
// 跳转到审核详情页进行处理
|
||||
uni.navigateTo({
|
||||
url: `/pages/verify/detail/index?id=${item.id}`
|
||||
})
|
||||
};
|
||||
|
||||
const goDetail = (item) => {
|
||||
// 可跳转到任务详情或审批详情
|
||||
// 跳转到审核详情页
|
||||
uni.navigateTo({
|
||||
url: `/pages/verify/detail/index?id=${item.id}`
|
||||
})
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ export const useDictStore = defineStore('dict', {
|
|||
// 如果响应直接是数组,使用 response
|
||||
// 如果响应有 rows 字段,使用 response.rows
|
||||
const dictList = Array.isArray(response) ? response : (response.rows || [])
|
||||
|
||||
console.log('dictList',dictList)
|
||||
|
||||
// 过滤掉 status 不为 '0' 的字典项(停用的字典)
|
||||
const activeDictList = dictList.filter(item => item.status === '0' || item.status === 0)
|
||||
|
|
|
|||
|
|
@ -53,8 +53,10 @@ export const getDictLabels = (dictType, dictValues) => {
|
|||
* @param {boolean} forceRefresh 是否强制刷新
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const initDictData = async (forceRefresh = false) => {
|
||||
export const initDictData = async (forceRefresh = true) => {
|
||||
const dictStore = useDictStore()
|
||||
|
||||
console.log('initDictData',dictStore)
|
||||
// 先从缓存初始化
|
||||
dictStore.initFromCache()
|
||||
// 然后加载最新数据
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user