Files
zhxg_app/pages/OneStopCommunity/communityBuilding/add.vue

766 lines
20 KiB
Vue
Raw 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="form-container">
<!-- 活动主题 -->
<view class="form-item">
<text class="label required">活动主题</text>
<input
v-model="formData.theme"
placeholder="请输入活动主题"
class="input-box"
/>
</view>
<!-- 活动概况 -->
<view class="form-item">
<text class="label required">活动概况</text>
<textarea
v-model="formData.overview"
placeholder="请输入内容"
class="textarea-box"
rows="4"
></textarea>
</view>
<!-- 参与人员类型 -->
<view class="form-item" @click="showParticipantModal = true">
<text class="label required">参与人员类型</text>
<view class="selected-text">
{{ formData.participantType.length > 0
? formData.participantType.join('、')
: '请选择参与人员类型' }}
</view>
<view class="picker-box">
点击选择参与人员类型
</view>
</view>
<!-- 弹出选择框 -->
<view v-if="showParticipantModal" class="modal-overlay" @click="closeParticipantModal">
<view class="modal-content" @click.stop>
<view class="popup-header">
<text class="popup-title">选择参与人员类型</text>
<view class="popup-actions">
<button class="popup-btn popup-cancel" @click="closeParticipantModal">取消</button>
<button class="popup-btn popup-confirm" @click="confirmParticipantSelection">确定</button>
</view>
</view>
<view v-for="(item, index) in participantTypeList" :key="index" class="checkbox-item" @click="toggleParticipantType(item.value)">
<view class="checkbox-wrapper">
<view
class="custom-checkbox"
:class="{ 'checkbox-checked': tempParticipantType.includes(item.value) }"
>
<text v-if="tempParticipantType.includes(item.value)" class="check-icon"></text>
</view>
<text class="checkbox-text">{{ item.text }}</text>
</view>
</view>
</view>
</view>
<!-- 主要参加人员名单 -->
<view class="form-item">
<text class="label required">主要参加人员名单</text>
<textarea
v-model="formData.participantList"
placeholder="请输入主要参加人员名单"
class="textarea-box"
rows="3"
></textarea>
</view>
<!-- 时间选择 -->
<view class="form-item">
<text class="label required">时间</text>
<uni-datetime-picker
mode="date"
v-model="formData.time"
@change="handleTimeChange"
/>
</view>
<!-- 地点 -->
<view class="form-item">
<text class="label required">地点</text>
<input
v-model="formData.address"
placeholder="请输入地点"
class="input-box"
/>
</view>
<!-- 服务学生人次步进器 -->
<view class="form-item">
<text class="label required">服务学生人次</text>
<view class="stepper-box">
<button
class="stepper-btn"
@click="handleStepperMinus"
:disabled="formData.serviceStudentNum <= 1"
>-</button>
<input
v-model="formData.serviceStudentNum"
class="stepper-input"
type="number"
/>
<button class="stepper-btn" @click="handleStepperPlus">+</button>
</view>
</view>
<!-- 活动图片上传 -->
<view class="form-img">
<text class="label required">活动图片</text>
<view class="example-body">
<uni-file-picker @select="uploadEvidence" @delete="onDelImg" :auto-upload="false"
limit="9"></uni-file-picker>
</view>
<text class="upload-tips">请上传大小不超过 0.5MB 格式为 png/jpg/jpeg 的文件</text>
</view>
<!-- 活动压缩文件上传 -->
<view class="form-item">
<text class="label required">活动压缩文件</text>
<uni-file-picker
@select="uploadCompressedFile"
@delete="onDeleteCompressedFile"
:auto-upload="false"
limit="9"
:file-list="formData.compressedFileList"
:accept="'file'"
:file-mediatype="'all'"
:list-styles="{width: '100%', height: '120rpx'}"
:del-icon="true"
/>
<view class="file-list">
<!-- <view v-for="(file, index) in formData.compressedFileList" :key="index" class="file-item">
<text class="file-name">{{ file.name }}</text>
<button class="file-action delete-btn" @click="onDeleteCompressedFile(index)">删除</button>
</view> -->
</view>
</view>
<!-- 按钮区域 -->
<view class="btn">
<button @click="handleCancel">取消</button>
<button @click="handleSubmit">确定</button>
</view>
</view>
</template>
<script>
import { OneStopCommunityConstructionAdd } from '@/api/OneStopCommunity/communityBuilding.js';
import { getAffixItems, deleteAffix, uploadFiles, download } from '@/api/affix.js';
import config from '@/config.js';
export default {
data() {
return {
formData: {
theme: '', // 活动主题
overview: '', // 活动概况
participantType: [], // 参与人员类型值(数组)
participantList: '', // 主要参加人员名单
time: '', // 时间
address: '', // 地点
serviceStudentNum: 1, // 服务学生人次
imageFileList: [], // 活动图片文件列表
compressedFileList: [], // 压缩文件列表
},
showParticipantModal: false, // 控制参与人员类型选择弹窗显示
tempParticipantType: [], // 临时存储选中的参与人员类型
participantTypeList: [
{ text: '学校领导', value: '学校领导' },
{ text: '部门/学校领导', value: '部门/学校领导' },
{ text: '辅导员', value: '辅导员' },
{ text: '专任教师', value: '专任教师' },
{ text: '行政人员', value: '行政人员' },
{ text: '其他', value: '其他' }
]
};
},
onLoad() {
// 初始化临时选择
this.tempParticipantType = [...this.formData.participantType];
},
mounted() {
// 确保初始状态下临时选择与实际选择同步
this.tempParticipantType = [...this.formData.participantType];
},
methods: {
handleParticipantChange(e) {
// 旧的方法,保留兼容性
this.tempParticipantType = e.detail.value;
},
toggleParticipantType(value) {
// 切换选中状态
const index = this.tempParticipantType.indexOf(value);
if (index > -1) {
// 如果已选中,则取消选中
this.tempParticipantType.splice(index, 1);
} else {
// 如果未选中,则添加到选中列表
this.tempParticipantType.push(value);
}
},
closeParticipantModal() {
// 关闭弹窗但不保存选择
this.showParticipantModal = false;
// 重置临时选择到之前的状态
this.tempParticipantType = [...this.formData.participantType];
},
confirmParticipantSelection() {
// 确认选择更新到formData
this.formData.participantType = [...this.tempParticipantType];
this.showParticipantModal = false;
},
handleTimeChange(value) {
this.formData.time = value;
},
handleStepperMinus() {
if (this.formData.serviceStudentNum > 1) {
this.formData.serviceStudentNum--;
}
},
handleStepperPlus() {
this.formData.serviceStudentNum++;
},
uploadEvidence(files) {
// 验证文件类型
const validExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp'];
const validFiles = [];
const invalidFiles = [];
for (let file of files.tempFiles) {
const fileName = file.name ? file.name.toLowerCase() : file.path.toLowerCase();
const isValidType = validExtensions.some(ext => fileName.endsWith(ext));
if (isValidType) {
validFiles.push(file);
} else {
invalidFiles.push(file.name || file.path);
}
}
if (invalidFiles.length > 0) {
uni.showToast({
title: `文件类型错误,请上传图片文件(${validExtensions.join('/')})`,
icon: 'none'
});
return;
}
// 只上传有效文件
if (validFiles.length > 0) {
// 遍历上传每个文件
validFiles.forEach(file => {
// 使用uploadFile插件上传文件
import("@/plugins/upload.js").then((module) => {
const uploadFile = module.default;
uploadFile('/common/upload', file.path).then((res) => {
try {
const response = JSON.parse(res);
if (response.code === 200) {
// 成功上传后,更新图片文件列表
// 根据/common/upload接口的实际返回数据结构处理
this.formData.imageFileList.push({
name: response.fileName,
url: response.url,
path: response.url
});
uni.showToast({ title: '图片上传成功', icon: 'success' });
} else {
uni.showToast({ title: response.msg || '上传失败', icon: 'none' });
}
} catch (e) {
console.error('解析上传响应失败:', e);
uni.showToast({ title: '响应解析失败', icon: 'none' });
}
}).catch(error => {
console.error('上传失败:', error);
uni.showToast({ title: '上传失败,请检查网络', icon: 'none' });
});
});
});
}
},
uploadCompressedFile(files) {
// 遍历上传每个文件
files.tempFiles.forEach(file => {
// 使用uploadFile插件上传文件
import("@/plugins/upload.js").then((module) => {
const uploadFile = module.default;
uploadFile('/affix/upload', file.path).then((res) => {
try {
const response = JSON.parse(res);
if (response.code === 200) {
// 成功上传后,更新压缩文件列表
// 根据affix/upload接口的实际返回数据结构处理
this.formData.compressedFileList.push({
id: response.id, // 保存返回的数字ID
name: response.trueName,
url: config.baseUrl + response.savePath, // 拼接完整URL
path: config.baseUrl + response.savePath
});
uni.showToast({ title: '文件上传成功', icon: 'success' });
} else {
uni.showToast({ title: response.msg || '上传失败', icon: 'none' });
}
} catch (e) {
console.error('解析上传响应失败:', e);
uni.showToast({ title: '响应解析失败', icon: 'none' });
}
}).catch(error => {
console.error('上传失败:', error);
uni.showToast({ title: '上传失败,请检查网络', icon: 'none' });
});
});
});
},
onDelImg(index) {
// 删除图片
this.formData.imageFileList.splice(index, 1);
},
onDeleteCompressedFile(index) {
// 删除压缩文件
this.formData.compressedFileList.splice(index, 1);
},
downloadFile(file) {
// 只允许下载压缩文件必须有id
if (!file.id) {
uni.showToast({
title: '图片文件不支持下载',
icon: 'none'
});
return;
}
// 使用API进行下载获取arraybuffer数据
download(file.id)
.then(response => {
// 将arraybuffer数据保存为临时文件
const fileName = file.name || 'downloaded_file';
const filePath = `${wx.env.USER_DATA_PATH}/${fileName}`;
// 使用微信小程序API将arraybuffer写入本地文件
const fsm = uni.getFileSystemManager();
fsm.writeFileSync(filePath, response, 'binary');
// 打开文档
uni.openDocument({
filePath: filePath,
success: (res) => {
console.log('打开文档成功', res);
uni.showToast({ title: '打开文档成功', icon: 'success' });
},
fail: (error) => {
console.error('打开文档失败:', error);
uni.showToast({ title: '无法打开文档', icon: 'none' });
}
});
})
.catch(error => {
console.error('下载失败:', error);
uni.showToast({ title: '下载失败', icon: 'none' });
});
},
handleCancel() {
// 取消操作
uni.navigateBack();
},
handleSubmit() {
// 提交表单
if (!this.formData.theme) {
uni.showToast({ title: '请输入活动主题', icon: 'none' });
return;
}
if (!this.formData.overview) {
uni.showToast({ title: '请输入活动概况', icon: 'none' });
return;
}
if (!this.formData.time) {
uni.showToast({ title: '请选择时间', icon: 'none' });
return;
}
if (!this.formData.address) {
uni.showToast({ title: '请输入地点', icon: 'none' });
return;
}
// 构建提交数据,确保字段名称和格式符合后端要求
const submitData = {
activityTheme: this.formData.theme, // 活动主题
activityProfile: this.formData.overview, // 活动概况
personnelType: Array.isArray(this.formData.participantType)
? this.formData.participantType.join(',') // 参与人员类型,转换为逗号分隔的字符串
: this.formData.participantType,
personnelList: this.formData.participantList, // 主要参加人员名单
constructTime: this.formData.time, // 时间
place: this.formData.address, // 地点
stuNumber: this.formData.serviceStudentNum, // 服务学生人次
// 处理图片文件列表,提取文件路径
activityPhoto: this.formData.imageFileList.map(file => file.url).join(','), // 活动照片使用URL
// 处理压缩文件列表,提取文件路径
activePackage: this.formData.compressedFileList.map(file => file.url).join(',') // 活动压缩文件,使用路径
};
// 调用 API 提交数据
OneStopCommunityConstructionAdd(submitData)
.then(response => {
if (response.code === 200) {
uni.showToast({ title: '提交成功' });
this.handleCancel(); // 提交成功后返回上一页
} else {
uni.showToast({ title: '提交失败,请重试', icon: 'none' });
}
})
.catch(error => {
console.error('提交失败:', error);
uni.showToast({ title: '网络错误,请检查连接', icon: 'none' });
});
}
}
};
</script>
<style scoped>
.form-container {
padding: 20rpx;
background-color: #ffffff;
min-height: 100vh;
}
.form-item {
margin-bottom: 30rpx;
}
.label {
display: block;
font-size: 28rpx;
color: #333333;
margin-bottom: 10rpx;
}
.required::before {
content: '*';
color: #ff4d4f;
margin-right: 4rpx;
}
.input-box {
width: 100%;
padding: 16rpx;
border: 1px solid #e5e5e5;
border-radius: 8rpx;
font-size: 28rpx;
color: #333333;
background-color: #ffffff;
line-height: normal;
box-sizing: border-box;
min-height: 60rpx;
}
.textarea-box {
width: 100%;
padding: 16rpx;
border: 1px solid #e5e5e5;
border-radius: 8rpx;
font-size: 28rpx;
color: #333333;
background-color: #ffffff;
line-height: 1.5;
box-sizing: border-box;
min-height: 120rpx;
}
.stepper-box {
display: flex;
align-items: center;
}
.stepper-btn {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
border: 1px solid #e5e5e5;
border-radius: 8rpx;
background-color: #f5f5f5;
font-size: 32rpx;
}
.stepper-input {
width: 120rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
border-top: 1px solid #e5e5e5;
border-bottom: 1px solid #e5e5e5;
margin: 0 -1px;
font-size: 28rpx;
color: #333333;
background-color: #ffffff;
box-sizing: border-box;
}
.example-body {
margin-top: 20rpx;
margin-bottom: 20rpx;
}
.btn {
display: flex;
justify-content: space-between;
position: fixed;
bottom: 0;
left: 0;
margin: 0 auto;
border-top: 1px solid #d6d6d6;
background: #fff;
padding: 10px;
right: 0;
}
.btn button {
width: 45%;
background-color: #4097FE;
color: white;
}
.btn button:first-child {
background-color: #fff;
color: #4097FE;
border: 1px solid #4097FE;
}
.upload-tips {
display: block;
font-size: 24rpx;
color: #999999;
margin-bottom: 10rpx;
}
.picker-box {
width: 100%;
padding: 16rpx;
border: 1px solid #e5e5e5;
border-radius: 8rpx;
font-size: 28rpx;
min-height: 60rpx;
line-height: normal;
background-color: #ffffff;
color: #333333;
box-sizing: border-box;
}
.popup-content {
background-color: #ffffff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
overflow: hidden;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1px solid #f0f0f0;
}
.popup-title {
font-size: 32rpx;
color: #333333;
}
.popup-actions {
display: flex;
}
.popup-btn {
padding: 10rpx 20rpx;
font-size: 28rpx;
line-height: 1;
}
.popup-cancel {
color: #666666;
background-color: transparent;
}
.popup-confirm {
color: #007aff;
background-color: transparent;
}
.checkbox-item {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 1px solid #f0f0f0;
}
.checkbox-item text {
margin-left: 20rpx;
font-size: 28rpx;
color: #333333;
}
.checkbox-item checkbox {
transform: scale(0.8);
}
.selected-text {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
min-height: 40rpx;
line-height: 40rpx;
}
.checkbox-group-container {
width: 100%;
}
.file-list {
margin-top: 10rpx;
}
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 0;
border-bottom: 1px dashed #e5e5e5;
}
.file-name {
font-size: 24rpx;
color: #666666;
}
.file-action {
padding: 6rpx 12rpx;
font-size: 24rpx;
border: 1px solid #e5e5e5;
border-radius: 4rpx;
}
.delete-btn {
color: #ff4d4f;
border-color: #ff4d4f;
}
.checkbox-group-container {
margin-top: 10rpx;
}
.checkbox-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1px solid #f0f0f0;
}
.checkbox-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #333;
}
.checkbox-wrapper {
display: flex;
align-items: center;
}
.custom-checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #ccc;
border-radius: 6rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.checkbox-checked {
border-color: #007aff;
background-color: #007aff;
}
.check-icon {
color: #fff;
font-size: 24rpx;
line-height: 1;
}
.popup-content {
background-color: #fff;
padding: 20rpx;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 20rpx;
border-bottom: 1px solid #f0f0f0;
margin-bottom: 20rpx;
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.popup-actions {
display: flex;
gap: 20rpx;
}
.popup-btn {
padding: 10rpx 20rpx;
font-size: 28rpx;
border-radius: 8rpx;
border: none;
}
.popup-cancel {
background-color: #f5f5f5;
color: #666;
}
.popup-confirm {
background-color: #007aff;
color: #fff;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
justify-content: flex-end;
align-items: flex-end;
}
.modal-content {
width: 100%;
max-height: 80%;
overflow-y: auto;
background-color: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
padding: 20rpx;
transform: translateY(0);
}
.btn-group {
display: flex;
justify-content: flex-end;
margin-top: 60rpx;
gap: 20rpx;
}
.cancel-btn {
padding: 16rpx 32rpx;
background-color: #f5f5f5;
color: #333333;
border-radius: 8rpx;
font-size: 28rpx;
}
.confirm-btn {
padding: 16rpx 32rpx;
background-color: #007aff;
color: #ffffff;
border-radius: 8rpx;
font-size: 28rpx;
}
</style>