提交代码

This commit is contained in:
zlwu committed 2025-08-08 20:35:17 +08:00
1 parent 30042caabb
commit 600f0c621c
108 files changed
+20109 -1605

No files matched your search

+533 -83
View File
@@ -1,10 +1,14 @@
<template>
<u-popup
v-model="visible"
:show="visible"
mode="center"
width="90%"
height="70%"
border-radius="12"
:round="12"
:custom-style="{
width: '95vw',
height: '85vh',
maxWidth: '800px',
maxHeight: '700px',
}"
@close="handleClose"
>
<view class="approval-record-container">
@@ -14,90 +18,255 @@
</view>
<view class="content">
<u-loading-page v-if="loading" />
<view v-else-if="recordList.length === 0" class="empty">
<u-icon name="info-circle" size="48" color="#ccc" />
<text class="empty-text">暂无审批记录</text>
</view>
<scroll-view v-else scroll-y class="record-list">
<view v-for="(record, index) in recordList" :key="index" class="record-item">
<view class="record-header">
<view class="approver-info">
<u-avatar
:src="record.avatar || '/static/images/default-avatar.png'"
size="60"
/>
<view class="approver-details">
<text class="approver-name">
{{ record.approverName || '未知' }}
<u-tabs
v-model="tabActiveName"
:list="tabList"
:is-scroll="false"
active-color="#1989fa"
inactive-color="#666"
bar-width="60"
@change="handleTabChange"
/>
<!-- 流程图标签页 -->
<!-- <view v-if="tabActiveName === 0" class="tab-content flow-chart-tab">
<u-loading-page v-if="loading" />
<view v-else-if="!insId" class="empty">
<u-icon name="info-circle" size="48" color="#ccc" />
<text class="empty-text">暂无流程图数据</text>
</view>
<flowChart v-else :ins-id="insId" class="flow-chart" />
</view> -->
<!-- 审批信息标签页 -->
<view v-if="tabActiveName === 0" class="tab-content approval-info-tab">
<u-loading-page v-if="loading" />
<view v-else-if="historyList.length === 0" class="empty">
<u-icon name="info-circle" size="48" color="#ccc" />
<text class="empty-text">暂无审批记录</text>
</view>
<scroll-view v-else scroll-y class="record-list">
<view
v-for="(record, index) in historyList"
:key="index"
class="record-card"
>
<!-- 卡片头部 -->
<view class="card-header">
<view class="task-info">
<view class="task-index">#{{ index + 1 }}</view>
<view class="task-details">
<text class="task-name">
{{ record.nodeName || '未知任务' }}
</text>
<text class="task-duration">
{{ record.runDuration || '-' }}
</text>
</view>
</view>
<view :class="['status-badge', getStatusClass(record.flowStatus)]">
{{ getStatusText(record.flowStatus) }}
</view>
</view>
<!-- 办理人信息 -->
<view class="approver-section">
<text class="section-label">办理人:</text>
<view class="approver-tags">
<template v-if="record.approveName">
<u-tag
v-for="(name, nameIndex) in record.approveName.split(
',',
)"
:key="nameIndex"
:text="name.trim()"
type="success"
size="mini"
class="approver-tag"
/>
</template>
<u-tag v-else text="无" type="info" size="mini" />
</view>
</view>
<!-- 审批意见 -->
<view v-if="record.message" class="comment-section">
<text class="section-label">审批意见:</text>
<text class="comment-text">{{ record.message }}</text>
</view>
<!-- 时间信息 -->
<view class="time-section">
<view class="time-row">
<text class="time-label">开始时间:</text>
<text class="time-value">
{{ formatTime(record.createTime) }}
</text>
<text class="approver-role">
{{ record.roleName || '未知角色' }}
</view>
<view v-if="record.updateTime" class="time-row">
<text class="time-label">结束时间:</text>
<text class="time-value">
{{ formatTime(record.updateTime) }}
</text>
</view>
</view>
<view :class="['status-badge', getStatusClass(record.result)]">
{{ getStatusText(record.result) }}
<!-- 附件信息 -->
<view
v-if="record.attachmentList && record.attachmentList.length > 0"
class="attachment-section"
>
<text class="section-label">附件:</text>
<view class="attachment-list">
<view
v-for="(file, fileIndex) in record.attachmentList"
:key="fileIndex"
class="attachment-item"
@click="handleDownload(file.ossId, file.originalName)"
>
<u-icon name="download" size="16" color="#1989fa" />
<text class="attachment-name">{{ file.originalName }}</text>
</view>
</view>
</view>
</view>
<view v-if="record.comment" class="comment">
<text class="comment-label">审批意见:</text>
<text class="comment-text">{{ record.comment }}</text>
</view>
<view class="record-footer">
<text class="time">{{ formatTime(record.createTime) }}</text>
</view>
</view>
</scroll-view>
</scroll-view>
</view>
</view>
</view>
</u-popup>
</template>
<script>
import { request } from '@/common/request.js';
import { request, downloadFile } from '@/common/request.js';
import { useDict, getDictLabel } from '@/common/dict.js';
import FlowChart from './flowChart.vue';
export default {
name: 'ApprovalRecord',
components: {
FlowChart,
},
data() {
return {
visible: false,
loading: false,
recordList: [],
historyList: [],
insId: null,
tabActiveName: 0, // 由于只有审批信息标签页可用,使用索引0
tabList: [{ name: '审批信息' }], // 只显示审批信息标签页(流程图被注释)
currentBusinessId: '',
// 字典数据
dictionaries: {},
};
},
created() {
this.initDictionaries();
},
methods: {
// 打开弹窗
// 初始化字典数据
initDictionaries() {
console.log('🔍 初始化审批记录字典数据');
// 加载工作流任务状态字典
const dictManager = useDict('wf_task_status');
// 设置响应式更新回调(Vue 2必须)
dictManager.setUpdateCallback((dictType, data) => {
this.$set(this.dictionaries, dictType, data);
console.log(`📋 字典 ${dictType} 数据更新完成:`, data);
});
},
// 初始化审批记录(与web端API保持一致)
async init(businessId) {
console.log('=== 审批记录组件初始化 ===');
console.log('传入的businessId:', businessId);
console.log('businessId类型:', typeof businessId);
this.currentBusinessId = businessId.toString();
console.log('设置currentBusinessId为:', this.currentBusinessId);
this.visible = true;
console.log('设置visible为true');
this.loading = true;
this.tabActiveName = 0; // 默认显示审批信息标签页(索引0,因为只有一个标签页)
this.historyList = [];
this.insId = null;
console.log('开始加载审批记录...');
await this.loadApprovalRecords();
console.log('审批记录加载完成');
},
// 关闭弹窗
handleClose() {
this.visible = false;
this.recordList = [];
this.historyList = [];
this.insId = null;
},
// 加载审批记录
// 标签页切换
handleTabChange(index) {
this.tabActiveName = index;
},
// 加载审批记录(使用与web端相同的API)
async loadApprovalRecords() {
if (!this.currentBusinessId) return;
console.log('=== 加载审批记录 ===');
console.log('currentBusinessId:', this.currentBusinessId);
if (!this.currentBusinessId) {
console.error('currentBusinessId为空,无法加载审批记录');
return;
}
try {
this.loading = true;
console.log('设置loading为true');
// 使用与web端相同的API路径和防缓存参数
const apiUrl = `/workflow/instance/flowHisTaskList/${
this.currentBusinessId
}?t=${Math.random()}`;
console.log('API请求URL:', apiUrl);
const response = await request({
url: `/workflow/task/approvalRecords/${this.currentBusinessId}`,
url: apiUrl,
method: 'GET',
});
if (response.code === 200) {
this.recordList = response.data || [];
console.log('API响应:', response);
if (response.code === 200 && response.data) {
this.historyList = response.data.list || [];
this.insId = response.data.instanceId;
console.log('审批记录列表:', this.historyList);
console.log('实例ID:', this.insId);
console.log('记录数量:', this.historyList.length);
// 处理附件信息(与web端保持一致)
if (this.historyList.length > 0) {
console.log('开始处理附件信息...');
for (const item of this.historyList) {
if (item.ext) {
try {
const attachmentRes = await this.getAttachmentsByIds(item.ext);
item.attachmentList = attachmentRes.data || [];
} catch (error) {
console.error('获取附件失败:', error);
item.attachmentList = [];
}
} else {
item.attachmentList = [];
}
}
console.log('附件信息处理完成');
}
} else {
console.error('API响应异常:', response);
uni.showToast({
title: response.msg || '获取审批记录失败',
icon: 'none',
@@ -111,30 +280,107 @@ export default {
});
} finally {
this.loading = false;
console.log('设置loading为false');
console.log('当前visible状态:', this.visible);
}
},
// 获取状态样式类
getStatusClass(result) {
switch (result) {
case 'approve':
return 'status-approve';
// 获取附件信息(使用与web端相同的API)
async getAttachmentsByIds(ids) {
const response = await request({
url: `/resource/oss/listByIds/${ids}`,
method: 'GET',
});
return response;
},
// 获取状态样式类(使用字典方式)
getStatusClass(flowStatus) {
console.log('flowStatus:', flowStatus);
// 使用字典获取状态样式类
const dictData = this.dictionaries.wf_task_status || [];
console.log('dictData:', dictData);
const dictItem = dictData.find((item) => String(item.value) === String(flowStatus));
console.log('dictItem:', dictItem);
// 如果字典中有配置样式类,则使用字典的elTagClass
if (dictItem && dictItem.elTagClass) {
return dictItem.elTagClass;
}
// 兜底逻辑:保持原有的switch逻辑作为备用
switch (flowStatus) {
case '3': // 完成
case 'complete':
return 'status-complete';
case '2': // 进行中
case 'waiting':
return 'status-waiting';
case '4': // 拒绝
case 'reject':
return 'status-reject';
case '5': // 终止
case 'terminate':
return 'status-terminate';
case '6': // 回退
case 'back':
return 'status-back';
case '7': // 委托
case 'delegate':
return 'status-delegate';
case '8': // 转办
case 'transfer':
return 'status-transfer';
case '9': // 加签
case 'countersign':
return 'status-countersign';
default:
return 'status-pending';
}
},
// 获取状态文本
getStatusText(result) {
switch (result) {
case 'approve':
return '同意';
// 获取状态文本(使用字典方式)
getStatusText(flowStatus) {
// 使用字典获取状态文本
const dictData = this.dictionaries.wf_task_status || [];
const statusText = getDictLabel(dictData, flowStatus);
// 如果字典中没有找到对应值,使用兜底逻辑
if (statusText && statusText !== flowStatus) {
return statusText;
}
// 兜底逻辑:保持原有的switch逻辑作为备用
switch (flowStatus) {
case '3':
case 'complete':
return '完成';
case '2':
case 'waiting':
return '进行中';
case '4':
case 'reject':
return '拒绝';
case '5':
case 'terminate':
return '终止';
case '6':
case 'back':
return '回退';
case '7':
case 'delegate':
return '委托';
case '8':
case 'transfer':
return '转办';
case '9':
case 'countersign':
return '加签';
case '1':
case 'draft':
return '草稿';
default:
return '待审批';
return '待处理';
}
},
@@ -149,6 +395,70 @@ export default {
minute: '2-digit',
});
},
// 下载附件(使用统一的downloadFile方法)
async handleDownload(ossId, originalName) {
try {
uni.showLoading({
title: '正在下载...',
mask: true,
});
console.log('开始下载附件:', { ossId, originalName });
// 使用统一的downloadFile方法,传入文件名以支持H5端下载
const downloadUrl = `/resource/oss/download/${ossId}`;
const result = await downloadFile(downloadUrl, originalName);
console.log('下载完成:', result);
// 根据平台处理下载结果
// #ifdef APP-PLUS
// App端保存文件
if (result.statusCode === 200) {
uni.saveFile({
tempFilePath: result.tempFilePath,
success: function (res) {
uni.showToast({
title: '下载成功',
icon: 'success',
});
},
fail: function () {
uni.showToast({
title: '保存失败',
icon: 'none',
});
},
});
}
// #endif
// #ifdef H5
// H5端已通过downloadFile处理并直接触发浏览器下载,无需额外处理
console.log('H5端下载已由downloadFile方法处理');
// #endif
// #ifdef MP-WEIXIN
// 小程序端提示用户
uni.showModal({
title: '提示',
content: '小程序暂不支持文件下载,请在App或网页端操作',
showCancel: false,
});
// #endif
} catch (error) {
console.error('下载失败:', error);
uni.showToast({
title: error.message || '下载失败',
icon: 'none',
});
} finally {
uni.hideLoading();
}
},
// 注:getDownloadUrl方法已被移除,现在统一使用request.js中的downloadFile方法
},
};
</script>
@@ -178,7 +488,28 @@ export default {
.content {
flex: 1;
position: relative;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tab-content {
flex: 1;
overflow: hidden;
}
.flow-chart-tab {
padding: 20rpx;
}
.flow-chart {
width: 100%;
height: 100%;
min-height: 400rpx;
}
.approval-info-tab {
padding: 0;
}
.empty {
@@ -198,42 +529,59 @@ export default {
.record-list {
height: 100%;
padding: 20rpx;
padding: 20rpx 12rpx;
}
.record-item {
.record-card {
background: #f8f9fa;
border-radius: 12rpx;
padding: 24rpx;
margin: 0 8rpx 20rpx 8rpx;
border: 1rpx solid #e9ecef;
width: calc(100% - 16px);
box-sizing: border-box;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20rpx;
}
.record-header {
.task-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
align-items: flex-start;
gap: 12rpx;
flex: 1;
}
.approver-info {
display: flex;
align-items: center;
gap: 16rpx;
.task-index {
background: #1989fa;
color: #fff;
padding: 4rpx 8rpx;
border-radius: 6rpx;
font-size: 22rpx;
font-weight: 500;
min-width: 32rpx;
text-align: center;
}
.approver-details {
.task-details {
display: flex;
flex-direction: column;
gap: 4rpx;
flex: 1;
}
.approver-name {
.task-name {
font-size: 28rpx;
font-weight: 500;
color: #333;
line-height: 1.4;
}
.approver-role {
.task-duration {
font-size: 24rpx;
color: #666;
}
@@ -243,49 +591,151 @@ export default {
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
white-space: nowrap;
}
.status-approve {
.status-complete {
background: #e8f5e8;
color: #52c41a;
}
.status-waiting {
background: #fff7e6;
color: #fa8c16;
}
.status-reject {
background: #fff2f0;
color: #ff4d4f;
}
.status-terminate {
background: #f6f6f6;
color: #666;
}
.status-back {
background: #fff2e8;
color: #fa541c;
}
.status-delegate {
background: #f0f5ff;
color: #1890ff;
}
.status-transfer {
background: #f9f0ff;
color: #722ed1;
}
.status-countersign {
background: #e6fffb;
color: #13c2c2;
}
.status-pending {
background: #fff7e6;
color: #fa8c16;
background: #f0f0f0;
color: #999;
}
.comment {
.approver-section,
.comment-section,
.time-section,
.attachment-section {
margin-bottom: 16rpx;
padding: 16rpx;
background: #fff;
border-radius: 8rpx;
}
.comment-label {
.section-label {
font-size: 26rpx;
color: #666;
margin-right: 8rpx;
font-weight: 500;
margin-bottom: 8rpx;
display: block;
}
.approver-tags {
display: flex;
flex-wrap: wrap;
gap: 8rpx;
}
.approver-tag {
margin: 0;
}
.comment-text {
font-size: 26rpx;
color: #333;
line-height: 1.5;
padding: 12rpx;
background: #fff;
border-radius: 8rpx;
display: block;
}
.record-footer {
.time-section {
padding: 12rpx;
background: #fff;
border-radius: 8rpx;
}
.time-row {
display: flex;
justify-content: flex-end;
justify-content: space-between;
align-items: center;
margin-bottom: 8rpx;
}
.time {
font-size: 24rpx;
color: #999;
.time-row:last-child {
margin-bottom: 0;
}
.time-label {
font-size: 24rpx;
color: #666;
}
.time-value {
font-size: 24rpx;
color: #333;
}
.attachment-section {
padding: 12rpx;
background: #fff;
border-radius: 8rpx;
}
.attachment-list {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.attachment-item {
display: flex;
align-items: center;
gap: 8rpx;
padding: 8rpx 12rpx;
background: #f8f9fa;
border-radius: 6rpx;
border: 1rpx solid #e9ecef;
}
.attachment-name {
font-size: 24rpx;
color: #1989fa;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 标签页样式调整 */
/* #ifdef APP-PLUS || H5 */
.flow-chart-tab {
background: #f8f9fa;
}
/* #endif */
</style>