添加编辑发布 页面

This commit is contained in:
浅念
2026-02-09 17:51:32 +08:00
parent ea2e2fec25
commit f9b199f6a6
4 changed files with 851 additions and 3 deletions
+85 -3
View File
@@ -1,6 +1,6 @@
<template>
<view class="card-item">
<view class="user" @click="handleUserClick">
<view class="card-item" @click="handleUserClick">
<view class="user" >
<image :src="item.user.avatar_pull || '/static/image/user.png'" class="avatar"></image>
<view class="info">
<view class="name">{{ item.user.nickname || '用户名' }}</view>
@@ -40,7 +40,7 @@
/>
</view>
<view class="location" v-if="item.location_name">
<view class="location" v-if="item.location_name" @click.stop="locationfn(item)">
<uni-icons type="location" color="#999" size="18"></uni-icons>
<text>{{ item.location_name }}</text>
</view>
@@ -48,6 +48,9 @@
<view class="comment" @click="handleComment">
评论({{ item.comment || 0 }})
</view>
<view v-if="ismysq" class="actions">
<view class="edit-btn" @click.stop="handleEdit">编辑</view>
</view>
</view>
</template>
@@ -139,6 +142,76 @@ export default {
},
handleComment() {
this.$emit('comment', this.item);
},
handleEdit() {
this.$emit('edit', this.item);
},
// 点击位置:获取(模糊)当前位置 -> 打开系统地图并可导航到店铺位置
async locationfn(item) {
try {
const lat = Number((item && item.latitude));
const lng = Number((item && item.longitude));
if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
uni.showToast({ title: '该店铺未设置定位', icon: 'none' });
return;
}
// 1) 先尝试获取(模糊)当前位置,主要用于触发授权 & 让地图页显示“我的位置”
await this._getFuzzyCurrentLocationSafe();
// 2) 打开地图并指向店铺坐标;地图页自带“导航/到这去”
uni.openLocation({
latitude: lat,
longitude: lng,
name: (item && item.location_name) || '店铺位置',
address: (item && item.location_address) || (item && item.location_name) || '',
scale: 16,
fail: () => {
uni.showToast({ title: '打开地图失败', icon: 'none' });
}
});
} catch (e) {
// 兜底:即使定位授权失败,也尽量打开店铺位置
const lat = Number((item && item.latitude));
const lng = Number((item && item.longitude));
if (Number.isFinite(lat) && Number.isFinite(lng)) {
uni.openLocation({
latitude: lat,
longitude: lng,
name: (item && item.location_name) || '店铺位置',
address: (item && item.location_address) || (item && item.location_name) || '',
scale: 16
});
} else {
uni.showToast({ title: '打开地图失败', icon: 'none' });
}
}
},
// 获取模糊定位(不抛错版本):用于触发授权&让地图显示“我的位置”
_getFuzzyCurrentLocationSafe() {
return new Promise((resolve) => {
// 仅微信小程序支持 getFuzzyLocation
// #ifdef MP-WEIXIN
uni.authorize({
scope: 'scope.userFuzzyLocation',
success: () => {
// 直接调用微信的模糊定位
wx.getFuzzyLocation({
type: 'wgs84',
success: () => resolve(),
fail: () => resolve()
});
},
fail: () => resolve()
});
// #endif
// #ifndef MP-WEIXIN
// 其他端不处理,直接放过
resolve();
// #endif
});
}
}
}
@@ -254,4 +327,13 @@ export default {
font-size: 26rpx;
color: #666;
}
.actions{
display: flex;
justify-content: flex-end;
}
.edit-btn{
font-size: 26rpx;
color: #3BC6BD;
padding: 10rpx 0;
}
</style>
+8
View File
@@ -57,6 +57,14 @@
"navigationStyle":"custom"
}
},
{
"path":"pages/publish/edit",
"style":{
"navigationBarTitleText":"编辑发布",
"enablePullDownRefresh":false,
"navigationStyle":"custom"
}
},
{
"path" : "pages/login/agreement",
"style" :
+6
View File
@@ -22,6 +22,7 @@
:ismysq="true"
@userClick="goto"
@view="goto"
@edit="handleEdit"
@contact="handleContact"
@comment="handleComment">
</post-card>
@@ -99,6 +100,11 @@ export default {
url: '/pages/square/detiles?id='+item.id
});
},
handleEdit(item){
uni.navigateTo({
url: '/pages/publish/edit?id=' + item.id
});
},
click(item) {
this.page = 1
this.hasMore = true
+752
View File
@@ -0,0 +1,752 @@
<template>
<view class="publish-page">
<uni-nav-bar leftText=" " leftIcon="back" :fixed="true" :border="false" @clickLeft="leftClick" color="#000000" backgroundColor="#fff" :statusBar="true" title="编辑发布"></uni-nav-bar>
<view class="form-card">
<view class="select-input gradient-bg position" @click="onSelectCategory()">
<view class="category-content">
<text class="category-text">{{form.category || '请输入/选择型号'}}</text>
</view>
<u-icon name="arrow-down" color="#3BC8BE" size="20"></u-icon>
<!-- 型号选择弹层 -->
<view v-if="showSelect1" class="select-popup">
<!-- 点击空白处关闭下拉 -->
<view class="popup-mask" @click="showSelect1=false"></view>
<view class="popup-panel" @click.stop>
<view class="popup-header">
<input class="popup-search" v-model="searchKey1" placeholder="搜索型号" />
<text class="popup-close" @click="showSelect1=false">取消</text>
</view>
<scroll-view scroll-y class="popup-list">
<view
v-for="item in filteredCategories"
:key="item.id"
class="popup-item"
@click.stop="selectCategory(item)"
>
{{ item.model }} ({{item.product_name}})
</view>
<view v-if="!filteredCategories.length" class="popup-empty">暂无数据</view>
</scroll-view>
</view>
</view>
</view>
<view class="conts mag-top">
<view class="">
<textarea v-model="form.content" class="content-textarea" placeholder="请输入发布内容"
:maxlength="maxWords" @input="handleContentInput" @focus="checkLogin()" />
</view>
<view class="media-upload-wrapper">
<uni-file-picker
ref="mediaPicker"
v-model="fileList"
fileMediatype="image"
limit="9"
:disabled="!isLogin"
@select="selectMedia"
@delete="delfn"
:imageStyles="imageStyles"
>
<view class="upload-icon-wrapper">
<image src="/static/image/photo.png" mode="" class="addimg"></image>
</view>
</uni-file-picker>
</view>
<view class="help-text">
<u-icon name="info-circle" size="18" color="#A6ABB7"></u-icon>
<text>请上传产品实物的正、侧、背多角度照片(禁止效果图),图片不多于9张</text>
</view>
</view>
<view class="mag-top">
<picker mode="selector" :range="conditionOptions" range-key="name" :disabled="!isLogin" @tap="checkLogin()" @change="onCategoryChange">
<view class="select-input gradient-bg">
<view class="category-content">
<text class="category-text">{{form.age || '请选择样机新旧程度'}}</text>
</view>
<u-icon name="arrow-down" color="#3BC8BE" size="20"></u-icon>
</view>
</picker>
</view>
<view class="mag-top prices">
<input
class="uni-input"
type="digit"
:value="form.price"
placeholder="请输入折扣价格"
@input="onPriceInput"
@blur="onPriceBlur"
/>
<text>元</text>
</view>
<view class="mag-top">
<view style="margin-bottom: 10rpx;"><span style="color: red;">*</span> 是否包邮:</view>
<uni-data-checkbox v-model="form.is_free" :localdata="freeshipping" />
</view>
<view class="detils" v-if="skudetils!=''">
<view class="product-card">
<view class="title">产品名称:{{skudetils.product_name}}</view>
<view class="price-row">
<text class="spec-label">零售价:</text>
<text class="spec-value">{{ skudetils.price }}</text>
</view>
<view class="spec-row">
<text class="spec-label">品牌:</text>
<text class="spec-value">{{ skudetils.brand }}</text>
</view>
<view class="spec-row" v-for="(item, idx) in skudetils.spec_json" :key="idx">
<text class="spec-label">{{item.attr_name}}:</text>
<text class="spec-value">{{ item.attr_value }}</text>
</view>
</view>
<rich-text :nodes="productcontents"></rich-text>
</view>
</view>
<view class="foot">
<view class="agreement">
<button class="primary-btn gradient-btn" :disabled="userinfo.is_release!=1" @click="submitForm">保存修改</button>
</view>
</view>
<u-popup :show="showlogin" :closeOnClickOverlay="false" :round="20" mode="center">
<view class="popupcontent">
<image class="log1" src="/static/image/log1.png"></image>
<view class="title">您还未登录!登录后方可编辑内容!</view>
<view class="yhxy">
<checkbox-group name="" style="width: 28px;display: inline-block;">
<label @tap="selfChangde()">
<checkbox :checked="selfChecked" activeBackgroundColor="#3F435B"
style="transform:scale(0.7);border-radius: 50%" />
</label>
</checkbox-group>
{{$t('home.argin')}}<text class="xy" @click="gouser">{{$t('home.UserAgreement')}}</text>{{$t('home.and')}}<text
class="xy" @click="goprivacy">{{$t('home.Privacy')}}</text>
</view>
</view>
<view class="cent">
<button class="btn1" @click="showlogin = !showlogin">{{$t('cancel')}}</button>
<button v-if="selfChecked" class="btn" open-type="getPhoneNumber" @click="showlogin = !showlogin"
@getphonenumber="getPhoneNumber">{{$t('login')}}</button>
<button v-else class="btn" @click="loginbtn">{{$t('login')}}</button>
</view>
</u-popup>
</view>
</template>
<script>
import { request } from '@/api/apiT.js'
export default {
name: 'EditPublishPage',
data(){
return {
postId:'',
showSelect1:false,
searchKey1:'',
productcontents:'',
userinfo:'',
skudetils:'',
// 单选数据源
freeshipping: [ {
text: '全国包邮',
value: 1
}, {
text: '省内包邮',
value: 2
}, {
text: '邮费电联',
value: 3
}],
categoryOptions:[],
conditionOptions:[
{name:'全新(未开箱)',value:'10'},
{name:'95%新(开箱无瑕)',value:'9'},
{name:'90%新(外观轻损<2处)',value:'8'},
{name:'85%新(外观轻损<3处)',value:'7'},
{name:'75%新(外观轻损<5处)',value:'6'},
{name:'70%新(外观轻损<6处)',value:'5'},
{name:'60%新(外观轻损<7处)',value:'4'},
{name:'不足60%新(外观损伤>7处)',value:'3'}
],
form:{
age:'',
category:'',
sku_id:'',
content:'',
media:[],
price:'',
is_free:''
},
selfChecked: false,
showlogin:false,
maxWords:2000,
maxMedia:9,
imageStyles:{},
fileList:[]
}
},
computed:{
isLogin() {
return !!uni.getStorageSync('is_login')
},
filteredCategories(){
if(!this.searchKey1) return this.categoryOptions
return this.categoryOptions.filter(i => (i.model || '').toLowerCase().includes(this.searchKey1.toLowerCase()))
}
},
onLoad(options) {
this.postId = options.id || ''
this.getinfo()
if(this.postId){
this.loadPostDetail()
}
},
methods:{
leftClick(){
uni.navigateBack()
},
getinfo(){
request({
url:'common/user/info',
method:'POST',
}).then(res => {
this.userinfo=res.data.data.user || {}
uni.setStorageSync('is_release', this.userinfo.is_release);
})
},
loadPostDetail(){
request({
url:'common/post/show',
method:'GET',
data:{
id:this.postId
}
}).then(res => {
if(res.data.code === 200){
const data = res.data.data || {}
this.form.content = data.content || ''
this.form.price = data.price != null ? String(data.price) : ''
this.form.is_free = data.is_free != null ? data.is_free : ''
this.form.age = data.age || ''
if(data.sku){
this.skudetils = data.sku
this.skudetils.spec_json = JSON.parse(data.sku.spec_json || '[]')
this.form.category = data.sku.model || ''
this.form.sku_id = data.sku.id || ''
if(data.sku.desc){
this.productcontents = this.processRichText(data.sku.desc || '')
}
}
if(Array.isArray(data.media)){
this.form.media = data.media.map(m => m.url || '').filter(Boolean)
this.fileList = data.media.map(m => ({ url: m.url_pull || m.img_src || m.url || '' })).filter(item => item.url)
}
}
})
},
onSelectCategory() {
if (!uni.getStorageSync('is_login')) {
this.showlogin = true
return
}
this.getcategory()
this.showSelect1 = true
},
checkLogin(cb) {
if (!this.isLogin) {
this.showlogin = true
return
}
if (typeof cb === 'function') {
cb()
}
},
// 处理富文本HTML,让图片自适应
processRichText(html) {
if (!html) return ''
// 使用正则表达式给所有 img 标签添加样式
return html.replace(/<img([^>]*)>/gi, (match, attrs) => {
// 检查是否已有 style 属性
if (attrs.includes('style=')) {
// 如果已有 style,添加 max-width
return match.replace(/style=["']([^"']*)["']/i, (styleMatch, styleContent) => {
// 移除已有的 max-width 和 width
let newStyle = styleContent.replace(/max-width\s*:\s*[^;]+;?/gi, '').replace(/width\s*:\s*[^;]+;?/gi, '')
newStyle = newStyle.trim()
if (newStyle && !newStyle.endsWith(';')) {
newStyle += ';'
}
newStyle += 'max-width: 100%; height: auto;'
return `style="${newStyle}"`
})
} else {
// 如果没有 style,添加 style 属性
return `<img${attrs} style="max-width: 100%; height: auto;">`
}
})
},
selectCategory(item){
console.log(item)
this.skudetils = item
this.form.category = item.model || ''
this.form.sku_id = item.id || ''
// 处理富文本,让图片自适应
if(item.desc !=''&& item.desc !=null){
this.productcontents = this.processRichText(item.desc || '')
}
this.showSelect1 = false
},
goprivacy(){
uni.navigateTo({
url: '/pages/login/agreement',// 替换为你要跳转的页面路径
});
},
gouser(){
uni.navigateTo({
url: '/pages/login/agreementuser',// 替换为你要跳转的页面路径
});
},
selfChangde(e) {
this.selfChecked = !this.selfChecked;
},
loginbtn(){
if(!this.selfChecked){
uni.showToast({
title:this.$t('home.arginimg'),
icon: 'none'
});
return false
}
},
getPhoneNumber(e) {
let that = this;
request({
url:'miniapp/phone',
method:'POST',
data:{
phone_code: e.detail.code,
}
}).then(res => {
if (res.data.code == -1) {
uni.showToast({
title: res.data.msg,
icon: 'none'
});
} else if (res.data.code == 200) {
uni.showToast({
title: '登录成功',
icon: 'none'
});
//返回数据接收成功
uni.setStorageSync('is_login', true);
}
})
},
//商品下拉
getcategory(){
request({
url:'common/sku/preset',
method:'GET'
}).then(res => {
if (res.data.code == 200) {
this.categoryOptions = res.data.data|| [];
}
})
},
onCategoryChange(e){
const index = e.detail.value; // picker 返回的 index
const selectCity = this.conditionOptions[index]; // 直接取整条数据
this.form.age = selectCity.name
},
handleContentInput(e){
if(e.detail.value.length>this.maxWords){
this.form.content = e.detail.value.slice(0,this.maxWords)
}
},
sanitize(v) {
v = String(v || '');
v = v.replace(/[^\d.]/g, ''); // 只留数字和点
const i = v.indexOf('.');
if (i !== -1) {
v = v.slice(0, i + 1) + v.slice(i + 1).replace(/\./g, '');
}
return v;
},
onPriceInput(e) {
const v = this.sanitize(e.detail.value);
// 关键:先更新数据,再 nextTick 强制触发视图回写
if (v !== this.form.price) {
this.form.price = v;
this.$nextTick(() => {
// 再写一次,逼迫输入框用受控值覆盖(对合成态更稳)
this.form.price = v;
});
}
},
onPriceBlur(e) {
// 兜底:失焦时彻底清洗,保证最终不会残留中文
const v = this.sanitize(e.detail.value);
if (v !== this.form.price) this.form.price = v;
},
uploadfn(imgUrl){
return new Promise((resolve, reject) => {
uni.uploadFile({
url: 'https://bsh.info.yw.hnzhwlkj.cn/upload/image?scene=postlist',
filePath: imgUrl,
name: 'image',
header:{'content-type': 'application/x-www-form-urlencoded','Fdesk-Auth-Token': 'Bearer ' + uni.getStorageSync('token'),'Fdesk-Language': uni.getStorageSync('i18locale')?uni.getStorageSync('i18locale'):'zh','Fdesk-Login-Type': 'wechat','Fdesk-System-Code':'wechat'},
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.form.media.length
if(allowCount <= 0){
uni.showToast({
title:`最多只能上传${this.maxMedia}张图片`,
icon:'none'
})
return
}
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))).then(urls => {
urls.forEach(url => {
this.form.media.push(url)
})
}).catch(() => {
uni.showToast({
title:'图片上传失败,请重试',
icon:'none'
})
})
},
delfn(e){
this.form.media.splice(e.index, 1)
},
submitForm(){
// 必填校验
if(!this.form.category || !this.form.sku_id){
uni.showToast({
title: '请选择型号',
icon: 'none'
});
return
}
if(!this.form.content || !this.form.content.trim()){
uni.showToast({
title: '请输入文字内容',
icon: 'none'
});
return
}
if(!this.form.age){
uni.showToast({
title: '请选择样机新旧程度',
icon: 'none'
});
return
}
if(!this.form.price || !this.form.price.trim()){
uni.showToast({
title: '请输入折扣价格',
icon: 'none'
});
return
}
if(this.form.is_free===''){
uni.showToast({
title: '请选择包邮方式',
icon: 'none'
});
return
}
if(!this.form.media || this.form.media.length === 0){
uni.showToast({
title: '请至少上传一张图片',
icon: 'none'
});
return
}
if(!uni.getStorageSync('is_login')){
this.showlogin = true
return
}
const payload = {
age:this.form.age,
sku_id:this.form.sku_id,
content:this.form.content,
is_free:this.form.is_free,
price:this.form.price,
media:this.form.media,
id:this.postId
}
request({
url:'common/post/update',
method:'POST',
data:payload
}).then(res => {
uni.showToast({
title: res.data.message || '保存成功',
icon: 'none'
});
if(res.data.code ==200){
setTimeout(() => {
uni.navigateBack()
}, 1000);
}
})
}
}
}
</script>
<style lang="scss">
.prices{
display: flex;
align-items: center;
gap: 10px;
}
.detils{
margin-top: 40rpx;
overflow: hidden;
word-wrap: break-word;
word-break: break-all;
}
// 确保富文本中的图片自适应
.rich-wrap{
width: 100%;
overflow: hidden;
}
.cent{
display: flex;
align-items: center;
justify-content: space-around;
padding: 0px 10px 20px;
.btn1{
background-color: #fff;
border: 1px solid #206ff4;
color: #206ff4;
border-radius: 50px;
width: 46%;
margin: 10px 0px 20px;
height: 40px;
line-height: 40px;
}
.btn{
background-color: #206ff4;
color: white;
border-radius: 50px;
width: 46%;
margin: 10px 0px 20px;
height: 40px;
line-height: 40px;
}
}
.form-card{
background: #fff;
padding: 30rpx;
margin-bottom: 400rpx;
box-shadow: 0 12rpx 30rpx rgba(0,0,0,0.04);
}
.select-input{
height: 88rpx;
border-radius: 12rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.gradient-bg{
background: #F4F4F4;
color: #999999;
}
.conts{
background: #F4F4F4;
color: #999999;
padding: 20rpx;
border-radius: 10rpx;
}
.uni-input{
background: #F4F4F4;
padding: 20rpx 25rpx;
border-radius: 10rpx;
}
.category-content{
display: flex;
align-items: center;
flex: 1;
}
.category-text{
font-size: 28rpx;
color: #999999;
font-weight: 500;
}
.mag-top{
margin-top: 15px;
}
.content-textarea{
width: 100%;
border-radius: 16rpx;
max-height: 160rpx;
padding: 24rpx 0px;
font-size: 28rpx;
color: #2F323A;
box-sizing: border-box;
}
.media-upload-wrapper{
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.upload-icon-wrapper{
display: flex;
flex-direction: column;
align-items: center;
}
.addimg{
width: 215rpx;
height: 215rpx;
}
.help-text{
font-size: 24rpx;
color: #A6ABB7;
margin-top: 40rpx;
display: flex;
align-items: center;
gap: 8rpx;
}
.foot{
background: #fff;
position: fixed;
bottom: 0;
width: 100%;
z-index: 11;
}
.agreement{
margin: 20px 12px;
}
.primary-btn{
color: #fff;
border-radius: 20rpx;
height: 88rpx;
line-height: 88rpx;
font-size: 32rpx;
border: none;
}
.gradient-btn{
background: #3BC8BE;
margin-top: 20rpx;
}
.position{
position: relative;
}
.select-popup{
position: absolute;
left: 0;
right: 0;
top: 100%; /* 紧贴选择框下方 */
z-index: 1000;
}
.popup-panel{
position: absolute;
left: 0;
right: 0;
top: 8rpx; /* 与选择框留一点间距 */
background: #fff;
border-top-left-radius: 20rpx;
border-top-right-radius: 20rpx;
border-bottom-left-radius: 20rpx;
border-bottom-right-radius: 20rpx;
max-height: 40vh; /* 控制整体可视高度,下拉列表形式 */
display: flex;
flex-direction: column;
padding: 20rpx;
box-sizing: border-box;
z-index: 1001;
}
.popup-header{
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.popup-search{
flex: 1;
height: 72rpx;
border-radius: 10rpx;
background: #f4f4f4;
padding: 0 20rpx;
font-size: 28rpx;
color: #333;
margin-right: 12rpx;
box-sizing: border-box;
}
.popup-mask{
position: fixed;
inset: 0;
background: transparent;
}
.popup-close{
font-size: 28rpx;
color: #3BC8BE;
}
.popup-list{
flex: 1;
max-height: calc(40vh - 140rpx); /* 预留头部高度,保证可滚动 */
}
.popup-item{
padding: 20rpx 12rpx;
border-bottom: 1px solid #f0f0f0;
font-size: 28rpx;
color: #333;
}
.popup-item:last-child{
border-bottom: none;
}
.popup-empty{
text-align: center;
color: #999;
font-size: 26rpx;
padding: 30rpx 0;
}
.popupcontent{
background-size: 100% 100%;
padding: 26px 15px;
text-align: center;
background-repeat: no-repeat;
.title{
color: #333333;
font-size: 15px;
margin-bottom: 15px;
}
}
.log1{
width: 180px;
height: 180px;
}
.yhxy{
color: #999999;
font-size: 12px;
margin-bottom: 5px;
.xy{
color: #297EFF;
}
}
</style>