You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

570 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="container">
<view class="card">
<text class="section-title">供需类型</text>
<view class="type-selector">
<view :class="['type-button', form.type === 'supply' ? 'active' : '']" @click="form.type = 'supply'">
<u-icon name="server-fill" :color="form.type === 'supply' ? '#C9A36D' : '#909399'"></u-icon>
<text class="type-text">供应</text>
</view>
<view :class="['type-button', form.type === 'demand' ? 'active' : '']" @click="form.type = 'demand'">
<u-icon name="order" :color="form.type === 'demand' ? '#C9A36D' : '#909399'"></u-icon>
<text class="type-text">需求</text>
</view>
</view>
</view>
<view class="card">
<text class="section-title">基本信息</text>
<u-form :model="form" ref="uForm">
<u-form-item label="标题" label-width="150" prop="title" :border-bottom="false">
<u-input v-model="form.title" placeholder="请输入标题, 简明扼要" :maxlength="50" type="text"
:custom-style="inputStyle('title')" @focus="activeInput = 'title'" @blur="activeInput = null" />
</u-form-item>
<u-form-item label="详细描述" label-width="150" prop="description" :border-bottom="false">
<u-input v-model="form.description" type="textarea" placeholder="请详细描述您的供需内容..." :maxlength="500" height="200"
:custom-style="inputStyle('description')" @focus="activeInput = 'description'" @blur="activeInput = null" />
</u-form-item>
<u-form-item label="行业标签" label-width="150" prop="tags" :border-bottom="false">
<u-input v-model="tagInput" type="text" placeholder="输入后按回车键确认"
:custom-style="inputStyle('tags')" @focus="activeInput = 'tags'" @blur="activeInput = null" @confirm="addTag" />
</u-form-item>
<view class="tag-container" v-if="form.tags.length > 0">
<view v-for="(tag, index) in form.tags" :key="index" class="tag-item">
<text>{{ tag }}</text>
<u-icon name="close" size="20" @click="removeTag(index)"></u-icon>
</view>
</view>
<view class="form-tip">建议添加相关行业标签, 方便其他校友找到你</view>
</u-form>
</view>
<view class="card">
<text class="section-title">图片上传</text>
<view class="image-upload">
<view class="image-list">
<view v-for="(image, index) in form.images" :key="index" class="image-item">
<image :src="image" mode="aspectFill" class="uploaded-image"></image>
<view class="delete-btn" @click="removeImage(index)">
<u-icon name="close" color="#fff" size="20"></u-icon>
</view>
</view>
<view v-if="form.images.length < 9" class="upload-btn" @click="chooseImage">
<u-icon name="camera-fill" color="#C9A36D" size="40"></u-icon>
<text class="upload-text">添加图片</text>
</view>
</view>
<text class="upload-tip">最多上传9张图片支持jpg、png格式</text>
</view>
</view>
<view class="card">
<text class="section-title">联系方式</text>
<u-form-item label="联系人" label-width="150" prop="contactName" :border-bottom="false">
<u-input v-model="form.contactName" placeholder="请输入联系人姓名" type="text"
:custom-style="inputStyle('contactName')" @focus="activeInput = 'contactName'" @blur="activeInput = null" />
</u-form-item>
<view class="contact-selector">
<view :class="['contact-button', form.contactType === 'wechat' ? 'active' : '']" @click="form.contactType = 'wechat'">
<u-icon name="chat-fill" :color="form.contactType === 'wechat' ? '#C9A36D' : '#909399'"></u-icon>
<text class="contact-text">微信</text>
</view>
<view :class="['contact-button', form.contactType === 'phone' ? 'active' : '']" @click="form.contactType = 'phone'">
<u-icon name="phone-fill" :color="form.contactType === 'phone' ? '#C9A36D' : '#909399'"></u-icon>
<text class="contact-text">电话</text>
</view>
<view :class="['contact-button', form.contactType === 'email' ? 'active' : '']" @click="form.contactType = 'email'">
<u-icon name="email-fill" :color="form.contactType === 'email' ? '#C9A36D' : '#909399'"></u-icon>
<text class="contact-text">邮箱</text>
</view>
</view>
<u-input v-model="form.contactValue" :placeholder="contactPlaceholder"
:custom-style="inputStyle('contact')" @focus="activeInput = 'contact'" @blur="activeInput = null" />
<text class="section-subtitle">公开模式</text>
<view class="privacy-selector">
<view :class="['privacy-button', form.publicWay === 1 ? 'active' : '']" @click="form.publicWay = 1">
<u-icon name="eye-fill" :color="form.publicWay === 1 ? '#C9A36D' : '#909399'"></u-icon>
<text class="privacy-text">直接公开</text>
</view>
<view :class="['privacy-button', form.publicWay === 2 ? 'active' : '']" @click="form.publicWay = 2">
<u-icon name="chat-fill" :color="form.publicWay === 2 ? '#C9A36D' : '#909399'"></u-icon>
<text class="privacy-text">私信后公开</text>
</view>
<view :class="['privacy-button', form.publicWay === 3 ? 'active' : '']" @click="form.publicWay = 3">
<u-icon name="lock-fill" :color="form.publicWay === 3 ? '#C9A36D' : '#909399'"></u-icon>
<text class="privacy-text">不公开</text>
</view>
</view>
<text class="privacy-notice">隐私保护: 您的联系方式仅对感兴趣的校友可见, 平台内置防骚扰机制, 保护您的隐私安全。</text>
</view>
<view class="card">
<text class="section-title">时效性</text>
<view class="expiry-selector">
<view :class="['expiry-button', form.expiryType === 'longterm' ? 'active' : '']" @click="form.expiryType = 'longterm'">
<u-icon name="clock-fill" :color="form.expiryType === 'longterm' ? '#C9A36D' : '#909399'"></u-icon>
<text class="expiry-text">长期有效</text>
</view>
<view :class="['expiry-button', form.expiryType === 'specific' ? 'active' : '']" @click="form.expiryType = 'specific'">
<u-icon name="calendar-fill" :color="form.expiryType === 'specific' ? '#C9A36D' : '#909399'"></u-icon>
<text class="expiry-text">具体日期</text>
</view>
</view>
<view v-if="form.expiryType === 'specific'" class="date-picker">
<u-form-item label="到期日期" label-width="150" prop="expiryDate" :border-bottom="false">
<u-input v-model="form.expiryDate" placeholder="请选择到期日期" type="text" readonly
:custom-style="inputStyle('expiryDate')" @click="showDatePicker = true" />
</u-form-item>
</view>
</view>
<view class="footer-button">
<u-button type="primary" shape="circle" @click="submit">{{ form.type === 'supply' ? '发布供应' : '发布需求' }}</u-button>
</view>
<!-- 日期选择器 -->
<u-picker mode="time" v-model="showDatePicker" @confirm="onDateConfirm" :params="dateParams"></u-picker>
</view>
</template>
<script>
import uPicker from '@/uview-ui/components/u-picker/u-picker.vue';
export default {
components: {
uPicker
},
data() {
return {
activeInput: null,
tagInput: '',
header: {}, // 添加header对象
form: {
type: 'supply', // supply or demand
title: '',
description: '',
tags: [],
contactType: 'wechat', // wechat, phone, email
contactValue: '',
contactName: '', // 联系人
publicWay: 1, // 公开模式: 1-直接公开, 2-私信后公开, 3-不公开
expiryType: 'longterm', // 时效性: longterm, specific
expiryDate: '', // 具体日期
images: [], // 图片列表
},
showDatePicker: false,
dateParams: {
year: true,
month: true,
day: true,
hour: false,
minute: false,
second: false
}
};
},
computed:{
contactPlaceholder(){
const placeholders = {
wechat: '输入微信号',
phone: '输入电话号码',
email: '输入邮箱地址'
}
return placeholders[this.form.contactType]
}
},
onLoad() {
// 获取当前用户信息,设置默认联系人
const currentUser = this.$store.state.vuex_user;
if (currentUser && currentUser.name) {
this.form.contactName = currentUser.name;
}
// 设置Authorization header参考coursePay.vue的实现
let token = uni.getStorageSync('stbc1_lifeData') ? uni.getStorageSync('stbc1_lifeData').vuex_token : '';
this.header.Authorization = `Bearer ${token}`;
},
methods: {
inputStyle(name) {
const style = {
backgroundColor: '#f7f8fa',
borderRadius: '16rpx',
padding: '18rpx 25rpx',
border: '1px solid #f7f8fa',
transition: 'border-color 0.2s ease',
};
if (this.activeInput === name) {
style.borderColor = '#D4C3AB';
}
return style;
},
addTag() {
if (this.tagInput && !this.form.tags.includes(this.tagInput)) {
this.form.tags.push(this.tagInput);
this.tagInput = '';
}
},
removeTag(index) {
this.form.tags.splice(index, 1);
},
chooseImage() {
uni.chooseImage({
count: 9 - this.form.images.length, // 最多可以选择的图片张数
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: (res) => {
this.form.images.push(...res.tempFilePaths);
}
});
},
removeImage(index) {
this.form.images.splice(index, 1);
},
onDateConfirm(e) {
const year = e.year;
const month = e.month.toString().padStart(2, '0');
const day = e.day.toString().padStart(2, '0');
this.form.expiryDate = `${year}-${month}-${day}`;
this.showDatePicker = false;
},
// 新增图片上传方法
uploadImage(filePath) {
return new Promise((resolve, reject) => {
// 使用uni.uploadFile直接上传参考coursePay.vue的实现
uni.uploadFile({
url: this.$u.http.config.baseUrl + '/api/mobile/upload-file',
filePath: filePath,
name: 'file',
header: this.header, // 使用this.header
success: (uploadRes) => {
try {
const result = JSON.parse(uploadRes.data);
// 根据coursePay.vue的实现直接返回包含id的对象
if (result && result.id) {
resolve(result);
} else if (result.code === 200 && result.data) {
resolve(result.data);
} else {
reject(new Error(result.message || '上传失败'));
}
} catch (e) {
reject(new Error('上传响应解析失败'));
}
},
fail: (error) => {
reject(error);
}
});
});
},
async submit() {
// 表单验证
if (!this.form.title.trim()) {
this.$u.toast('请输入标题');
return;
}
if (!this.form.description.trim()) {
this.$u.toast('请输入详细描述');
return;
}
if (!this.form.contactName.trim()) {
this.$u.toast('请输入联系人');
return;
}
if (!this.form.contactValue.trim()) {
this.$u.toast('请输入联系方式');
return;
}
if (this.form.expiryType === 'specific' && !this.form.expiryDate) {
this.$u.toast('请选择到期日期');
return;
}
// 显示加载提示
uni.showLoading({
title: '发布中...'
});
try {
// 先上传图片文件
const fileIds = [];
if (this.form.images.length > 0) {
for (let i = 0; i < this.form.images.length; i++) {
const imagePath = this.form.images[i];
try {
const uploadResult = await this.uploadImage(imagePath);
// 根据coursePay.vue的实现直接使用result.id
if (uploadResult && uploadResult.id) {
fileIds.push(uploadResult.id);
} else {
throw new Error('未获取到文件ID');
}
} catch (error) {
console.error('图片上传失败:', error);
this.$u.toast(`${i + 1}张图片上传失败`);
return;
}
}
}
// 构建请求参数
const params = {
title: this.form.title,
type: this.form.type === 'supply' ? 1 : 2,
content: this.form.description,
tag: this.form.tags.join(','),
wechat: this.form.contactType === 'wechat' ? this.form.contactValue : '',
mobile: this.form.contactType === 'phone' ? this.form.contactValue : '',
email: this.form.contactType === 'email' ? this.form.contactValue : '',
contact_name: this.form.contactName, // 修改为contact_name
public_way: this.form.publicWay, // 修改为public_way
file_ids: fileIds, // 直接使用数组,不转换为字符串
};
// 只有选择具体日期时才传expire_time
if (this.form.expiryType === 'specific' && this.form.expiryDate) {
params.expire_time = this.form.expiryDate + ' 23:59:59'; // 设置为当天结束时间
}
// 调用保存接口
const res = await this.$u.api.supplyDemandSave(params);
uni.hideLoading();
uni.showToast({
title: '发布成功, 请等待审核',
icon: 'success',
success: () => {
setTimeout(() => {
uni.navigateBack();
}, 1200);
}
});
} catch (error) {
uni.hideLoading();
console.error('发布失败:', error);
this.$u.toast('发布失败,请重试');
}
}
}
}
</script>
<style lang="scss" scoped>
.container {
min-height: 100vh;
background: linear-gradient(to bottom, #e9f2fa, #f5f7fa);
padding: 30rpx 20rpx 140rpx 20rpx;
box-sizing: border-box;
}
.card {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
display: block;
margin-bottom: 30rpx;
}
.section-subtitle {
font-size: 28rpx;
color: #606266;
margin-top: 20rpx;
margin-bottom: 10rpx;
}
.type-selector, .contact-selector, .privacy-selector, .expiry-selector {
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
}
.type-button {
width: 48%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx 0;
transition: all 0.3s;
&.active {
background-color: #fdf3e8;
border: 1rpx solid #e8d1b5;
.type-text {
color: #C9A36D;
}
}
}
.contact-button, .privacy-button, .expiry-button {
width: 31%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx 0;
transition: all 0.3s;
&.active {
background-color: #fdf3e8;
border: 1rpx solid #e8d1b5;
.contact-text, .privacy-text, .expiry-text {
color: #C9A36D;
}
}
}
.type-text, .contact-text, .privacy-text, .expiry-text {
font-size: 28rpx;
margin-left: 10rpx;
color: #606266;
}
.form-tip {
font-size: 24rpx;
color: #909399;
margin-top: 10rpx;
padding-left: 150rpx;
}
.tag-container {
display: flex;
flex-wrap: wrap;
margin-top: 20rpx;
padding-left: 150rpx;
}
.tag-item {
background-color: #f5f5f5;
color: #606266;
padding: 10rpx 20rpx;
border-radius: 12rpx;
margin-right: 15rpx;
margin-bottom: 15rpx;
display: flex;
align-items: center;
text {
margin-right: 10rpx;
}
}
.image-upload {
margin-top: 20rpx;
}
.image-list {
display: flex;
flex-wrap: wrap;
margin-bottom: 10rpx;
}
.image-item {
position: relative;
width: 180rpx;
height: 180rpx;
margin-right: 15rpx;
margin-bottom: 15rpx;
border-radius: 12rpx;
overflow: hidden;
background-color: #f5f5f5;
.uploaded-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.delete-btn {
position: absolute;
top: 5rpx;
right: 5rpx;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 50%;
width: 40rpx;
height: 40rpx;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
}
.upload-btn {
width: 180rpx;
height: 180rpx;
border: 1rpx dashed #C9A36D;
border-radius: 12rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #C9A36D;
font-size: 28rpx;
transition: all 0.3s;
&:active {
background-color: #fdf3e8;
}
}
.upload-text {
margin-top: 10rpx;
}
.upload-tip {
font-size: 24rpx;
color: #909399;
margin-top: 10rpx;
}
.date-picker {
margin-top: 20rpx;
padding-left: 150rpx;
}
.privacy-notice {
display: block;
font-size: 24rpx;
color: #909399;
margin-top: 30rpx;
line-height: 1.6;
}
.footer-button {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
padding: 20rpx 40rpx;
background-color: #fff;
box-sizing: border-box;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
}
// --- Custom Input Styles ---
::v-deep .u-form-item .u-line {
display: none;
}
::v-deep .u-input__body {
background-color: #f7f8fa !important;
border-radius: 16rpx !important;
padding: 18rpx 25rpx !important;
border: 1px solid #f7f8fa !important;
transition: border-color 0.2s ease;
}
::v-deep .u-input--focus .u-input__body {
border-color: #D4C3AB !important;
}
</style>