371 lines
8.7 KiB
Vue
371 lines
8.7 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">
|
||
<view class="label">
|
||
<u-icon name="photo" color="#3B8CFF" size="38"></u-icon>
|
||
上传照片
|
||
</view>
|
||
<view class="upload-area">
|
||
<view class="upload-list">
|
||
<view class="upload-item" v-for="(item, index) in fileList" :key="index">
|
||
<image :src="item.url" mode="aspectFill" @click="previewImage(index)"></image>
|
||
<view class="delete-btn" @click="handleDelete(index)">
|
||
<u-icon name="close" color="#fff" size="24"></u-icon>
|
||
</view>
|
||
</view>
|
||
<view class="upload-btn" @click="showUploadAction" v-if="fileList.length < 9">
|
||
<u-icon name="plus" size="40" color="#c0c4cc"></u-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<text class="note">注:照片支持分批上传,但最大数量为9</text>
|
||
</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 // 返回上一级页面
|
||
});
|
||
},
|
||
|
||
// 显示上传选项
|
||
showUploadAction() {
|
||
uni.showActionSheet({
|
||
itemList: ['拍照', '从相册选择'],
|
||
success: (res) => {
|
||
if (res.tapIndex === 0) {
|
||
this.chooseMedia('camera');
|
||
} else {
|
||
this.chooseMedia('album');
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 选择媒体文件
|
||
async chooseMedia(sourceType) {
|
||
try {
|
||
// 1. 选择图片
|
||
const res = await new Promise((resolve, reject) => {
|
||
uni.chooseImage({
|
||
count: 9 - this.fileList.length,
|
||
sourceType: [sourceType === 'camera' ? 'camera' : 'album'],
|
||
success: resolve,
|
||
fail: reject
|
||
});
|
||
});
|
||
|
||
// 2. 添加到预览列表
|
||
const newFiles = res.tempFilePaths.map(url => ({
|
||
url,
|
||
status: 'uploading'
|
||
}));
|
||
this.fileList = [...this.fileList, ...newFiles];
|
||
|
||
// 3. 逐个上传文件
|
||
for (const file of newFiles) {
|
||
await this.uploadFile(file);
|
||
}
|
||
|
||
} catch (err) {
|
||
console.error('选择图片失败:', err);
|
||
uni.showToast({ title: '选择图片失败', icon: 'none' });
|
||
}
|
||
},
|
||
|
||
// 单个文件上传
|
||
async uploadFile(file) {
|
||
try {
|
||
const res = await new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: `${BASE_URL}/api/v1/upload`,
|
||
filePath: file.url,
|
||
name: 'file',
|
||
header: {
|
||
'Authorization': `Bearer ${uni.getStorageSync('token')}`,
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
success: (uploadRes) => {
|
||
try {
|
||
const data = JSON.parse(uploadRes.data);
|
||
if (data.success) {
|
||
resolve(data.data); // 使用服务器返回的完整URL
|
||
} else {
|
||
reject(data.message || '上传失败');
|
||
}
|
||
} catch (e) {
|
||
reject('解析响应失败');
|
||
}
|
||
},
|
||
fail: (err) => reject(err)
|
||
});
|
||
});
|
||
|
||
// 更新文件状态
|
||
const index = this.fileList.findIndex(f => f.url === file.url);
|
||
if (index !== -1) {
|
||
this.$set(this.fileList, index, {
|
||
...file,
|
||
status: 'success',
|
||
serverUrl: res // 存储服务器返回的URL
|
||
});
|
||
|
||
// 添加到提交数组
|
||
this.uploadedImageUrls.push(res);
|
||
}
|
||
|
||
} catch (err) {
|
||
console.error('上传失败:', err);
|
||
const index = this.fileList.findIndex(f => f.url === file.url);
|
||
if (index !== -1) {
|
||
this.$set(this.fileList, index, {
|
||
...file,
|
||
status: 'failed',
|
||
error: err.message || err
|
||
});
|
||
}
|
||
uni.showToast({
|
||
title: `上传失败: ${err.message || err}`,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
// 预览图片
|
||
previewImage(index) {
|
||
const images = this.fileList.map(item => item.url);
|
||
|
||
uni.previewImage({
|
||
current: index,
|
||
urls: images
|
||
});
|
||
},
|
||
|
||
// 删除文件
|
||
handleDelete(index) {
|
||
const file = this.fileList[index];
|
||
|
||
if (file.serverUrl) {
|
||
const imageIndex = this.uploadedImageUrls.indexOf(file.serverUrl);
|
||
if (imageIndex !== -1) this.uploadedImageUrls.splice(imageIndex, 1);
|
||
}
|
||
|
||
this.fileList.splice(index, 1);
|
||
},
|
||
|
||
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-section {
|
||
.label {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-bottom: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.u-icon {
|
||
margin-right: 12rpx;
|
||
}
|
||
}
|
||
|
||
.upload-area {
|
||
margin-top: 16rpx;
|
||
|
||
.upload-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin: -5rpx;
|
||
|
||
.upload-item, .upload-btn {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
margin: 5rpx;
|
||
position: relative;
|
||
background: #f8f8f8;
|
||
border-radius: 8rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.delete-btn {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border-radius: 0 0 0 8rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.upload-btn {
|
||
border: 1rpx dashed #c0c4cc;
|
||
}
|
||
}
|
||
}
|
||
|
||
.note {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 24rpx;
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.publish-section {
|
||
text-align: center;
|
||
margin-top: 100rpx;
|
||
}
|
||
}
|
||
</style> |