Files
smt-used-car-h5/pages/index/detilsPro.vue
T
2026-03-11 11:15:33 +08:00

840 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page">
<view class="content">
<!-- 基础信息卡片 -->
<view class="card pro-card">
<view class="heard">
<view class="">
编号:{{detail.code?detail.code:'~'}}
</view>
<!-- <view class="pro-mark">
<text class="pro-mark-text">专业发布</text>
</view>
<view>
<u-icon @click="openPriceDialog()" name="download" size="25" />
</view> -->
</view>
<view class="title-row">
<text class="name">{{ detail.product_name }}</text>
</view>
<view class="kv">
<view class="kv-item">
<text class="k">车价(¥)</text>
<text class="v">{{ detail.price }}</text>
</view>
<view class="kv-item">
<text class="k">车源地</text>
<text class="v">{{ detail.source_place}}</text>
</view>
<view class="kv-item">
<text class="k">上牌时间</text>
<text class="v">{{ detail.plate_time }}</text>
</view>
<view class="kv-item">
<text class="k">排量</text>
<text class="v">{{ detail.displacement }}</text>
</view>
<view class="kv-item">
<text class="k">里程</text>
<text class="v">{{ detail.mileage }}<text v-if="detail.mileage">Km</text></text>
</view>
<view class="kv-item">
<text class="k">马力</text>
<text class="v">{{ detail.power }}</text>
</view>
<view class="kv-item">
<text class="k">车身颜色</text>
<text class="v">{{ detail.body_color }}</text>
</view>
<view class="kv-item">
<text class="k">创建时间</text>
<text class="v">{{ detail.created_at }}</text>
</view>
</view>
</view>
<!-- 车况介绍 -->
<view class="card">
<view class="section-title">车况介绍</view>
<text class="desc" v-if="detail.condition_desc!=''">{{ detail.condition_desc }}</text>
<view class="noneview" v-else>
<img src="/static/image/none.png" alt="">
</view>
</view>
<!-- 视频区域(专业发布特有) -->
<view class="card video-card" v-if="videoList.length > 0">
<view class="section-title">
<u-icon name="video" size="18" color="#2E6CF6"></u-icon>
<text class="title-text">产品视频</text>
<text class="video-count">({{ videoList.length }}个)</text>
</view>
<view class="video-grid">
<view
class="video-item"
v-for="(video, index) in videoList"
:key="index"
@click="playVideo(video)"
>
<video
class="video-player"
:src="video"
:poster="video + '?vframe/jpg/offset/1'"
controls
show-center-play-btn
object-fit="cover"
></video>
</view>
</view>
</view>
<!-- FAQ 问答区域(专业发布特有) -->
<view class="card faq-card" v-if="faqList.length > 0">
<view class="section-title">
<u-icon name="question-circle" size="18" color="#FF972F"></u-icon>
<text class="title-text">常见问题 FAQ</text>
<text class="faq-count">({{ faqList.length }}组)</text>
</view>
<view class="faq-list">
<view class="faq-item" v-for="(faq, index) in faqList" :key="index">
<view class="faq-question">
<text class="faq-label">问:</text>
<text class="faq-text">{{ faq.question }}</text>
</view>
<view class="faq-answer">
<text class="faq-label">答:</text>
<text class="faq-text">{{ faq.answer }}</text>
</view>
</view>
</view>
</view>
<!-- 产品图 -->
<view class="hero">
<view class="section-title">产品图</view>
<view class="img-grid">
<image
v-for="(src, i) in detail.images_pull"
:key="i"
class="grid-img"
:src="src"
mode="aspectFill"
@click="previewImages(i)"
/>
</view>
</view>
<!-- 附件 -->
<view class="hero">
<view class="section-title">附件</view>
<view v-if="detail.attachment_pull.length>0">
<view v-for="(file, i) in detail.attachment_pull" :key="i" class="fujian" @click="reviewFile(file)">
<p>{{file.name}}</p>
<view>查看</view>
</view>
</view>
<view class="noneview" v-else>
<img src="/static/image/none.png" alt="">
</view>
</view>
</view>
<!-- 价格设置弹框 -->
<uni-popup ref="inputDialog" type="dialog">
<view class="price-dialog">
<view class="pd-title">设置价格</view>
<view class="pd-row">
<view class="pd-label">贸易条款</view>
<view class="pd-price-line">
<uni-data-select
v-model="priceForm.trade_term"
class="tradeTerm"
:localdata="tradeTermOptions"
:clear="false"
/>
</view>
</view>
<view class="pd-row">
<view class="pd-label">价格</view>
<view class="pd-price-line">
<view class="pd-input-suffix-wrap">
<input
class="pd-input pd-input-price pd-input-with-suffix"
v-model="priceForm.price"
type="number"
inputmode="decimal"
placeholder="请输入数字"
@input="onPriceInput"
/>
<view class="pd-currency-suffix">
<uni-data-select
v-model="priceForm.currency"
:localdata="currencyOptions"
:clear="false"
/>
</view>
</view>
</view>
</view>
<!-- 语言单选 -->
<view class="pd-row">
<view class="pd-label">语言</view>
<radio-group class="pd-radio-group" @change="onLangChange">
<label class="pd-radio" v-for="l in langOptions" :key="l.value">
<radio :value="l.value" :checked="priceForm.lang === l.value" />
<text class="pd-radio-text">{{ l.text }}</text>
</label>
</radio-group>
</view>
<!-- 操作按钮 -->
<view class="pd-actions">
<view class="pd-btn pd-cancel" @tap="closePriceDialog">取消</view>
<view class="pd-btn pd-confirm" @tap="confirmPriceDialog">确认</view>
</view>
</view>
</uni-popup>
<!-- 视频播放弹窗 -->
<uni-popup ref="videoPopup" type="center">
<view class="video-popup">
<view class="video-popup-close" @click="closeVideoPopup">
<u-icon name="close" size="24" color="#fff"></u-icon>
</view>
<video
class="popup-video-player"
:src="currentVideoUrl"
controls
autoplay
object-fit="contain"
></video>
</view>
</uni-popup>
</view>
</template>
<script>
import { request, showToast } from '@/api/apiT.js'
export default {
data() {
return {
id: '',
appid: '',
detail: {},
currentImageIndex: 0,
currentVideoUrl: '',
priceForm: {
price: "",
currency: "USD",
lang: "en"
},
currencyOptions: [
{ value: "USD", text: "美元" },
{ value: "CNY", text: "人民币" },
{ value: "RUB", text: "卢布" },
{ value: "AED", text: "迪拉姆" },
{ value: "EUR", text: "欧元" }
],
langOptions: [
{ text: "英语", value: "en" },
{ text: "俄语", value: "ru" },
{ text: "西班牙语", value: "es" },
{ text: "阿拉伯语", value: "ar" }
],
tradeTermOptions: [
{ value: "FOB", text: "FOB" },
{ value: "CIF", text: "CIF" },
{ value: "EXW", text: "EXW" },
{ value: "DDP", text: "DDP" },
{ value: "CIP", text: "CIP" },
{ value: "FCA", text: "FCA" },
{ value: "CFR", text: "CFR" }
]
}
},
computed: {
// 处理图片列表
images() {
const raw = this.detail.images_pull || []
if (Array.isArray(raw)) {
return raw.filter(Boolean)
}
if (typeof raw === 'string') {
return raw.split(',').map(s => (s || '').trim()).filter(Boolean)
}
return []
},
// 处理视频列表
videoList() {
// 优先使用 video_pull(完整URL)
if (Array.isArray(this.detail.video_pull) && this.detail.video_pull.length) {
return this.detail.video_pull.filter(Boolean)
}
// 兼容 intro_video 字段
if (!this.detail.intro_video) return []
if (Array.isArray(this.detail.intro_video)) {
return this.detail.intro_video.filter(Boolean)
}
if (typeof this.detail.intro_video === 'string') {
return this.detail.intro_video.split(',').map(s => (s || '').trim()).filter(Boolean)
}
return []
},
// 处理FAQ列表
faqList() {
if (!this.detail.faqs) return []
if (Array.isArray(this.detail.faqs)) {
return this.detail.faqs.filter(f => f.question && f.answer)
}
return []
}
},
onLoad(options) {
this.id = options?.id || ''
const appid = options?.appid || uni.getStorageSync('fsappid') || ''
if (appid) uni.setStorageSync('fsappid', appid)
// 登录检查
const token = uni.getStorageSync('token')
if (!token) {
const redirect = `/pages/index/detilsPro?id=${this.id}${appid ? `&appid=${encodeURIComponent(appid)}` : ''}`
uni.setStorageSync('post_login_redirect', redirect)
uni.reLaunch({
url: `/pages/login/index?appid=${encodeURIComponent(appid)}&redirect=${encodeURIComponent(redirect)}`
})
return
}
this.getshows()
},
methods: {
// 获取专业发布详情
getshows() {
const that = this
request({
url: 'common/usedcardetail/show',
method: 'GET',
data: { id: that.id }
}).then(res => {
if (res.data.code == 200) {
this.detail = res.data.data
}
})
},
// 预览图片
previewImages(startIndex) {
if (!this.images || this.images.length === 0) return
this.currentImageIndex = startIndex || 0
uni.previewImage({
current: this.images[this.currentImageIndex],
urls: this.images
})
},
// 播放视频
playVideo(videoUrl) {
this.currentVideoUrl = videoUrl
this.$refs.videoPopup.open()
},
// 关闭视频弹窗
closeVideoPopup() {
this.$refs.videoPopup.close()
this.currentVideoUrl = ''
},
// 下载文件
downloadByAnchor(url, filename) {
try {
const a = document.createElement('a')
a.href = url
a.download = filename || ''
a.target = '_blank'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
} catch (e) {
if (typeof window !== 'undefined') window.open(url, '_blank')
}
},
// 查看附件
reviewFile(file) {
this.downloadByAnchor(file.src_pull, file.name)
},
// 打开价格弹框
openPriceDialog() {
this.priceForm = {
trade_term: '',
price: "",
currency: "USD",
lang: "en"
}
this.$refs.inputDialog.open()
},
// 关闭价格弹框
closePriceDialog() {
this.$refs.inputDialog.close()
},
// 价格输入处理
onPriceInput(e) {
let v = (e.detail && e.detail.value) ? String(e.detail.value) : String(this.priceForm.price || "")
v = v.replace(/[^\d.]/g, "")
const parts = v.split(".")
if (parts.length > 2) {
v = parts[0] + "." + parts.slice(1).join("")
}
v = v.replace(/^0+(\d)/, "$1")
if (v.startsWith(".")) v = "0" + v
this.priceForm.price = v
},
// 语言切换
onLangChange(e) {
this.priceForm.lang = e.detail.value
},
// 确认价格弹框
confirmPriceDialog() {
if (!this.priceForm.trade_term) {
return uni.showToast({ title: "请选择贸易条款", icon: "none" })
}
if (!this.priceForm.price) {
return uni.showToast({ title: "请输入价格", icon: "none" })
}
const payload = {
trade_term: this.priceForm.trade_term,
price: this.priceForm.price,
currency: this.priceForm.currency,
lang: this.priceForm.lang
}
this.closePriceDialog()
this.dialogInputConfirm(payload)
},
// 生成PDF
dialogInputConfirm(val) {
if (!val.price) {
uni.showToast({ title: '请输入价格', icon: 'none' })
return
}
request({
url: 'common/usedcardetail/produce/pdf',
method: 'get',
data: {
id: this.id,
price: val.price,
currency: val.currency,
lang: val.lang,
trade_term: val.trade_term
}
}).then(res => {
if (res.data.code == 200) {
this.downloadpdf(this.id)
} else {
uni.showToast({ title: res.data.msg || '生成失败', icon: 'none' })
}
})
},
// 下载PDF
downloadpdf(item) {
request({
url: 'common/usedcardetail/pdf',
method: 'get',
data: { id: item }
}).then(res => {
if (res.data.code == 200) {
this.downloadByAnchor(res.data.data.pdf_url, res.data.data.file_name)
}
})
}
}
}
</script>
<style scoped lang="scss">
.page {
min-height: 100vh;
background: #f6f7fb;
}
.content {
padding: 18rpx;
box-sizing: border-box;
}
// 专业发布卡片样式
.pro-card {
background: linear-gradient(135deg, #fff 0%, #f8f9ff 100%);
border: 1px solid rgba(46, 108, 246, 0.1);
}
.heard {
padding: 5px 0px;
color: #666;
font-size: 12px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 10rpx;
}
// 专业发布标记
.pro-mark {
padding: 4rpx 12rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #2E6CF6 0%, #1a5dd9 100%);
box-shadow: 0 4rpx 12rpx rgba(46, 108, 246, 0.3);
.pro-mark-text {
font-size: 20rpx;
color: #fff;
font-weight: 600;
}
}
.card {
margin-top: 16rpx;
background: #fff;
border-radius: 18rpx;
padding: 18rpx 18rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
box-sizing: border-box;
}
// 视频卡片样式
.video-card {
border: 1px solid rgba(46, 108, 246, 0.15);
}
// FAQ卡片样式
.faq-card {
border: 1px solid rgba(255, 151, 47, 0.15);
}
.title-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.name {
font-size: 34rpx;
font-weight: 700;
color: #111827;
line-height: 1.2;
}
.kv {
display: flex;
flex-direction: column;
}
.kv-item {
padding: 16rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #f1f5f9;
}
.kv-item:last-child {
border-bottom: none;
padding-bottom: 0;
}
.k {
width: 180rpx;
font-size: 26rpx;
color: #374151;
flex-shrink: 0;
}
.v {
font-size: 26rpx;
color: #111827;
text-align: right;
flex: 1;
margin-left: 12rpx;
}
.section-title {
font-size: 28rpx;
font-weight: 700;
color: #111827;
margin-bottom: 10rpx;
display: flex;
align-items: center;
gap: 8rpx;
.title-text {
flex: 1;
}
.video-count,
.faq-count {
font-size: 22rpx;
color: #999;
font-weight: 400;
}
}
.desc {
font-size: 26rpx;
color: #374151;
line-height: 1.7;
white-space: pre-wrap;
}
.hero {
position: relative;
border-radius: 18rpx;
overflow: hidden;
margin-top: 16rpx;
background: #fff;
padding: 18rpx 18rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.06);
}
.img-grid {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
padding: 10rpx;
box-sizing: border-box;
}
.grid-img {
width: calc((100% - 20rpx) / 3);
height: 220rpx;
border-radius: 14rpx;
background: #f3f4f6;
}
// 视频网格样式
.video-grid {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.video-item {
width: calc((100% - 16rpx) / 2);
height: 240rpx;
border-radius: 14rpx;
overflow: hidden;
background: #000;
}
.video-player {
width: 100%;
height: 100%;
}
// FAQ列表样式
.faq-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.faq-item {
padding: 16rpx;
border-radius: 12rpx;
background: linear-gradient(135deg, rgba(255, 151, 47, 0.05) 0%, rgba(255, 151, 47, 0.02) 100%);
border: 1px solid rgba(255, 151, 47, 0.1);
}
.faq-question,
.faq-answer {
display: flex;
align-items: flex-start;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
}
.faq-label {
font-size: 24rpx;
font-weight: 600;
color: #FF972F;
flex-shrink: 0;
margin-right: 8rpx;
}
.faq-text {
flex: 1;
font-size: 24rpx;
color: #374151;
line-height: 1.6;
}
.fujian {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #ebebeb;
padding: 8px 10px;
border-radius: 4px;
}
.noneview {
text-align: center;
}
// 价格弹框样式
.tradeTerm {
border: 1px solid #eae9e9;
border-radius: 6px;
}
.price-dialog {
width: 640rpx;
background: #fff;
border-radius: 24rpx;
padding: 28rpx;
}
.pd-title {
font-size: 34rpx;
font-weight: 600;
color: #111;
margin-bottom: 20rpx;
text-align: center;
}
.pd-row {
display: flex;
align-items: center;
margin-top: 40rpx;
}
.pd-label {
width: 120rpx;
font-size: 28rpx;
color: #333;
}
.pd-input {
flex: 1;
height: 66rpx;
border: 1px solid #eee;
border-radius: 16rpx;
padding: 0 18rpx;
font-size: 30rpx;
}
.pd-input-suffix-wrap {
position: relative;
flex: 1;
}
.pd-input-with-suffix {
padding-right: 180rpx;
}
.pd-currency-suffix {
position: absolute;
right: 14rpx;
top: 50%;
transform: translateY(-50%);
width: 140rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: flex-end;
}
::v-deep .uni-select {
border: none !important;
}
.pd-radio-group {
display: flex;
flex-wrap: wrap;
gap: 16rpx 22rpx;
margin-top: 12rpx;
}
.pd-radio {
display: flex;
align-items: center;
}
.pd-radio-text {
margin-left: 10rpx;
font-size: 26rpx;
color: #333;
}
.pd-actions {
display: flex;
gap: 18rpx;
margin-top: 40rpx;
}
.pd-btn {
flex: 1;
height: 84rpx;
border-radius: 18rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
}
.pd-cancel {
background: #f5f5f5;
color: #333;
}
.pd-confirm {
background: #3F435B;
color: #fff;
font-weight: 600;
}
.pd-price-line {
flex: 1;
display: flex;
align-items: center;
gap: 16rpx;
}
.pd-input-price {
flex: 1;
min-width: 240rpx;
}
// 视频弹窗样式
.video-popup {
position: relative;
width: 680rpx;
height: 480rpx;
background: #000;
border-radius: 20rpx;
overflow: hidden;
}
.video-popup-close {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.popup-video-player {
width: 100%;
height: 100%;
}
</style>