282 lines
7.0 KiB
Vue
282 lines
7.0 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view class="content">
|
||
<view class="content_wapper">
|
||
<!-- 添加标题 -->
|
||
<view class="input-section">
|
||
<u-input placeholder="添加标题" v-model="title" border></u-input>
|
||
</view>
|
||
|
||
<!-- 输入求助内容 -->
|
||
<view class="textarea-section">
|
||
<u--textarea placeholder="请输入求助内容..." v-model="content" border height="300"></u--textarea>
|
||
</view>
|
||
|
||
<!-- 图片上传 -->
|
||
<view class="upload-section">
|
||
<u-upload
|
||
@afterRead="afterRead"
|
||
@delete="deletePic"
|
||
:fileList="fileList"
|
||
:maxCount="9"
|
||
:previewFullImage="true"
|
||
:accept="'image/*'"
|
||
:capture="['album', 'camera']"
|
||
width="220"
|
||
height="220">
|
||
<view class="upload-btn" v-if="fileList.length < 1">
|
||
<u-icon name="plus" size="40" color="#666"></u-icon>
|
||
</view>
|
||
</u-upload>
|
||
</view>
|
||
|
||
<!-- 发布按钮 -->
|
||
<view class="publish-section">
|
||
<u-button type="primary" text="立即发布" @click="publish"></u-button>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
<Footer></Footer>
|
||
</view>
|
||
|
||
</template>
|
||
|
||
<script>
|
||
import Footer from '@/components/footer_common.vue';
|
||
import {
|
||
get,
|
||
post
|
||
} from '@/utils/request';
|
||
import {
|
||
IMAGE_BASE_URL,
|
||
BASE_URL
|
||
} from '@/utils/config';
|
||
export default {
|
||
components: {
|
||
Footer
|
||
},
|
||
data() {
|
||
return {
|
||
title: '',
|
||
content: '',
|
||
fileList: [],
|
||
uploadedImageUrls: []
|
||
};
|
||
},
|
||
methods: {
|
||
handleBack() {
|
||
uni.navigateBack({
|
||
delta: 1 // 返回上一级页面
|
||
});
|
||
},
|
||
async afterRead(event) {
|
||
// 处理单张或多张图片上传
|
||
const files = event.file; // 可能是数组或单个文件
|
||
const uploadFiles = Array.isArray(files) ? files : [files];
|
||
|
||
// 检查文件类型 - 更健壮的验证方式
|
||
for (const file of uploadFiles) {
|
||
// 方法1:检查文件扩展名
|
||
const fileExt = file.url.split('.').pop().toLowerCase();
|
||
const allowedExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
|
||
|
||
// 方法2:检查MIME类型(如果可用)
|
||
const isImage = file.type?.startsWith('image/') ||
|
||
allowedExts.includes(fileExt);
|
||
|
||
if (!isImage) {
|
||
uni.showToast({
|
||
title: '只能上传图片文件(JPG/PNG等)',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
uni.showLoading({ title: '上传中...' });
|
||
|
||
try {
|
||
// 上传所有图片
|
||
const uploadPromises = uploadFiles.map(file => this.uploadAvatar(file.url));
|
||
const uploadedUrls = await Promise.all(uploadPromises);
|
||
|
||
// 更新上传的图片URL数组
|
||
this.uploadedImageUrls = [...this.uploadedImageUrls, ...uploadedUrls];
|
||
|
||
// 更新文件列表显示
|
||
this.fileList = [
|
||
...this.fileList,
|
||
...uploadFiles.map((file, index) => ({
|
||
url: file.url,
|
||
status: 'success',
|
||
uploadedUrl: uploadedUrls[index]
|
||
}))
|
||
];
|
||
|
||
uni.showToast({ title: '上传成功', icon: 'success' });
|
||
} catch (error) {
|
||
console.error('上传失败:', error);
|
||
uni.showToast({
|
||
title: '上传失败: ' + error.message,
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
} finally {
|
||
uni.hideLoading();
|
||
}
|
||
},
|
||
deletePic(event) {
|
||
// 删除对应的图片URL
|
||
const deletedFile = this.fileList[event.index];
|
||
this.uploadedImageUrls = this.uploadedImageUrls.filter(
|
||
url => url !== deletedFile.uploadedUrl
|
||
);
|
||
|
||
// 更新文件列表
|
||
this.fileList.splice(event.index, 1);
|
||
},
|
||
uploadAvatar(filePath) {
|
||
return new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: `${BASE_URL}/api/v1/upload`,
|
||
filePath: filePath,
|
||
name: 'file',
|
||
header: {
|
||
'Authorization': `Bearer ${uni.getStorageSync('token')}`
|
||
},
|
||
success: (uploadRes) => {
|
||
try {
|
||
const res = JSON.parse(uploadRes.data);
|
||
if (res && res.success) {
|
||
resolve(res.data);
|
||
} else {
|
||
reject(new Error(res.message || '上传失败'));
|
||
}
|
||
} catch (e) {
|
||
reject(new Error('解析响应失败'));
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
reject(new Error('上传失败: ' + JSON.stringify(err)));
|
||
}
|
||
});
|
||
});
|
||
},
|
||
async publish() {
|
||
if (!this.title.trim()) {
|
||
uni.showToast({
|
||
title: '请输入标题',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!this.content.trim()) {
|
||
uni.showToast({
|
||
title: '请输入内容',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
uni.showLoading({
|
||
title: '发布中...'
|
||
});
|
||
|
||
try {
|
||
// 构造请求数据,图片URL以数组形式传递
|
||
const postData = {
|
||
title: this.title,
|
||
content: this.content,
|
||
images: this.uploadedImageUrls.length > 0 ?
|
||
this.uploadedImageUrls :
|
||
[] // 如果没有图片则传空数组
|
||
};
|
||
|
||
// 调用API
|
||
const res = await post('/api/v1/app_auth/reciprocities', postData);
|
||
if (!res || !res.success) {
|
||
throw new Error('发布失败');
|
||
}
|
||
|
||
uni.showToast({
|
||
title: '发布成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
// 发布成功后返回上一页
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 1500);
|
||
} catch (error) {
|
||
console.error('发布失败:', error);
|
||
uni.showToast({
|
||
title: '发布失败',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
uni.hideLoading();
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.container {
|
||
|
||
width: 100%;
|
||
height: 100vh;
|
||
/* 占满整个视口高度 */
|
||
opacity: 1;
|
||
background: linear-gradient(0deg, rgba(240, 240, 240, 1) 0%, rgba(255, 250, 250, 0) 100%),
|
||
linear-gradient(0deg, rgba(255, 241, 235, 1) 0%, rgba(192, 219, 250, 1) 100%);
|
||
position: relative;
|
||
|
||
.content {
|
||
width: 100%;
|
||
position: absolute;
|
||
margin-top: 40rpx;
|
||
border-top-left-radius: 30rpx;
|
||
border-top-right-radius: 30rpx;
|
||
background-color: rgba(255, 255, 255, 0.65);
|
||
padding-bottom: 60rpx;
|
||
|
||
.content_wapper {
|
||
width: 90%;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
}
|
||
|
||
.input-section {
|
||
border-bottom: 2rpx solid #C3DCFA;
|
||
height: 90rpx;
|
||
line-height: 90rpx;
|
||
}
|
||
|
||
.input-section,
|
||
.textarea-section,
|
||
.upload-section {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.upload-btn {
|
||
width: 220rpx;
|
||
height: 220rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: #f0f0f0;
|
||
border-radius: 10rpx;
|
||
}
|
||
|
||
.publish-section {
|
||
text-align: center;
|
||
margin-top: 100rpx;
|
||
}
|
||
|
||
}
|
||
</style> |