OfficeSystem/pages/submit-task/index.vue
2025-11-05 15:23:52 +08:00

641 lines
13 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="submit-task-page">
<!-- 自定义导航栏 -->
<view class="custom-navbar">
<view class="navbar-content">
<text class="nav-btn" @click="handleCancel"></text>
<text class="nav-title">提交详情</text>
<view class="nav-placeholder"></view>
</view>
</view>
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y>
<!-- 输入提交说明 -->
<view class="form-item">
<view class="form-icon">📄</view>
<textarea
v-model="formData.description"
class="description-input"
placeholder="输入提交说明"
placeholder-style="color: #999;"
:maxlength="500"
auto-height
/>
</view>
<!-- 输入任务进度 -->
<view class="form-item clickable-item" @click="openProgressPicker">
<view class="form-icon">👤%</view>
<view class="form-content">
<text v-if="formData.progress !== null" class="form-value">{{ formData.progress }}%</text>
<text v-else class="form-placeholder">输入任务进度</text>
</view>
<text class="arrow"></text>
</view>
<!-- 添加照片 -->
<view class="form-item clickable-item" @click="chooseImages">
<view class="form-icon">🏔️</view>
<text class="form-label">添加照片</text>
<text class="arrow"></text>
</view>
<!-- 照片预览 -->
<view class="images-preview" v-if="formData.images.length > 0">
<view
class="image-item"
v-for="(image, index) in formData.images"
:key="index"
>
<image :src="image" mode="aspectFill" class="preview-image" @click="previewImage(index)" />
<view class="remove-btn" @click="removeImage(index)">✕</view>
</view>
</view>
<!-- 添加文件 -->
<view class="form-item clickable-item" @click="chooseFiles">
<view class="form-icon">📄</view>
<text class="form-label">添加文件</text>
<text class="arrow"></text>
</view>
<!-- 文件列表 -->
<view class="files-list" v-if="formData.files.length > 0">
<view
class="file-item"
v-for="(file, index) in formData.files"
:key="index"
>
<text class="file-icon">📄</text>
<text class="file-name">{{ file.name }}</text>
<view class="remove-btn" @click="removeFile(index)">✕</view>
</view>
</view>
</scroll-view>
<!-- 进度选择弹窗 -->
<view v-if="showProgressPicker" class="modal-mask" @click="showProgressPicker = false">
<view class="modal-content progress-modal" @click.stop>
<view class="modal-title">选择任务进度</view>
<view class="progress-content">
<slider
:value="formData.progress || 0"
:min="0"
:max="100"
:step="10"
:show-value="true"
activeColor="#1976d2"
@change="onProgressChange"
/>
<view class="progress-options">
<view
class="progress-option"
v-for="progress in progressOptions"
:key="progress"
:class="{ active: formData.progress === progress }"
@click="selectProgress(progress)"
>
<text>{{ progress }}%</text>
</view>
</view>
</view>
<view class="modal-actions">
<text class="modal-btn cancel-btn" @click="showProgressPicker = false">取消</text>
<text class="modal-btn confirm-btn" @click="confirmProgress">确定</text>
</view>
</view>
</view>
<!-- 确认提交按钮 -->
<view class="submit-button-wrapper">
<uv-button
type="primary"
size="normal"
:disabled="!canSubmit"
@click="handleSubmit"
>
确认提交
</uv-button>
</view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
// 表单数据
const formData = ref({
description: '',
progress: null,
images: [],
files: []
});
// 任务ID
const taskId = ref(null);
// 进度选择弹窗
const showProgressPicker = ref(false);
const tempProgress = ref(null);
// 打开进度选择器
const openProgressPicker = () => {
tempProgress.value = formData.value.progress !== null ? formData.value.progress : 0;
showProgressPicker.value = true;
};
// 进度选项
const progressOptions = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
// 是否可以提交
const canSubmit = computed(() => {
return formData.value.description.trim() !== '' ||
formData.value.images.length > 0 ||
formData.value.files.length > 0;
});
// 页面加载
onLoad((options) => {
taskId.value = options.taskId || options.id;
});
// 取消
const handleCancel = () => {
uni.showModal({
title: '提示',
content: '确定要取消提交吗?未保存的内容将丢失',
success: (res) => {
if (res.confirm) {
uni.navigateBack();
}
}
});
};
// 进度变化
const onProgressChange = (e) => {
tempProgress.value = e.detail.value;
};
// 选择进度
const selectProgress = (progress) => {
tempProgress.value = progress;
};
// 确认进度
const confirmProgress = () => {
formData.value.progress = tempProgress.value;
showProgressPicker.value = false;
};
// 选择图片
const chooseImages = () => {
uni.chooseImage({
count: 9 - formData.value.images.length,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
formData.value.images = [...formData.value.images, ...res.tempFilePaths];
},
fail: (err) => {
console.error('选择图片失败:', err);
uni.showToast({
title: '选择图片失败',
icon: 'none'
});
}
});
};
// 预览图片
const previewImage = (index) => {
uni.previewImage({
urls: formData.value.images,
current: index
});
};
// 删除图片
const removeImage = (index) => {
formData.value.images.splice(index, 1);
};
// 选择文件
const chooseFiles = () => {
// #ifdef H5 || APP-PLUS
uni.showActionSheet({
itemList: ['从相册选择', '拍照'],
success: (res) => {
if (res.tapIndex === 0) {
// 从相册选择
uni.chooseImage({
count: 9,
success: (res) => {
// 将图片转换为文件格式
const files = res.tempFilePaths.map((path, index) => ({
name: `image_${Date.now()}_${index}.jpg`,
path: path,
size: 0
}));
formData.value.files = [...formData.value.files, ...files];
}
});
} else {
uni.showToast({
title: '暂不支持文件选择',
icon: 'none'
});
}
}
});
// #endif
// #ifdef MP
uni.chooseFile({
count: 5 - formData.value.files.length,
type: 'file',
success: (res) => {
const files = res.tempFiles.map(file => ({
name: file.name || `file_${Date.now()}.${file.path.split('.').pop()}`,
path: file.path,
size: file.size
}));
formData.value.files = [...formData.value.files, ...files];
},
fail: (err) => {
console.error('选择文件失败:', err);
uni.showToast({
title: '选择文件失败',
icon: 'none'
});
}
});
// #endif
};
// 删除文件
const removeFile = (index) => {
formData.value.files.splice(index, 1);
};
// 提交任务
const handleSubmit = () => {
if (!canSubmit.value) {
uni.showToast({
title: '请至少填写提交说明或添加附件',
icon: 'none'
});
return;
}
uni.showLoading({
title: '提交中...'
});
// 准备提交数据
const submitData = {
taskId: taskId.value,
description: formData.value.description.trim(),
progress: formData.value.progress,
images: formData.value.images,
files: formData.value.files.map(file => ({
name: file.name,
path: file.path,
size: file.size
}))
};
// TODO: 调用提交接口
// 实际使用时应该调用API接口上传数据
// uni.request({
// url: '/api/task/submit',
// method: 'POST',
// data: submitData,
// success: (res) => {
// // 处理成功响应
// },
// fail: (err) => {
// // 处理错误
// }
// });
// 模拟提交请求
setTimeout(() => {
uni.hideLoading();
// 提交成功后,将数据传递回任务详情页
const submitRecord = {
userName: '当前用户', // TODO: 从用户信息获取
time: new Date().toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}).replace(/\//g, '-'),
content: submitData.description || '',
progress: submitData.progress,
attachments: [
...submitData.images.map(img => ({ type: 'image', path: img })),
...submitData.files.map(file => ({ type: 'file', name: file.name, path: file.path }))
],
canEdit: true,
showDelayBtn: false
};
// 将提交记录存储到本地,供任务详情页使用
uni.setStorageSync('newSubmitRecord', submitRecord);
uni.showToast({
title: '提交成功',
icon: 'success'
});
// 延迟返回,让用户看到成功提示
setTimeout(() => {
uni.navigateBack();
}, 1500);
}, 1000);
};
</script>
<style lang="scss" scoped>
.submit-task-page {
min-height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
padding-bottom: 80px;
}
/* 自定义导航栏 */
.custom-navbar {
background-color: #fff;
border-bottom: 1px solid #eee;
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: 20px;
color: #333;
padding: 4px 8px;
cursor: pointer;
}
.nav-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.nav-placeholder {
width: 36px;
}
/* 内容滚动区域 */
.content-scroll {
flex: 1;
padding: 16px;
}
/* 表单项 */
.form-item {
display: flex;
align-items: center;
padding: 16px;
background-color: #fff;
border-radius: 8px;
margin-bottom: 12px;
gap: 12px;
}
.clickable-item {
cursor: pointer;
&:active {
background-color: #f5f5f5;
}
}
.form-icon {
font-size: 20px;
flex-shrink: 0;
}
.form-content {
flex: 1;
display: flex;
flex-direction: column;
}
.form-label {
flex: 1;
font-size: 15px;
color: #333;
}
.form-value {
font-size: 15px;
color: #333;
font-weight: 500;
}
.form-placeholder {
font-size: 15px;
color: #999;
}
.arrow {
font-size: 20px;
color: #999;
flex-shrink: 0;
}
.description-input {
flex: 1;
min-height: 80px;
font-size: 15px;
color: #333;
line-height: 1.6;
}
/* 图片预览 */
.images-preview {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
padding: 0 16px;
}
.image-item {
position: relative;
width: 100px;
height: 100px;
border-radius: 8px;
overflow: hidden;
}
.preview-image {
width: 100%;
height: 100%;
}
.remove-btn {
position: absolute;
top: 4px;
right: 4px;
width: 24px;
height: 24px;
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
cursor: pointer;
}
/* 文件列表 */
.files-list {
padding: 0 16px;
margin-bottom: 12px;
}
.file-item {
display: flex;
align-items: center;
padding: 12px;
background-color: #fff;
border-radius: 8px;
margin-bottom: 8px;
gap: 12px;
}
.file-icon {
font-size: 20px;
flex-shrink: 0;
}
.file-name {
flex: 1;
font-size: 14px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 进度选择弹窗 */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background-color: #fff;
border-radius: 12px;
width: 90%;
max-width: 400px;
padding: 20px;
}
.modal-title {
font-size: 18px;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 20px;
}
.progress-content {
padding: 20px 0;
}
.progress-options {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 20px;
}
.progress-option {
flex: 1;
min-width: 60px;
padding: 10px;
text-align: center;
background-color: #f5f5f5;
border-radius: 6px;
font-size: 14px;
color: #666;
cursor: pointer;
&.active {
background-color: #1976d2;
color: #fff;
}
&:active {
opacity: 0.8;
}
}
.modal-actions {
display: flex;
gap: 12px;
margin-top: 20px;
}
.modal-btn {
flex: 1;
padding: 12px;
text-align: center;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
}
.cancel-btn {
background-color: #f5f5f5;
color: #666;
}
.confirm-btn {
background-color: #1976d2;
color: #fff;
}
/* 提交按钮 */
.submit-button-wrapper {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 16px;
background-color: #fff;
border-top: 1px solid #eee;
z-index: 100;
}
</style>