432 lines
13 KiB
Vue
Raw Normal View History

2025-10-07 08:58:02 +08:00
<!-- src/pages/base/message/detail.vue -->
<template>
<view class="message-detail-page">
<view v-if="isLoading" class="loading-indicator">加载中...</view>
<view v-else class="detail-content">
<view class="detail-header">
<view class="title-tag-row">
<text class="detail-title">{{ rw.rwmc }}</text>
</view>
<view class="detail-meta">
<text>{{ rw.rwkstime }}</text>
<!-- <text>{{ messageDetail.timeAgo }}</text>-->
</view>
</view>
<view class="detail-body">
<BasicForm :schema="schema" v-model="formData">
<!-- 注册自定义组件 -->
<template #ImageVideoUpload="{ field, label, required, componentProps, onChange }">
<ImageVideoUpload
v-model:image-list="formData[`${field}_images`]"
v-model:video-list="formData[`${field}_videos`]"
v-model:file-list="formData[`${field}_files`]"
@image-upload-success="(file, index) => onChange?.(file, field)"
@video-upload-success="(file, index) => onChange?.(file, field)"
@file-upload-success="(file, index) => onChange?.(file, field)"
v-bind="componentProps"
/>
</template>
</BasicForm>
</view>
<view class="detail-footer">
<button type="primary" class="action-button" @click="saveRwZx">提交</button>
</view>
</view>
<!-- <view v-else class="empty-state">消息详情未找到</view>-->
</view>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import {onLoad} from '@dcloudio/uni-app';
import {rwFindInfoByRwId, rwflFindRwlxsByRwId, rwzxExecutedInfoByRwIdAndJsApi, rwzxSaveApi} from "@/api/base/server";
import {useForm} from "@/components/BasicForm/hooks/useForm";
import {navigateBack, showToast} from "@/utils/uniapp";
import {useUserStore} from "@/store/modules/user";
import { ImageVideoUpload, type FileItem, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
import { attachmentUpload } from "@/api/system/upload";
interface MessageDetail {
id: string; // Assuming an ID is passed or can be derived
title: string;
desc: string;
date: string;
timeAgo: string;
tagText: string;
tagType: string;
// Add other fields as necessary
}
const formData: any = ref({})
const messageId = ref<string>('');
const jsId = ref<string>(''); // 教师ID
const executionId = ref<string>(''); // 执行ID
const messageDetail = ref<MessageDetail | null>({
id: 'todo1',
title: '教务通知 (待办)',
desc: '学校召开期初教学准备会议暨首次教学工作例会. 会议强调了新学期的教学重点和要求,请各位老师认真准备。',
date: '2025-02-17',
timeAgo: '8 mins 前',
tagText: '通知',
tagType: 'notice',
likes: 6,
comments: 12
});
const isLoading = ref(false);
const rwflx: any = ref([])
const rw = ref({})
const schema = reactive<FormsSchema[]>([])
const {getUser} = useUserStore()
async function saveRwZx() {
const result = [];
for (let i = 0; i < rwflx.value.length; i++) {
const fieldId = rwflx.value[i].id;
let fieldValue = formData.value[fieldId];
// 处理上传类型的任务
if (rwflx.value[i].rwfl == "sctp" || rwflx.value[i].rwfl == "scsp" || rwflx.value[i].rwfl == "scwd") {
// 收集上传文件的URL
const fileUrls = [];
if (rwflx.value[i].rwfl == "sctp") {
// 图片上传
const images = formData.value[`${fieldId}_images`] || [];
fileUrls.push(...images.map((img: any) => img.url).filter(Boolean));
} else if (rwflx.value[i].rwfl == "scsp") {
// 视频上传
const videos = formData.value[`${fieldId}_videos`] || [];
fileUrls.push(...videos.map((video: any) => video.url).filter(Boolean));
} else if (rwflx.value[i].rwfl == "scwd") {
// 文档上传
const files = formData.value[`${fieldId}_files`] || [];
fileUrls.push(...files.map((file: any) => file.url).filter(Boolean));
}
fieldValue = fileUrls.join(',');
}
console.log(44, fieldId, fieldValue)
if (rwflx.value[i].rwbs && (!fieldValue || fieldValue == "")) {
showToast("请填写必填项!")
return;
}
result.push({
rwlxId: fieldId,
rwzxqdtx: fieldValue,
})
}
await rwzxSaveApi({
id: executionId.value, // 执行记录ID如果有的话就是更新没有就是新增
rwId: rw.value.id, // 任务ID
rwzxfzr: jsId.value || getUser.id, // 执行人教师ID
mobile: getUser.mobile, // 手机号
rwzxqdDtos: result // 执行清单
})
showToast("操作成功!");
uni.navigateBack({delta: 1})
}
const rwzxqds = ref([])
onLoad(async (options) => {
console.log('页面加载参数:', options);
let taskId = '';
// 处理参数接收,兼容多种传参方式
if (options && options.params) {
try {
const params = JSON.parse(decodeURIComponent(options.params));
taskId = params.id;
jsId.value = params.jsId || '';
executionId.value = params.executionId || '';
console.log('解析参数成功:', { taskId, jsId: jsId.value, executionId: executionId.value });
} catch (error) {
console.error('解析参数失败:', error);
taskId = options.id || '';
}
} else if (options && options.id) {
taskId = options.id;
}
if (taskId) {
const {result} = await rwFindInfoByRwId({
rwId: taskId
});
rwflx.value = result.rwlxes;
rw.value = result;
for (let i = 0; i < rwflx.value.length; i++) {
// 处理上传类型的任务
if (rwflx.value[i].rwfl == "sctp" || rwflx.value[i].rwfl == "scsp" || rwflx.value[i].rwfl == "scwd") {
// 初始化表单数据
const fieldId = rwflx.value[i].id;
if (rwflx.value[i].rwfl == "sctp") {
formData.value[`${fieldId}_images`] = [];
} else if (rwflx.value[i].rwfl == "scsp") {
formData.value[`${fieldId}_videos`] = [];
} else if (rwflx.value[i].rwfl == "scwd") {
formData.value[`${fieldId}_files`] = [];
}
// 根据任务类型配置上传组件
let componentConfig = {};
if (rwflx.value[i].rwfl == "sctp") {
// 上传图片
componentConfig = {
component: "ImageVideoUpload",
componentProps: {
enableImage: true,
enableVideo: false,
enableFile: false,
maxImageCount: 5,
uploadApi: attachmentUpload,
compressConfig: COMPRESS_PRESETS.medium
}
};
} else if (rwflx.value[i].rwfl == "scsp") {
// 上传视频
componentConfig = {
component: "ImageVideoUpload",
componentProps: {
enableImage: false,
enableVideo: true,
enableFile: false,
maxVideoCount: 3,
uploadApi: attachmentUpload,
compressConfig: COMPRESS_PRESETS.medium
}
};
} else if (rwflx.value[i].rwfl == "scwd") {
// 上传文档
componentConfig = {
component: "ImageVideoUpload",
componentProps: {
enableImage: false,
enableVideo: false,
enableFile: true,
maxFileCount: 5,
allowedFileTypes: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'mp3', 'wav', 'zip', 'rar'],
uploadApi: attachmentUpload,
compressConfig: COMPRESS_PRESETS.medium
}
};
}
schema.push({
field: `${rwflx.value[i].id}`,
label: `${rwflx.value[i].rwbt}`,
required: rwflx.value[i].rwbs,
itemProps: {
labelPosition: "top",
},
...componentConfig
})
} else if (rwflx.value[i].rwfl == "text") {
schema.push({
field: `${rwflx.value[i].id}`,
label: `${rwflx.value[i].rwbt}`,
component: "BasicInput",
required: rwflx.value[i].rwbs,
itemProps: {
labelPosition: "top",
},
componentProps: {
type: "textarea",
placeholder: "请输入内容"
},
})
} else if (rwflx.value[i].rwfl == "dxsx" || rwflx.value[i].rwfl == "dxxz") {
let options = rwflx.value[i].remark.split("");
let range = [];
for (let i = 0; i < options.length; i++) {
range.push({
name: options[i]
});
}
schema.push({
field: `${rwflx.value[i].id}`,
label: `${rwflx.value[i].rwbt}`,
component: "BasicPicker",
required: rwflx.value[i].rwbs,
itemProps: {
labelPosition: "top",
},
componentProps: {
range: range,
rangeKey: "name",
savaKey: "name",
},
})
}
}
const res = await rwzxExecutedInfoByRwIdAndJsApi({
rwId: taskId,
jsId: jsId.value || getUser.id // 使用传入的jsId如果没有则使用当前用户ID
});
if (res && res.result && res.result.length) {
rwzxqds.value = res.result;
const showData = {};
for (let i = 0; i < rwzxqds.value.length; i++) {
showData[rwzxqds.value[i].rwlxId] = rwzxqds.value[i].rwzxqdtx;
}
formData.value = showData;
}
} else {
console.error('Message ID/Data is missing!');
uni.showToast({title: '加载失败,缺少信息', icon: 'none'});
}
});
</script>
<style scoped lang="scss">
.message-detail-page {
background-color: #f4f5f7;
min-height: 100vh;
padding: 15px;
box-sizing: border-box;
}
.loading-indicator,
.empty-state {
text-align: center;
color: #999;
padding: 40px 15px;
font-size: 14px;
}
.detail-content {
background-color: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.detail-header {
border-bottom: 1px solid #f0f0f0;
padding-bottom: 15px;
margin-bottom: 20px;
.title-tag-row {
display: flex;
justify-content: space-between;
align-items: flex-start; // Align items to the top
margin-bottom: 10px;
}
.detail-title {
font-size: 18px;
font-weight: bold;
color: #333;
flex: 1; // Allow title to take available space
margin-right: 10px; // Space between title and tag
line-height: 1.4;
text-align: center; // 居中显示
}
.tag { // Reuse tag styles from index page if possible, or define here
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
color: #ffffff;
white-space: nowrap;
flex-shrink: 0; // Prevent tag from shrinking
&.notice {
background-color: #447ade;
}
&.task {
background-color: #19be6b;
}
&.approval {
background-color: #ff9f0a;
}
&.submit {
background-color: #8e8e93;
}
}
.detail-meta {
font-size: 12px;
color: #999;
text {
margin-right: 15px;
}
}
}
.detail-body {
margin-bottom: 40px;
.detail-desc {
font-size: 15px;
color: #555;
line-height: 1.7;
word-break: break-word;
}
}
.detail-footer {
// text-align: center; // Removed center alignment
// Add margin if needed, e.g., margin-top: 20px;
}
// Style for the action button
.action-button {
width: 100%; // Make button full width
height: 44px; // Standard button height
line-height: 44px; // Match height for vertical centering
font-size: 16px; // Slightly larger font
font-weight: 500; // Medium weight
border-radius: 8px; // Consistent border radius
margin-top: 20px; // Add space above the button
// Ensure primary color is applied correctly (uni-app default should work)
// background-color: #447ade;
// color: #ffffff;
}
// 为输入框添加基本边框(参考原生 textFiled 样式)
:deep(.uni-input),
:deep(.uni-textarea) {
width: 100% !important;
min-height: 35px !important;
font-size: 13px !important;
border: 1px #CCCCCC solid !important;
border-radius: 3px !important;
padding: 8px 12px !important;
box-sizing: border-box !important;
}
// 为 BasicForm 组件内的输入框添加边框
:deep(.basic-form) {
.uni-input,
.uni-textarea {
width: 100% !important;
min-height: 35px !important;
font-size: 13px !important;
border: 1px #CCCCCC solid !important;
border-radius: 3px !important;
padding: 8px 12px !important;
box-sizing: border-box !important;
}
}
// 尝试直接选择 input 和 textarea 标签
:deep(input),
:deep(textarea) {
width: 100% !important;
min-height: 35px !important;
font-size: 13px !important;
border: 1px #CCCCCC solid !important;
border-radius: 3px !important;
padding: 8px 12px !important;
box-sizing: border-box !important;
}
</style>