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.

680 lines
22 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 :class="['type-button', form.type === 'finance' ? 'active' : '']" @click="form.type = 'finance'">
<u-icon name="rmb-circle" :color="form.type === 'finance' ? '#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>
<!-- 投融资专用字段 -->
<template v-if="form.type === 'finance'">
<u-form-item label="资金类型" label-width="150" prop="fund_type" :border-bottom="false">
<u-radio-group v-model="form.fund_type">
<u-radio name="投资">投资</u-radio>
<u-radio name="融资">融资</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="金额" label-width="150" prop="amount" :border-bottom="false">
<u-input v-model="form.amount" type="number" placeholder="请输入金额(万元)" :custom-style="inputStyle('amount')" @focus="activeInput='amount'" @blur="activeInput=null" />
</u-form-item>
<u-form-item label="融资阶段" label-width="150" prop="fund_stage" :border-bottom="false">
<u-input v-model="form.fund_stage" placeholder="如:天使轮/A轮/B轮..." :custom-style="inputStyle('fund_stage')" @focus="activeInput='fund_stage'" @blur="activeInput=null" />
</u-form-item>
<u-form-item label="公司名称" label-width="150" prop="fund_company" :border-bottom="false">
<u-input v-model="form.fund_company" placeholder="请输入公司名称" :custom-style="inputStyle('fund_company')" @focus="activeInput='fund_company'" @blur="activeInput=null" />
</u-form-item>
<u-form-item label="行业类型" label-width="150" prop="industry_type" :border-bottom="false">
<u-input v-model="form.industry_type" placeholder="如:智能制造/生物医药..." :custom-style="inputStyle('industry_type')" @focus="activeInput='industry_type'" @blur="activeInput=null" />
</u-form-item>
<u-form-item label="主要产品" label-width="150" prop="product" :border-bottom="false">
<u-input v-model="form.product" placeholder="请输入主要产品" :custom-style="inputStyle('product')" @focus="activeInput='product'" @blur="activeInput=null" />
</u-form-item>
<u-form-item label="简要描述" label-width="150" prop="finance_desc" :border-bottom="false">
<u-input v-model="form.finance_desc" type="textarea" placeholder="请简要描述投融资需求..." :maxlength="500" height="200" :custom-style="inputStyle('finance_desc')" @focus="activeInput='finance_desc'" @blur="activeInput=null" />
</u-form-item>
</template>
<!-- 非投融资:沿用原有描述/标签 -->
<template v-else>
<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>
</template>
</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">更新并发布</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对象
id:'',
form: {
type: 'supply', // supply or demand or finance or industry
title: '',
description: '',
// 投融资字段
fund_type: '投资',
amount: '',
fund_stage: '',
fund_company: '',
industry_type: '',
product: '',
finance_desc: '',
tags: [],
contactType: 'wechat', // wechat, phone, email
contactValue: '',
contactName: '', // 联系人
publicWay: 1, // 公开模式: 1-直接公开, 2-私信后公开, 3-不公开
expiryType: 'longterm', // 时效性: longterm, specific
expiryDate: '', // 具体日期
images: [], // 图片列表
},
existingFiles: [], // 已存在的文件信息包含id和url
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(options) {
this.id = options.id;
if(this.id){
this.getDetail();
}
// 获取当前用户信息,设置默认联系人
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) => {
// 新选择的图片添加到images数组中
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 getDetail() {
const res = await this.$u.api.supplyDemandDetail({
id: this.id
});
// 映射详情数据到表单字段
this.form = {
type: res.type === 1 ? 'supply' : res.type === 2 ? 'demand' : res.type === 3 ? 'finance' : 'industry',
title: res.title || '',
description: res.content || '',
// 投融资字段映射
fund_type: res.fund_type || '投资',
amount: res.amount || '',
fund_stage: res.fund_stage || '',
fund_company: res.fund_company || '',
industry_type: res.industry_type || '',
product: res.product || '',
finance_desc: res.desc || res.content || '',
tags: res.tag ? res.tag.split(',').filter(t => t.trim()) : [],
contactType: res.wechat ? 'wechat' : res.mobile ? 'phone' : res.email ? 'email' : 'wechat',
contactValue: res.wechat || res.mobile || res.email || '',
contactName: res.contact_name || '',
publicWay: res.public_way || 1,
expiryType: res.expire_time ? 'specific' : 'longterm',
expiryDate: res.expire_time ? res.expire_time.split(' ')[0] : '',
images: res.files ? res.files.map(f => f.url) : []
};
// 保存已存在的文件信息
this.existingFiles = res.files ? res.files.map(f => ({
id: f.id,
url: f.url
})) : [];
},
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];
// 检查是否是已存在的文件
const existingFile = this.existingFiles.find(f => f.url === imagePath);
if (existingFile) {
// 使用已存在文件的ID
fileIds.push(existingFile.id);
} else {
// 新上传的文件
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 : this.form.type === 'demand' ? 2 : this.form.type === 'finance' ? 3 : 4,
content: this.form.type === 'finance' ? this.form.finance_desc : this.form.description,
tag: this.form.type === 'finance' ? '' : 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, // 直接使用数组,不转换为字符串
};
// 投融资字段追加
if (this.form.type === 'finance') {
params.fund_type = this.form.fund_type || ''
params.amount = this.form.amount || ''
params.fund_stage = this.form.fund_stage || ''
params.fund_company = this.form.fund_company || ''
params.industry_type = this.form.industry_type || ''
params.product = this.form.product || ''
params.desc = this.form.finance_desc || ''
}
// 只有选择具体日期时才传expire_time
if (this.form.expiryType === 'specific' && this.form.expiryDate) {
params.expire_time = this.form.expiryDate + ' 23:59:59'; // 设置为当天结束时间
}
// 调用保存接口
if(this.id){
params.id = this.id;
}
params.status = 1
const res = await this.$u.api.supplyDemandSave(params);
uni.hideLoading();
uni.showToast({
title: this.id ? '更新成功' : '发布成功',
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>