rhb-server/mes-ui/rhb-app/packageForm/pages/historyMessage.vue
2025-10-20 11:14:41 +08:00

1037 lines
24 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="container">
<!-- 帮扶手册历史记录 -->
<view v-if="type === 'help'" class="help-history">
<view class="page-title">帮扶手册历史记录</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据为空状态 -->
<view v-else-if="groupedData.length === 0" class="empty-container">
<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"></u-empty>
<text class="empty-text">暂无历史记录</text>
</view>
<!-- 分组数据显示 -->
<view v-else class="history-list">
<view v-for="(group, groupIndex) in groupedData" :key="groupIndex" class="history-card">
<!-- 时间标题 -->
<view class="card-header">
<text class="time-text">{{ formatTime(group.createTime) }}</text>
</view>
<!-- 图片列表 -->
<view class="image-list">
<view
v-for="(item, itemIndex) in group.items"
:key="itemIndex"
class="image-item"
@click="previewImage(item.manualPhoto, item.manualPhotoName)"
>
<image
:src="item.manualPhoto"
:alt="item.manualPhotoName"
mode="aspectFill"
class="preview-image"
/>
<view class="image-name">{{ item.manualPhotoName }}</view>
</view>
</view>
</view>
</view>
</view>
<!-- 需求历史记录 -->
<view v-else-if="type === 'need'" class="need-history">
<view class="page-title">需求历史记录</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据为空状态 -->
<view v-else-if="needData.length === 0" class="empty-container">
<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"></u-empty>
<text class="empty-text">暂无需求记录</text>
</view>
<!-- 需求数据显示 -->
<view v-else class="need-list">
<view v-for="(item, index) in needData" :key="index" class="need-card">
<!-- 时间标题 -->
<view class="card-header">
<text class="time-text">{{ formatTime(item.createTime) }}</text>
</view>
<!-- 需求内容 -->
<view class="need-content">
<view class="need-item">
<text class="need-label">就业需求:</text>
<text class="need-value">{{ item.jobNeed || '无' }}</text>
</view>
<view class="need-item">
<text class="need-label">培训需求:</text>
<text class="need-value">{{ item.trainingNeed || '无' }}</text>
</view>
<view class="need-item">
<text class="need-label">其他诉求:</text>
<text class="need-value">{{ item.otherNeed || '无' }}</text>
</view>
<view class="need-item">
<text class="need-label">意见建议:</text>
<text class="need-value">{{ item.suggestion || '无' }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 满意度历史记录 -->
<view v-else-if="type === 'satisfaction'" class="satisfaction-history">
<view class="page-title">满意度历史记录</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据为空状态 -->
<view v-else-if="satisfactionData.length === 0" class="empty-container">
<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"></u-empty>
<text class="empty-text">暂无满意度记录</text>
</view>
<!-- 满意度数据显示 -->
<view v-else class="satisfaction-list">
<view v-for="(item, index) in satisfactionData" :key="index" class="satisfaction-card">
<!-- 时间标题 -->
<view class="card-header">
<text class="time-text">{{ formatTime(item.createTime) }}</text>
</view>
<!-- 满意度内容 -->
<view class="satisfaction-content">
<view class="satisfaction-item">
<text class="satisfaction-label">对村两委满意度:</text>
<view class="satisfaction-rating">
<text class="rating-text">{{ getSatisfactionText(item.villageSatisfaction) }}</text>
</view>
</view>
<view class="satisfaction-item">
<text class="satisfaction-label">对驻村工作队满意度:</text>
<view class="satisfaction-rating">
<text class="rating-text">{{ getSatisfactionText(item.workTeamSatisfaction) }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 家庭成员历史记录 -->
<view v-else-if="type === 'member'" class="member-history">
<view class="page-title">家庭成员历史记录</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据为空状态 -->
<view v-else-if="remarkHistory.length === 0" class="empty-container">
<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"></u-empty>
<text class="empty-text">暂无历史记录</text>
</view>
<!-- 历史变更记录 -->
<view v-else class="remark-history">
<view class="remark-list">
<view v-for="(item, index) in remarkHistory" :key="index" class="remark-card">
<view class="remark-header">
<text class="remark-time">{{ item.updateTime }}</text>
</view>
<view class="remark-content">
<text class="remark-message">{{ item.msg }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 签名历史记录 -->
<view v-else-if="type === 'sign'" class="signature-history">
<view class="page-title">签名历史记录</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-container">
<u-loading-icon mode="spinner" size="40"></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 数据为空状态 -->
<view v-else-if="signatureData.length === 0" class="empty-container">
<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png"></u-empty>
<text class="empty-text">暂无签名记录</text>
</view>
<!-- 签名记录列表 -->
<view v-else class="signature-list">
<view v-for="(item, index) in signatureData" :key="index" class="signature-card">
<!-- 时间标题 -->
<view class="card-header">
<text class="time-text">{{ formatTime(item.createTime) }}</text>
<text class="method-text">{{ getRecordMethodText(item.recordMethod) }}</text>
</view>
<!-- 签名图片 -->
<view class="signature-images">
<!-- 入户员签名 -->
<view class="signature-item">
<text class="signature-label">入户员签名:</text>
<view class="signature-image-container">
<image
v-if="item.signatureImg"
:src="item.signatureImg"
mode="aspectFit"
class="signature-image"
@click="previewImage(item.signatureImg, '入户员签名')"
/>
<text v-else class="no-signature">无签名</text>
</view>
</view>
<!-- 户主签名 -->
<view class="signature-item">
<text class="signature-label">户主签名:</text>
<view class="signature-image-container">
<image
v-if="item.holderSignatureImg"
:src="item.holderSignatureImg"
mode="aspectFit"
class="signature-image"
@click="previewImage(item.holderSignatureImg, '户主签名')"
/>
<text v-else class="no-signature">无签名</text>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 其他类型的处理 -->
<view v-else class="other-type">
<u-empty mode="page" icon="http://cdn.uviewui.com/uview/empty/page.png"></u-empty>
<text class="other-text">暂不支持该类型的历史记录</text>
</view>
</view>
</template>
<script>
import { helpApi, needApi, satisfactionApi, memberApi,signatureApi , systemApi} from '@/utils/api.js'
import dayjs from 'dayjs'
export default {
data() {
return {
type: '', // 页面类型参数
id: '', // 家庭ID参数
loading: false, // 加载状态
helpData: [], // 帮扶手册原始数据
groupedData: [], // 分组后的数据
needData: [], // 需求原始数据
satisfactionData: [], // 满意度原始数据
memberData: null, // 成员原始数据
remarkHistory: [], // 解析后的备注历史记录
signatureData: [] ,// 签名记录数据
queryDictTypeDO: {
dictTypeList: ['biz_record_type'
].join(','),
},
queryDictTypeDO1: {
dictTypeList: ['biz_satisfaction'
].join(','),
},
satisfactionDict:[],
recordMethodDict:[],
}
},
onLoad(options) {
// 获取页面参数
this.type = options.type || ''
this.id = options.id || ''
// 如果是帮扶手册类型且有ID则加载数据
if (this.type === 'help' && this.id) {
this.loadHelpHistory()
}
// 如果是需求类型且有ID则加载数据
else if (this.type === 'need' && this.id) {
this.loadNeedHistory()
}
// 如果是满意度类型且有ID则加载数据
else if (this.type === 'satisfaction' && this.id) {
this.loadSatisfactionHistory();
}
// 如果是家庭成员类型且有ID则加载数据
else if (this.type === 'member' && this.id) {
this.loadMemberDetail()
}
// 如果是签名类型且有ID则加载数据
else if (this.type === 'sign' && this.id) {
this.loadSignatureHistory()
}
},
methods: {
// 加载帮扶手册历史记录
async loadHelpHistory() {
if (!this.id) {
uni.showToast({
title: '缺少必要参数',
icon: 'none'
})
return
}
this.loading = true
try {
const response = await helpApi.getDetailList(this.id)
console.log('帮扶手册历史数据:', response)
if (response ) {
this.helpData = response
this.processData()
} else {
this.helpData = []
this.groupedData = []
}
} catch (error) {
console.error('获取帮扶手册历史记录失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
this.helpData = []
this.groupedData = []
} finally {
this.loading = false
}
},
// 加载需求历史记录
async loadNeedHistory() {
if (!this.id) {
uni.showToast({
title: '缺少必要参数',
icon: 'none'
})
return
}
this.loading = true
try {
const response = await needApi.getDetailList(this.id)
console.log('需求历史数据:', response)
if (response) {
this.needData = response
} else {
this.needData = []
}
} catch (error) {
console.error('获取需求历史记录失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
this.needData = []
} finally {
this.loading = false
}
},
// 加载满意度历史记录
async loadSatisfactionHistory() {
if (!this.id) {
uni.showToast({
title: '缺少必要参数',
icon: 'none'
})
return
}
this.loading = true
try {
const response = await satisfactionApi.getDetailList(this.id)
this.satisfactionDict = await systemApi.getDictType(this.queryDictTypeDO1);
if (response) {
this.satisfactionData = response
} else {
this.satisfactionData = []
}
} catch (error) {
console.error('获取满意度历史记录失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
this.satisfactionData = []
} finally {
this.loading = false
}
},
// 加载家庭成员详情
async loadMemberDetail() {
if (!this.id) {
uni.showToast({
title: '缺少必要参数',
icon: 'none'
})
return
}
this.loading = true
try {
const response = await memberApi.getDetail(this.id)
console.log('家庭成员详情数据:', response)
if (response) {
this.memberData = response
this.processRemarkHistory()
} else {
this.memberData = null
this.remarkHistory = []
}
} catch (error) {
console.error('获取家庭成员详情失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
this.memberData = null
this.remarkHistory = []
} finally {
this.loading = false
}
},
// 加载签名历史记录
async loadSignatureHistory() {
if (!this.id) {
uni.showToast({
title: '缺少必要参数',
icon: 'none'
})
return
}
this.loading = true
try {
const response = await signatureApi.getDetailList(this.id)
this.recordMethodDict = await systemApi.getDictType(this.queryDictTypeDO);
if (response) {
this.signatureData = response
} else {
this.signatureData = []
}
} catch (error) {
console.error('获取签名历史记录失败:', error)
uni.showToast({
title: '获取数据失败',
icon: 'none'
})
this.signatureData = []
} finally {
this.loading = false
}
},
// 处理数据按groupId分组并按createTime排序
processData() {
if (!this.helpData || this.helpData.length === 0) {
this.groupedData = []
return
}
// 按groupId分组
const groupMap = new Map()
this.helpData.forEach(item => {
const groupId = item.groupId
if (!groupMap.has(groupId)) {
groupMap.set(groupId, {
groupId: groupId,
createTime: item.createTime,
items: []
})
}
groupMap.get(groupId).items.push(item)
})
// 转换为数组并按createTime降序排序
this.groupedData = Array.from(groupMap.values()).sort((a, b) => {
return b.createTime - a.createTime
})
console.log('分组后的数据:', this.groupedData)
},
// 处理备注历史记录
processRemarkHistory() {
if (!this.memberData || !this.memberData.remark) {
this.remarkHistory = []
return
}
try {
const remarkStr = '['+ this.memberData.remark+']';
const parsed = JSON.parse(remarkStr)
// 如果是数组,直接使用
if (Array.isArray(parsed)) {
this.remarkHistory = parsed
}
// 如果是对象,包装成数组
else if (typeof parsed === 'object') {
this.remarkHistory = [parsed]
}
// 其他情况,设为空数组
else {
this.remarkHistory = []
}
// 处理msg中的Φ符号替换为换行符
this.remarkHistory.forEach(item => {
if (item.msg) {
item.msg = item.msg.replace(/Φ/g, '\n')
}
})
// 按updateTime降序排序
this.remarkHistory.sort((a, b) => {
const timeA = new Date(a.updateTime).getTime()
const timeB = new Date(b.updateTime).getTime()
return timeB - timeA
})
console.log('解析后的备注历史:', this.remarkHistory)
} catch (error) {
console.error('解析备注历史记录失败:', error)
this.remarkHistory = []
}
},
// 格式化时间
formatTime(timestamp) {
if (!timestamp) return ''
return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
},
// 获取满意度文字描述
getSatisfactionText(rating) {
var str = '';
this.satisfactionDict.forEach(item =>{
if(item.value == rating+'' ){
str = item.label
}
})
return str
},
// 获取入户方式文字描述
getRecordMethodText(method) {
var str = '';
this.recordMethodDict.forEach(item =>{
if(item.value == method+'' ){
str = item.label
}
})
return str
},
// 预览图片
previewImage(imageSrc, imageName) {
if (!imageSrc) {
uni.showToast({
title: '图片地址无效',
icon: 'none'
})
return
}
// 如果是base64格式需要特殊处理
if (imageSrc.startsWith('data:image/')) {
// 对于base64图片直接显示
uni.previewImage({
urls: [imageSrc],
current: imageSrc,
success: () => {
console.log('图片预览成功')
},
fail: (error) => {
console.error('图片预览失败:', error)
uni.showToast({
title: '图片预览失败',
icon: 'none'
})
}
})
} else {
// 对于网络图片,直接预览
uni.previewImage({
urls: [imageSrc],
current: imageSrc,
success: () => {
console.log('图片预览成功')
},
fail: (error) => {
console.error('图片预览失败:', error)
uni.showToast({
title: '图片预览失败',
icon: 'none'
})
}
})
}
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.help-history {
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
padding: 20rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
.loading-text {
margin-top: 20rpx;
color: #999;
font-size: 28rpx;
}
}
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
.empty-text {
margin-top: 20rpx;
color: #999;
font-size: 28rpx;
}
}
.history-list {
.history-card {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.card-header {
padding: 24rpx;
background-color: #f8f9fa;
border-bottom: 1rpx solid #e9ecef;
.time-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
}
.image-list {
padding: 24rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.image-item {
width: 200rpx;
text-align: center;
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.05);
}
.preview-image {
width: 200rpx;
height: 200rpx;
border-radius: 8rpx;
border: 2rpx solid #e9ecef;
object-fit: cover;
}
.image-name {
margin-top: 12rpx;
font-size: 24rpx;
color: #666;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
.need-history {
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
padding: 20rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
.need-list {
.need-card {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.card-header {
padding: 24rpx;
background-color: #f8f9fa;
border-bottom: 1rpx solid #e9ecef;
.time-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
}
.need-content {
padding: 24rpx;
.need-item {
display: flex;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.need-label {
width: 160rpx;
font-size: 28rpx;
color: #333;
font-weight: 500;
flex-shrink: 0;
}
.need-value {
flex: 1;
font-size: 28rpx;
color: #666;
line-height: 1.5;
word-break: break-all;
}
}
}
}
}
.satisfaction-history {
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
padding: 20rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
.satisfaction-list {
.satisfaction-card {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.card-header {
padding: 24rpx;
background-color: #f8f9fa;
border-bottom: 1rpx solid #e9ecef;
.time-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
}
.satisfaction-content {
padding: 24rpx;
.satisfaction-item {
display: flex;
align-items: center;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.satisfaction-label {
width: 280rpx;
font-size: 28rpx;
color: #333;
font-weight: 500;
flex-shrink: 0;
}
.satisfaction-rating {
flex: 1;
display: flex;
align-items: center;
gap: 20rpx;
.rating-text {
font-size: 28rpx;
color: #333;
min-width: 120rpx;
}
.rating-stars {
display: flex;
gap: 8rpx;
.star {
font-size: 32rpx;
color: #ddd;
transition: color 0.2s ease;
&.star-filled {
color: #ffd700;
}
}
}
}
}
}
}
}
.other-type {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
.other-text {
margin-top: 20rpx;
color: #999;
font-size: 28rpx;
}
}
.member-history {
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
padding: 20rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
.remark-history {
.remark-list {
.remark-card {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.remark-header {
padding: 20rpx 24rpx;
background-color: #f8f9fa;
border-bottom: 1rpx solid #e9ecef;
.remark-time {
font-size: 26rpx;
color: #666;
font-weight: 500;
}
}
.remark-content {
padding: 24rpx;
.remark-message {
font-size: 28rpx;
color: #333;
line-height: 1.6;
word-break: break-all;
white-space: pre-line;
}
}
}
}
}
.signature-history {
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
text-align: center;
margin-bottom: 30rpx;
padding: 20rpx 0;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
.signature-list {
.signature-card {
background-color: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
overflow: hidden;
.card-header {
padding: 24rpx;
background-color: #f8f9fa;
border-bottom: 1rpx solid #e9ecef;
display: flex;
justify-content: space-between;
align-items: center;
.time-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
.method-text {
font-size: 28rpx;
color: #999;
font-weight: 400;
}
}
.signature-images {
padding: 24rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.signature-item {
width: 45%; /* 两列布局 */
text-align: left;
cursor: pointer;
transition: transform 0.2s ease;
&:hover {
transform: scale(1.05);
}
.signature-label {
font-size: 28rpx;
color: #333;
font-weight: 500;
margin-bottom: 10rpx;
}
.signature-image-container {
width: 100%;
height: 200rpx; /* 固定高度 */
border: 2rpx solid #e9ecef;
border-radius: 8rpx;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background-color: #f0f0f0;
}
.signature-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.no-signature {
font-size: 28rpx;
color: #999;
text-align: center;
line-height: 200rpx; /* 固定高度 */
}
}
}
}
}
</style>