Files
used-car-h5/pages/index/publishedit.vue
T
2026-01-06 10:32:46 +08:00

423 lines
10 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>
<!-- <uni-nav-bar leftText=" " @clickLeft="leftClick" leftIcon="back" :fixed="true" :border="false" color="#fff" backgroundColor="#3F435B" :statusBar="true" title="车辆编辑"></uni-nav-bar> -->
<view class="content">
<view class="cell">
<view>产品名称 <text class="red">*</text></view>
<view class="right">
<input placeholder="请输入产品名称" v-model="carinfo.product_name" />
</view>
</view>
<view class="cell">
<view>车辆识别码</view>
<view class="right">
<input placeholder="请输入车架号/VIN码" v-model="carinfo.vin" />
</view>
</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">
<input placeholder="请输入上牌地" v-model="carinfo.plate_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.mileage" />
</view>
</view>
<view class="cell">
<view>车身颜色</view>
<view class="right">
<input placeholder="请输入车身颜色" v-model="carinfo.body_color" />
</view>
</view>
<view class="cont">
<view class="conts">产品图</view>
<view class="media-upload-wrapper">
<uni-file-picker
ref="mediaPicker"
v-model="fileList"
:limit="maxMedia"
file-mediatype="image"
mode="grid"
:auto-upload="false"
@select="selectMedia"
@delete="delfn"
>
<view class="upload-icon-wrapper">
<image src="/static/image/img.png" mode="" class="addimg"></image>
</view>
</uni-file-picker>
</view>
</view>
<view class="cont">
<view class="conts">车况介绍</view>
<view class="">
<textarea auto-height v-model="carinfo.condition_desc" placeholder="请输入车况介绍"/>
</view>
</view>
</view>
<view class="zw">
<button class="btn" @click="draftadd">提交</button>
</view>
</view>
</template>
<script>
import { request,showToast } from '@/api/apiT.js'
export default {
data() {
return {
maxMedia:20,
// uni-file-picker 回显/展示用(url/name/extname)
fileList: [],
id:'',
_uploading:false,
carinfo:{
id:'',
product_name:'',
vin:'',
price:'',
source_place:'',
plate_place:'',
plate_time:'',
displacement:'',
body_color:'',
// 提交给后端用:相对路径数组(最终 join(','))
images:[],
condition_desc:'',
mileage:''
}
}
},
onLoad(options) {
this.id = options?.id || ''
this.getshows()
},
methods: {
leftClick() {
uni.navigateBack()
},
getshows(){
let that = this;
request({
url:'common/usedcar/show',
method:'GET',
data:{
id:that.id
}
}).then(res => {
if (res.data.code == 200) {
const d = res.data.data || {}
// 1) images:后端返回的是逗号字符串,这里转成数组(用于编辑提交)
const keys = String(d.images || '')
.split(',')
.map(s => String(s || '').trim())
.filter(Boolean)
// 2) 表单字段
this.carinfo = {
...this.carinfo,
...d,
images: keys
}
// 3) 图片回显:优先用 images_pull(签名URL用于显示)
const pulls = Array.isArray(d.images_pull) && d.images_pull.length
? d.images_pull
: keys
this.fileList = pulls.map((u, idx) => {
const raw = String(u || '')
const filename = raw.split('?')[0].split('/').pop() || `img_${idx + 1}`
const ext = filename.includes('.') ? filename.split('.').pop() : ''
return { url: raw, name: filename, extname: ext }
})
}
})
},
onBorrowTimeChange(e) {
this.carinfo.plate_time = e
},
uploadfn(imgUrl){
return new Promise((resolve, reject) => {
uni.uploadFile({
url: 'https://usedcar.dev.hnxinyouhe.com/upload/image?scene=usedcarimg',
filePath: imgUrl,
name: 'image',
header:{'Fdesk-Auth-Token': uni.getStorageSync('token'),'Fdesk-Login-Type': 'feishu','Fdesk-System-Code':'fdesk'},
success: (uploadFileRes) => {
const res = JSON.parse(uploadFileRes.data || '{}')
if(res && res.data && res.data.href){
resolve(res.data.href)
}else{
reject('上传失败')
}
},
fail: (err) => reject(err)
});
})
},
selectMedia(e){
const allowCount = this.maxMedia - (this.carinfo.images?.length || 0)
if(allowCount <= 0){
uni.showToast({
title:`最多只能上传${this.maxMedia}张图片`,
icon:'none'
})
return
}
const tempFiles = e.tempFiles || []
const tempFilePaths = (tempFiles.length
? tempFiles.map(f => f.path || f.url).filter(Boolean)
: (e.tempFilePaths || [])
)
const paths = tempFilePaths.slice(0, allowCount)
if(paths.length === 0) return
if(this._uploading) return
this._uploading = true
// 先占位,保证 delete 的 index 与 images 数组对得上
const startAt = this.carinfo.images.length
for(let i=0;i<paths.length;i++){
this.carinfo.images.push('__PENDING__')
}
Promise.all(paths.map(p => this.uploadfn(p)))
.then(keys => {
keys.forEach((key, i) => {
// 替换占位
this.$set(this.carinfo.images, startAt + i, String(key))
})
})
.catch(() => {
// 失败:移除占位,同时把 fileList 对应项删掉(避免页面“假回显”)
this.carinfo.images.splice(startAt, paths.length)
if(Array.isArray(this.fileList) && this.fileList.length >= startAt + paths.length){
this.fileList.splice(startAt, paths.length)
}
uni.showToast({
title:'图片上传失败,请重试',
icon:'none'
})
})
.finally(() => {
this._uploading = false
})
},
delfn(e){
// fileList 会由 uni-file-picker 自己维护;这里同步删除提交用的 images
this.carinfo.images.splice(e.index, 1)
},
draftadd(){
const payload = {
...this.carinfo,
// images 数组 => "url1,url2,url3"
images: (this.carinfo.images || [])
.map(s => String(s || '').trim())
.filter(s => !!s && s !== '__PENDING__')
.join(',')
}
request({
url:'common/usedcar/update',
method:'post',
data:{
id:payload.id,
product_name:payload.product_name,
vin:payload.vin,
price:payload.price,
source_place:payload.source_place,
plate_place:payload.plate_place,
plate_time:payload.plate_time,
displacement:payload.displacement,
body_color:payload.body_color,
images:payload.images,
condition_desc:payload.condition_desc,
mileage:payload.mileage
}
}).then(res => {
uni.showToast({
title: res.data.message,
icon: 'none'
});
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/mine',// 替换为你要跳转的页面路径
});
}, 1000);
})
}
}
}
</script>
<style lang="scss">
.uniui-right{
position: absolute;
top: 2px;
}
.red{
color: red;
}
.content{
margin: 20px 12px;
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: 18px 20px;
/* border-bottom: 1px solid #C3C3C3; */
align-items: center;
font-weight: 500;
}
.cont::after,.cell::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;
}
.cell1{
display: flex;
justify-content:space-between;
padding: 18px 20px;
align-items: center;
font-weight: 500;
}
.right{
text-align: right;
}
.select{
display: flex;
position: relative;
margin-right: 10px;
}
.foot{
position: fixed;
bottom: 0px;
width: 100%;
height: 120px;
background: #FFFFFF;
z-index: 2;
}
.btn{
background-color: #3F435B;
color: white;
border-radius: 50px;
width: 92%;
margin-bottom: 40px;
margin: 15px 4%;
}
.actives{
background-color: #dfdfdf;
}
.text{
display: flex;
align-items: center;
justify-content: space-between;
line-height: 30px;
padding: 10px 15px 0px;
.qd{
color: #3471FF;
}
}
.popup-content {
padding: 20px;
background-color: #fff;
}
.option-list {
max-height: 400rpx;
margin-top: 20rpx;
}
.option-item {
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.option-item:last-child {
border-bottom: none;
}
.no-data {
text-align: center;
padding: 20rpx;
color: #999;
}
.addimg{
width: 215rpx;
height: 215rpx;
}
</style>