成功引入字典到pinia,应用字典实现审核详细页面

This commit is contained in:
WindowBird 2025-11-14 14:02:47 +08:00
parent 240f9fa966
commit ff177972d3
7 changed files with 608 additions and 12 deletions

View File

@ -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
}
});
};

View File

@ -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
}
});
};

View File

@ -21,6 +21,13 @@
"navigationBarTitleText": "审批管理"
}
},
{
"path": "pages/verify/detail/index",
"style": {
"navigationBarTitleText": "审核详情",
"navigationStyle": "custom"
}
},
{
"path": "pages/login/index",
"style": {

View 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>

View File

@ -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(() => {

View File

@ -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)

View File

@ -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()
// 然后加载最新数据