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.

758 lines
20 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" v-if="detail">
<scroll-view scroll-y="true" class="scroll-container">
<view class="user-info-header">
<u-avatar :src="detail.user && detail.user.headimgurl || 'https://via.placeholder.com/80'" size="80"></u-avatar>
<view class="user-details">
<text class="user-name">{{ detail.user && detail.user.name || detail.user && detail.user.nickname || '匿名用户' }}</text>
<text class="post-time">{{ detail.created_at }}</text>
</view>
<view class="stats">
<text class="type-badge" :class="detail.type === 1 ? 'supply' : detail.type === 2 ? 'demand' : detail.type === 3 ? 'finance' : 'industry'">{{ detail.type === 1 ? '供应' : detail.type === 2 ? '需求' : detail.type === 3 ? '投融资' : '' }}</text>
<text class="views">{{ detail.contact_count }}人私信 {{ detail.view_count }}浏览</text>
</view>
</view>
<view class="content-card">
<text class="title">{{ detail.title }}</text>
<!-- 投融资专用信息展示 -->
<view v-if="detail.type === 3" class="finance-info">
<view class="finance-row">
<text class="label">资金类型</text>
<text class="value">{{ detail.fund_type || '-' }}</text>
</view>
<view class="finance-row">
<text class="label">金额</text>
<text class="value">{{ formatAmount(detail.amount) }}万元</text>
</view>
<view class="finance-row" v-if="detail.fund_stage">
<text class="label">融资阶段</text>
<text class="value">{{ detail.fund_stage }}</text>
</view>
<view class="finance-row" v-if="detail.fund_company">
<text class="label">公司名称</text>
<text class="value">{{ detail.fund_company }}</text>
</view>
<view class="finance-row" v-if="detail.industry_type">
<text class="label">行业类型</text>
<text class="value">{{ detail.industry_type }}</text>
</view>
<view class="finance-row" v-if="detail.product">
<text class="label">主要产品</text>
<text class="value">{{ detail.product }}</text>
</view>
</view>
<text class="description">{{ detail.type === 3 ? (detail.desc || detail.content) : detail.content }}</text>
<!-- 图片展示区域 -->
<view class="images-container" v-if="detail.files && detail.files.length > 0">
<view class="images-title">
<u-icon name="photo-fill" color="#cba579" size="24"></u-icon>
<text class="images-title-text">相关图片 ({{ imageFiles.length }})</text>
</view>
<view class="images-grid" v-if="imageFiles.length > 0">
<view v-for="(file, index) in imageFiles" :key="file.id" class="image-item" @click="previewImage(file, index)">
<image :src="file.url" mode="aspectFill" class="content-image"></image>
<view class="image-overlay">
<u-icon name="eye-fill" color="#fff" size="20"></u-icon>
</view>
</view>
</view>
</view>
<view class="tags" v-if="detail.tag && detail.type !== 3">
<text v-for="tag in detail.tag.split(',')" :key="tag" class="tag">{{ tag }}</text>
</view>
<!-- 过期提示 -->
<view v-if="isExpired" class="expired-badge">
<u-icon name="clock-fill" color="#909399" size="24"></u-icon>
<text class="expired-badge-text">已过期</text>
</view>
</view>
<!-- 联系方式卡片 -->
<view class="contact-card" v-if="hasContactInfo">
<view class="contact-header">
<u-icon name="phone-fill" color="#cba579" size="32"></u-icon>
<text class="contact-title">联系方式</text>
</view>
<view class="contact-content" v-if="!needMessageToShowContact">
<!-- 联系人信息 -->
<view class="contact-item" v-if="detail.user">
<view class="contact-label">
<u-icon name="account-fill" color="#cba579" size="24"></u-icon>
<text class="label-text">联系人</text>
</view>
<text class="contact-value">{{ detail.user.name || detail.user.nickname || '匿名用户' }}</text>
</view>
<view class="contact-item" v-if="detail.wechat">
<view class="contact-label">
<u-icon name="chat-fill" color="#07c160" size="24"></u-icon>
<text class="label-text">微信</text>
</view>
<text class="contact-value wechat-clickable" @click="openWechat(detail.wechat)">{{ detail.wechat }}</text>
</view>
<view class="contact-item" v-if="detail.mobile">
<view class="contact-label">
<u-icon name="phone-fill" color="#007aff" size="24"></u-icon>
<text class="label-text">电话</text>
</view>
<text class="contact-value phone-clickable" @click="makePhoneCall(detail.mobile)">{{ detail.mobile }}</text>
</view>
<view class="contact-item" v-if="detail.email">
<view class="contact-label">
<u-icon name="email-fill" color="#ff6b35" size="24"></u-icon>
<text class="label-text">邮箱</text>
</view>
<text class="contact-value">{{ detail.email }}</text>
</view>
</view>
<view class="contact-content" v-else>
<view class="contact-placeholder">
<u-icon name="lock-fill" color="#c0c4cc" size="48"></u-icon>
<text class="placeholder-text">私信后自动公开联系方式</text>
</view>
</view>
</view>
<!-- 底部占位避免内容被footer遮挡 -->
<view class="bottom-placeholder"></view>
</scroll-view>
<view class="footer">
<!-- <view class="footer-action" @click="toggleCollect">
<u-icon :name="isCollected ? 'star-fill' : 'star'" size="40" :color="isCollected ? '#f29100' : '#606266'"></u-icon>
<text :style="{ color: isCollected ? '#f29100' : '#606266' }">收藏</text>
</view> -->
<u-button v-if="!isExpired" type="primary" shape="circle" class="message-btn" @click="goToChat">私信</u-button>
<view v-else class="expired-notice">
<text class="expired-text"></text>
</view>
</view>
</view>
</template>
<script>
import uAvatar from '@/uview-ui/components/u-avatar/u-avatar.vue';
import uIcon from '@/uview-ui/components/u-icon/u-icon.vue';
import uButton from '@/uview-ui/components/u-button/u-button.vue';
export default {
components: {
uAvatar,
uIcon,
uButton
},
data() {
return {
detail: null,
isCollected: false
}
},
computed: {
hasContactInfo() {
if (!this.detail) return false;
// 根据public_way字段控制联系方式的显示
switch (this.detail.public_way) {
case 1: // 直接公开
return this.detail.user || this.detail.wechat || this.detail.mobile || this.detail.email;
case 2: // 私信后自动公开
return this.detail.user || this.detail.wechat || this.detail.mobile || this.detail.email;
case 3: // 不公开
return false;
default:
return this.detail.user || this.detail.wechat || this.detail.mobile || this.detail.email;
}
},
// 新增计算属性:是否需要私信后查看联系方式
needMessageToShowContact() {
// 如果已经私信过messages_count > 0则显示联系方式
if (this.detail && this.detail.messages_count > 0) {
return false;
}
// 否则根据public_way判断是否需要私信后查看
return this.detail && this.detail.public_way === 2;
},
// 新增计算属性:判断是否已过期
isExpired() {
if (!this.detail || !this.detail.expire_time) {
return false;
}
// 使用moment比较当前时间和到期时间
const currentTime = this.$moment();
const expireTime = this.$moment(this.detail.expire_time);
return currentTime.isAfter(expireTime);
},
// 新增计算属性:过滤出图片类型的文件
imageFiles() {
if (!this.detail || !this.detail.files) return [];
return this.detail.files.filter(f => {
const ext = f.extension ? f.extension.toLowerCase() : '';
return ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'].includes(ext);
});
}
},
onLoad(options) {
console.log("页面ID:", options.id);
this.fetchDetailData(options.id);
},
methods: {
formatAmount(val){
if(val==null||val==='') return '-'
const num = Number(val)
if(isNaN(num)) return String(val)
return num.toLocaleString(undefined,{minimumFractionDigits:2, maximumFractionDigits:2})
},
fetchDetailData(id) {
this.$u.api.supplyDemandDetail({ id: id }).then(res => {
console.log('详情数据:', res);
if (res && res.id) {
this.detail = res;
} else {
this.$u.toast('获取详情失败');
}
}).catch(err => {
console.error('获取详情失败:', err);
this.$u.toast('网络错误,请重试');
});
},
toggleCollect() {
this.isCollected = !this.isCollected;
this.$u.toast(this.isCollected ? '收藏成功' : '取消收藏');
},
goToChat() {
if (this.detail && this.detail.user && this.detail.user.id) {
const userName = this.detail.user.name || this.detail.user.nickname || '匿名用户';
uni.navigateTo({
url: `/packages/chat/chatWindow?userId=${this.detail.user.id}&userName=${userName}`
});
// 如果是私信后自动公开的情况,且还没有私信过,显示联系方式
if (this.detail.public_way === 2 && this.detail.messages_count === 0) {
this.$u.toast('私信后联系方式将自动公开');
}
} else {
this.$u.toast('用户信息不完整');
}
},
// 新增方法:预览图片
previewImage(file, index) {
if (this.imageFiles.length === 0) return;
uni.previewImage({
urls: this.imageFiles.map(f => f.url),
current: file.url,
index: index
});
},
// 新增方法:拨打电话
makePhoneCall(phoneNumber) {
uni.showModal({
title: '拨打电话',
content: `是否拨打 ${phoneNumber}`,
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
phoneNumber: phoneNumber,
success: () => {
console.log('拨号成功');
},
fail: (err) => {
console.error('拨号失败:', err);
this.$u.toast('拨号失败,请重试');
}
});
}
}
});
},
// 新增方法:打开微信
openWechat(wechatId) {
console.log('点击微信,微信号:', wechatId);
// 尝试使用微信的URL Scheme打开微信
const wechatUrl = `weixin://`;
// 检查是否在APP环境中且有plus对象
if (typeof window !== 'undefined' && window.plus && plus.runtime) {
console.log('检测到plus环境尝试打开微信');
plus.runtime.openURL(wechatUrl, (err) => {
if (err) {
console.error('打开微信失败:', err);
this.showWechatCopyDialog(wechatId);
} else {
console.log('微信打开成功');
this.$u.toast('请在微信中搜索并添加好友');
}
});
} else {
console.log('非APP环境或plus不可用直接显示复制对话框');
// 非APP环境直接显示复制对话框
this.showWechatCopyDialog(wechatId);
}
},
// 显示微信复制对话框
showWechatCopyDialog(wechatId) {
console.log('准备显示微信复制对话框,微信号:', wechatId);
// 在小程序中使用showModal
uni.showModal({
title: '添加微信好友',
content: `微信号:${wechatId}\n请复制微信号到微信中搜索添加`,
confirmText: '复制',
cancelText: '取消',
showCancel: true,
success: (res) => {
console.log('showModal success callback:', res);
if (res.confirm) {
console.log('用户点击确认,准备复制微信号');
// 尝试复制微信号到剪贴板
this.tryCopyToClipboard(wechatId);
}
},
fail: (err) => {
console.error('showModal fail callback:', err);
// 如果showModal失败尝试使用toast提示
uni.showToast({
title: `微信号:${wechatId}`,
icon: 'none',
duration: 3000
});
}
});
},
// 尝试复制到剪贴板的方法(适配隐私保护指引)
tryCopyToClipboard(text) {
// 检查小程序隐私协议是否已同意
if (typeof wx !== 'undefined' && wx.requirePrivacyAuthorize) {
// 需要用户同意隐私协议
wx.requirePrivacyAuthorize({
success: () => {
// 用户同意后,执行复制操作
this.executeCopyToClipboard(text);
},
fail: () => {
// 用户拒绝,显示手动复制提示
this.showManualCopyTip(text);
}
});
} else {
// 直接尝试复制
this.executeCopyToClipboard(text);
}
},
// 执行复制到剪贴板
executeCopyToClipboard(text) {
uni.setClipboardData({
data: text,
success: () => {
console.log('复制成功');
uni.showToast({
title: '微信号已复制到剪贴板',
icon: 'success',
duration: 2000
});
},
fail: (err) => {
console.error('复制失败:', err);
// 如果复制失败,显示手动复制的提示
this.showManualCopyTip(text);
}
});
},
// 显示手动复制提示
showManualCopyTip(text) {
uni.showModal({
title: '复制失败',
content: `微信号:${text}\n\n由于权限限制请手动复制微信号到微信中搜索添加`,
confirmText: '知道了',
showCancel: false,
success: () => {
// 可以尝试使用其他方式提示用户
uni.showToast({
title: '请手动复制微信号',
icon: 'none',
duration: 3000
});
}
});
}
}
}
</script>
<style lang="scss" scoped>
.container {
background: linear-gradient(to bottom, #e9f2fa, #f5f7fa);
height: 100vh;
display: flex;
flex-direction: column;
}
.scroll-container {
flex: 1;
height: calc(100vh - 120rpx); /* 减去footer的高度 */
}
.user-info-header {
display: flex;
align-items: center;
padding: 30rpx;
background: transparent;
}
.user-details {
display: flex;
flex-direction: column;
margin-left: 20rpx;
flex-grow: 1;
}
.user-name {
font-size: 32rpx;
font-weight: bold;
}
.post-time {
font-size: 24rpx;
color: #909399;
margin-top: 5rpx;
}
.stats {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.type-badge {
font-size: 24rpx;
padding: 8rpx 15rpx;
border-radius: 10rpx;
}
.type-badge.supply {
background-color: #fff0e6;
color: #f29100;
}
.type-badge.demand {
background-color: #e6f0ff;
color: #007aff;
}
.type-badge.finance {
background-color: #e6f0ff;
color: #007aff;
}
.type-badge.industry {
background-color: #e6f0ff;
color: #007aff;
}
.views {
font-size: 24rpx;
color: #909399;
margin-top: 10rpx;
}
.content-card {
background-color: #fff;
margin: 30rpx 30rpx 0;
padding: 30rpx;
border-radius: 20rpx;
}
/* 投融资信息块 */
.finance-info {
background: #fffaf3;
border: 1rpx solid #f5e6cd;
border-radius: 14rpx;
padding: 20rpx;
margin-bottom: 20rpx;
}
.finance-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12rpx 0;
border-bottom: 1rpx solid #f5f0e6;
}
.finance-row:last-child { border-bottom: none; }
.finance-row .label { color: #8a6d3b; font-size: 26rpx; }
.finance-row .value { color: #333; font-weight: 500; font-size: 28rpx; }
.title {
font-size: 36rpx;
font-weight: bold;
display: block;
margin-bottom: 20rpx;
}
.description {
font-size: 28rpx;
color: #606266;
line-height: 1.8;
display: block;
margin-bottom: 30rpx;
}
.content-image {
width: 100%;
border-radius: 12rpx;
margin-bottom: 30rpx;
}
.images-container {
margin-top: 30rpx;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
}
.images-title {
display: flex;
align-items: center;
margin-bottom: 20rpx;
padding-left: 10rpx;
}
.images-title-text {
font-size: 28rpx;
color: #333;
margin-left: 10rpx;
font-weight: 500;
}
.images-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160rpx, 1fr));
gap: 15rpx;
padding: 0 5rpx;
}
.image-item {
position: relative;
width: 100%;
height: 160rpx;
border-radius: 12rpx;
overflow: hidden;
background-color: #f5f5f5;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
cursor: pointer;
}
.image-item:active {
transform: scale(0.95);
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
}
.image-item image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.image-item:active image {
transform: scale(1.05);
}
.image-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.3) 100%);
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.image-item:active .image-overlay {
opacity: 1;
}
.tags {
display: flex;
flex-wrap: wrap;
}
.tag {
background-color: #f5f5f5;
color: #606266;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
margin-right: 15rpx;
margin-bottom: 10rpx;
}
.expired-badge {
display: flex;
align-items: center;
background-color: #f5f5f5;
color: #909399;
font-size: 24rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
margin-top: 20rpx;
width: fit-content;
}
.expired-badge-text {
margin-left: 8rpx;
color: #909399;
}
.contact-card {
background-color: #fff;
margin: 30rpx 30rpx 0;
padding: 30rpx;
border-radius: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.contact-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 15rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.contact-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-left: 10rpx;
}
.contact-content {
display: flex;
flex-direction: column;
}
.contact-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
border-bottom: 1rpx solid #f8f8f8;
}
.contact-item:last-child {
border-bottom: none;
}
.contact-label {
display: flex;
align-items: center;
}
.label-text {
font-size: 28rpx;
color: #606266;
margin-left: 10rpx;
font-weight: 500;
}
.contact-value {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.phone-clickable {
color: #007aff;
text-decoration: underline;
cursor: pointer;
transition: color 0.3s ease;
}
.phone-clickable:active {
color: #0056b3;
}
.wechat-clickable {
color: #07c160;
text-decoration: underline;
cursor: pointer;
transition: color 0.3s ease;
}
.wechat-clickable:active {
color: #06ad56;
}
.contact-placeholder {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 0;
color: #c0c4cc;
}
.placeholder-text {
margin-top: 15rpx;
font-size: 28rpx;
color: #c0c4cc;
}
.bottom-placeholder {
height: 140rpx; /* 为footer预留足够空间 */
}
.footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 70rpx;
background-color: #fff;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
box-sizing: border-box;
z-index: 999;
height: 120rpx;
}
.footer-action {
display: flex;
flex-direction: column;
align-items: center;
color: #606266;
font-size: 24rpx;
}
.message-btn {
width: 250rpx;
}
.expired-notice {
display: flex;
align-items: center;
justify-content: center;
width: 250rpx;
height: 80rpx;
background-color: #f5f5f5;
border-radius: 40rpx;
border: 1rpx solid #e4e7ed;
}
.expired-text {
font-size: 28rpx;
color: #909399;
font-weight: 500;
}
</style>