1406 lines
33 KiB
Vue
1406 lines
33 KiB
Vue
<template>
|
||
<view class="pages">
|
||
<view class="content">
|
||
<!-- 产品名称(带绑定功能) -->
|
||
<view class="cell">
|
||
<view>产品名称 <text class="red">*</text></view>
|
||
<view class="right right-bind">
|
||
<input class="inp" placeholder="请输入车辆的品牌 系列查找车型" v-model="carinfo.product_name" />
|
||
<view class="bind-btn" @tap.stop="onBindByName">绑定</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 绑定结果提示 -->
|
||
<view v-if="bindState.boundProduct" class="bind-tip">
|
||
<text class="bind-tip-text">已绑定:{{ bindState.boundProduct.name || bindState.boundProduct.product_name || bindState.boundProduct.title || '-' }}</text>
|
||
<text class="bind-tip-clear" @tap.stop="clearBind">清除</text>
|
||
</view>
|
||
<view v-else class="bind-tip">
|
||
<text>例:比亚迪海狮07DM</text>
|
||
</view>
|
||
|
||
<!-- 基本信息字段 -->
|
||
<view class="cell">
|
||
<view>车价(¥)</view>
|
||
<view class="right">
|
||
<input placeholder="请输入车价" v-model="carinfo.price" />
|
||
</view>
|
||
</view>
|
||
<view class="cell">
|
||
<view>车源地</view>
|
||
<view class="right">
|
||
<input placeholder="请输入车源地" v-model="carinfo.source_place" />
|
||
</view>
|
||
</view>
|
||
<view class="cell">
|
||
<view>上牌时间</view>
|
||
<view class="right">
|
||
<uni-datetime-picker
|
||
ref="borrowTimePicker"
|
||
type="date"
|
||
v-model="carinfo.plate_time"
|
||
placeholder="请选择时间"
|
||
:border="false"
|
||
:clear-icon="false"
|
||
@change="onBorrowTimeChange"
|
||
>
|
||
<view class="custom-datetime-input">
|
||
<text class="custom-datetime-text" :class="{ 'placeholder': !carinfo.plate_time }">
|
||
{{ carinfo.plate_time || '请选择时间' }}
|
||
</text>
|
||
<uni-icons type="calendar" size="16" color="#999"></uni-icons>
|
||
</view>
|
||
</uni-datetime-picker>
|
||
</view>
|
||
</view>
|
||
<view class="cell">
|
||
<view>排量</view>
|
||
<view class="right">
|
||
<input placeholder="请输入排量" v-model="carinfo.displacement" />
|
||
</view>
|
||
</view>
|
||
<view class="cell">
|
||
<view>马力</view>
|
||
<view class="right">
|
||
<input placeholder="请输入马力" v-model="carinfo.power" />
|
||
</view>
|
||
</view>
|
||
<view class="cell">
|
||
<view>里程(Km)</view>
|
||
<view class="right">
|
||
<input placeholder="请输入里程" v-model="carinfo.mileage" />
|
||
</view>
|
||
</view>
|
||
<view class="cell">
|
||
<view>车身颜色</view>
|
||
<view class="right">
|
||
<input placeholder="请输入车身颜色" v-model="carinfo.body_color" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 产品图上传 -->
|
||
<view class="section-title">
|
||
<text class="title-text">产品图</text>
|
||
</view>
|
||
<view class="cont">
|
||
<view class="media-upload-wrapper">
|
||
<!-- #ifdef H5 -->
|
||
<view class="h5-picker-grid">
|
||
<view class="h5-thumb" v-for="(u, idx) in imagesurl" :key="idx">
|
||
<image :src="u" mode="aspectFill" class="h5-thumb-img"></image>
|
||
<view class="h5-thumb-del" @tap.stop="delfn({ index: idx })">删除</view>
|
||
</view>
|
||
<view class="upload-icon-wrapper" @tap="pickImagesH5">
|
||
<image src="/static/image/img.png" class="addimg"></image>
|
||
</view>
|
||
</view>
|
||
<!-- #endif -->
|
||
<!-- #ifndef H5 -->
|
||
<uni-file-picker ref="mediaPicker" :limit="maxMedia" fileMediatype="image" @select="selectMedia" @delete="delfn" >
|
||
<view class="upload-icon-wrapper">
|
||
<image src="/static/image/img.png" class="addimg"></image>
|
||
</view>
|
||
</uni-file-picker>
|
||
<!-- #endif -->
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 车况介绍 -->
|
||
<view class="section-title">
|
||
<text class="title-text">车况介绍</text>
|
||
</view>
|
||
<view class="cont">
|
||
<view class="">
|
||
<textarea auto-height v-model="carinfo.condition_desc" placeholder="请输入车况介绍"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ========== 视频上传(专业发布专属功能)========== -->
|
||
<view class="section-title">
|
||
<text class="title-text">视频介绍</text>
|
||
</view>
|
||
|
||
<!-- 已上传视频列表 -->
|
||
<view class="video-list" v-if="videoList.length > 0">
|
||
<view class="video-item" v-for="(video, index) in videoList" :key="index">
|
||
<video
|
||
:src="video.url"
|
||
class="video-preview"
|
||
:show-center-play-btn="true"
|
||
:controls="true"
|
||
></video>
|
||
<view class="video-delete" @tap="deleteVideo(index)">
|
||
<uni-icons type="clear" size="20" color="#fff"></uni-icons>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 上传视频按钮 -->
|
||
<view class="video-upload-btn" @tap="chooseVideo">
|
||
<uni-icons type="videocam" size="40" color="#999"></uni-icons>
|
||
<text class="upload-text">点击上传视频</text>
|
||
</view>
|
||
|
||
<!-- 附件上传 -->
|
||
<view class="section-title">
|
||
<text class="title-text">附件</text>
|
||
</view>
|
||
<view class="cont">
|
||
<view class="media-upload-wrapper">
|
||
<uni-file-picker ref="mediaPicker1" limit="20" fileMediatype="all" @select="selectMedia1" @delete="delfn1" >
|
||
<view class="upload-icon-wrapper">
|
||
<image src="/static/image/photo.png" mode="" class="addimg"></image>
|
||
</view>
|
||
</uni-file-picker>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ========== FAQ 问题答案设置(专业发布专属功能)========== -->
|
||
<view class="faq-section">
|
||
<view class="section-title">
|
||
<text class="title-text">FAQ</text>
|
||
</view>
|
||
|
||
<!-- FAQ 列表 -->
|
||
<view class="faq-list">
|
||
<view
|
||
class="faq-item"
|
||
v-for="(faq, index) in faqList"
|
||
:key="index"
|
||
>
|
||
<view class="faq-header">
|
||
<text class="faq-index">Q{{ index + 1 }}</text>
|
||
<view class="faq-actions">
|
||
<text class="faq-delete" @tap="deleteFaq(index)" v-if="faqList.length > 1">删除</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="faq-input-group">
|
||
<view class="faq-label">问题</view>
|
||
<input
|
||
class="faq-input"
|
||
v-model="faq.question"
|
||
placeholder="请输入问题,如:车辆是否有事故?"
|
||
/>
|
||
</view>
|
||
|
||
<view class="faq-input-group">
|
||
<view class="faq-label">答案</view>
|
||
<textarea
|
||
class="faq-textarea"
|
||
v-model="faq.answer"
|
||
placeholder="请输入答案"
|
||
auto-height
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 添加 FAQ 按钮 -->
|
||
<view class="add-faq-btn" @tap="addFaq">
|
||
<uni-icons type="plus" size="18" color="#2E6CF6"></uni-icons>
|
||
<text class="add-faq-text">添加问题</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部提交按钮 -->
|
||
<view class="zw">
|
||
<button class="btn" @click="updateSubmit">保存修改</button>
|
||
</view>
|
||
|
||
<!-- 绑定产品弹框 -->
|
||
<view v-if="bindState.showDialog" class="bind-mask" @tap="closeBindDialog">
|
||
<view class="bind-dialog" @tap.stop>
|
||
<view class="bind-dialog-head">
|
||
<text class="bind-dialog-title">选择要绑定的产品</text>
|
||
<text class="bind-dialog-close" @tap="closeBindDialog">×</text>
|
||
</view>
|
||
|
||
<!-- 弹框内搜索 -->
|
||
<view class="bind-search" v-if="bindState.list && bindState.list.length">
|
||
<input class="bind-search-input" v-model="bindState.keyword" placeholder="搜索产品名称" placeholder-class="bind-search-placeholder" />
|
||
<text v-if="bindState.keyword" class="bind-search-clear" @tap.stop="bindState.keyword = ''">清除</text>
|
||
</view>
|
||
|
||
<scroll-view scroll-y class="bind-dialog-body">
|
||
<view v-if="bindState.loading" class="bind-loading">加载中…</view>
|
||
<view v-else-if="!bindState.list.length" class="bind-empty">未找到匹配的产品</view>
|
||
<view v-else-if="!filteredBindList.length" class="bind-empty">未找到匹配结果</view>
|
||
<view v-else>
|
||
<view
|
||
class="bind-item"
|
||
:class="{ active: bindState.selectedId === getBindItemId(it, idx) }"
|
||
v-for="(it, idx) in filteredBindList"
|
||
:key="getBindItemId(it, idx)"
|
||
@tap="selectBind(it, idx)"
|
||
>
|
||
<view class="bind-item-main">
|
||
<text class="bind-item-title">{{ it.name || it.product_name || it.title || ('产品 ' + (idx + 1)) }}</text>
|
||
<text class="bind-item-sub">{{ formatBindSub(it) }}</text>
|
||
</view>
|
||
<view class="bind-item-check">
|
||
<view class="check-dot" v-if="bindState.selectedId === getBindItemId(it, idx)"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<view class="bind-dialog-foot">
|
||
<view class="btn btn-ghost" @tap="closeBindDialog">取消</view>
|
||
<view class="btn btn-primary" :class="{ disabled: !bindState.selectedId }" @tap="confirmBind">确认绑定</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- #ifdef H5 -->
|
||
<CustomTabBar class="h5-custom-tabbar" />
|
||
<!-- #endif -->
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { request, showToast } from '@/api/apiT.js'
|
||
import CustomTabBar from '@/custom-tab-bar/index.vue'
|
||
|
||
export default {
|
||
components: {
|
||
CustomTabBar
|
||
},
|
||
data() {
|
||
return {
|
||
id: '', // 车辆详情ID
|
||
maxMedia: 20,
|
||
maxVideos: 3, // 最多上传3个视频
|
||
|
||
// 绑定产品状态
|
||
bindState: {
|
||
showDialog: false,
|
||
loading: false,
|
||
list: [],
|
||
selectedId: '',
|
||
boundProduct: null,
|
||
keyword: ''
|
||
},
|
||
|
||
// 图片相关
|
||
imagesurl: [], // 用于显示的图片URL列表(可能是带签名的完整URL)
|
||
|
||
// 视频相关
|
||
videoList: [], // { url: '', name: '' }
|
||
|
||
// FAQ 列表
|
||
faqList: [
|
||
{ question: '', answer: '' }
|
||
],
|
||
|
||
// 车辆信息
|
||
carinfo: {
|
||
id: '',
|
||
product_name: '',
|
||
vin: '',
|
||
price: '',
|
||
source_place: '',
|
||
plate_place: '',
|
||
plate_time: '',
|
||
displacement: '',
|
||
power: '',
|
||
body_color: '',
|
||
images: [], // 存储图片文件名(用于提交)
|
||
condition_desc: '',
|
||
mileage: '',
|
||
attachment: [],
|
||
// 绑定产品的相关信息
|
||
crawl_title: '',
|
||
crawl_brand: '',
|
||
crawl_series: '',
|
||
crawl_year: '',
|
||
crawl_msrp: '',
|
||
crawl_car_id: '',
|
||
crawl_brand_id: '',
|
||
crawl_series_id: '',
|
||
// 视频文件名列表
|
||
videos: [],
|
||
// FAQ 数据(提交时转为JSON字符串)
|
||
faq_data: []
|
||
}
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
this.id = options?.id || ''
|
||
if (this.id) {
|
||
this.getDetail()
|
||
} else {
|
||
uni.showToast({
|
||
title: '缺少车辆ID',
|
||
icon: 'none'
|
||
})
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
}
|
||
},
|
||
computed: {
|
||
// 弹框内搜索过滤
|
||
filteredBindList() {
|
||
const list = Array.isArray(this.bindState.list) ? this.bindState.list : []
|
||
const kw = String(this.bindState.keyword || '').trim().toLowerCase()
|
||
if (!kw) return list
|
||
return list.filter((it, idx) => {
|
||
const id = String(it.id || it.product_id || it.code || idx).toLowerCase()
|
||
const name = String(it.name || it.product_name || it.title || '').toLowerCase()
|
||
const sub = String(this.formatBindSub(it) || '').toLowerCase()
|
||
return id.includes(kw) || name.includes(kw) || sub.includes(kw)
|
||
})
|
||
}
|
||
},
|
||
methods: {
|
||
onBorrowTimeChange(e) {
|
||
this.carinfo.plate_time = e
|
||
},
|
||
|
||
// ========== 获取车辆详情(数据回显)==========
|
||
getDetail() {
|
||
uni.showLoading({ title: '加载中...' })
|
||
request({
|
||
url: 'common/usedcardetail/show',
|
||
method: 'get',
|
||
data: { id: this.id }
|
||
}).then(res => {
|
||
uni.hideLoading()
|
||
if (res.data.code !== 200) {
|
||
uni.showToast({
|
||
title: res.data.message || '获取详情失败',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
const data = res.data.data || {}
|
||
this.initFormData(data)
|
||
}).catch(err => {
|
||
uni.hideLoading()
|
||
console.error('[getDetail error]', err)
|
||
uni.showToast({
|
||
title: '获取详情失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
},
|
||
|
||
// 初始化表单数据
|
||
initFormData(data) {
|
||
// 基本信息
|
||
this.carinfo = {
|
||
...this.carinfo,
|
||
id: data.id,
|
||
product_name: data.product_name || '',
|
||
vin: data.vin || '',
|
||
price: data.price || '',
|
||
source_place: data.source_place || '',
|
||
plate_place: data.plate_place || '',
|
||
plate_time: data.plate_time || '',
|
||
displacement: data.displacement || '',
|
||
power: data.power || '',
|
||
body_color: data.body_color || '',
|
||
mileage: data.mileage || '',
|
||
condition_desc: data.condition_desc || '',
|
||
// 绑定产品信息
|
||
crawl_title: data.crawl_title || '',
|
||
crawl_brand: data.crawl_brand || '',
|
||
crawl_series: data.crawl_series || '',
|
||
crawl_year: data.crawl_year || '',
|
||
crawl_msrp: data.crawl_msrp || '',
|
||
crawl_car_id: data.crawl_car_id || '',
|
||
crawl_brand_id: data.crawl_brand_id || '',
|
||
crawl_series_id: data.crawl_series_id || '',
|
||
// 图片文件名列表(用于提交)
|
||
images: data.images ? String(data.images).split(',').filter(Boolean) : [],
|
||
// 视频文件名列表(用于提交)
|
||
videos: data.intro_video ? String(data.intro_video).split(',').filter(Boolean) : []
|
||
}
|
||
|
||
// 图片回显:优先使用 images_pull(带签名URL)
|
||
if (Array.isArray(data.images_pull) && data.images_pull.length) {
|
||
this.imagesurl = data.images_pull
|
||
} else if (this.carinfo.images.length) {
|
||
// 如果没有 images_pull,使用 images 并拼接基础URL
|
||
this.imagesurl = this.carinfo.images.map(img => {
|
||
// 如果已经是完整URL,直接使用
|
||
if (img.startsWith('http')) return img
|
||
// 否则拼接基础URL(根据实际情况调整)
|
||
return img
|
||
})
|
||
}
|
||
|
||
// 视频回显:优先使用 video_pull(带签名URL)
|
||
if (Array.isArray(data.video_pull) && data.video_pull.length) {
|
||
this.videoList = data.video_pull.map((url, idx) => ({
|
||
url: url,
|
||
name: this.carinfo.videos[idx] || `video_${idx + 1}`
|
||
}))
|
||
} else if (this.carinfo.videos.length) {
|
||
this.videoList = this.carinfo.videos.map((name, idx) => ({
|
||
url: name.startsWith('http') ? name : name,
|
||
name: name
|
||
}))
|
||
}
|
||
|
||
// FAQ 回显
|
||
if (Array.isArray(data.faqs) && data.faqs.length) {
|
||
this.faqList = data.faqs.map(faq => ({
|
||
question: faq.question || '',
|
||
answer: faq.answer || ''
|
||
}))
|
||
} else {
|
||
this.faqList = [{ question: '', answer: '' }]
|
||
}
|
||
|
||
// 附件回显
|
||
if (Array.isArray(data.attachment_pull) && data.attachment_pull.length) {
|
||
this.carinfo.attachment = data.attachment_pull.map(att => ({
|
||
name: att.name || att.original_name || '',
|
||
src: att.src || att.file_name || ''
|
||
}))
|
||
} else if (data.attachment) {
|
||
try {
|
||
const parsed = typeof data.attachment === 'string' ? JSON.parse(data.attachment) : data.attachment
|
||
this.carinfo.attachment = Array.isArray(parsed) ? parsed : []
|
||
} catch (e) {
|
||
this.carinfo.attachment = []
|
||
}
|
||
}
|
||
|
||
// 已绑定产品提示
|
||
if (data.crawl_car_id || data.crawl_title) {
|
||
this.bindState.boundProduct = {
|
||
car_id: data.crawl_car_id,
|
||
title: data.crawl_title,
|
||
brand_name: data.crawl_brand,
|
||
series_name: data.crawl_series,
|
||
year: data.crawl_year,
|
||
msrp: data.crawl_msrp,
|
||
brand_id: data.crawl_brand_id,
|
||
series_id: data.crawl_series_id
|
||
}
|
||
}
|
||
},
|
||
|
||
// ========== 绑定产品相关方法 ==========
|
||
onBindByName() {
|
||
const name = String(this.carinfo.product_name || '').trim()
|
||
if (!name) {
|
||
return uni.showToast({ title: '请先输入产品名称', icon: 'none' })
|
||
}
|
||
|
||
this.bindState.showDialog = true
|
||
this.bindState.loading = true
|
||
this.bindState.list = []
|
||
this.bindState.selectedId = ''
|
||
this.bindState.keyword = ''
|
||
|
||
request({
|
||
url: 'common/usedcar/crawl',
|
||
method: 'get',
|
||
data: { keyword: name }
|
||
}).then(res => {
|
||
if(res.data.code == 200){
|
||
this.bindState.list = res.data.data.model
|
||
} else {
|
||
uni.showToast({ title: res.data.message, icon: 'none' })
|
||
}
|
||
}).catch(() => {
|
||
uni.showToast({ title: '拉取产品失败,请稍后重试', icon: 'none' })
|
||
}).finally(() => {
|
||
this.bindState.loading = false
|
||
})
|
||
},
|
||
|
||
closeBindDialog() {
|
||
this.bindState.showDialog = false
|
||
this.bindState.keyword = ''
|
||
},
|
||
|
||
getBindItemId(it, idx) {
|
||
return String(it.id || it.product_id || it.requisition_item_id || it.code || idx)
|
||
},
|
||
|
||
selectBind(it, idx) {
|
||
this.bindState.selectedId = this.getBindItemId(it, idx)
|
||
this.bindState._selected = it
|
||
},
|
||
|
||
formatBindSub(it) {
|
||
const parts = []
|
||
if (it.sku || it.code) parts.push('编码:' + (it.sku || it.code))
|
||
if (it.category_name) parts.push('分类:' + it.category_name)
|
||
if (it.price) parts.push('价格:' + it.price)
|
||
return parts.join(' · ')
|
||
},
|
||
|
||
confirmBind() {
|
||
if (!this.bindState.selectedId || !this.bindState._selected) return
|
||
|
||
const it = this.bindState._selected
|
||
|
||
this.bindState.boundProduct = it
|
||
this.carinfo.crawl_title = it.title
|
||
this.carinfo.crawl_brand = it.brand_name
|
||
this.carinfo.crawl_series = it.series_name
|
||
this.carinfo.crawl_year = it.year
|
||
this.carinfo.crawl_msrp = it.msrp
|
||
this.carinfo.crawl_car_id = it.car_id
|
||
this.carinfo.crawl_brand_id = it.brand_id
|
||
this.carinfo.crawl_series_id = it.series_id
|
||
console.log(it)
|
||
this.closeBindDialog()
|
||
uni.showToast({ title: '绑定成功', icon: 'none' })
|
||
},
|
||
|
||
clearBind() {
|
||
this.carinfo.crawl_title = ''
|
||
this.carinfo.crawl_brand = ''
|
||
this.carinfo.crawl_series = ''
|
||
this.carinfo.crawl_year = ''
|
||
this.carinfo.crawl_msrp = ''
|
||
this.carinfo.crawl_car_id = ''
|
||
this.carinfo.crawl_brand_id = ''
|
||
this.carinfo.crawl_series_id = ''
|
||
this.bindState.boundProduct = null
|
||
uni.showToast({ title: '已清除绑定', icon: 'none' })
|
||
},
|
||
|
||
// ========== 图片上传相关方法 ==========
|
||
uploadfn(imgUrl, scene) {
|
||
uni.showLoading({
|
||
title: '上传中'
|
||
});
|
||
return new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: 'https://usedcar.dev.hnxinyouhe.com/upload?scene=' + scene,
|
||
filePath: imgUrl,
|
||
name: 'file',
|
||
header: {
|
||
'Fdesk-Auth-Token': uni.getStorageSync('token'),
|
||
'Fdesk-Login-Type': 'feishu',
|
||
'Fdesk-System-Code': 'fdesk'
|
||
},
|
||
success: (uploadFileRes) => {
|
||
const res = JSON.parse(uploadFileRes.data || '{}')
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: res.message,
|
||
icon: 'none'
|
||
})
|
||
if(res && res.data && res.data.file_name){
|
||
resolve(res.data.file_name)
|
||
} else {
|
||
reject('上传失败')
|
||
}
|
||
},
|
||
fail: (err) => reject(err)
|
||
});
|
||
})
|
||
},
|
||
|
||
uploadfndile(imgUrl, scene) {
|
||
uni.showLoading({
|
||
title: '上传中'
|
||
});
|
||
return new Promise((resolve, reject) => {
|
||
uni.uploadFile({
|
||
url: 'https://usedcar.dev.hnxinyouhe.com/upload?scene=' + scene,
|
||
filePath: imgUrl,
|
||
name: 'file',
|
||
header: {
|
||
'Fdesk-Auth-Token': uni.getStorageSync('token'),
|
||
'Fdesk-Login-Type': 'feishu',
|
||
'Fdesk-System-Code': 'fdesk'
|
||
},
|
||
success: (uploadFileRes) => {
|
||
const res = JSON.parse(uploadFileRes.data || '{}')
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: res.message,
|
||
icon: 'none'
|
||
})
|
||
if(res && res.data && res.data.file_name){
|
||
resolve(res.data)
|
||
} else {
|
||
reject(res)
|
||
}
|
||
},
|
||
fail: (err) => reject(err)
|
||
});
|
||
})
|
||
},
|
||
|
||
pickImagesH5() {
|
||
uni.chooseFile({
|
||
count: 100,
|
||
type: 'image',
|
||
success: (res) => {
|
||
const paths = (res.tempFilePaths || (res.tempFiles || []).map(it => it.path)).filter(Boolean)
|
||
if (!paths.length) return
|
||
Promise.all(paths.map(p => this.uploadfndile(p, 'usedcarimg'))).then(urls => {
|
||
urls.forEach(url => {
|
||
this.imagesurl.push(url.file_url)
|
||
this.carinfo.images.push(url.file_name)
|
||
})
|
||
}).catch((e) => {
|
||
uni.showToast({ title: e.message, icon: 'none' })
|
||
})
|
||
}
|
||
})
|
||
},
|
||
|
||
selectMedia(e) {
|
||
const allowCount = this.maxMedia - this.carinfo.images.length
|
||
const tempFilePaths = e.tempFilePaths || (e.tempFiles || []).map(item => item.path || item.url)
|
||
const paths = tempFilePaths.slice(0, allowCount)
|
||
if(paths.length === 0){
|
||
return
|
||
}
|
||
Promise.all(paths.map(p => this.uploadfn(p, 'usedcarimg'))).then(urls => {
|
||
urls.forEach(url => {
|
||
this.carinfo.images.push(url)
|
||
this.imagesurl.push(url) // H5环境可能需要调整
|
||
})
|
||
}).catch(() => {
|
||
uni.showToast({
|
||
title: '图片上传失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
},
|
||
|
||
delfn(e) {
|
||
this.imagesurl.splice(e.index, 1)
|
||
this.carinfo.images.splice(e.index, 1)
|
||
},
|
||
|
||
selectMedia1(e) {
|
||
const allowCount = this.maxMedia - this.carinfo.attachment.length
|
||
const tempFilePaths = e.tempFilePaths || (e.tempFiles || []).map(item => item.path || item.url)
|
||
const paths = tempFilePaths.slice(0, allowCount)
|
||
if(paths.length === 0){
|
||
return
|
||
}
|
||
Promise.all(paths.map(p => this.uploadfndile(p, 'usedcarfile'))).then(urls => {
|
||
urls.forEach(url => {
|
||
const obj = {}
|
||
obj.name = url.original_name
|
||
obj.src = url.file_name
|
||
this.carinfo.attachment.push(obj)
|
||
})
|
||
}).catch(() => {
|
||
uni.showToast({
|
||
title: '图片上传失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
},
|
||
|
||
delfn1(e) {
|
||
this.carinfo.attachment.splice(e.index, 1)
|
||
},
|
||
|
||
// ========== 视频上传相关方法(专业发布专属)==========
|
||
chooseVideo() {
|
||
// 检查是否已达到最大视频数量
|
||
if (this.videoList.length >= this.maxVideos) {
|
||
return uni.showToast({
|
||
title: `最多只能上传${this.maxVideos}个视频`,
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
// #ifdef H5
|
||
// H5 端使用 chooseFile 选择视频
|
||
uni.chooseFile({
|
||
count: this.maxVideos - this.videoList.length,
|
||
type: 'video',
|
||
success: (res) => {
|
||
const files = res.tempFiles || []
|
||
if (!files.length) return
|
||
|
||
// 检查文件大小
|
||
const maxSize = 100 * 1024 * 1024 // 100MB
|
||
for (let file of files) {
|
||
if (file.size > maxSize) {
|
||
uni.showToast({
|
||
title: '视频大小不能超过100MB',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
}
|
||
|
||
// 上传视频
|
||
files.forEach(file => {
|
||
this.uploadVideo(file.path)
|
||
})
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// #ifndef H5
|
||
// 小程序/App 端使用 chooseVideo
|
||
uni.chooseVideo({
|
||
sourceType: ['camera', 'album'],
|
||
maxDuration: 300, // 最大5分钟
|
||
success: (res) => {
|
||
this.uploadVideo(res.tempFilePath)
|
||
}
|
||
})
|
||
// #endif
|
||
},
|
||
|
||
uploadVideo(filePath) {
|
||
uni.showLoading({ title: '视频上传中...' })
|
||
|
||
this.uploadfndile(filePath, 'usedcarvideo').then(res => {
|
||
this.videoList.push({
|
||
url: res.file_url,
|
||
name: res.file_name
|
||
})
|
||
this.carinfo.videos.push(res.file_name)
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '视频上传成功',
|
||
icon: 'success'
|
||
})
|
||
}).catch(err => {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '视频上传失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
},
|
||
|
||
deleteVideo(index) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定删除这个视频吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.videoList.splice(index, 1)
|
||
this.carinfo.videos.splice(index, 1)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
|
||
// ========== FAQ 相关方法(专业发布专属)==========
|
||
addFaq() {
|
||
this.faqList.push({
|
||
question: '',
|
||
answer: ''
|
||
})
|
||
},
|
||
|
||
deleteFaq(index) {
|
||
if (this.faqList.length <= 1) {
|
||
return uni.showToast({
|
||
title: '至少保留一个问题',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
this.faqList.splice(index, 1)
|
||
},
|
||
|
||
validateFaq() {
|
||
// 过滤掉空的问题,返回有效的 FAQ 数组
|
||
const validFaqs = this.faqList.filter(faq =>
|
||
faq.question && faq.question.trim() &&
|
||
faq.answer && faq.answer.trim()
|
||
)
|
||
|
||
// 返回有效的 FAQ 数组(可能为空数组)
|
||
return validFaqs
|
||
},
|
||
|
||
// ========== 提交更新 ==========
|
||
updateSubmit() {
|
||
// 验证必填项
|
||
if (!this.carinfo.product_name) {
|
||
return uni.showToast({ title: '请输入产品名称', icon: 'none' })
|
||
}
|
||
|
||
uni.showLoading({ title: '提交中...' })
|
||
|
||
// 获取 FAQ 数据(允许为空)
|
||
const validFaqs = this.validateFaq()
|
||
|
||
const payload = {
|
||
// 基础字段
|
||
id: this.carinfo.id,
|
||
product_name: this.carinfo.product_name,
|
||
vin: this.carinfo.vin,
|
||
price: this.carinfo.price,
|
||
source_place: this.carinfo.source_place,
|
||
plate_time: this.carinfo.plate_time,
|
||
displacement: this.carinfo.displacement,
|
||
power: this.carinfo.power,
|
||
body_color: this.carinfo.body_color,
|
||
mileage: this.carinfo.mileage,
|
||
condition_desc: this.carinfo.condition_desc,
|
||
|
||
// 绑定产品相关字段
|
||
crawl_title: this.carinfo.crawl_title,
|
||
crawl_brand: this.carinfo.crawl_brand,
|
||
crawl_series: this.carinfo.crawl_series,
|
||
crawl_year: this.carinfo.crawl_year,
|
||
crawl_msrp: this.carinfo.crawl_msrp,
|
||
crawl_car_id: this.carinfo.crawl_car_id,
|
||
crawl_brand_id: this.carinfo.crawl_brand_id,
|
||
crawl_series_id: this.carinfo.crawl_series_id,
|
||
|
||
// 图片数组 => "url1,url2,url3"
|
||
images: (this.carinfo.images || [])
|
||
.map(s => String(s || '').trim())
|
||
.filter(Boolean)
|
||
.join(','),
|
||
|
||
// 视频数组 => "url1,url2,url3" (后端字段名: intro_video)
|
||
intro_video: (this.carinfo.videos || [])
|
||
.map(s => String(s || '').trim())
|
||
.filter(Boolean)
|
||
.join(','),
|
||
|
||
// 附件数据(转为JSON字符串)
|
||
attachment: JSON.stringify(this.carinfo.attachment || []),
|
||
|
||
// FAQ 数据:直接传数组格式 [{"question": "", "answer": ""}]
|
||
faqs: validFaqs,
|
||
}
|
||
|
||
// 移除空值字段
|
||
Object.keys(payload).forEach(key => {
|
||
if (payload[key] === '' || payload[key] === null || payload[key] === undefined) {
|
||
delete payload[key]
|
||
}
|
||
})
|
||
|
||
request({
|
||
url: 'common/usedcardetail/update',
|
||
method: 'post',
|
||
data: payload
|
||
}).then(res => {
|
||
uni.hideLoading()
|
||
if(res.data.code == 200){
|
||
uni.showToast({
|
||
title: res.data.message || '保存成功',
|
||
icon: 'success'
|
||
})
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
} else {
|
||
uni.showToast({
|
||
title: res.data.message || '保存失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}).catch(err => {
|
||
uni.hideLoading()
|
||
console.error('[update error]', err)
|
||
uni.showToast({
|
||
title: err.data?.message || '保存失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.pages {
|
||
max-width: 650px;
|
||
}
|
||
/* #ifdef H5 */
|
||
.pages {
|
||
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
|
||
}
|
||
/* #endif */
|
||
|
||
.red {
|
||
color: red;
|
||
}
|
||
|
||
.content {
|
||
background: #FFFFFF;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.zw {
|
||
height: 120px;
|
||
}
|
||
|
||
.cont {
|
||
position: relative;
|
||
padding: 18px 20px;
|
||
align-items: center;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.conts {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.media-upload-wrapper {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.cell {
|
||
position: relative;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 14px 10px;
|
||
align-items: center;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.cell::after, .cont::after {
|
||
content: '';
|
||
position: absolute;
|
||
width: 200%;
|
||
height: 200%;
|
||
top: 0;
|
||
left: 0;
|
||
transform-origin: 0 0;
|
||
border-width: 0;
|
||
border-bottom-width: 1px;
|
||
border-style: solid;
|
||
transform: scale(0.5, 0.5);
|
||
border-radius: 1px;
|
||
box-sizing: border-box;
|
||
pointer-events: none;
|
||
border-color: #C3C3C3;
|
||
}
|
||
|
||
.right {
|
||
text-align: right;
|
||
}
|
||
|
||
.btn {
|
||
background-color: #3F435B;
|
||
color: white;
|
||
border-radius: 50px;
|
||
width: 92%;
|
||
margin-bottom: 40px;
|
||
margin: 15px 4%;
|
||
}
|
||
|
||
.addimg {
|
||
width: 215rpx;
|
||
height: 215rpx;
|
||
}
|
||
|
||
/* 产品名称 + 绑定按钮 */
|
||
.right-bind {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
display: flex;
|
||
width: 74%;
|
||
}
|
||
|
||
.right-bind .inp {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.bind-btn {
|
||
margin-left: 16rpx;
|
||
padding: 0 18rpx;
|
||
height: 56rpx;
|
||
line-height: 56rpx;
|
||
border-radius: 12rpx;
|
||
background: #2E6CF6;
|
||
color: #fff;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.bind-tip {
|
||
padding: 12rpx 18rpx;
|
||
background: #F3F6FF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.bind-tip-text {
|
||
color: #2B3A55;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.bind-tip-clear {
|
||
color: #2E6CF6;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
/* 弹框搜索 */
|
||
.bind-search {
|
||
margin: 5px 24rpx 16rpx;
|
||
padding: 12rpx 18rpx;
|
||
border-radius: 12rpx;
|
||
background: #F3F6FF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.bind-search-input {
|
||
flex: 1;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.bind-search-clear {
|
||
margin-left: 16rpx;
|
||
font-size: 24rpx;
|
||
color: #2E6CF6;
|
||
}
|
||
|
||
/* 绑定弹框 */
|
||
.bind-mask {
|
||
position: fixed;
|
||
left: 0; right: 0; top: 0; bottom: 0;
|
||
background: rgba(0,0,0,0.45);
|
||
z-index: 999;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 40rpx;
|
||
}
|
||
|
||
.bind-dialog {
|
||
width: 100%;
|
||
max-width: 680rpx;
|
||
background: #fff;
|
||
border-radius: 20rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bind-dialog-head {
|
||
height: 96rpx;
|
||
padding: 0 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.bind-dialog-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #111;
|
||
}
|
||
|
||
.bind-dialog-close {
|
||
font-size: 40rpx;
|
||
color: #999;
|
||
line-height: 1;
|
||
}
|
||
|
||
.bind-dialog-body {
|
||
max-height: 720rpx;
|
||
padding: 12rpx 0;
|
||
}
|
||
|
||
.bind-loading, .bind-empty {
|
||
padding: 40rpx 24rpx;
|
||
color: #888;
|
||
text-align: center;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.bind-item {
|
||
padding: 18rpx 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.bind-item.active {
|
||
background: #F3F6FF;
|
||
}
|
||
|
||
.bind-item-main {
|
||
flex: 1;
|
||
padding-right: 16rpx;
|
||
}
|
||
|
||
.bind-item-title {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #222;
|
||
display: block;
|
||
}
|
||
|
||
.bind-item-sub {
|
||
margin-top: 6rpx;
|
||
font-size: 24rpx;
|
||
color: #777;
|
||
display: block;
|
||
}
|
||
|
||
.bind-item-check {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 50%;
|
||
border: 2rpx solid #2E6CF6;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.check-dot {
|
||
width: 22rpx;
|
||
height: 22rpx;
|
||
border-radius: 50%;
|
||
background: #2E6CF6;
|
||
}
|
||
|
||
.bind-dialog-foot {
|
||
padding: 16rpx 24rpx 22rpx;
|
||
display: flex;
|
||
gap: 16rpx;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
.bind-dialog-foot .btn{
|
||
text-align: center;
|
||
padding: 6px 0;
|
||
}
|
||
|
||
.btn-ghost {
|
||
background: #F5F6F8;
|
||
color: #333;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #2E6CF6;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-primary.disabled {
|
||
opacity: .45;
|
||
}
|
||
|
||
/* H5 自定义选择器 */
|
||
.h5-picker-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16rpx;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.h5-thumb {
|
||
width: 210rpx;
|
||
height: 210rpx;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
position: relative;
|
||
background: #f6f6f6;
|
||
}
|
||
|
||
.h5-thumb-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.h5-thumb-del {
|
||
position: absolute;
|
||
right: 8rpx;
|
||
top: 8rpx;
|
||
background: rgba(0,0,0,0.45);
|
||
color: #fff;
|
||
font-size: 22rpx;
|
||
padding: 6rpx 10rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
/* ========== 视频上传区域样式 ========== */
|
||
.section-title {
|
||
padding: 20rpx 24rpx 12rpx;
|
||
background: #F8F9FA;
|
||
border-bottom: 1px solid #eee;
|
||
|
||
.title-text {
|
||
display: block;
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.title-desc {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.video-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
padding: 20rpx 24rpx;
|
||
}
|
||
|
||
.video-item {
|
||
position: relative;
|
||
width: 320rpx;
|
||
height: 240rpx;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
background: #000;
|
||
}
|
||
|
||
.video-preview {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.video-delete {
|
||
position: absolute;
|
||
top: 12rpx;
|
||
right: 12rpx;
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
background: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.video-upload-btn {
|
||
margin: 20rpx 24rpx;
|
||
padding: 40rpx 24rpx;
|
||
border: 2rpx dashed #ddd;
|
||
border-radius: 12rpx;
|
||
background: #fafafa;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 12rpx;
|
||
|
||
.upload-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.upload-tip {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
/* ========== FAQ 区域样式 ========== */
|
||
.faq-section {
|
||
background: #fff;
|
||
margin-top: 20rpx;
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.faq-list {
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.faq-item {
|
||
padding: 24rpx 0;
|
||
margin-bottom: 20rpx;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
.faq-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.faq-index {
|
||
display: inline-block;
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
background: #2E6CF6;
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.faq-delete {
|
||
color: #ff4d4f;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.faq-input-group {
|
||
margin-bottom: 20rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.faq-label {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.faq-input {
|
||
padding: 16rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
background: #fff;
|
||
border: 1px solid #e0e0e0;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.faq-textarea {
|
||
width: auto;
|
||
min-height: 120rpx;
|
||
padding: 16rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
background: #fff;
|
||
border: 1px solid #e0e0e0;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.add-faq-btn {
|
||
margin: 0 24rpx;
|
||
padding: 24rpx;
|
||
border-radius: 12rpx;
|
||
background: #F3F6FF;
|
||
border: 1px dashed #2E6CF6;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
|
||
.add-faq-text {
|
||
font-size: 28rpx;
|
||
color: #2E6CF6;
|
||
}
|
||
}
|
||
|
||
.custom-datetime-input {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.custom-datetime-text.placeholder {
|
||
color: #999;
|
||
}
|
||
</style>
|