432 lines
13 KiB
Vue
432 lines
13 KiB
Vue
|
|
<!-- 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>
|