|
|
@@ -1,7 +1,7 @@
|
|
|
<!--
|
|
|
花店后台编辑商品
|
|
|
谁用:hdApp 管理端修改花束
|
|
|
- 解决问题:详情 specList 首项即主记录自身;提交时把首规格名称回写主表单 specName
|
|
|
+ 解决问题:详情 specList 首项即主记录自身;开启多规格时隐藏卡片外主图/封面,改在规格卡片内编辑(规格1回写主记录);提交时把首规格名称回写主表单 specName;多规格用可横滑 tab 切换卡片编辑
|
|
|
-->
|
|
|
<template>
|
|
|
<view class="app-content">
|
|
|
@@ -9,7 +9,135 @@
|
|
|
<form @submit="formSubmit">
|
|
|
<view class="module-com input-line-wrap">
|
|
|
|
|
|
- <view class="module-com input-line-wrap goods-wrap">
|
|
|
+ <tui-list-cell class="line-cell" :hover="false">
|
|
|
+ <view class="tui-title required">名称</view>
|
|
|
+ <input v-model="form.name" placeholder-class="phcolor" class="tui-input" name="name" placeholder="请输入"
|
|
|
+ maxlength="50" type="text" />
|
|
|
+ </tui-list-cell>
|
|
|
+
|
|
|
+ <view class="spec-switch-card">
|
|
|
+ <view class="spec-switch-left">
|
|
|
+ <view class="spec-switch-icon">▦</view>
|
|
|
+ <view>
|
|
|
+ <view class="spec-switch-title">启用多规格</view>
|
|
|
+ <view class="spec-switch-desc">开启后可设置商品多规格属性</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="spec-switch-right">
|
|
|
+ <switch :checked="form.specEnabled == 1" @change="toggleSpecs" color="#09C567" />
|
|
|
+ <text>{{ form.specEnabled == 1 ? '已开启' : '未开启' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view v-if="form.specEnabled == 1" class="spec-wrap">
|
|
|
+ <view class="spec-card">
|
|
|
+ <view class="spec-card-head">
|
|
|
+ <view class="spec-card-head-left">
|
|
|
+ <view class="spec-card-title">规格</view>
|
|
|
+ <!-- 新增规格放标题旁,避免卡片下方占一整行 -->
|
|
|
+ <button class="spec-add-btn" @click.stop.prevent="addSpec">新增规格</button>
|
|
|
+ </view>
|
|
|
+ <button v-if="form.specList.length > 1" class="spec-remove"
|
|
|
+ @click.stop.prevent="removeSpec(specTabIndex)">删除</button>
|
|
|
+ </view>
|
|
|
+ <!-- 规格 tab:每新增一组出现一项,超出屏宽后可左右滑动 -->
|
|
|
+ <scroll-view
|
|
|
+ class="spec-tab-scroll"
|
|
|
+ scroll-x
|
|
|
+ :scroll-into-view="specTabScrollId"
|
|
|
+ :scroll-with-animation="true"
|
|
|
+ :show-scrollbar="false"
|
|
|
+ >
|
|
|
+ <view class="spec-tab-list">
|
|
|
+ <view
|
|
|
+ v-for="(spec, index) in form.specList"
|
|
|
+ :id="specTabDomId(index)"
|
|
|
+ :key="index"
|
|
|
+ :class="specTabIndex == index ? 'spec-tab-item active' : 'spec-tab-item'"
|
|
|
+ @click.stop.prevent="selectSpecTab(index)"
|
|
|
+ >
|
|
|
+ <text class="spec-tab-text">{{ specTabLabel(index, spec) }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ <view v-if="currentSpec" class="spec-card-body">
|
|
|
+ <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title required">{{ currentSpecTitle }}名称</view>
|
|
|
+ <input :value="currentSpec.specName" @input="setSpecField(specTabIndex, 'specName', $event.detail.value)"
|
|
|
+ placeholder-class="phcolor" class="tui-input" placeholder="请输入规格名称" maxlength="50" type="text" />
|
|
|
+ </tui-list-cell>
|
|
|
+ <!-- 多规格时图片/封面改在卡片内编辑;规格1对应主记录,其余规格不传则沿用主图 -->
|
|
|
+ <view class="spec-image-wrap">
|
|
|
+ <view class="module-tit">
|
|
|
+ <span>图片</span>
|
|
|
+ <span class="app-color-3">{{ specTabIndex == 0 ? '(最多5张)' : '(不传则使用主商品图片,最多5张)' }}</span>
|
|
|
+ </view>
|
|
|
+ <view class="module-det">
|
|
|
+ <htz-image-upload :key="specTabIndex" :max="5" :compress="true" :value="currentSpec.currentImgData || []" :headers="headers"
|
|
|
+ @input="setSpecField(specTabIndex, 'currentImgData', $event)"
|
|
|
+ @uploadSuccess="specImgUploadSuccess($event, specTabIndex)" @imgDelete="specImgDeleteFn($event, specTabIndex)"
|
|
|
+ :action="getLoginInfo.imgUploadApi">
|
|
|
+ </htz-image-upload>
|
|
|
+ </view>
|
|
|
+ <view v-if="currentSpec.shopImg && currentSpec.shopImg.length > 0" class="spec-cover-row">
|
|
|
+ <view class="spec-cover-title">封面</view>
|
|
|
+ <view class="spec-cover-btns">
|
|
|
+ <button class="admin-button-com" :class="[(currentSpec.shortCover == item) ? '' : 'default']"
|
|
|
+ v-for="(item, imgIndex) in currentSpec.shopImg" :key="imgIndex"
|
|
|
+ @click.stop.prevent="selSpecCoverFn(specTabIndex, imgIndex)">第{{ imgIndex + 1 }}张</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title">价格类型</view>
|
|
|
+ <view class="btn-group">
|
|
|
+ <button class="admin-button-com middle" :class="[currentSpec.priceType == 1 ? '' : 'default']"
|
|
|
+ @click.stop.prevent="setSpecPriceType(specTabIndex, '1')">有价格</button>
|
|
|
+ <button class="admin-button-com middle" :class="[currentSpec.priceType == 0 ? '' : 'default']"
|
|
|
+ @click.stop.prevent="setSpecPriceType(specTabIndex, '0')">无价格</button>
|
|
|
+ </view>
|
|
|
+ </tui-list-cell>
|
|
|
+ <tui-list-cell v-if="currentSpec.priceType == 1" class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title required">价格</view>
|
|
|
+ <input :value="currentSpec.price" @input="setSpecField(specTabIndex, 'price', $event.detail.value)"
|
|
|
+ placeholder-class="phcolor" class="tui-input" placeholder="请输入价格" maxlength="10" type="digit" />
|
|
|
+ </tui-list-cell>
|
|
|
+ <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title">库存</view>
|
|
|
+ <view class="btn-group">
|
|
|
+ <button class="admin-button-com middle" :class="[currentSpec.stockSet == 0 ? '' : 'default']"
|
|
|
+ @click.stop.prevent="setSpecStockSet(specTabIndex, '0')">管够</button>
|
|
|
+ <button class="admin-button-com middle" :class="[currentSpec.stockSet == 1 ? '' : 'default']"
|
|
|
+ @click.stop.prevent="setSpecStockSet(specTabIndex, '1')">自定义</button>
|
|
|
+ </view>
|
|
|
+ </tui-list-cell>
|
|
|
+ <tui-list-cell v-if="currentSpec.stockSet == 1" class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title">库存</view>
|
|
|
+ <input :value="currentSpec.stock" @input="setSpecField(specTabIndex, 'stock', $event.detail.value)"
|
|
|
+ placeholder-class="phcolor" class="tui-input" placeholder="请输入库存数量" maxlength="10" type="number" />
|
|
|
+ </tui-list-cell>
|
|
|
+ <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title">用花支数</view>
|
|
|
+ <input :value="currentSpec.flowerNum" @input="setSpecField(specTabIndex, 'flowerNum', $event.detail.value)"
|
|
|
+ placeholder-class="phcolor" class="tui-input" placeholder="用于判断花束大小" maxlength="5" type="number" />
|
|
|
+ </tui-list-cell>
|
|
|
+ <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title">重量</view>
|
|
|
+ <input :value="currentSpec.weight" @input="setSpecField(specTabIndex, 'weight', $event.detail.value)"
|
|
|
+ placeholder-class="phcolor" class="tui-input" placeholder="公斤,可输入小数" maxlength="10" type="digit" />
|
|
|
+ </tui-list-cell>
|
|
|
+ <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
+ <view class="tui-title">已售数量</view>
|
|
|
+ <input :value="currentSpec.sold" @input="setSpecField(specTabIndex, 'sold', $event.detail.value)"
|
|
|
+ placeholder-class="phcolor" class="tui-input" placeholder="客户端展示:已售数量+实售数量" maxlength="10"
|
|
|
+ type="number" />
|
|
|
+ </tui-list-cell>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 开启多规格后主图/封面改在规格卡片内编辑,避免与规格1重复 -->
|
|
|
+ <view v-if="form.specEnabled != 1" class="module-com input-line-wrap goods-wrap">
|
|
|
<view class="module-tit">
|
|
|
<span>图片</span>
|
|
|
<span class="app-color-3">(最多5张)</span>
|
|
|
@@ -21,7 +149,7 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <tui-list-cell class="line-cell category-wrap" :hover="false">
|
|
|
+ <tui-list-cell v-if="form.specEnabled != 1" class="line-cell category-wrap" :hover="false">
|
|
|
<view class="tui-title required">封面</view>
|
|
|
<view class="tui-input">
|
|
|
<block v-if="coverImgData.length > 0">
|
|
|
@@ -54,106 +182,6 @@
|
|
|
</view>
|
|
|
</tui-list-cell>
|
|
|
|
|
|
- <tui-list-cell class="line-cell" :hover="false">
|
|
|
- <view class="tui-title required">名称</view>
|
|
|
- <input v-model="form.name" placeholder-class="phcolor" class="tui-input" name="name" placeholder="请输入"
|
|
|
- maxlength="50" type="text" />
|
|
|
- </tui-list-cell>
|
|
|
- <view class="spec-switch-card">
|
|
|
- <view class="spec-switch-left">
|
|
|
- <view class="spec-switch-icon">▦</view>
|
|
|
- <view>
|
|
|
- <view class="spec-switch-title">启用多规格</view>
|
|
|
- <view class="spec-switch-desc">开启后可设置商品多规格属性</view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="spec-switch-right">
|
|
|
- <switch :checked="form.specEnabled == 1" @change="toggleSpecs" color="#09C567" />
|
|
|
- <text>{{ form.specEnabled == 1 ? '已开启' : '未开启' }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view v-if="form.specEnabled == 1" class="spec-wrap">
|
|
|
- <view v-for="(spec, index) in form.specList" :key="spec.id || index" class="spec-card">
|
|
|
- <view class="spec-card-head">
|
|
|
- <view class="spec-card-title">规格组名称{{ index + 1 }}</view>
|
|
|
- <button v-if="form.specList.length > 1" class="spec-remove"
|
|
|
- @click.stop.prevent="removeSpec(index)">删除</button>
|
|
|
- </view>
|
|
|
- <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title required">规格组名称{{ index + 1 }}</view>
|
|
|
- <input :value="spec.specName" @input="setSpecField(index, 'specName', $event.detail.value)"
|
|
|
- placeholder-class="phcolor" class="tui-input" placeholder="请输入规格名称" maxlength="50" type="text" />
|
|
|
- </tui-list-cell>
|
|
|
- <view class="spec-image-wrap">
|
|
|
- <view class="module-tit">
|
|
|
- <span>图片</span>
|
|
|
- <span class="app-color-3">(不传则使用主商品图片,最多5张)</span>
|
|
|
- </view>
|
|
|
- <view class="module-det">
|
|
|
- <htz-image-upload :max="5" :compress="true" :value="spec.currentImgData || []" :headers="headers"
|
|
|
- @input="setSpecField(index, 'currentImgData', $event)"
|
|
|
- @uploadSuccess="specImgUploadSuccess($event, index)" @imgDelete="specImgDeleteFn($event, index)"
|
|
|
- :action="getLoginInfo.imgUploadApi">
|
|
|
- </htz-image-upload>
|
|
|
- </view>
|
|
|
- <view v-if="spec.shopImg && spec.shopImg.length > 0" class="spec-cover-row">
|
|
|
- <view class="spec-cover-title">封面</view>
|
|
|
- <view class="spec-cover-btns">
|
|
|
- <button class="admin-button-com" :class="[(spec.shortCover == item) ? '' : 'default']"
|
|
|
- v-for="(item, imgIndex) in spec.shopImg" :key="imgIndex"
|
|
|
- @click.stop.prevent="selSpecCoverFn(index, imgIndex)">第{{ imgIndex + 1 }}张</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title">价格类型</view>
|
|
|
- <view class="btn-group">
|
|
|
- <button class="admin-button-com middle" :class="[spec.priceType == 1 ? '' : 'default']"
|
|
|
- @click.stop.prevent="setSpecPriceType(index, '1')">有价格</button>
|
|
|
- <button class="admin-button-com middle" :class="[spec.priceType == 0 ? '' : 'default']"
|
|
|
- @click.stop.prevent="setSpecPriceType(index, '0')">无价格</button>
|
|
|
- </view>
|
|
|
- </tui-list-cell>
|
|
|
- <tui-list-cell v-if="spec.priceType == 1" class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title required">价格</view>
|
|
|
- <input :value="spec.price" @input="setSpecField(index, 'price', $event.detail.value)"
|
|
|
- placeholder-class="phcolor" class="tui-input" placeholder="请输入价格" maxlength="10" type="digit" />
|
|
|
- </tui-list-cell>
|
|
|
- <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title">库存</view>
|
|
|
- <view class="btn-group">
|
|
|
- <button class="admin-button-com middle" :class="[spec.stockSet == 0 ? '' : 'default']"
|
|
|
- @click.stop.prevent="setSpecStockSet(index, '0')">管够</button>
|
|
|
- <button class="admin-button-com middle" :class="[spec.stockSet == 1 ? '' : 'default']"
|
|
|
- @click.stop.prevent="setSpecStockSet(index, '1')">自定义</button>
|
|
|
- </view>
|
|
|
- </tui-list-cell>
|
|
|
- <tui-list-cell v-if="spec.stockSet == 1" class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title">库存</view>
|
|
|
- <input :value="spec.stock" @input="setSpecField(index, 'stock', $event.detail.value)"
|
|
|
- placeholder-class="phcolor" class="tui-input" placeholder="请输入库存数量" maxlength="10" type="number" />
|
|
|
- </tui-list-cell>
|
|
|
- <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title">用花支数</view>
|
|
|
- <input :value="spec.flowerNum" @input="setSpecField(index, 'flowerNum', $event.detail.value)"
|
|
|
- placeholder-class="phcolor" class="tui-input" placeholder="用于判断花束大小" maxlength="5" type="number" />
|
|
|
- </tui-list-cell>
|
|
|
- <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title">重量</view>
|
|
|
- <input :value="spec.weight" @input="setSpecField(index, 'weight', $event.detail.value)"
|
|
|
- placeholder-class="phcolor" class="tui-input" placeholder="公斤,可输入小数" maxlength="10" type="digit" />
|
|
|
- </tui-list-cell>
|
|
|
- <tui-list-cell class="line-cell spec-line" :hover="false">
|
|
|
- <view class="tui-title">已售数量</view>
|
|
|
- <input :value="spec.sold" @input="setSpecField(index, 'sold', $event.detail.value)"
|
|
|
- placeholder-class="phcolor" class="tui-input" placeholder="客户端展示:已售数量+实售数量" maxlength="10"
|
|
|
- type="number" />
|
|
|
- </tui-list-cell>
|
|
|
- </view>
|
|
|
- <button class="spec-add-btn" @click.stop.prevent="addSpec">新增规格组</button>
|
|
|
- </view>
|
|
|
-
|
|
|
<tui-list-cell v-if="form.specEnabled != 1" class="line-cell" :hover="false">
|
|
|
<view class="tui-title">价格类型</view>
|
|
|
<view class="btn-group">
|
|
|
@@ -306,7 +334,10 @@ export default {
|
|
|
},
|
|
|
categoryData: [], useCaseData: [],
|
|
|
categoryIds: [],
|
|
|
- sn: 0
|
|
|
+ sn: 0,
|
|
|
+ // 当前展示的规格 tab 下标;仅前端展示用,不随表单提交
|
|
|
+ specTabIndex: 0,
|
|
|
+ specTabScrollId: 'specTab0'
|
|
|
};
|
|
|
},
|
|
|
onLoad() {
|
|
|
@@ -330,6 +361,26 @@ export default {
|
|
|
soldBlurHandler() {
|
|
|
return this.createBlurHandler('sold');
|
|
|
},
|
|
|
+ /**
|
|
|
+ * 当前 tab 对应的规格对象,供卡片内容绑定
|
|
|
+ */
|
|
|
+ currentSpec() {
|
|
|
+ const list = this.form.specList || [];
|
|
|
+ if (!list.length) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ const index = this.specTabIndex;
|
|
|
+ if (index < 0 || index >= list.length) {
|
|
|
+ return list[0];
|
|
|
+ }
|
|
|
+ return list[index];
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 卡片标题用「规格N」,与 tab 序号保持一致
|
|
|
+ */
|
|
|
+ currentSpecTitle() {
|
|
|
+ return '规格' + (this.specTabIndex + 1);
|
|
|
+ },
|
|
|
},
|
|
|
methods: {
|
|
|
init() { },
|
|
|
@@ -357,6 +408,24 @@ export default {
|
|
|
return ''
|
|
|
}
|
|
|
},
|
|
|
+ /**
|
|
|
+ * 从图片列表解析真正的封面短路径。
|
|
|
+ * 旧表单默认 cover='1' 表示「第1张」,不是路径;只有 preferred 真在 shopImg 里才采用。
|
|
|
+ * @param {Array} shopImg 短路径图片列表
|
|
|
+ * @param {*} preferredCover 用户选中的封面或历史 cover
|
|
|
+ * @returns {string} 有效短路径,没有图则空字符串
|
|
|
+ */
|
|
|
+ resolveCoverPath(shopImg, preferredCover) {
|
|
|
+ const list = Array.isArray(shopImg) ? shopImg : [];
|
|
|
+ if (!list.length) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ const preferred = preferredCover == null ? '' : String(preferredCover);
|
|
|
+ if (preferred && list.indexOf(preferred) !== -1) {
|
|
|
+ return preferred;
|
|
|
+ }
|
|
|
+ return list[0] || '';
|
|
|
+ },
|
|
|
imgDeleteFn(res) {
|
|
|
const index = res.index
|
|
|
// 先记录对应的短路径(无域名、无参数)
|
|
|
@@ -449,6 +518,8 @@ export default {
|
|
|
this.form.specEnabled = Number(res.data.specEnabled || res.data.goodsStyle || 0);
|
|
|
// 详情 specList 首项即主记录自身(id=主商品id),createSpecData 直接兼容
|
|
|
this.form.specList = (res.data.specList || []).map(item => this.createSpecData(item));
|
|
|
+ this.specTabIndex = 0;
|
|
|
+ this.scrollToSpecTab();
|
|
|
|
|
|
// 设置价格类型
|
|
|
if (res.data.priceType == "0") {
|
|
|
@@ -486,6 +557,8 @@ export default {
|
|
|
this.form.cover = this.form.shopImg[0];
|
|
|
this.form.shortCover = this.form.shopImg[0];
|
|
|
}
|
|
|
+ // 多规格时规格1在卡片内编辑主图,需把主记录图片灌进首规格
|
|
|
+ this.syncFormImagesToFirstSpec()
|
|
|
getClass().then(res => {
|
|
|
if (res.code && res.code == 1) {
|
|
|
this.categoryData = res.data
|
|
|
@@ -518,7 +591,8 @@ export default {
|
|
|
createSpecData(data = {}) {
|
|
|
const shopImg = Array.isArray(data.shortImgList) ? data.shortImgList : (Array.isArray(data.shopImg) ? data.shopImg : []);
|
|
|
const currentImgData = Array.isArray(data.currentImgData) ? data.currentImgData : (Array.isArray(data.smallImgList) ? data.smallImgList : []);
|
|
|
- const shortCover = data.shortCover || (shopImg.includes(data.cover) ? data.cover : '') || shopImg[0] || '';
|
|
|
+ // 封面必须是图片列表中的路径,避免把历史占位值 '1' 当成封面
|
|
|
+ const shortCover = this.resolveCoverPath(shopImg, data.shortCover || data.cover);
|
|
|
return {
|
|
|
id: data.id || 0,
|
|
|
specName: data.specName || '',
|
|
|
@@ -535,8 +609,70 @@ export default {
|
|
|
shortCover
|
|
|
}
|
|
|
},
|
|
|
- addSpec() { this.form.specList.push(this.createSpecData()) },
|
|
|
- removeSpec(index) { this.form.specList.splice(index, 1) },
|
|
|
+ /**
|
|
|
+ * 生成规格 tab 的 DOM id,供 scroll-into-view 定位(必须以字母开头)
|
|
|
+ * @param {number} index 规格下标
|
|
|
+ * @returns {string}
|
|
|
+ */
|
|
|
+ specTabDomId(index) {
|
|
|
+ return 'specTab' + index;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * tab 文案:已填规格名则展示名称,便于多规格辨认;否则回退为「规格N」
|
|
|
+ * @param {number} index 规格下标
|
|
|
+ * @param {Object} spec 规格对象
|
|
|
+ * @returns {string}
|
|
|
+ */
|
|
|
+ specTabLabel(index, spec) {
|
|
|
+ const name = spec && spec.specName ? String(spec.specName).trim() : '';
|
|
|
+ if (name) {
|
|
|
+ return name;
|
|
|
+ }
|
|
|
+ return '规格' + (index + 1);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 横向滚动定位到当前 tab。先清空再赋值,避免 scroll-into-view 同值不触发
|
|
|
+ */
|
|
|
+ scrollToSpecTab() {
|
|
|
+ this.specTabScrollId = '';
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.specTabScrollId = this.specTabDomId(this.specTabIndex);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 点击 tab 切换当前规格卡片
|
|
|
+ * @param {number} index 目标规格下标
|
|
|
+ */
|
|
|
+ selectSpecTab(index) {
|
|
|
+ this.specTabIndex = index;
|
|
|
+ this.scrollToSpecTab();
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 新增规格组后切到新 tab,让用户直接编辑
|
|
|
+ */
|
|
|
+ addSpec() {
|
|
|
+ this.form.specList.push(this.createSpecData());
|
|
|
+ this.specTabIndex = this.form.specList.length - 1;
|
|
|
+ this.scrollToSpecTab();
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 删除指定规格。删当前项后校正下标,避免越界或指向错位的规格
|
|
|
+ * @param {number} index 要删除的规格下标
|
|
|
+ */
|
|
|
+ removeSpec(index) {
|
|
|
+ if (this.form.specList.length <= 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.form.specList.splice(index, 1);
|
|
|
+ const lastIndex = this.form.specList.length - 1;
|
|
|
+ // 删的是当前或更前的项时,下标要跟着列表左移
|
|
|
+ if (this.specTabIndex > lastIndex) {
|
|
|
+ this.specTabIndex = lastIndex;
|
|
|
+ } else if (this.specTabIndex > index) {
|
|
|
+ this.specTabIndex = this.specTabIndex - 1;
|
|
|
+ }
|
|
|
+ this.scrollToSpecTab();
|
|
|
+ },
|
|
|
setSpecField(index, field, value) {
|
|
|
const spec = this.form.specList[index] || this.createSpecData();
|
|
|
const nextSpec = { ...spec, [field]: value };
|
|
|
@@ -548,7 +684,7 @@ export default {
|
|
|
if (uploadRes.code == 1) {
|
|
|
const currentImgData = [...(spec.currentImgData || []), uploadRes.data.smallUrl];
|
|
|
const shopImg = [...(spec.shopImg || []), uploadRes.data.shortUrl];
|
|
|
- const shortCover = spec.shortCover || uploadRes.data.shortUrl;
|
|
|
+ const shortCover = this.resolveCoverPath(shopImg, spec.shortCover || spec.cover);
|
|
|
this.$set(this.form.specList, index, {
|
|
|
...spec,
|
|
|
currentImgData,
|
|
|
@@ -565,7 +701,7 @@ export default {
|
|
|
const shopImg = [...(spec.shopImg || [])];
|
|
|
const shortDelPath = (shopImg[imgIndex] || this.normalizeImgPath(res.del)).replace(/^\//, '');
|
|
|
shopImg.splice(imgIndex, 1);
|
|
|
- const shortCover = shopImg.includes(spec.shortCover) ? spec.shortCover : (shopImg[0] || '');
|
|
|
+ const shortCover = this.resolveCoverPath(shopImg, spec.shortCover || spec.cover);
|
|
|
this.$set(this.form.specList, index, {
|
|
|
...spec,
|
|
|
currentImgData,
|
|
|
@@ -586,11 +722,57 @@ export default {
|
|
|
shortCover
|
|
|
});
|
|
|
},
|
|
|
+ /**
|
|
|
+ * 开启多规格时把当前主图/封面灌进规格1,避免卡片内上传区空白
|
|
|
+ */
|
|
|
+ syncFormImagesToFirstSpec() {
|
|
|
+ if (this.form.specEnabled != 1 || !this.form.specList || !this.form.specList.length) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const firstSpec = this.form.specList[0];
|
|
|
+ const shopImg = Array.isArray(this.form.shopImg) ? this.form.shopImg.slice() : [];
|
|
|
+ const currentImgData = Array.isArray(this.currentImgData) ? this.currentImgData.slice() : [];
|
|
|
+ const cover = this.resolveCoverPath(shopImg, this.form.shortCover || this.form.cover);
|
|
|
+ this.$set(this.form.specList, 0, {
|
|
|
+ ...firstSpec,
|
|
|
+ shopImg,
|
|
|
+ currentImgData,
|
|
|
+ cover,
|
|
|
+ shortCover: cover
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 多规格时规格1在卡片内编辑图片/封面,关闭多规格或提交前把规格1回写到主表单
|
|
|
+ */
|
|
|
+ syncFirstSpecImagesToForm() {
|
|
|
+ if (this.form.specEnabled != 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const firstSpec = (this.form.specList && this.form.specList[0]) || null;
|
|
|
+ if (!firstSpec) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.form.shopImg = Array.isArray(firstSpec.shopImg) ? firstSpec.shopImg.slice() : [];
|
|
|
+ this.currentImgData = Array.isArray(firstSpec.currentImgData) ? firstSpec.currentImgData.slice() : [];
|
|
|
+ const cover = this.resolveCoverPath(this.form.shopImg, firstSpec.shortCover || firstSpec.cover);
|
|
|
+ this.form.cover = cover;
|
|
|
+ this.form.shortCover = cover;
|
|
|
+ },
|
|
|
toggleSpecs(e) {
|
|
|
- this.form.specEnabled = e.detail.value ? 1 : 0;
|
|
|
- if (this.form.specEnabled && !this.form.specList.length) {
|
|
|
- this.form.specList.push(this.createSpecData(this.form))
|
|
|
+ const enabled = e.detail.value ? 1 : 0;
|
|
|
+ // 关闭前先把规格1图片回写主表单,此时 specEnabled 仍为 1
|
|
|
+ if (!enabled) {
|
|
|
+ this.syncFirstSpecImagesToForm();
|
|
|
}
|
|
|
+ this.form.specEnabled = enabled;
|
|
|
+ if (this.form.specEnabled) {
|
|
|
+ if (!this.form.specList.length) {
|
|
|
+ this.form.specList.push(this.createSpecData(this.form));
|
|
|
+ }
|
|
|
+ this.syncFormImagesToFirstSpec();
|
|
|
+ }
|
|
|
+ this.specTabIndex = 0;
|
|
|
+ this.scrollToSpecTab();
|
|
|
},
|
|
|
setSpecPriceType(index, type) {
|
|
|
const spec = this.form.specList[index] || this.createSpecData();
|
|
|
@@ -674,16 +856,20 @@ export default {
|
|
|
return '';
|
|
|
}
|
|
|
if (!this.form.specList.length) {
|
|
|
- return '请至少添加一个规格组';
|
|
|
+ return '请至少添加一个规格';
|
|
|
}
|
|
|
for (let i = 0; i < this.form.specList.length; i++) {
|
|
|
const spec = this.form.specList[i];
|
|
|
- const name = `规格组${i + 1}`;
|
|
|
+ const name = `规格${i + 1}`;
|
|
|
if (!spec.specName || !String(spec.specName).trim()) {
|
|
|
+ this.specTabIndex = i;
|
|
|
+ this.scrollToSpecTab();
|
|
|
return `请输入${name}名称`;
|
|
|
}
|
|
|
const numErr = this.validateGoodsNumberFields(spec, name);
|
|
|
if (numErr) {
|
|
|
+ this.specTabIndex = i;
|
|
|
+ this.scrollToSpecTab();
|
|
|
return numErr;
|
|
|
}
|
|
|
}
|
|
|
@@ -698,7 +884,7 @@ export default {
|
|
|
formData.specName = '';
|
|
|
return formData;
|
|
|
}
|
|
|
- formData.specList = formData.specList.map(item => {
|
|
|
+ formData.specList = formData.specList.map((item, index) => {
|
|
|
const spec = this.createSpecData(item);
|
|
|
spec.priceType = parseInt(spec.priceType);
|
|
|
spec.price = spec.priceType === 1 ? parseFloat(spec.price || 0) : 0;
|
|
|
@@ -708,7 +894,13 @@ export default {
|
|
|
spec.weight = parseFloat(spec.weight || 0);
|
|
|
spec.sold = parseInt(spec.sold || 0);
|
|
|
spec.shopImg = Array.isArray(spec.shopImg) ? spec.shopImg : [];
|
|
|
- spec.cover = spec.shopImg.length > 0 ? (spec.shortCover || spec.cover || spec.shopImg[0]) : '';
|
|
|
+ spec.cover = this.resolveCoverPath(spec.shopImg, spec.shortCover || spec.cover);
|
|
|
+ // 规格1即主记录:把卡片内编辑的图片/封面回写到主表单字段
|
|
|
+ if (index == 0) {
|
|
|
+ formData.shopImg = spec.shopImg.slice();
|
|
|
+ formData.cover = spec.cover;
|
|
|
+ formData.shortCover = spec.cover;
|
|
|
+ }
|
|
|
delete spec.currentImgData;
|
|
|
delete spec.shortCover;
|
|
|
return spec;
|
|
|
@@ -778,25 +970,18 @@ export default {
|
|
|
// return false
|
|
|
// }
|
|
|
|
|
|
+ // 多规格时图片在规格1卡片内,先回写再校验主记录是否有图
|
|
|
+ if (this.form.specEnabled == 1) {
|
|
|
+ this.syncFirstSpecImagesToForm();
|
|
|
+ }
|
|
|
if (this.$util.isEmpty(this.form.shopImg)) {
|
|
|
this.$msg("请上传图片")
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
let form = JSON.parse(JSON.stringify(this.form))
|
|
|
- // 修复封面处理逻辑
|
|
|
- if (this.form.shopImg.length > 0) {
|
|
|
- // 找到当前封面在图片列表中的索引
|
|
|
- const coverIndex = this.form.shopImg.findIndex(img => img === this.form.shortCover);
|
|
|
- if (coverIndex !== -1) {
|
|
|
- form.cover = this.form.shopImg[coverIndex];
|
|
|
- } else {
|
|
|
- // 如果找不到封面,使用第一张图片
|
|
|
- form.cover = this.form.shopImg[0];
|
|
|
- }
|
|
|
- } else {
|
|
|
- form.cover = '';
|
|
|
- }
|
|
|
+ // 提交前按图片列表校正封面,避免把序号占位值写入 xhGoods.cover
|
|
|
+ form.cover = this.resolveCoverPath(this.form.shopImg, this.form.shortCover || this.form.cover);
|
|
|
form = this.normalizeSpecSubmitData(form)
|
|
|
updateGoodsData({ ...form }).then(res => {
|
|
|
//this.removeFromSaveDirect()
|
|
|
@@ -1042,39 +1227,111 @@ export default {
|
|
|
}
|
|
|
|
|
|
.spec-wrap {
|
|
|
- padding: 0 0 20upx;
|
|
|
+ padding: 0 20upx 20upx;
|
|
|
}
|
|
|
|
|
|
.spec-card {
|
|
|
- margin-bottom: 24upx;
|
|
|
+ margin-bottom: 20upx;
|
|
|
background: #ffffff;
|
|
|
- border-top: 1px solid #eeeeee;
|
|
|
- border-bottom: 1px solid #eeeeee;
|
|
|
+ border: 3px solid #e8e8e8;
|
|
|
+ border-radius: 16upx;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.spec-card-head {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
- padding: 20upx 30upx 0;
|
|
|
+ padding: 20upx 24upx 12upx;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-card-head-left {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
.spec-card-title {
|
|
|
font-size: 30upx;
|
|
|
color: #222222;
|
|
|
font-weight: 700;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.spec-remove {
|
|
|
- margin: 0;
|
|
|
+ margin: 0 0 0 16upx;
|
|
|
padding: 0 20upx;
|
|
|
- height: 54upx;
|
|
|
- line-height: 54upx;
|
|
|
+ height: 52upx;
|
|
|
+ line-height: 52upx;
|
|
|
border: 1px solid #eeeeee;
|
|
|
border-radius: 8upx;
|
|
|
background: #ffffff;
|
|
|
color: #999999;
|
|
|
font-size: 24upx;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-add-btn {
|
|
|
+ margin: 0 0 0 16upx;
|
|
|
+ padding: 0 20upx;
|
|
|
+ height: 52upx;
|
|
|
+ line-height: 52upx;
|
|
|
+ border-radius: 8upx;
|
|
|
+ background: #09C567;
|
|
|
+ color: #ffffff;
|
|
|
+ font-size: 24upx;
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-left: 40upx;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-add-btn::after,
|
|
|
+.spec-remove::after {
|
|
|
+ border: none;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-tab-scroll {
|
|
|
+ width: 100%;
|
|
|
+ height: 80upx;
|
|
|
+ white-space: nowrap;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-tab-list {
|
|
|
+ display: inline-flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ height: 80upx;
|
|
|
+ padding: 0 12upx;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-tab-item {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 80upx;
|
|
|
+ padding: 0 28upx;
|
|
|
+ margin-right: 8upx;
|
|
|
+ font-size: 28upx;
|
|
|
+ color: #666666;
|
|
|
+ flex-shrink: 0;
|
|
|
+ max-width: 240upx;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-tab-item.active {
|
|
|
+ color: #09C567;
|
|
|
+ font-weight: 700;
|
|
|
+ border-bottom: 4upx solid #09C567;
|
|
|
+}
|
|
|
+
|
|
|
+.spec-tab-text {
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
|
|
|
.spec-line {
|
|
|
@@ -1119,15 +1376,6 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.spec-add-btn {
|
|
|
- margin: 20upx 24upx 0;
|
|
|
- height: 88upx;
|
|
|
- line-height: 88upx;
|
|
|
- border-radius: 8upx;
|
|
|
- background: #09C567;
|
|
|
- color: #ffffff;
|
|
|
- font-size: 30upx;
|
|
|
-}
|
|
|
|
|
|
/* 确保所有选中的按钮都使用相同的颜色 */
|
|
|
.admin-button-com:not(.default) {
|