后台商品「高级设置」新增 media_order(1=主图在前 2=视频在前), 前端按其决定
商品详情页媒体区默认展示哪一个, 以及底部「图片/视频」切换按钮的排列顺序.
改动:
- goods-detail-view 组件:
- 新增 applyMediaOrder() 按 media_order 决定默认展示主图还是视频
- 新增 mediaOrderKey 计算属性 + watcher: 只在「媒体显示顺序/视频地址」
变化时重新应用, 避免用户手动切到「视频」后被详情数据深层变更打回
- 新增 mediaTabs 计算属性: 切换按钮顺序跟随配置
- 切换按钮改用 switchMediaTab(), 切回主图时暂停视频
- common/js/goods_detail_base.js: 父级那行 switchMedia 赋值原本是死代码
(父级无此 data 字段, 无模板读取), 改成与新规则一致, 避免两处规则打架
447 lines
15 KiB
JavaScript
447 lines
15 KiB
JavaScript
// 商品详情业务
|
||
import htmlParser from '@/common/js/html-parser';
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
skuId: 0,
|
||
goodsId: 0,
|
||
// 商品详情
|
||
goodsSkuDetail: {
|
||
goods_id: 0,
|
||
goods_service: []
|
||
},
|
||
preview: 0, //是否开启预览,0:不开启,1:开启
|
||
//评价
|
||
contactData: {
|
||
title: '',
|
||
path: '',
|
||
img: ''
|
||
},
|
||
|
||
shareQuery: '', // 分享参数
|
||
shareUrl: '', // 分享链接
|
||
|
||
source_member: 0, //分享人的id
|
||
chatRoomParams: {}, // 联系客服参数
|
||
isIphoneX: false, //判断手机是否是iphoneX以上
|
||
whetherCollection: 0,
|
||
posterParams: {}, //海报所需参数
|
||
shareImg: '',
|
||
navbarData: {
|
||
title: '',
|
||
topNavColor: "#ffffff",
|
||
topNavBg: false,
|
||
navBarSwitch: true, // 导航栏是否显示
|
||
textNavColor: "#333333",
|
||
moreLink: {
|
||
name: ""
|
||
},
|
||
navStyle: 1,
|
||
bgUrl: '',
|
||
textImgPosLink: 'left'
|
||
},
|
||
}
|
||
},
|
||
onLoad(data) {
|
||
//刷新多语言
|
||
this.$langConfig.refresh();
|
||
// #ifdef MP-ALIPAY
|
||
let options = my.getLaunchOptionsSync();
|
||
options.query && Object.assign(data, options.query);
|
||
// #endif
|
||
|
||
this.preview = data.preview || 0;
|
||
this.isIphoneX = this.$util.uniappIsIPhoneX();
|
||
|
||
if (data.source_member) {
|
||
uni.setStorageSync('source_member', data.source_member);
|
||
this.source_member = data.source_member;
|
||
}
|
||
//记录分享关系
|
||
if (this.storeToken && uni.getStorageSync('source_member')) {
|
||
this.$util.onSourceMember(uni.getStorageSync('source_member'));
|
||
}
|
||
|
||
// 小程序扫码进入
|
||
if (data.scene) {
|
||
var sceneParams = decodeURIComponent(data.scene);
|
||
sceneParams = sceneParams.split('&');
|
||
if (sceneParams.length) {
|
||
sceneParams.forEach(item => {
|
||
if (item.indexOf('m') != -1) uni.setStorageSync('source_member', item.split('-')[1]);
|
||
if (item.indexOf('is_test') != -1) uni.setStorageSync('is_test', 1);
|
||
});
|
||
}
|
||
}
|
||
},
|
||
onShow() {
|
||
},
|
||
methods: {
|
||
// 处理商品详情数据
|
||
async handleGoodsSkuData() {
|
||
// 鸿蒙兼容: 外层包 try/catch, 任何同步抛错都不会导致 page 整体空白
|
||
try {
|
||
if (this.goodsSkuDetail.config) {
|
||
this.navbarData.navBarSwitch = this.goodsSkuDetail.config.nav_bar_switch;
|
||
}
|
||
|
||
this.whetherCollection = this.goodsSkuDetail.is_collect; // 用户关注商品状态
|
||
|
||
this.modifyGoodsInfo();
|
||
|
||
// 初始化商品详情视图数据
|
||
try {
|
||
if (this.$refs && this.$refs.goodsDetailView) {
|
||
this.$refs.goodsDetailView.init({
|
||
sku_id: this.skuId,
|
||
goods_id: this.goodsSkuDetail.goods_id,
|
||
preview: this.preview,
|
||
source_member: this.source_member,
|
||
posterParams: this.posterParams,
|
||
posterApi: this.posterApi,
|
||
shareUrl: this.shareUrl,
|
||
goodsRoute: this.goodsRoute,
|
||
isVirtual: this.goodsSkuDetail.is_virtual,
|
||
deliveryType: this.goodsSkuDetail.express_type,
|
||
whetherCollection: this.goodsSkuDetail.is_collect,
|
||
evaluateConfig: this.goodsSkuDetail.evaluate_config,
|
||
evaluateList: this.goodsSkuDetail.evaluate_list,
|
||
evaluateCount: this.goodsSkuDetail.evaluate_count
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.error('goodsDetailView.init failed:', e);
|
||
}
|
||
|
||
//媒体
|
||
// 媒体显示顺序以后台商品「高级设置 → 媒体显示顺序」media_order 为准
|
||
// (1=主图在前 2=视频在前); 真正渲染媒体区的是 goods-detail-view 组件,
|
||
// 组件内部按同一规则切换, 这里保持一致避免两处规则打架
|
||
if (this.goodsSkuDetail.video_url) {
|
||
this.switchMedia = (Number(this.goodsSkuDetail.media_order) === 2) ? "video" : "img";
|
||
}
|
||
|
||
if (!Array.isArray(this.goodsSkuDetail.sku_images)) {
|
||
if (this.goodsSkuDetail.sku_images) this.goodsSkuDetail.sku_images = this.goodsSkuDetail.sku_images.split(",");
|
||
else this.goodsSkuDetail.sku_images = [];
|
||
}
|
||
|
||
// 多规格时合并主图
|
||
if (this.goodsSkuDetail.goods_spec_format && this.goodsSkuDetail.goods_image) {
|
||
|
||
if (!Array.isArray(this.goodsSkuDetail.goods_image)) this.goodsSkuDetail.goods_image = this.goodsSkuDetail.goods_image.split(",");
|
||
|
||
this.goodsSkuDetail.sku_images = this.goodsSkuDetail.goods_image.concat(this.goodsSkuDetail.sku_images);
|
||
}
|
||
|
||
let maxHeight = '';
|
||
let systemInfo = uni.getSystemInfoSync();
|
||
this.goodsSkuDetail.goods_image_list.forEach((item, index) => {
|
||
if (typeof item.pic_spec == "string")
|
||
item.pic_spec = item.pic_spec.split('*');
|
||
|
||
let ratio = item.pic_spec[0] / systemInfo.windowWidth;
|
||
item.pic_spec[0] = item.pic_spec[0] / ratio;
|
||
item.pic_spec[1] = item.pic_spec[1] / ratio;
|
||
|
||
if (!maxHeight || maxHeight > item.pic_spec[1]) {
|
||
maxHeight = item.pic_spec[1];
|
||
}
|
||
});
|
||
this.goodsSkuDetail.swiperHeight = maxHeight + 'px';
|
||
|
||
this.goodsSkuDetail.unit = this.goodsSkuDetail.unit || this.$lang('unit.piece');
|
||
|
||
// 当前商品SKU规格
|
||
if (this.goodsSkuDetail.sku_spec_format) this.goodsSkuDetail.sku_spec_format = JSON.parse(this.goodsSkuDetail.sku_spec_format);
|
||
|
||
// 商品属性
|
||
if (this.goodsSkuDetail.goods_attr_format) {
|
||
let goods_attr_format = JSON.parse(this.goodsSkuDetail.goods_attr_format);
|
||
this.goodsSkuDetail.goods_attr_format = this.$util.unique(goods_attr_format, "attr_id");
|
||
for (var i = 0; i < this.goodsSkuDetail.goods_attr_format.length; i++) {
|
||
for (var j = 0; j < goods_attr_format.length; j++) {
|
||
if (this.goodsSkuDetail.goods_attr_format[i].attr_id == goods_attr_format[j].attr_id && this.goodsSkuDetail.goods_attr_format[i].attr_value_id != goods_attr_format[j].attr_value_id) {
|
||
this.goodsSkuDetail.goods_attr_format[i].attr_value_name += "、" + goods_attr_format[j].attr_value_name;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 商品SKU格式
|
||
if (this.goodsSkuDetail.goods_spec_format) this.goodsSkuDetail.goods_spec_format = JSON.parse(this.goodsSkuDetail.goods_spec_format);
|
||
|
||
// 商品详情
|
||
if (this.goodsSkuDetail.goods_content) this.goodsSkuDetail.goods_content = (this.goodsSkuDetail.goods_content);
|
||
// if (this.goodsSkuDetail.goods_content) this.goodsSkuDetail.goods_content = htmlParser(this.goodsSkuDetail.goods_content);
|
||
|
||
//商品服务
|
||
if (this.goodsSkuDetail.goods_service) {
|
||
for (let i in this.goodsSkuDetail.goods_service) {
|
||
this.goodsSkuDetail.goods_service[i]['icon'] = this.goodsSkuDetail.goods_service[i]['icon'] ? JSON.parse(this.goodsSkuDetail.goods_service[i]['icon']) : '';
|
||
}
|
||
}
|
||
|
||
this.contactData = {
|
||
title: this.goodsSkuDetail.sku_name,
|
||
path: this.shareUrl,
|
||
img: this.$util.img(this.goodsSkuDetail.sku_image, {
|
||
size: 'big'
|
||
})
|
||
};
|
||
try {
|
||
if (this.$refs && this.$refs.goodsPromotion) {
|
||
this.$refs.goodsPromotion.refresh(this.goodsSkuDetail.goods_promotion);
|
||
}
|
||
} catch (e) { console.error('goodsPromotion refresh failed:', e); }
|
||
|
||
// 性能优化: 商品表单(留言表单)仅在商品详情打开"立即留言"弹窗时才需要,
|
||
// 不再阻塞首次渲染。改为在后台并发请求, 用户真正打开弹窗时数据已就绪。
|
||
try {
|
||
this._goodsFormPromise = this.getGoodsForm();
|
||
} catch (e) {
|
||
console.error('getGoodsForm kicked off failed:', e);
|
||
}
|
||
} catch (e) {
|
||
console.error('handleGoodsSkuData failed:', e);
|
||
}
|
||
},
|
||
/**
|
||
* 刷新商品详情数据
|
||
* @param {Object} data
|
||
*/
|
||
refreshGoodsSkuDetail(data) {
|
||
this.goodsSkuDetail = Object.assign({}, this.goodsSkuDetail, data);
|
||
if (this.$refs.goodsPromotion) this.$refs.goodsPromotion.refresh(this.goodsSkuDetail.goods_promotion);
|
||
if (this.$refs.goodsDetailView) {
|
||
|
||
// 初始化商品详情视图数据
|
||
this.goodsSkuDetail.unit = this.goodsSkuDetail.unit || this.$lang('unit.piece');
|
||
|
||
// 解决轮播图数量不一致时,切换到第一个
|
||
if (this.swiperCurrent > this.goodsSkuDetail.sku_images.length) {
|
||
this.swiperAutoplay = true;
|
||
this.swiperCurrent = 1;
|
||
setTimeout(() => {
|
||
this.swiperAutoplay = false;
|
||
}, 40);
|
||
}
|
||
|
||
}
|
||
this.navbarData.title = this.goodsSkuDetail.goods_name.length > 9 ? this.goodsSkuDetail.goods_name.substr(0, 9) + "..." : this.goodsSkuDetail.goods_name;
|
||
this.$langConfig.title(this.navbarData.title);
|
||
|
||
if (this.goodsSkuDetail.membercard) {
|
||
this.membercard = this.goodsSkuDetail.membercard;
|
||
}
|
||
},
|
||
goodsDetailViewInit() {
|
||
// 初始化商品详情视图数据
|
||
if (this.$refs.goodsDetailView) this.$refs.goodsDetailView.init({
|
||
sku_id: this.skuId,
|
||
goods_id: this.goodsSkuDetail.goods_id,
|
||
preview: this.preview,
|
||
source_member: this.source_member,
|
||
posterParams: this.posterParams,
|
||
posterApi: this.posterApi,
|
||
shareUrl: this.shareUrl,
|
||
goodsRoute: this.goodsRoute,
|
||
isVirtual: this.goodsSkuDetail.is_virtual,
|
||
deliveryType: this.goodsSkuDetail.express_type,
|
||
whetherCollection: this.goodsSkuDetail.is_collect,
|
||
evaluateConfig: this.goodsSkuDetail.evaluate_config,
|
||
evaluateList: this.goodsSkuDetail.evaluate_list,
|
||
evaluateCount: this.goodsSkuDetail.evaluate_count
|
||
});
|
||
},
|
||
goHome() {
|
||
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
||
this.$util.redirectTo(this.$util.INDEX_PAGE_URL);
|
||
},
|
||
goCart() {
|
||
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
||
this.$util.redirectTo('/pages_goods/cart');
|
||
},
|
||
//-------------------------------------关注-------------------------------------
|
||
//更新商品信息
|
||
modifyGoodsInfo() {
|
||
if (this.preview) return; // 开启预览,禁止任何操作和跳转
|
||
//更新商品点击量
|
||
this.$api.sendRequest({
|
||
url: "/api/goods/modifyclicks",
|
||
data: {
|
||
sku_id: this.skuId
|
||
},
|
||
success: res => {
|
||
}
|
||
});
|
||
|
||
//添加足迹
|
||
this.$api.sendRequest({
|
||
url: "/api/goodsbrowse/add",
|
||
data: {
|
||
goods_id: this.goodsSkuDetail.goods_id,
|
||
sku_id: this.skuId
|
||
},
|
||
success: res => {
|
||
}
|
||
});
|
||
},
|
||
//-------------------------------------关注-------------------------------------
|
||
async editCollection() {
|
||
if (this.$refs.goodsDetailView) {
|
||
this.whetherCollection = await this.$refs.goodsDetailView.collection();
|
||
}
|
||
},
|
||
openSharePopup() {
|
||
if (this.$refs.goodsDetailView) {
|
||
this.$refs.goodsDetailView.openSharePopup();
|
||
}
|
||
},
|
||
//弹幕
|
||
getBarrageData() {
|
||
this.$api.sendRequest({
|
||
url: '/api/goods/goodsbarrage',
|
||
data: {
|
||
goods_id: this.goodsSkuDetail.goods_id
|
||
},
|
||
success: res => {
|
||
if (res.code == 0 && res.data) {
|
||
let barrageData = [];
|
||
for (let i in res.data.list) {
|
||
if (res.data.list[i]['title']) {
|
||
let title = res.data.list[i]['title'].substr(0, 1) + '*' + res.data.list[i]['title'].substr(res.data.list[i]['title'].length - 1, 1)
|
||
barrageData.push({
|
||
img: res.data.list[i]['img'] ? res.data.list[i]['img'] : this.$util.getDefaultImage().head,
|
||
title: title + '已下单'
|
||
});
|
||
}
|
||
}
|
||
this.goodsSkuDetail.barrageData = barrageData;
|
||
}
|
||
}
|
||
});
|
||
},
|
||
/**
|
||
* 设置公众号分享
|
||
*/
|
||
setPublicShare() {
|
||
let shareUrl = this.$config.h5Domain + this.shareUrl;
|
||
if (this.memberInfo && this.memberInfo.member_id) shareUrl += '&source_member=' + this.memberInfo.member_id;
|
||
|
||
this.$util.setPublicShare({
|
||
title: this.goodsSkuDetail.goods_name,
|
||
desc: '',
|
||
link: shareUrl,
|
||
imgUrl: typeof this.goodsSkuDetail.goods_image == 'object' ? this.goodsSkuDetail.goods_image[0] : this.goodsSkuDetail.goods_image.split(',')[0]
|
||
})
|
||
},
|
||
/**
|
||
* 获取商品表单
|
||
* 后端 goodsform 接口与 info() 对齐,返回 {json_data, form_config(camelCase),
|
||
* form_name, form_tip, form_banner, show_banner, show_tip}。
|
||
* 全部字段都落到 goodsSkuDetail 上:表单字段 / 样式 / banner / tip 元数据,
|
||
* 给 detail.vue 的 diy-form 直接消费。所有分支都做兜底初始化,避免下游拿到 undefined。
|
||
*/
|
||
async getGoodsForm() {
|
||
// 防御: sku 切换/数据未到时 goods_id 可能为 0/空, 此时直接走空数据分支,
|
||
// 避免后端拿到非法 goods_id 导致 4xx/5xx 后还把脏数据塞给前端。
|
||
if (!this.goodsSkuDetail || !this.goodsSkuDetail.goods_id) {
|
||
const emptyFn = () => {
|
||
this.$set(this.goodsSkuDetail, 'goods_form', []);
|
||
this.$set(this.goodsSkuDetail, 'goods_form_config', {});
|
||
this.$set(this.goodsSkuDetail, 'goods_form_name', '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_tip', '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_banner', '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_show_banner', false);
|
||
this.$set(this.goodsSkuDetail, 'goods_form_show_tip', false);
|
||
};
|
||
emptyFn();
|
||
return;
|
||
}
|
||
let res;
|
||
try {
|
||
res = await this.$api.sendRequest({
|
||
url: "/form/api/form/goodsform",
|
||
data: {
|
||
goods_id: this.goodsSkuDetail.goods_id,
|
||
format: 'object'
|
||
},
|
||
async: false
|
||
});
|
||
} catch (e) {
|
||
console.warn('getGoodsForm request failed', e);
|
||
res = null;
|
||
}
|
||
const empty = () => {
|
||
this.$set(this.goodsSkuDetail, 'goods_form', []);
|
||
this.$set(this.goodsSkuDetail, 'goods_form_config', {});
|
||
this.$set(this.goodsSkuDetail, 'goods_form_name', '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_tip', '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_banner', '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_show_banner', false);
|
||
this.$set(this.goodsSkuDetail, 'goods_form_show_tip', false);
|
||
};
|
||
if (!res || res.code != 0 || !res.data) {
|
||
empty();
|
||
return;
|
||
}
|
||
if (Array.isArray(res.data)) {
|
||
// 旧版扁平数组兼容
|
||
empty();
|
||
this.$set(this.goodsSkuDetail, 'goods_form', res.data);
|
||
return;
|
||
}
|
||
this.$set(this.goodsSkuDetail, 'goods_form', res.data.json_data || []);
|
||
this.$set(this.goodsSkuDetail, 'goods_form_config', res.data.form_config || {});
|
||
this.$set(this.goodsSkuDetail, 'goods_form_name', res.data.form_name || '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_tip', res.data.form_tip || '');
|
||
this.$set(this.goodsSkuDetail, 'goods_form_banner', res.data.form_banner || '');
|
||
// show_banner/show_tip 后端存 0/1,归一化 boolean
|
||
this.$set(
|
||
this.goodsSkuDetail,
|
||
'goods_form_show_banner',
|
||
res.data.show_banner == 1 ? true : (res.data.show_banner === 0 ? false : true)
|
||
);
|
||
this.$set(
|
||
this.goodsSkuDetail,
|
||
'goods_form_show_tip',
|
||
res.data.show_tip == 1 ? true : (res.data.show_tip === 0 ? false : true)
|
||
);
|
||
}
|
||
},
|
||
/**
|
||
* 自定义分享内容
|
||
* @param {Object} res
|
||
*/
|
||
onShareAppMessage(res) {
|
||
var path = this.shareUrl;
|
||
if (this.memberInfo && this.memberInfo.member_id) path += '&source_member=' + this.memberInfo.member_id;
|
||
return {
|
||
title: this.goodsSkuDetail.sku_name,
|
||
imageUrl: this.shareImg ? this.$util.img(this.shareImg) : this.$util.img(this.goodsSkuDetail.sku_image, {
|
||
size: 'big'
|
||
}),
|
||
path: path,
|
||
success: res => {
|
||
},
|
||
fail: res => {
|
||
}
|
||
};
|
||
},
|
||
// 分享到微信朋友圈
|
||
// #ifdef MP-WEIXIN
|
||
onShareTimeline() {
|
||
let query = this.shareQuery;
|
||
if (this.memberInfo && this.memberInfo.member_id) query += '&source_member=' + this.memberInfo.member_id;
|
||
return {
|
||
title: this.goodsSkuDetail.sku_name,
|
||
query: query,
|
||
imageUrl: this.$util.img(this.goodsSkuDetail.sku_image, {
|
||
size: 'big'
|
||
})
|
||
};
|
||
}
|
||
// #endif
|
||
} |