Authored by shuaiguo

Merge branch 'refs/heads/develop'

Showing 44 changed files with 3266 additions and 245 deletions

Too many changes to show.

To preserve performance only 44 of 44+ files are displayed.

{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Attach by Process ID",
"processId": "5390",
"skipFiles": [
"<node_internals>/**",
],
"sourceMaps": true,
"internalConsoleOptions": "openOnSessionStart"
}
]
}
... ...
... ... @@ -28,7 +28,7 @@ const xianyu = {
* @ description: 导航栏右侧自定义按钮 设为关闭
* @ author: huzhiming
* @ date: 2019-11-14 16:15:07
* @ version: V1.0.5
* @ version: v1.0.0
*/
closeNavRightItem() {
... ... @@ -41,7 +41,7 @@ const xianyu = {
* @ description: 导航栏右侧自定义按钮 设为可见
* @ author: huzhiming
* @ date: 2019-11-05 11:54:24
* @ version: V1.0.5
* @ version: v1.0.0
*/
setNavRightItem (shareParam = null, handler) {
window._xianyuShare = () => {
... ... @@ -64,7 +64,7 @@ const xianyu = {
* @ description: 配置分享参数进行分享操作
* @ author: huzhiming
* @ date: 2019-11-05 10:38:24
* @ version: V1.0.5
* @ version: v1.0.0
*/
setXianyuShare (param = {
shareType: 'activity', // 类型,默认activity
... ...
... ... @@ -211,6 +211,20 @@ export default {
type: 4
};
}
case 'second': {
return {
pay: 'XY_UFO_SALE_PAY',
result: 'XY_UFO_SALE_PAY_RES',
type: 5
};
}
case 'newSecond': {
return {
pay: 'XY_UFO_SALE_PAY',
result: 'XY_UFO_SALE_PAY_RES',
type: 6
};
}
default: {
// PASS
return {
... ...
... ... @@ -3,6 +3,7 @@ import createReport from 'report-error';
import {
SET_ENV,
SET_LOGIN_INFO
} from 'store/yoho/types';
... ... @@ -14,6 +15,7 @@ export default context => {
const {url} = context;
store.commit(SET_ENV, {context});
store.commit(SET_LOGIN_INFO, context.user);
router.push(url);
router.onReady(() => {
const matched = router.getMatchedComponents();
... ...
... ... @@ -122,7 +122,7 @@ export default {
//编辑地址时保存item
if (!isAdd) {
let addressInfo = JSON.parse(item || '{}');
Object.assign(addressInfo, { isUpdate: !isAdd, orderCode: '' });
Object.assign(addressInfo, { isUpdate: !isAdd });
this.STORE_UPDATE_ADDRESS_INFO(addressInfo);
} else {
... ...
... ... @@ -23,7 +23,7 @@
<FormItem>
<div class="wrapper-area">
<label class="input-label">所在区域</label>
<div class="wrapper-arrow" @click="chooseArea">
<div class="wrapper-arrow" @click="popArea">
<template v-if="area">
<label class="text-label">{{ area }}</label>
</template>
... ... @@ -44,8 +44,8 @@
:textValue="address"
></CInput>
</FormItem>
<!-- 订单修改地址隐藏 -->
<div v-if="!orderCode" class="wrapper-tag">
<div class="wrapper-tag">
<p v-if="addressTags.length" class="tag-text">设置标签:</p>
<div class="radio-container">
<RadioGroup class="wrapper-radio">
... ... @@ -60,27 +60,26 @@
</RadioGroup>
</div>
</div>
<!-- 订单修改地址隐藏 -->
<div v-if="!orderCode" class="wrapper-del">
<Radio
class="radio"
:label="{ text: '设为默认地址', value: true }"
style="flex: 0 1 100%;"
checked="is_default"
v-model="is_default"
></Radio>
</div>
<div class="wrapper-default">
<Radio
class="radio"
:label="{ text: '设为默认地址', value: true }"
style="flex: 0 1 100%;"
checked="is_default"
v-model="is_default"
></Radio>
</div>
<div class="white-space"></div>
</div>
<div class="white-space"></div>
<div :class="submitClass" @touchend="onSubmit">确 认</div>
<AddressAct
class="address-act"
ref="addressAct"
v-show="isShowProvince"
<EditAreaAct
ref="areaAct"
v-show="showAreaAct"
@popHidden="popHidden"
@modifyAddressAct="modifyAddressAct"
></AddressAct>
@modifyAreaInfo="modifyAreaInfo"
></EditAreaAct>
</LayoutApp>
</template>
... ... @@ -89,12 +88,10 @@ import Layout from '../../../components/layout/layout-app';
import Input from './components/input';
import FormItem from './components/form-item';
import Radio from './components/radio';
import AddressAct from './components/address-act';
import RadioGroup from './components/radio-group';
import { Scroll } from 'cube-ui';
import EditAreaAct from './components/edit-area-act';
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapMutations, mapActions } = createNamespacedHelpers(
'address/address'
);
... ... @@ -105,19 +102,18 @@ export default {
LayoutApp: Layout,
CInput: Input,
FormItem,
Radio,
AddressAct,
RadioGroup,
Scroll
Radio,
EditAreaAct
},
data() {
return {
isShowProvince: false,
showAreaAct: false,
isUpdate: false,
updateMobileNum: '',
isMobileNumEdit: false,
title: '',
orderCode: '',
mobileNumEdit: false,
mobileNum: '',
consignee: '',
address_id: '',
... ... @@ -131,10 +127,10 @@ export default {
},
watch: {
mobile: function(val) {
if (val === this.updateMobileNum) {
this.isMobileNumEdit = false;
if (val === this.mobileNum) {
this.mobileNumEdit = false;
} else {
this.isMobileNumEdit = true;
this.mobileNumEdit = true;
}
}
},
... ... @@ -163,14 +159,11 @@ export default {
'fetchAddressTags',
'addUserAddress',
'updateUserAddress',
'deleteUserAddress',
'updateReceiptAddressInOrder'
'deleteUserAddress'
]),
async onSubmit() {
let data = this.validator();
// 订单编辑地址
const orderCode = this.orderCode;
if (!data) {
return;
... ... @@ -179,15 +172,7 @@ export default {
let result = {};
if (this.isUpdate) {
//更新和添加地址接口不同
if (orderCode) {
result = await this.updateReceiptAddressInOrder({
...data,
orderCode
});
} else {
result = await this.updateUserAddress(data);
}
result = await this.updateUserAddress(data);
} else {
result = await this.addUserAddress(data);
}
... ... @@ -223,7 +208,7 @@ export default {
}
let reg;
if (this.isUpdate && !this.isMobileNumEdit) {
if (this.isUpdate && !this.mobileNumEdit) {
reg = /^[0123456789*]{11}$/;
} else {
reg = /^[0123456789]{11}$/;
... ... @@ -277,21 +262,19 @@ export default {
}).show();
}
},
chooseArea() {
this.isShowProvince = true;
popArea() {
this.showAreaAct = true;
this.$refs.paneBody.style.overflow = 'hidden';
this.$refs.addressAct.parentHandleclick({
this.$refs.areaAct.parentHandleclick({
areaCode: this.area_code
});
},
popHidden() {
let that = this;
this.$refs.paneBody.style.overflow = 'auto';
that.isShowProvince = false;
this.showAreaAct = false;
},
modifyAddressAct(info) {
modifyAreaInfo(info) {
if (info) {
let that = this;
that.area_code = info.code;
... ... @@ -314,22 +297,18 @@ export default {
},
activated() {
this.fetchAddressTags();
let addressInfo = this.updateAddressInfo;
this.isUpdate = addressInfo.isUpdate;
this.orderCode = addressInfo.orderCode;
this.title = addressInfo.isUpdate ? '编辑地址' : '添加地址';
// 订单编辑 不查标签
if (!this.orderCode) {
this.fetchAddressTags();
}
if (addressInfo.isUpdate) {
this.updateMobileNum = addressInfo.mobile;
this.consignee = addressInfo.consignee;
this.address_id = addressInfo.address_id;
this.mobile = addressInfo.mobile;
this.mobileNum = addressInfo.mobile;
this.area_code = addressInfo.area_code || addressInfo.areaCode;
this.area = addressInfo.area;
this.address = addressInfo.address;
... ... @@ -353,22 +332,6 @@ export default {
padding-right: 40px;
}
.wrapper-del {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 80px;
.del-address {
width: 150px;
text-align: center;
line-height: 80px;
color: #cd001a;
font-size: 26px;
}
}
.wrapper-area {
display: flex;
flex-direction: column;
... ... @@ -423,6 +386,14 @@ export default {
}
}
.wrapper-default {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 80px;
}
.radio-container {
width: 100%;
margin-top: 30px;
... ...
<template>
<LayoutApp title="修改地址" :show-back="true">
<div class="pane-body" ref="paneBody">
<FormItem>
<CInput
label="收货人"
place-holder="请写姓名"
v-model="consignee"
:textValue="consignee"
type="text"
></CInput>
</FormItem>
<FormItem>
<CInput
label="手机号"
place-holder="请填写手机号"
v-model="mobile"
:textValue="mobile"
></CInput>
</FormItem>
<template>
<FormItem>
<div class="wrapper-province">
<label class="province-label">所在省</label>
<label class="province-text">{{province}}</label>
</div>
</FormItem>
</template>
<template>
<FormItem>
<div class="wrapper-area">
<label class="input-label">所在区域</label>
<div class="wrapper-arrow" @click="popArea">
<template v-if="area">
<label class="text-label">{{ area }}</label>
</template>
<template v-else>
<label class="choose-area">请选择</label>
</template>
<div class="arrow"></div>
</div>
</div>
</FormItem>
</template>
<FormItem>
<CInput
label="详细地址"
place-holder="请输入详细地址"
v-model="address"
:textValue="address"
></CInput>
</FormItem>
<div class="tip-wrapper">
<span><i class="iconfont iconquestion icon-class"></i></span>
<p class="tip">暂不支持省地址修改,地址仅可修改一次,请您谅解</p>
</div>
</div>
<div class="white-space"></div>
<div :class="submitClass" @touchend="onSubmit">确认修改</div>
<ModifyAreaAct
ref="areaAct"
v-show="showAreaAct"
@popHidden="popHidden"
@modifyAreaInfo="modifyAreaInfo"
></ModifyAreaAct>
</LayoutApp>
</template>
<script>
import Layout from '../../../components/layout/layout-app';
import Input from './components/input';
import FormItem from './components/form-item';
import Radio from './components/radio';
import RadioGroup from './components/radio-group';
import ModifyAreaAct from './components/modify-area-act';
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapMutations, mapActions } = createNamespacedHelpers(
'address/address'
);
export default {
name: 'addressModify',
components: {
LayoutApp: Layout,
CInput: Input,
FormItem,
RadioGroup,
Radio,
ModifyAreaAct
},
data() {
return {
showAreaAct: false,
mobileNum: '',
mobileNumEdit: false,
orderCode: '',
fromPage: '',
consignee: '',
address_id: '',
mobile: '',
area_code: '',
area: '',
fullArea: '',
province: '',
address: '',
};
},
watch: {
mobile: function(val) {
if (val === this.mobileNum) {
this.mobileNumEdit = false;
} else {
this.mobileNumEdit = true;
}
}
},
computed: {
...mapState(['updateAddressInfo']),
submitClass() {
return [
'sure-btn',
{
active: this.inNotEmpty
}
];
},
inNotEmpty() {
return (
this.consignee &&
this.mobile &&
this.area &&
this.address
);
}
},
methods: {
...mapMutations({}),
...mapActions(['updateReceiptAddressInOrder', 'updateDeliverAddressInOrder', 'fetchAddressProvinces']),
async onSubmit() {
let data = this.validator();
if (!data) {
return;
}
let result = {};
const orderCode = this.orderCode;
if (this.fromPage === 'BuyerOrder') {
result = await this.updateReceiptAddressInOrder({
...data,
orderCode
});
} else { //fromPage='SellerOrder'
result = await this.updateDeliverAddressInOrder({
...data,
orderCode
});
}
if (result && result.code === 200) {
this.$createToast({
type: 'txt',
txt: result.message.split('.').join('')
}).show();
await this.sleep(1000);
this.$router.go(-1);
} else {
this.$createToast({
type: 'error',
txt: result.message,
mask: true
}).show();
}
},
validator() {
let username = this.consignee.replace(/(^\s+)|(\s+$)/g, '');
// 简单的表单校验
if (!username) {
this.showToast('收件人不能为空');
return false;
}
if (!/^[\u4E00-\u9FA5A-Za-z0-9*]+$/gi.test(username)) {
this.showToast('收货人姓名不支持特殊符号');
return false;
}
let reg;
if (!this.mobileNumEdit) {
reg = /^[0123456789*]{11}$/;
} else {
reg = /^[0123456789]{11}$/;
}
if (!this.mobile) {
this.showToast('手机号不能为空');
return false;
} else {
if (!reg.test(this.mobile)) {
this.showToast('请输入正确11位手机号码');
return;
}
}
if (!this.area_code || !this.area) {
this.showToast('省市区不能为空');
return false;
}
if (!this.address) {
this.showToast('地址不能为空');
return false;
}
let fullArea = this.province + this.area;
return {
id: this.address_id || '',
consignee: username,
mobile: this.mobile,
area_code: this.area_code,
area: fullArea,
address: this.address,
is_default: 'N', //is_default和tag_code直接设置空即可
tag_code: ''
};
},
popArea() {
this.showAreaAct = true;
this.$refs.paneBody.style.overflow = 'hidden';
this.$refs.areaAct.parentHandleclick({
areaCode: this.area_code,
provinceTitle: this.province
});
},
popHidden() {
this.$refs.paneBody.style.overflow = 'auto';
this.showAreaAct = false;
},
modifyAreaInfo(info) {
if (info) {
let that = this;
that.area_code = info.code;
that.area = info.area.split(this.province).join('');
}
},
showToast(tip) {
this.$createToast({
type: 'txt',
txt: tip
}).show();
},
sleep(n) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, n);
});
}
},
async activated() {
let addressInfo = this.updateAddressInfo;
this.fromPage = this.$route.query.fromPage;
this.orderCode = addressInfo.orderCode;
this.consignee = addressInfo.consignee;
this.address_id = addressInfo.address_id;
this.mobile = addressInfo.mobile;
this.mobileNum = addressInfo.mobile; //用作监听有没有修改电话
this.area_code = addressInfo.area_code || addressInfo.areaCode;
this.fullArea = addressInfo.area;
this.address = addressInfo.address;
let provinceId = this.area_code.substring(0, 2);
let provinceCaption = '';
const provinceResult = await this.fetchAddressProvinces(0);
let provinces = provinceResult.data;
provinces.map(info => {
if (info.id === provinceId) {
provinceCaption = info.caption;
}
});
this.province = provinceCaption;
this.area = this.fullArea.split(this.province).join('');
}
};
</script>
<style lang="scss" scoped>
.pane-body {
height: 100%;
overflow-y: auto;
padding-top: 12px;
padding-left: 40px;
padding-right: 40px;
}
.wrapper-province {
display: flex;
flex-direction: column;
.province-label {
font-size: 36px;
display: inline-block;
font-weight: bold;
margin-bottom: 30px;
color: #ccc;
}
.province-text {
font-size: 28px;
width: 100%;
color: #ccc;
display: inline-block;
}
}
.wrapper-area {
display: flex;
flex-direction: column;
.input-label {
font-size: 36px;
display: inline-block;
font-weight: bold;
margin-bottom: 30px;
}
.text-label {
font-size: 28px;
width: 100%;
color: #444444;
display: inline-block;
}
.choose-area {
font-size: 28px;
width: 100%;
color: #cccccc;
}
.wrapper-arrow {
display: flex;
justify-content: space-between;
}
.arrow {
height: 44px;
width: 44px;
background: url("~statics/image/address/arrow-right.png");
background-size: cover;
}
}
.tip-wrapper {
display: flex;
flex-direction: row;
margin-top: 5px;
height: 40px;
.icon-class {
line-height: 40px;
color: #d8d8d8;
font-size: 24px;
}
.tip {
margin-left: 8px;
line-height: 40px;
font-size: 24px;
color: #a1a1a1;
}
}
.white-space {
position: relative;
display: block;
overflow: hidden;
height: 120px;
}
.sure-btn {
height: 80px;
line-height: 80px;
font-size: 32px;
font-weight: bold;
text-align: center;
background-color: #cccccc;
color: white;
border-radius: 40px;
position: fixed;
left: 32px;
right: 32px;
bottom: 24px;
&.active {
background-color: #002b47;
}
}
</style>
... ...
... ... @@ -234,7 +234,7 @@ export default {
let returnTitle = this.returnTitle();
this.$emit("popHidden");
this.$emit("modifyAddressAct", {
this.$emit("modifyAreaInfo", {
code: id,
area: returnTitle
});
... ...
<template>
<div class="address-select-component">
<div class="address-select-box">
<div class="component-title">
<span class="title">选择地区</span>
<span class="icon-close close" @click="closeAddBox"></span>
</div>
<ul class="head-address-ul">
<li
v-if="province.title"
:class="{ 'head-address-li': province.titleActive }"
>{{province.title}}</li>
<li
v-if="city.title"
:class="{ 'head-address-li': city.titleActive }"
@click="clickTitle('city')"
>{{city.title}}</li>
<li
v-if="area.title"
:class="{ 'head-address-li': area.titleActive }"
@click="clickTitle('area')"
>{{area.title}}</li>
<li
v-if="street.title"
:class="{ 'head-address-li': street.titleActive }"
@click="clickTitle('street')"
>{{street.title}}</li>
</ul>
<div class="address-container">
<div class="address-content">
<ul v-if="province.showList" class="address-ul">
<li
v-for="(pprovince, index) in provinces"
:key="index"
:class="{active: pprovince.id === province.id}"
>
{{pprovince.caption}}
<span v-if="pprovince.id === province.id" class="icon-check"></span>
</li>
</ul>
<ul v-if="city.showList" class="address-ul">
<li
v-for="(pcity, index) in citys"
:key="index"
:class="{active: pcity.id === city.id}"
@click="switchAddress(pcity.id, pcity.caption)"
>
{{pcity.caption}}
<span v-if="pcity.id === city.id" class="icon-check"></span>
</li>
</ul>
<ul v-if="area.showList" class="address-ul">
<li
v-for="(parea, index) in areas"
:key="index"
:class="{active: parea.id === area.id}"
@click="switchAddress(parea.id, parea.caption)"
>
{{parea.caption}}
<span v-if="parea.id === area.id" class="icon-check"></span>
</li>
</ul>
<ul v-if="street.showList" class="address-ul">
<li
v-for="(pstreet, index) in streets"
:key="index"
:class="{active: pstreet.id === street.id}"
@click="switchAddress(pstreet.id, pstreet.caption)"
>
{{pstreet.caption}}
<span v-if="pstreet.id === street.id" class="icon-check"></span>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
import { createNamespacedHelpers } from "vuex";
const { mapState, mapMutations, mapActions } = createNamespacedHelpers(
"address/address"
);
export default {
data() {
return {
provinces: [],
citys: [],
areas: [],
streets: [],
province: {
id: "",
title: "",
allTitle: "",
showList: false,
titleActive: false
},
city: {
id: "",
title: "",
allTitle: "",
showList: false,
titleActive: false
},
area: {
id: "",
title: "",
allTitle: "",
showList: false,
titleActive: false
},
street: {
id: "",
title: "",
allTitle: "",
showList: false,
titleActive: false
}
};
},
computed: {
...mapState(["provincesList"])
},
methods: {
...mapMutations({}),
...mapActions(["fetchAddressProvinces"]),
/* 处理编辑时父组件传递的省市区 */
async parentHandleclick({ areaCode, provinceTitle }) {
if (areaCode === "" || areaCode === undefined) {
return;
}
if (areaCode.length < 2) {
return;
}
//省
this.province.allTitle = provinceTitle;
this.province.title = this.titleHandle(provinceTitle);
if (areaCode.length < 4) {
return;
}
//市
let citysCaption = "";
this.city.id = areaCode.substring(0, 4);
const cityResult = await this.fetchAddressProvinces(
areaCode.substring(0, 2)
);
this.citys = cityResult.data;
this.citys.map(info => {
if (info.id === this.city.id) {
citysCaption = info.caption;
}
});
this.city.allTitle = citysCaption;
this.city.title = this.titleHandle(citysCaption);
if (areaCode.length < 6) {
return;
}
//县
let areasCaption = "";
this.area.id = areaCode.substring(0, 6);
const areaResult = await this.fetchAddressProvinces(
areaCode.substring(0, 4)
);
this.areas = areaResult.data;
this.areas.map(info => {
if (info.id === this.area.id) {
areasCaption = info.caption;
}
});
this.area.allTitle = areasCaption;
this.area.title = this.titleHandle(areasCaption);
if (areaCode.length < 9) {
return;
}
//街道
let streetsCaption = "";
this.street.id = areaCode.substring(0, 9);
const result = await this.fetchAddressProvinces(areaCode.substring(0, 6));
this.streets = result.data;
this.streets.map(info => {
if (info.id === this.street.id) {
streetsCaption = info.caption;
}
});
this.street.allTitle = streetsCaption;
this.street.title = this.titleHandle(streetsCaption);
this.street.showList = this.street.titleActive = true;
this.province.showList = this.city.showList = this.area.showList = false;
this.province.titleActive = this.city.titleActive = this.area.titleActive = false;
},
/* 获取地址数据绑定 */
async switchAddress(id, caption) {
if (!id) {
return;
}
this.changeShow(id, caption);
const result = await this.fetchAddressProvinces(id);
let resultData = result.data;
//length小于1时弹窗消失并传数据给上层
if (resultData && resultData.length < 1) {
let returnTitle = this.returnTitle();
this.$emit("popHidden");
this.$emit("modifyAreaInfo", {
code: id,
area: returnTitle
});
}
/* 数据绑定 */
switch ((id + "").length) {
case 2:
this.citys = resultData;
break;
case 4:
this.areas = resultData;
break;
case 6:
this.streets = resultData;
break;
default:
this.provinces = resultData;
break;
}
},
/* 选择地址后的显示控制 */
changeShow(id, caption) {
switch ((id + "").length) {
case 2:
if (this.province.id && this.province.id !== id) {
this.area.title = this.street.title = "";
}
this.city.title = "请选择";
this.province.id = id;
this.province.allTitle = caption;
this.province.title = this.titleHandle(caption);
this.city.showList = this.city.titleActive = true;
this.province.showList = this.area.showList = this.street.showList = false;
this.province.titleActive = this.area.titleActive = this.street.titleActive = false;
break;
case 4:
if (this.city.id && this.city.id !== id) {
this.street.title = "";
}
this.area.title = "请选择";
this.city.id = id;
this.city.allTitle = caption;
this.city.title = this.titleHandle(caption);
this.area.showList = this.area.titleActive = true;
this.province.showList = this.city.showList = this.street.showList = false;
this.province.titleActive = this.city.titleActive = this.street.titleActive = false;
break;
case 6:
this.street.title = "请选择";
this.area.id = id;
this.area.allTitle = caption;
this.area.title = this.titleHandle(caption);
this.street.showList = this.street.titleActive = true;
this.province.showList = this.city.showList = this.area.showList = false;
this.province.titleActive = this.city.titleActive = this.area.titleActive = false;
break;
case 9:
this.street.id = id;
this.street.allTitle = caption;
this.street.title = this.titleHandle(caption);
break;
default:
if (this.province.id.length === 0) {
this.province.title = "请选择";
}
this.province.showList = this.province.titleActive = true;
this.city.showList = this.area.showList = this.street.showList = false;
this.city.titleActive = this.area.titleActive = this.street.titleActive = false;
break;
}
},
/* 点击地址标题时的处理 */
clickTitle(type) {
switch (type) {
case "city":
this.city.titleActive = true;
this.city.showList = true;
this.province.titleActive = this.area.titleActive = this.street.titleActive = false;
this.province.showList = this.area.showList = this.street.showList = false;
break;
case "area":
this.area.titleActive = true;
this.area.showList = true;
this.province.titleActive = this.city.titleActive = this.street.titleActive = false;
this.province.showList = this.city.showList = this.street.showList = false;
break;
case "street":
this.street.titleActive = true;
this.street.showList = true;
this.province.titleActive = this.city.titleActive = this.area.titleActive = false;
this.province.showList = this.city.showList = this.area.showList = false;
break;
default:
break;
}
},
/* 关闭地址选择组件 */
closeAddBox() {
this.$emit("popHidden");
},
/* 返回标题处理 */
returnTitle() {
let getTitle = "";
let returnTitle = "";
getTitle =
this.province.allTitle +
this.city.allTitle +
this.area.allTitle +
this.street.allTitle;
if (getTitle.length > 18) {
returnTitle = getTitle.substr(0, 8) + "..." + getTitle.substr(-8);
} else {
returnTitle = getTitle;
}
return returnTitle;
},
/* 标题长度处理 */
titleHandle(caption) {
if (caption && caption.length > 3) {
return caption.substring(0, 3) + "...";
} else {
return caption;
}
}
},
activated() {
// 重置data数据
Object.assign(this.$data, this.$options.data());
}
};
</script>
<style lang="scss">
.disable {
color: #a6a6a6;
}
.address-select-component {
position: fixed;
bottom: 0;
left: 0;
top: 0;
width: 100%;
background: rgba(0, 0, 0, 0.5);
.address-select-box {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #fff;
.component-title {
text-align: center;
line-height: 88px;
font-size: 34px;
font-weight: bold;
color: #000;
padding: 0 30px;
.close {
float: right;
}
.icon-close {
height: 40px;
width: 40px;
margin-top: 24px;
background: url("~statics/image/address/close.png");
background-size: cover;
}
}
.head-address-ul {
padding: 0;
margin: 0 0 0 30px;
list-style: none;
overflow: hidden;
font-size: 28px;
color: #999;
li {
display: block;
float: left;
height: 60px;
font-size: 28px;
line-height: 60px;
position: relative;
margin-right: 70px;
}
li:last-child {
margin-right: 30px;
}
.head-address-li {
color: #000;
}
.head-address-li:after {
width: 100%;
border-bottom: 4px solid #000;
position: absolute;
bottom: 0;
left: 0;
content: "";
}
}
.address-container {
margin: 0;
overflow: hidden;
height: 100%;
width: 100%;
border-top: solid 1px #eee;
.address-content {
transform: translate(0, 0) translateZ(0);
height: 620px;
}
}
.address-ul {
padding: 0;
margin: 0;
list-style: none;
height: 100%;
overflow: auto;
font-size: 28px;
color: #999;
justify-content: center;
li {
height: 80px;
line-height: 80px;
padding-left: 30px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
&.active {
font-size: 30px;
color: #000;
font-weight: bold;
}
.icon-check {
display: inline-block;
margin-left: 18px;
height: 30px;
width: 30px;
background-repeat: no-repeat;
background: url("~statics/image/address/check.png");
background-size: 100% 100%;
}
}
}
}
}
</style>
... ...
... ... @@ -6,7 +6,12 @@ export default [
},
{
name: 'addressEdit',
path: '/xianyu/address/edit/:orderCode?', // code 订单编码
path: '/xianyu/address/edit',
component: () => import(/* webpackChunkName: "address" */ './addressEdit'),
},
{
name: 'addressModify',
path: '/xianyu/address/modify',
component: () => import(/* webpackChunkName: "address" */ './addressModify'),
},
];
... ...
... ... @@ -4,6 +4,16 @@
<li v-for="(item, index) in list" :key="index">
<LayoutLink :href="item.url" :report="{PAGE_URL, ...item.reportParams}" reportEvent="XY_UFO_MAIN_EVENT">
<ImageFormat :data-secc="item.src" :lazy="false" :src="item.src" :alt="item.alt" :width="item.width || 750" :height="item.height || 160" />
<div
v-if="videoUrl"
class="video-mask"
@click="e => onClickHandler(e, item.url, index)"></div>
<VideoPlayer
:ref="`videoPlayer-${index+1}`"
class="video-player"
v-if="videoUrl"
:source="videoUrl"
/>
</LayoutLink>
</li>
</ul>
... ... @@ -11,6 +21,8 @@
</template>
<script>
import VideoPlayer from "@/components/video-player";
export default {
name: 'banner',
props: {
... ... @@ -22,16 +34,64 @@ export default {
type: String,
}
},
components: { VideoPlayer },
computed: {
videoUrl() {
let videoUrl = null;
for(const resourceInfo of this.list) {
const { url} = resourceInfo;
if(/(\.mp4)/.test(url)) {
videoUrl = url
break;
}
}
return videoUrl;
},
},
methods: {
onClickHandler(e, url, index) {
e.stopPropagation();
e.preventDefault();
if(/(\.mp4)/.test(url)) {
const key = `videoPlayer-${index+1}`;
this.$refs[key][0].parentHandleclick()
}
}
}
};
</script>
<style lang="scss" scoped>
.video-mask {
z-index: 10;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.video-player {
display: block;
height: 100%;
width: 100%;
opacity: 0;
position: absolute;
top: 0;
}
.banner {
width: 100%;
/deep/ .layout-link {
position: relative;
height: 100%;
width: 100%;
box-sizing: border-box;
display: inline-block;
}
img {
width: 100%;
display: block;
... ...
... ... @@ -3,20 +3,52 @@
<div class="swiper-item swiper-item-left">
<LayoutLink :href="list[0].url" class="img-link" :report="{PAGE_URL, ...list[0].reportParams}" reportEvent="XY_UFO_MAIN_EVENT">
<ImageFormat :lazy="false" class="item-imge" :src="list[0].src" :width="310" :height="402"></ImageFormat>
<div
v-if="videoInfo && videoInfo.id === list[0].id"
class="video-mask"
@click="e => onClickHandler(e, list[0].url)"></div>
<VideoPlayer
ref="videoPlayer"
class="video-player"
v-if="videoInfo && videoInfo.id === list[0].id"
:source="videoInfo.url"
/>
</LayoutLink>
</div>
<div class="swiper-item swiper-item-right">
<LayoutLink :href="list[1].url" class="img-link" :report="{PAGE_URL, ...list[1].reportParams}" reportEvent="XY_UFO_MAIN_EVENT">
<ImageFormat :lazy="false" class="item-imge" :src="list[1].src" :width="380" :height="196"></ImageFormat>
<div
v-if="videoInfo && videoInfo.id === list[1].id"
class="video-mask"
@click="e => onClickHandler(e, list[1].url)"></div>
<VideoPlayer
ref="videoPlayer"
class="video-player"
v-if="videoInfo && videoInfo.id === list[1].id"
:source="videoInfo.url"
/>
</LayoutLink>
<LayoutLink :href="list[2].url" class="img-link img-link2" :report="{PAGE_URL, ...list[2].reportParams}" reportEvent="XY_UFO_MAIN_EVENT">
<ImageFormat :lazy="false" class="item-imge" :src="list[2].src" :width="380" :height="196"></ImageFormat>
<div
v-if="videoInfo && videoInfo.id === list[2].id"
class="video-mask"
@click="e => onClickHandler(e, list[2].url)"></div>
<VideoPlayer
ref="videoPlayer"
class="video-player"
v-if="videoInfo && videoInfo.id === list[2].id"
:source="videoInfo.url"
/>
</LayoutLink>
</div>
</div>
</template>
<script>
import VideoPlayer from "@/components/video-player";
export default {
name: 'swiper',
props: {
... ... @@ -27,21 +59,59 @@ export default {
type: String,
}
},
components: {VideoPlayer},
computed: {
videoInfo() {
let videoInfo = null;
for(const resourceInfo of this.list) {
const { url, id } = resourceInfo;
if(/(\.mp4)/.test(url)) {
videoInfo = {url, id};
break;
}
}
return videoInfo;
},
},
data() {
return {
params: {},
}
},
mounted() {
// console.log(this.list)
},
methods: {
onClickHandler(e, url) {
e.stopPropagation();
e.preventDefault();
if(/(\.mp4)/.test(url)) {
this.$refs.videoPlayer.parentHandleclick()
}
}
},
};
</script>
<style lang="scss" scoped>
.video-mask {
z-index: 10;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.video-player {
display: block;
height: 100%;
width: 100%;
opacity: 0;
position: absolute;
top: 0;
}
.swiper {
display: flex;
align-content: stretch;
... ... @@ -50,6 +120,10 @@ export default {
margin-left: 8px;
margin-right: 8px;
/deep/ .layout-link {
position: relative;
}
.swiper-item-left {
width: 44%;
}
... ...
... ... @@ -8,10 +8,15 @@ import Address from './address';
import Notice from './notice';
import Category from './category';
import Activitys from './activitys';
import Zhiming from './zhiming';
import Second from './second';
export default [
{
path: '/xianyu/yasTest',
name: 'yasTest',
component: () => import(/* webpackChunkName: "yasTest" */ './yasTest'),
},
{
path: '/xianyu',
redirect: { name: 'ChannelPage' },
},
... ... @@ -25,5 +30,5 @@ export default [
...Notice,
...Category,
...Activitys,
...Zhiming
...Second
];
... ...
... ... @@ -14,7 +14,6 @@
<div
v-if="product.customize_tag && product.customize_tag.length"
class="tag-wrapper">
<ImgSize class="item-tag" :src="product.customize_tag[0].url2" :width="200" :height="200"/>
</div>
... ... @@ -26,6 +25,10 @@
import {Scroll} from 'cube-ui';
import ImgSize from '../../../components/img-size';
// price:有限库存 最低价 【范围最广 】
// show_price:现货+二手+瑕疵 最低价 【范围第二】
// available_now_price:现货最低价 【范围最小】
export default {
props: {
list: Array,
... ... @@ -36,7 +39,7 @@ export default {
},
priceKey: {
type: String,
default: 'available_now_price',
default: 'show_price',
},
},
data: function() {
... ... @@ -157,7 +160,6 @@ export default {
border-radius: 16px;
width: 344px;
padding: 24px 24px 32px;
// height: 498px;
background: #fff;
margin-bottom: 16px;
position: relative;
... ... @@ -202,8 +204,8 @@ export default {
}
.item-tag {
width: 84px;
height: 26px;
max-width: 120px;
max-height: 40px;
}
.item-name {
... ...
... ... @@ -35,10 +35,11 @@ import { get } from 'lodash';
import { createNamespacedHelpers, mapState } from 'vuex';
const { mapState: mapOrderState, mapActions: mapOrderAction, mapMutations: mapOrderMutations } = createNamespacedHelpers('order/orderConfirm');
const { mapActions: mapSecondActions } = createNamespacedHelpers('second');
export default {
name: 'BuyOrderConfirm',
props: ['productId', 'storageId'],
props: ['productId', 'storageId', 'type', 'skup'],
data() {
return {
isActive: false
... ... @@ -71,15 +72,27 @@ export default {
computed: {
...mapOrderState(['address', 'orderDetail']),
...mapState({
productDetail: state => {
return {
goodImg: get(state.product.selectedProductInfo, 'product.goods_list[0].image_list[0].image_url', ''),
productName: get(state.product.selectedProductInfo, 'product.product_name', ''),
colorName: get(state.product.selectedProductInfo, 'product.goods_list[0].color_name', ''),
sizeName: get(state.product.selectedProductInfo, 'size.size_name', ''),
goodPrice: get(state.product.selectedProductInfo, 'size.least_price', ''),
skup: get(state.product.selectedProductInfo, 'size.skup', '')
};
productDetail(state) {
if (this.type === 'second') {
return {
goodImg: get(state.second.info, 'imageList[0]', ''),
productName: get(state.second.info, 'productName', ''),
colorName: get(state.second.info, 'colorName', ''),
sizeName: get(state.second.info, 'sizeName', ''),
goodPrice: get(state.second.info, 'price', ''),
skup: this.skup,
type: get(state.second.info, 'sechondHandTypeName', '') === '二手' ? 5 : 6,
};
} else {
return {
goodImg: get(state.product.selectedProductInfo, 'product.goods_list[0].image_list[0].image_url', ''),
productName: get(state.product.selectedProductInfo, 'product.product_name', ''),
colorName: get(state.product.selectedProductInfo, 'product.goods_list[0].color_name', ''),
sizeName: get(state.product.selectedProductInfo, 'size.size_name', ''),
goodPrice: get(state.product.selectedProductInfo, 'size.least_price', ''),
skup: get(state.product.selectedProductInfo, 'size.skup', '')
};
}
}
}),
couponList() {
... ... @@ -109,6 +122,7 @@ export default {
methods: {
...mapOrderAction(['fetchOrderAddress', 'fetchUserStatus', 'fetchPayList', 'fetchPayment', 'computeOrder', 'buyPayAction']),
...mapOrderMutations([Types.CHANGE_SELECT_COUPON_LIST, Types.CHANGE_SELECT_PROMOTION, Types.CLEAR_BUY_STATUS]),
...mapSecondActions(['fetchDetailById']),
replaceBr(str) {
return str ? str.replace(/\n/g, '<br />') : '';
},
... ... @@ -222,7 +236,7 @@ export default {
desc: '金额',
extra: JSON.stringify({
type: UserType.buy,
reportType: 'buy',
reportType: this.getType(this.productDetail.type),
back: {
name: 'buyOrderDetail',
params: {
... ... @@ -244,14 +258,24 @@ export default {
}).show();
//数据埋点
this.reportYas('XY_UFO_SC_ORD', {
ORD_NUM: result.data.orderCode,
PRD_ID: this.productId,
PRD_SKU: this.productDetail.skup,
PRD_SIZE: this.productDetail.sizeName,
ORD_AMOUNT: this.orderDetail.amount,
PRD_PRICE: this.productDetail.goodPrice,
});
if (this.productDetail.type) {
// 二手
this.reportYas('XY_UFO_RESALE_ORD', {
ORD_NUM: result.data.orderCode,
PRD_ID: this.productId,
PRD_SKU: this.productDetail.skup,
ORD_TYPE: this.productDetail.type
});
} else {
this.reportYas('XY_UFO_SC_ORD', {
ORD_NUM: result.data.orderCode,
PRD_ID: this.productId,
PRD_SKU: this.productDetail.skup,
PRD_SIZE: this.productDetail.sizeName,
ORD_AMOUNT: this.orderDetail.amount,
PRD_PRICE: this.productDetail.goodPrice,
});
}
},
onClose(orderCode) {
this.$router.replace({
... ... @@ -263,20 +287,33 @@ export default {
});
},
backAction() {
this.$router.replace({
name: 'ProductDetail',
params: {
productId: this.productId
}
});
if (this.productId) {
this.$router.replace({
name: 'ProductDetail',
params: {
productId: this.productId
}
});
} else if (this.type === 'second') {
this.$router.replace({
name: 'SecondProductDetail',
params: {
skup: this.skup
}
});
}
},
async init() {
this.fetchOrderAddress({ tabType: UserType.buy });
await this.$store.dispatch('product/getSelectedTradeProduct', {
productId: this.productId,
storageId: this.storageId
});
if (this.type === 'second') {
await this.fetchDetailById({ skup: this.skup });
} else {
await this.$store.dispatch('product/getSelectedTradeProduct', {
productId: this.productId,
storageId: this.storageId
});
}
let user = await this.$sdk.getUser();
... ... @@ -303,6 +340,16 @@ export default {
param: params
}
});
},
getType(type) {
switch (type) {
case 5:
return 'second';
case 6:
return 'newSecond';
default:
return 'buy';
}
}
}
};
... ...
... ... @@ -7,7 +7,7 @@
<div class="product-price">
<div class="red">¥{{data.goodPrice}}</div>
<div class="product-name">{{data.productName}}</div>
<div class="price">{{data.colorName}},{{data.sizeName}}</div>
<div class="price"><span class='icon-ufo'></span> {{data.colorName}},{{data.sizeName}}</div>
</div>
</div>
<div v-if="!getLogin" class="no-login-tip">完成“有货”登录授权后将显示商品优惠及运费信息,请先完成登录查看最终支付金额</div>
... ...
... ... @@ -5,7 +5,7 @@
</div>
<div class="product-price">
<div class="product-price-wrapper">
<div class="price">{{data.colorName}},{{data.sizeName}}</div>
<div class="price"><span class='icon-ufo'></span> {{data.colorName}},{{data.sizeName}}</div>
<div class="tip">{{ data.priceType}} <span class="price2">{{data.goodPrice}}</span></div>
<div v-if="data.priceBidType" class="tip">{{ data.priceBidType}} <span class="price2">{{data.goodBidPrice}}</span></div>
</div>
... ...
... ... @@ -30,6 +30,8 @@ export default [
props: route => ({
productId: route.query.productId,
storageId: route.query.storageId,
type: route.query.type,
skup: route.query.skup
}),
},
{
... ...
... ... @@ -40,7 +40,7 @@ export default {
addressInfo,
userAddress,
} = order;
const { productId, storageId, skup } = goodsInfo;
const { productId, storageId, skup, typeTag } = goodsInfo;
if (isDetail) {
this.setIsRefresh(true);
... ... @@ -117,12 +117,12 @@ export default {
let info = JSON.stringify(addressInfo || userAddress || {});
let updateInfo = JSON.parse(info || '{}');
Object.assign(updateInfo, { isUpdate: true, orderCode: orderCode });
Object.assign(updateInfo, { orderCode: orderCode });
this.STORE_UPDATE_ADDRESS_INFO(updateInfo);
this.$router.push({
name: 'addressEdit',
name: 'addressModify',
query: {
fromPage: 'OrderList',
fromPage: 'BuyerOrder',
},
});
break;
... ... @@ -215,6 +215,12 @@ export default {
price = priceInfo.realPayPrice;
pageBackName = 'buyOrderDetail';
}
let reportType = 'buy';
if (typeTag) {
reportType = typeTag === '全新瑕疵' ? 'newSecond' : 'second';
}
this.$createOrderPayType({
orderCode,
price: parseFloat(price || '').toFixed(2),
... ... @@ -234,7 +240,7 @@ export default {
code: orderCode,
},
},
reportType: 'buy',
reportType,
}),
}).show();
break;
... ...
... ... @@ -130,7 +130,7 @@ import DetailHeader from "./components/header";
import DetailFooter from "./components//detail-footer";
import OrderActions from "../components/order-actions";
import VideoPlayer from "../order-list/components/video-player";
import VideoPlayer from "@/components/video-player";
import orderActionMixin from "../mixin/order-action";
... ... @@ -253,6 +253,7 @@ export default {
.real-pay-price {
font-size: 28px;
color: #d0021b;
@include num;
}
}
... ...
... ... @@ -18,11 +18,12 @@
<div class="price-status">
<span class="price">¥{{ goodsInfo.goodPrice }}</span>
</div>
<p class="item-name">
<p class="item-name"><span>【{{ goodsInfo.typeTag }}】</span>
{{ goodsInfo.productName }}
</p>
</div>
<p class="item-spec">
<span class='icon-ufo'></span>
<span>{{ goodsInfo.colorName }},</span>
<span>{{ goodsInfo.sizeName }}码</span>
</p>
... ... @@ -42,11 +43,20 @@ export default {
computed: {
...mapGetters(["goodsInfo"]),
toLinkParam() {
const { productId, storageId } = this.goodsInfo;
return {
name: "ProductDetail",
params: { productId }
};
if (this.goodsInfo.typeTag && this.goodsInfo.typeTag === '二手') {
const { skup } = this.goodsInfo;
return {
name: "SecondProductDetail",
params: { skup }
};
} else {
const { productId, storageId } = this.goodsInfo;
return {
name: "ProductDetail",
params: { productId }
};
}
}
},
methods: {
... ...
... ... @@ -34,6 +34,10 @@
{{ userAddress.area }}{{ userAddress.address || "" }}
</p>
<p class="mobile">{{ userAddress.mobile }}</p>
<div v-if="userAddress.canModify" class="edit-address" @click="jumpToAddressModify">
<span class="mobile">编辑地址</span>
<i class="cubeic-arrow" ></i>
</div>
</div>
</div>
</div>
... ... @@ -42,7 +46,11 @@
<script>
import { createNamespacedHelpers } from "vuex";
import Clipboard from "clipboard";
import { STORE_UPDATE_ADDRESS_INFO } from 'store/address/address/types';
const { mapGetters } = createNamespacedHelpers("order/orderDetail");
const { mapMutations: addressMutations } = createNamespacedHelpers(
'address/address',
);
export default {
props: {
... ... @@ -52,7 +60,25 @@ export default {
}
},
computed: {
...mapGetters(["userAddress", "appraiseAddress"])
...mapGetters(["userAddress", "appraiseAddress", "orderCode"])
},
methods: {
...addressMutations(['STORE_UPDATE_ADDRESS_INFO']),
jumpToAddressModify() {
const orderCode = this.orderCode;
// 保存地址到store
let info = JSON.stringify(this.userAddress || {});
let updateInfo = JSON.parse(info || '{}');
Object.assign(updateInfo, { orderCode: orderCode });
this.STORE_UPDATE_ADDRESS_INFO(updateInfo);
this.$router.push({
name: 'addressModify',
query: {
fromPage: 'SellerOrder',
},
});
}
},
activated() {
this.$nextTick(() => {
... ... @@ -101,9 +127,29 @@ export default {
}
}
.edit-address {
display: flex;
align-items: center;
position: absolute;
bottom: 0;
right: 40px;
z-index: 99;
}
.address-wrapper {
display: flex;
align-items: center;
position: relative;
&:before {
content: "";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.consignee {
font-size: 32px;
... ... @@ -144,4 +190,4 @@ export default {
}
}
}
</style>
\ No newline at end of file
</style>
... ...
... ... @@ -232,6 +232,7 @@ export default {
.earnest-price {
font-size: 28px;
color: #d0021b;
@include num;
}
}
... ...
... ... @@ -20,6 +20,7 @@
{{ goodsInfo.productName }}
</p>
<p class="item-spec">
<span class="icon-ufo"></span>
<span v-for="(spec, i) in specList" :key="i">
{{ spec }}{{ i !== specList.length - 1 ? "," : "" }}
</span>
... ... @@ -163,4 +164,4 @@ export default {
}
}
}
</style>
\ No newline at end of file
</style>
... ...
... ... @@ -22,11 +22,12 @@
<div v-else class="price-status">
<span class="price">¥{{ goodsInfo.goodPrice }}</span>
</div>
<p class="item-name">
<p class="item-name"><span v-if="goodsInfo.typeTag"> 「{{goodsInfo.typeTag}}」 </span>
{{ goodsInfo.productName }}
</p>
</div>
<p class="item-spec">
<span class='icon-ufo'></span>
<span>{{ goodsInfo.colorName }},</span>
<span>{{ goodsInfo.sizeName }}码</span>
</p>
... ... @@ -140,6 +141,12 @@ export default {
color: #999;
letter-spacing: 0;
line-height: 36px;
span {
color: #000;
line-height: 40px;
font-weight: bold;
}
}
.item-spec {
... ...
... ... @@ -64,7 +64,7 @@ import { createNamespacedHelpers } from "vuex";
import OrderItem from "./components/order-item";
import StatusNav from "./components/status-nav";
import OrderItemHeader from "./components/order-item-header";
import VideoPlayer from "./components/video-player";
import VideoPlayer from "@/components/video-player";
import EmptyList from "components//ufo-no-item";
... ...
... ... @@ -22,6 +22,7 @@
<span>
{{ logisticInfo.expressCompanyName }}
</span>
<span class="icon-ufo"></span>
<!-- <span class="platform-info">{{ platformName }}</span> -->
</p>
<p>
... ... @@ -46,9 +47,45 @@
>
<template v-slot:content="{ detail: { miniFaultConfirm } }">
<div class="judge-content-wrapper" v-if="miniFaultConfirm">
<!-- 卖家评论 -->
<div v-if="miniFaultConfirm.secondDetailBySeller">
<p class="tip"><span class="tip-text-gray">{{miniFaultConfirm.secondDetailBySeller.title}}</span></p>
<div class="img-container">
<ul class="img-wrapper">
<li
v-for="(imgUrl, i) in miniFaultConfirm.secondDetailBySeller.imageUrlList.slice(
0,
3
)"
:key="i"
@click="showBigImage(miniFaultConfirm.secondDetailBySeller.imageUrlList, i)"
>
<ImageFormat
:data-secc="imgUrl"
:src="imgUrl"
alt=""
:width="70"
:height="70"
/>
</li>
<li v-if="miniFaultConfirm.secondDetailBySeller.imageUrlList.length > 3">
<Button
class="more"
@click="showBigImage(miniFaultConfirm.secondDetailBySeller.imageUrlList, 3)"
>查看更多</Button
>
</li>
</ul>
</div>
<p class="seller-tip">
<span class="tip-text-gray" v-if="miniFaultConfirm.secondDetailBySeller.sndTitle">{{miniFaultConfirm.secondDetailBySeller.sndTitle}}</span>
</p>
<p><span class="tip-text-gray" v-if="miniFaultConfirm.secondDetailBySeller.desc">{{miniFaultConfirm.secondDetailBySeller.desc}}</span></p>
</div>
<p class="tip">
<span
:class="[miniFaultConfirm.showBtn ? '' : 'tipTextGray']"
:class="[miniFaultConfirm.showBtn ? '' : 'tip-text-gray']"
>{{ miniFaultConfirm.text }}</span
>
<span v-if="miniFaultConfirm.showBtn">
... ... @@ -67,7 +104,7 @@
3
)"
:key="i"
@click="showBigImage(miniFaultConfirm, i)"
@click="showBigImage(miniFaultConfirm.imageUrls, i)"
>
<ImageFormat
:data-secc="imgUrl"
... ... @@ -80,7 +117,7 @@
<li v-if="miniFaultConfirm.imageUrls.length > 3">
<Button
class="more"
@click="showBigImage(miniFaultConfirm, 3)"
@click="showBigImage(miniFaultConfirm.imageUrls, 3)"
>查看更多</Button
>
</li>
... ... @@ -217,11 +254,11 @@ export default {
onCancel: () => {}
}).show();
},
showBigImage(data, index) {
showBigImage(imageUrls = [], index) {
// 点击小图展示大图
if (data.imageUrls && data.imageUrls.length > 0) {
if (imageUrls && imageUrls.length > 0) {
this.imageUrls = [];
data.imageUrls.forEach(val => {
imageUrls.forEach(val => {
this.imageUrls.push(val.replace(/{width}x{height}/, "750x"));
});
... ... @@ -352,12 +389,16 @@ export default {
}
}
.seller-tip {
margin-top: 20px;
}
.tip {
margin-top: 20px;
margin-bottom: 10px;
}
.tipTextGray {
.tip-text-gray {
color: #999;
}
... ...
... ... @@ -11,8 +11,8 @@
<div class="pro-info">
<p class="pro-name">{{productInfo.productName}}</p>
<p class="stock-info">
<!-- <Logo :text="`有货UFO`" class="logo-wrapper"></Logo>-->
<p class="stock-text">{{productInfo.colorName}}, {{productInfo.sizeNum}}个尺码, {{productInfo.storageNum}}个商品库存<p>
<!-- <Logo :text="`有货UFO`" class="logo-wrapper"></Logo> -->
<p class="stock-text"><span class="icon-ufo"></span> {{productInfo.colorName}}, {{productInfo.sizeNum}}个尺码, {{productInfo.storageNum}}个商品库存<p>
</p>
</div>
</div>
... ...
... ... @@ -10,7 +10,7 @@
></ImgSize>
<div class="pro-info">
<p class="pro-name">
{{ goodsInfo.colorName }}, {{ goodsInfo.sizeName }}码
<span class='icon-ufo'></span> {{ goodsInfo.colorName }}, {{ goodsInfo.sizeName }}码
</p>
<p class="stock-info ufo-font">
最低售价: ¥{{ goodsInfo.leastPrice || "-" }}
... ...
... ... @@ -47,7 +47,7 @@ export default {
* 1.P_NAME:页面名称,UFOProductDetail_LIST;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id;
* 4.PRD_ID:商品id;
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
recommendYasParams: {
... ... @@ -95,7 +95,7 @@ export default {
* 1.P_NAME:页面名称,XY_UFOProductDetail;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id
* 4.PRD_ID:商品id
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
this.yasTargets[id] = {
... ... @@ -103,7 +103,7 @@ export default {
offsetTop: productElList[idx].offsetTop,
offsetHeight: productElList[idx].offsetHeight,
},
yasParams: {...this.recommendYasParams, I_INDEX: idx + 1, PRD_SKN: item.id},
yasParams: {...this.recommendYasParams, I_INDEX: idx + 1, PRD_ID: item.id},
};
}
});
... ...
<template>
<div v-if="canShowTips" class="detail-useage-tips">
<div :class="['step', 'step-' + tipsStep]" @touchend="changeStep" @touchstart="stopPrevent"></div>
</div>
</template>
<script>
export default {
name: 'DetailUseageTips',
data() {
return {
tipsStep: 1,
canShowTips: 0,
touchStartY: 0
};
},
activated() {
this.tipsStep = 1;
this.canShowTips = 0;
this.touchStartY = 0;
this.checkCanShowTips();
},
methods: {
checkCanShowTips() { // 检查localStroage中有没有显示过提示的记录
let storage = localStorage.getItem('userTipsShow');
if (storage) {
this.canShowTips = 0;
} else {
this.canShowTips = 1;
}
},
setShowTips() { // 在localStorage中存储一个值,下次打开时不再显示提示
localStorage.setItem('userTipsShow', 1);
},
changeStep(e) {
if (Math.abs(e.changedTouches[0].pageY - this.touchStartY) < 10) {
this.tipsStep += 1;
if (this.tipsStep > 4) {
this.canShowTips = 0;
this.setShowTips();
}
}
},
stopPrevent(e) {
e.preventDefault();
this.touchStartY = e.touches[0].pageY;
}
}
};
</script>
<style lang="scss" scoped>
.detail-useage-tips {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 99;
background-color: rgba(0, 0, 0, 0.5);
.step {
position: relative;
width: 100%;
height: 100%;
background-size: 100% auto;
background-position: left center;
}
.step-1 {
background-image: url('~statics/image/product/detail-useage-tips-xs-1.png');
}
.step-2 {
background-image: url('~statics/image/product/detail-useage-tips-xs-2.png');
}
.step-3 {
background-image: url('~statics/image/product/detail-useage-tips-xs-3.png');
}
.step-4 {
background-image: url('~statics/image/product/detail-useage-tips-xs-4.png');
}
@media screen and (max-width: 375px) {
@media screen and (max-height: 812px) {
.step-1 {
background-image: url('~statics/image/product/detail-useage-tips-xs-1.png');
}
.step-2 {
background-position: left -200px;
background-image: url('~statics/image/product/detail-useage-tips-xs-2.png');
}
.step-3 {
background-position: left -160px;
background-image: url('~statics/image/product/detail-useage-tips-xs-3.png');
}
.step-4 {
background-position: left -136px;
background-image: url('~statics/image/product/detail-useage-tips-xs-4.png');
}
}
@media screen and (max-height: 667px) {
.step-1 {
background-image: url('~statics/image/product/detail-useage-tips-1.png');
}
.step-2 {
background-position: left -200px;
background-image: url('~statics/image/product/detail-useage-tips-2.png');
}
.step-3 {
background-position: left -160px;
background-image: url('~statics/image/product/detail-useage-tips-3.png');
}
.step-4 {
background-position: left -120px;
background-image: url('~statics/image/product/detail-useage-tips-4.png');
}
}
}
@media screen and (max-width: 414px){
@media screen and (max-height: 812px){
.step-1 {
background-image: url('~statics/image/product/detail-useage-tips-xs-1.png');
}
.step-2 {
background-position: left -200px;
background-image: url('~statics/image/product/detail-useage-tips-xs-2.png');
}
.step-3 {
background-position: left -160px;
background-image: url('~statics/image/product/detail-useage-tips-xs-3.png');
}
.step-4 {
background-position: left -120px;
background-image: url('~statics/image/product/detail-useage-tips-xs-4.png');
}
}
@media screen and (max-height: 667px) {
.step-1 {
background-image: url('~statics/image/product/detail-useage-tips-1.png');
}
.step-2 {
background-position: left -160px;
background-image: url('~statics/image/product/detail-useage-tips-2.png');
}
.step-3 {
background-position: left -112px;
background-image: url('~statics/image/product/detail-useage-tips-3.png');
}
.step-4 {
background-position: left -120px;
background-image: url('~statics/image/product/detail-useage-tips-4.png');
}
}
@media screen and (max-height: 560px){
.step-1 {
background-image: url('~statics/image/product/detail-useage-tips-1.png');
}
.step-2 {
background-position: left -160px;
background-image: url('~statics/image/product/detail-useage-tips-2.png');
}
.step-3 {
background-position: left -112px;
background-image: url('~statics/image/product/detail-useage-tips-3.png');
}
.step-4 {
background-position: left -200px;
background-image: url('~statics/image/product/detail-useage-tips-4.png');
}
}
}
}
@keyframes changeStep {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
... ...
... ... @@ -28,7 +28,7 @@
<script>
import { Scroll } from 'cube-ui';
import { orderBy } from 'lodash';
import { orderBy, get } from 'lodash';
const sizeMap = {
XXXS: -1, // ???
... ... @@ -122,10 +122,15 @@ export default {
},
methods: {
priceMap(info) {
let price;
if (info.least_price > 0) {
price = `${info.least_price}`;
let price = get(info, 'least_price', 0);
let secondHandPrice = get(info, 'second_hand_least_price', price);
if (price && secondHandPrice) {
price = Math.min(price, secondHandPrice);
} else if (price <= 0 && secondHandPrice > 0) {
price = secondHandPrice;
} else if (price > 0 && secondHandPrice <= 0) {
// pass
} else {
price = '-';
}
... ... @@ -138,15 +143,19 @@ export default {
// 出售: 只要上架,都可出售
const isMarketable = this.config.type === 'sell';
// 购买二手鞋
const isUsed = this.config.type === 'buy' && info.second_hand_skup > 0 && secondHandPrice > 0;
return {
size_id: info.size_id,
name: name[0],
subName: name[1],
price,
storage_id: info.storage_id,
available: isTradable || isMarketable,
available: isTradable || isMarketable || isUsed,
isTradable,
isMarketable,
isUsed
};
},
simplePriceMap(info) {
... ...
... ... @@ -25,11 +25,14 @@
:selected="selectedSize"
:config="config"
@select="onSelectSize"
@add="onAdd" />
@add="onAdd"/>
<transition name="slide-up">
<div class="footer" v-if="isAvailable">
<cube-button v-if="isMarketable" @click="convertToCash" :class="{active: isMarketable}">变现<span> <i>¥</i>{{cashPrice}}</span></cube-button>
<cube-button @click="select" :class="{active: isTradable}">{{config.title}}</cube-button>
<cube-button v-if="config.type === 'sell'" @click="select('sell')" :class="{active: isTradable}">{{config.btnTitle}}</cube-button>
<cube-button v-if="config.type === 'buy' && isUsed" @click="select('second')">二手<span> <i>¥</i>{{usedPrice}}</span></cube-button>
<cube-button v-if="config.type === 'buy' && isTradable" @click="select('buy')" class="active">{{config.btnTitle}}<span> <i>¥</i>{{latestPrice}}</span></cube-button>
</div>
</transition>
</div>
... ... @@ -47,6 +50,7 @@ import SizeList from './size-list';
import SquareImg from './square-img';
const { mapActions: mapProductActions, mapState } = createNamespacedHelpers('product');
const { mapActions: mapSecondActions } = createNamespacedHelpers('second');
export default {
name: 'SizeSelectSheet',
... ... @@ -90,15 +94,20 @@ export default {
return {};
},
selectedPrice() {
let price = this.product.least_price;
let price = get(this.selectedSize, 'least_price', 0);
let secondHandPrice = get(this.selectedSize, 'second_hand_least_price', 0);
if (this.config.type === 'sell') {
return price;
}
if (this.selectedSize.size_id > 0) {
if (this.selectedSize.least_price > 0) {
price = this.selectedSize.least_price;
if (price && secondHandPrice) {
price = Math.min(price, secondHandPrice);
} else if (price <= 0 && secondHandPrice > 0) {
price = secondHandPrice;
} else if (price > 0 && secondHandPrice <= 0) {
// pass
} else {
price = '-';
}
... ... @@ -106,12 +115,20 @@ export default {
return price;
},
/**
* 现货价格
*/
latestPrice() {
return this.selectedSize.least_price;
},
goods_name() {
return get(this.product, 'goods_list[0].goods_name', '');
},
sizeName() {
if (this.selectedSize && this.selectedSize.name) {
return this.selectedSize.name + '码';
if (this.selectedSize && this.selectedSize.size_name) {
return this.selectedSize.size_name + '码';
}
return null;
... ... @@ -125,6 +142,20 @@ export default {
},
/**
* 是二手鞋
*/
isUsed() {
return get(this.selectedSize, 'second_hand_skup', 0) && get(this.selectedSize, 'second_hand_least_price', 0);
},
/**
* 二手鞋的价格
*/
usedPrice() {
return get(this.selectedSize, 'second_hand_least_price');
},
/**
* 可交易(购买|出售)
*/
isTradable() {
... ... @@ -161,6 +192,7 @@ export default {
'SELLER_ASK_SET_PRODUCTINFO'
]),
...mapProductActions(['updateTradeInfo']),
...mapSecondActions(['fetchStorageCount']),
onHidden() {
this.$emit('hidden');
},
... ... @@ -172,7 +204,7 @@ export default {
this.$refs.sizeList.refreshScroll();
}
},
onSelectSize({selected: sizeInfo}) {
onSelectSize({ selected: sizeInfo }) {
this.updateTradeInfo({
productId: this.product.product_id,
sizeInfo,
... ... @@ -184,17 +216,23 @@ export default {
onAdd() {
this.$emit('add');
},
select() {
if (!this.isTradable) {
select(type) {
if (!this.isTradable && !this.isUsed) {
return;
}
const isSecond = type === 'second';
const secondSkup = get(this.selectedSize, 'second_hand_skup', 0);
this.$emit('select', {
size_name: this.selectedSize.size_name,
size_id: this.selectedSize.size_id,
productId: this.product.product_id,
storageId: this.selectedSize.storage_id,
skup: this.selectedSize.skup,
skup: isSecond ? secondSkup : this.selectedSize.skup,
bid_skup: this.selectedSize.bid_skup,
second_hand_skup: secondSkup,
type
});
},
convertToCash() {
... ...
... ... @@ -5,7 +5,7 @@
<div @click="onAllClick">全部 <i class="cubeic-arrow"></i></div>
</div>
<div class="row">
<div class="col" v-for="(product, idx) in viewList" :key="idx" @click="onItemClick(product)">
<div class="col" v-for="(product, idx) in viewList" :key="idx" @click="onItemClick(product, idx)">
<div class="product-item">
<square-img :src="product.default_images" :width="600" :height="600" />
</div>
... ... @@ -36,8 +36,8 @@ export default {
},
},
methods: {
onItemClick(item) {
this.$emit('itemClick', item);
onItemClick(item, index) {
this.$emit('itemClick', { product:item, index });
},
onAllClick() {
this.$emit('allClick');
... ...
... ... @@ -16,6 +16,9 @@ export default {
resource() {
return this.productDetail.resource || {};
},
videoResource() {
return this.productDetail.videoResource || {};
},
activity() {
return this.productDetail.activity;
},
... ...
$primary-color : #08304b;
$sub-color : #64ad88;
$primary-color: #08304b;
$sub-color: #64ad88;
$size-font: "PingFang SC", "HiraginoSansGB-W3", "SanFranciscoText-Regular", Helvetica, Roboto, "Heiti SC", "黑体", Arial, serif;
@mixin cube-ufo-btn {
[type="button"] {
... ...
... ... @@ -10,13 +10,14 @@
<cube-slide ref="slide" :options="slideOptions" :data="imageList">
<cube-slide-item v-for="(item, index) in imageList" :key="index">
<a click="javascript:void 0" class="square-img-container">
<square-img v-if="!item.initial" :src="item.image_url" :width="600" :height="600" />
<square-img v-else :src="item.image_url" :width="274" :height="274" /> <!-- 利用缓存, productList使用的size -->
<square-img v-if="!item.initial" :src="item.image_url" :width="600" :height="600"/>
<square-img v-else :src="item.image_url" :width="274" :height="274"/> <!-- 利用缓存, productList使用的size -->
</a>
</cube-slide-item>
<template slot="dots" slot-scope="props">
<div class="dot-wrap">
<span class="cube-dot" :class="{active: props.current === index}" v-for="(item, index) in props.dots" :key="item">&bull;</span>
<span class="cube-dot" :class="{active: props.current === index}" v-for="(item, index) in props.dots"
:key="item">&bull;</span>
</div>
</template>
</cube-slide>
... ... @@ -25,8 +26,8 @@
<div class="info-basic">
<div class="info-price">
<template v-if="productDetail.least_price == null">{{'\u200E'}}</template>
<template v-else><i>¥</i>{{productDetail.least_price}}</template>
<template v-if="price == null">{{'\u200E'}}</template>
<template v-else><i>¥</i>{{price}}</template>
</div>
<div class="info-name"><div>{{productDetail.product_name}}</div></div>
</div>
... ... @@ -35,7 +36,8 @@
</a>
<div class="info">
<transition-group name="info-list" tag="div" class="info-list">
<div class="info-list-item" transtion="fade" v-if="activity && activity.length !== 0" @click="showActivity" key="促销">
<div class="info-list-item" transtion="fade" v-if="activity && activity.length !== 0" @click="showActivity"
key="促销">
<div class="info-list-name">促销</div>
<div class="info-list-value info-promote">
<span>{{activity[0].promotionTypeStr}}</span>
... ... @@ -48,18 +50,36 @@
</div>
</transition-group>
<!-- 相关商品 -->
<top-list ref="topList" v-if="topList && topList.length !== 0" :list="topList" @itemClick="gotoProduct" @allClick="gotoBrand" />
<top-list ref="topList" v-if="topList && topList.length !== 0" :list="topList" @itemClick="gotoProduct"
@allClick="gotoBrand"/>
<img class="ref-img" v-lazy="prdDetailTip"/>
</div>
<img class="ref-img" v-lazy="prdDetailImage" />
<!-- 视频资源位 -->
<a
class="banner video-resource"
ref="videoResourceImg"
v-if="videoResource.src">
<div
class="video-mask"
@click="onVideoPlay"></div>
<VideoPlayer
ref="videoPlayer"
class="video-player"
:source="videoResource.url"
/>
<img-size :src="sizeImg(videoResource.src)"/>
</a>
<img class="ref-img" v-lazy="prdDetailImage"/>
<div class="recommend" v-if="recommend"><h2>相关推荐</h2>
<product-list ref="recommendList" :list="recommend" priceKey="price" :yas-params="recommendYasParams"/>
</div>
</LayoutScroll>
<div class="footer">
<div class="fav" @click="_toggleFav"><i class="iconfont" :class="[isFav ? 'icon-star-fill': 'icon-star-outline']" /><span>收藏</span></div>
<div class="fav" @click="_toggleFav"><i class="iconfont"
:class="[isFav ? 'icon-star-fill': 'icon-star-outline']"/><span>收藏</span></div>
<cube-button class="sell" @click="sell">出售</cube-button>
<cube-button class="buy active" @click="buy">购买</cube-button>
</div>
... ... @@ -75,6 +95,7 @@
@hide="onSizeSelectSheetHide"
@select="onSelectTradeProduct"
@add="onRequestSize"/>
<detail-useage-tips></detail-useage-tips>
</div>
</template>
... ... @@ -97,13 +118,18 @@ import TopList from './components/top-list';
import SquareImg from './components/square-img';
import stateShortCutsMixins from './mixins';
import trackingMixins from './tracking-mixins';
import DetailUseageTips from './components/detail-useage-tips';
import VideoPlayer from '@/components/video-player';
const { mapActions, mapState } = createNamespacedHelpers('product');
const { mapActions: mapSecondActions } = createNamespacedHelpers('second');
export default {
name: 'ProductDetail',
mixins: [stateShortCutsMixins, trackingMixins],
components: {
DetailUseageTips,
SizeSelectSheet,
ActivityListSheet,
SizeRequestSheet,
... ... @@ -116,6 +142,7 @@ export default {
'cube-slide': Slide,
'cube-slide-item': Slide.Item,
'cube-popup': Popup,
VideoPlayer
},
props: {
productId: {
... ... @@ -144,7 +171,7 @@ export default {
* 1.P_NAME:页面名称,XY_UFOProductDetail;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id;
* 4.PRD_ID:商品id;
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
recommendYasParams: {
... ... @@ -160,8 +187,7 @@ export default {
...mapState(['resourceContentCode']),
productDec() {
const goods = get(this.productDetail, 'goods_list[0]', {});
return [
const descList = [
{
text: '颜色',
value: goods.goods_name || '',
... ... @@ -175,15 +201,19 @@ export default {
text: '发售时间',
value: this.productDetail.sale_time
},
// {
// text: '发售价格',
// value: this.productDetail.offer_price
// },
{
text: '货号',
value: this.productDetail.product_code
}
];
if (this.productDetail.offer_price) {
descList.splice(-1, 0, {
text: '发售价格',
value: `¥${this.productDetail.offer_price} 仅供参考`
});
}
return descList;
},
sizeList() {
return get(this.productDetail, 'goods_list[0].size_list', null);
... ... @@ -191,6 +221,23 @@ export default {
title() {
return get(this.productDetail, 'product_name', '商品详情');
},
price() {
let price = [];
if (this.productDetail.least_price) {
price.push(this.productDetail.least_price);
}
if (this.productDetail.second_hand_least_price) {
price.push(this.productDetail.second_hand_least_price);
}
if (price.length === 0) {
return null;
}
return Math.min(...price);
}
},
mounted() {
this.$store.dispatch('getSysConfigQiugou');
... ... @@ -221,7 +268,7 @@ export default {
this.watchList = null;
}
},
asyncData({store, router}) {
asyncData({ store, router }) {
const productId = parseInt(router.params.productId, 10);
if (isNaN(productId)) {
... ... @@ -237,7 +284,7 @@ export default {
store.dispatch('product/setupInitialProductInfo', initialProductInfo);
}
return store.dispatch('product/fetchProductInfo', {productId});
return store.dispatch('product/fetchProductInfo', { productId });
},
activated() {
if (this.$refs.pageScroll) {
... ... @@ -284,12 +331,12 @@ export default {
if (yasParmas) {
if (yasParmas.P_NAME) {
yasParmas.FP_NAME = yasParmas.P_NAME
yasParmas.FP_NAME = yasParmas.P_NAME;
}
if (yasParmas.P_PARAM) {
yasParmas.FP_PARAM = yasParmas.P_PARAM
yasParmas.FP_PARAM = yasParmas.P_PARAM;
}
param = {...yasParmas, ...param};
param = { ...yasParmas, ...param };
}
this.$store.dispatch('reportYas', {
... ... @@ -303,7 +350,7 @@ export default {
* @ description: 配置打开闲鱼导航栏右上角分享按钮 && 配置分享信息 && 上报数据埋点
* @ author: huzhiming
* @ date: 2019-11-13 19:58:50
* @ version: V1.0.5
* @ version: v1.0.0
*/
this.settingShareHandler();
});
... ... @@ -333,6 +380,7 @@ export default {
methods: {
...mapActions(['fetchProductInfo', 'fetchBrandTop', 'fetchFav', 'setupInitialProductInfo',
'toggleFav', 'updateTradeInfo', 'getSelectedTradeProduct', 'payment', 'resetSelectedSize']),
...mapSecondActions(['fetchStorageCount']),
historyBackGuard() {
for (let key of Object.keys(this.actionSheetCloseMap)) {
if (this[key]) {
... ... @@ -386,13 +434,13 @@ export default {
* 1.P_NAME:页面名称,XY_UFOProductDetail;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id
* 4.PRD_ID:商品id
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
this.topList.slice(0, 3).forEach((value, i) => {
this.yasTargets[`topList${i}`] = {
el: elInfo,
yasParams: {...this.recommendYasParams, I_INDEX: i + 1, PRD_SKN: value.id, POS_ID: 1},
yasParams: { ...this.recommendYasParams, I_INDEX: i + 1, PRD_ID: value.id, POS_ID: 1 },
};
});
}
... ... @@ -411,7 +459,7 @@ export default {
offsetTop: productElList[i].offsetTop,
offsetHeight: productElList[i].offsetHeight,
},
yasParams: {...this.recommendYasParams, I_INDEX: i + 1, PRD_SKN: item.id},
yasParams: { ...this.recommendYasParams, I_INDEX: i + 1, PRD_ID: item.id },
};
}
});
... ... @@ -465,24 +513,52 @@ export default {
const isFav = !this.isFav;
this.toggleFav({productId: this.productId, isFav}).then(() => {
this.toggleFav({ productId: this.productId, isFav }).then(() => {
const txt = isFav ? '收藏成功' : '取消收藏成功';
this.$createToast({
txt,
type: 'txt',
}).show();
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_UFO_GDS_DT_FAV',
param: {
PRD_ID: this.productId,
FAVTYPE: isFav ? 1 : 0
}
}
});
});
},
// 进入商品详情
gotoProduct(product) {
gotoProduct({product, index}) {
this.$router.push({
name: this.$route.name,
params: {
productId: product.id,
productInfo: product,
},
yasParams: {
P_NAME: 'XY_UFOProductDetail',
P_PARAM: this.productId,
POS_ID: 1,
}
}
});
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_UFO_PRD_LIST_C',
param: {
P_NAME: 'XY_UFOProductDetail',
P_PARAM: this.productId,
POS_ID: 1,
I_INDEX: index + 1,
PRD_ID: product.id
}
}
});
},
... ... @@ -534,6 +610,7 @@ export default {
dest: 'OrderBuyConfirm',
type: 'buy',
title: '购买',
btnTitle: '现货'
};
this.showSizeSelectSheet = true;
},
... ... @@ -549,15 +626,17 @@ export default {
// 商品是否指定出售
const limitInfo = get(this.productDetail, 'limitInfo', {});
if(limitInfo.isLimit === '1') {
if (limitInfo.isLimit === '1') {
this.$createDialog({
type: "alert",
title: "商品出售限制",
type: 'alert',
title: '商品出售限制',
content: limitInfo.tip || '',
confirmBtn: { text: "我知道了" }
confirmBtn: { text: '我知道了' }
}).show();
return;
}
/**
* 数据埋点
* 商品详情页点击出售/购买/求购按钮
... ... @@ -579,6 +658,7 @@ export default {
dest: 'OrderSellConfirm',
type: 'sell',
title: '出售',
btnTitle: '出售',
};
this.showSizeSelectSheet = true;
},
... ... @@ -596,8 +676,11 @@ export default {
* 2.PRD_SKU:商品SKU;
* 3.PRD_SIZE:尺码;
* 新增字段:
* 4.ORD_TYPE:订单类型;1-出售,2-购买,3-求购,4-变现
* 4.ORD_TYPE:订单类型;1-出售,2-购买,3-求购,4-变现, 5-二手
*/
const isSecond = tradeProduct.type === 'second';
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_UFO_PRD_DT_BUY_SEL_C',
... ... @@ -605,7 +688,7 @@ export default {
PRD_ID: tradeProduct.productId,
PRD_SKU: tradeProduct.skup,
PRD_SIZE: tradeProduct.size_id,
ORD_TYPE: this.selectSizeConfig.type === 'buy' ? 2 : 1
ORD_TYPE: this.selectSizeConfig.type === 'buy' ? (isSecond ? 5 : 2) : 1
},
}
});
... ... @@ -657,10 +740,39 @@ export default {
this.showSizeSelectSheet = false;
this.$router.push({
name: this.selectSizeConfig.dest,
query: tradeProduct
});
if (isSecond) {
const { data: count } = await this.fetchStorageCount({ storageId: tradeProduct.storageId });
if (count === 1) {
// 跳转详情
this.$router.push({
name: 'SecondProductDetail',
params: {
skup: tradeProduct.skup,
yasParams: {
P_NAME: 'XY_UFO' + this.$route.name,
PRD_ID: tradeProduct.productId
}
}
});
} else if (count > 1) {
// 跳转列表
this.$router.push({
name: 'SecondSellList',
params: {
id: tradeProduct.storageId,
},
query: {
pid: tradeProduct.productId
}
});
}
} else {
this.$router.push({
name: this.selectSizeConfig.dest,
query: tradeProduct
});
}
},
// 添加尺寸
... ... @@ -724,22 +836,23 @@ export default {
},
}
});
this.$xianyu.goXianyuNewPage({url});
this.$xianyu.goXianyuNewPage({ url });
},
/*
* @ description: 配置打开闲鱼导航栏右上角分享按钮 && 配置分享信息 && 上报数据埋点
* @ author: huzhiming
* @ date: 2019-11-13 20:00:55
* @ version: V1.0.5
* @ version: v1.0.0
*/
async settingShareHandler() {
/*
* @ description: [数据上报]@hooks: 分享链接进入pv,查看闲鱼淘口令,地址栏会附带shareUserId参数,目前淘口令查看事件未知,后期若知道淘口令打开事件可调整此数据埋点
* @ author: huzhiming
* @ date: 2019-11-13 20:36:00
* @ version: V1.0.5
* @ version: v1.0.0
*/
if ( get(this.$route,'query.shareUserId','') !='' ) {
if (get(this.$route, 'query.shareUserId', '') !== '') {
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_SHARE_SUCCESS_L',
... ... @@ -751,35 +864,36 @@ export default {
});
}
let image = this.sizeImg(get(this.imageList,'[0].image_url',''),200,200),
yasReportHandler = {
// [数据上报]@hooks: 拉起分享弹窗触发
XY_SHARE_PRODUCT: ()=>{
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_SHARE_PRODUCT',
param: {
PRD_ID: this.productId,
SHARE_URL: location.href
}
let image = this.sizeImg(get(this.imageList, '[0].image_url', ''), 200, 200),
yasReportHandler = {
// [数据上报]@hooks: 拉起分享弹窗触发
XY_SHARE_PRODUCT: () => {
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_SHARE_PRODUCT',
param: {
PRD_ID: this.productId,
SHARE_URL: location.href
}
});
},
// [数据上报]@hooks: 点击对应分享平台触发
XY_SHARE_TYPE: (data) =>{
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_SHARE_TYPE',
param: {
SHARE_TYPE: data.pluginKey,
SHARE_URL: location.href
}
}
});
},
// [数据上报]@hooks: 点击对应分享平台触发
XY_SHARE_TYPE: (data) => {
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_SHARE_TYPE',
param: {
SHARE_TYPE: data.pluginKey,
SHARE_URL: location.href
}
});
}
}
});
}
};
await this.$xianyu.setNavRightItem({
this.$xianyu.setNavRightItem({
shareType: 'activity', // 类型,默认activity
image,
url: `${location.href}?isNeedRefresh=false`, // 分享链接
... ... @@ -787,23 +901,59 @@ export default {
title: `淘口令#${this.productDetail.product_name}`, // 分享标题
text: '' // 分享描述
}, yasReportHandler);
},
onVideoPlay() {
this.$refs.videoPlayer.parentHandleclick();
}
},
};
</script>
<style lang="scss" scoped>
@import "./product-detail";
.video-mask {
z-index: 10;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.video-player {
display: block;
height: 100%;
width: 100%;
opacity: 0;
position: absolute;
bottom: 0;
overflow: hidden;
/deep/ .video-js {
padding: 0;
height: 100%;
}
}
.video-resource {
margin-top: 30px;
position: relative;
overflow: hidden;
}
.cube-btn {
border-radius: 0;
}
.fade-enter, .fade-leave-to {
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active, .fade-leave-active {
.fade-enter-active,
.fade-leave-active {
transition: opacity 300ms linear;
}
... ...
<!--
* @description: 筛选组件
* @fileName: filtrate.vue
* @author: huzhiming
* @date: 2019-11-21 18:22:33
* @后台人员:
* @version: v1.0.0
* @path: 页面访问路径及参数说明
@调用示例:
import FilterList from './components/filter-list';
<FilterList
:visible.sync="filterModalVisible"
:storage_id="storage_id"
@submit="handleFilterSubmit"
></FilterList>
components: {
FilterList,
}
!-->
<template>
<div
class="container"
v-show="visible"
>
<!-- <div class="title">筛选
<div class="cancel" @click="$emit('update:visible', !visible)">取消</div>
</div> -->
<div class="content-search" v-if="filterData && filterData.length">
<div
class="item"
v-for="(filter, rowIndex) in filterData" :key="filter.filterId"
>
<div class="item-title">{{filter.filterName}}</div>
<div
v-if="filter.itemList && filter.itemList.length"
class="item-list"
>
<template v-if="filter.filterId === 'second_type'">
<div
class="item-text" :class="{'item-text-selected': selected[filter.filterId]===item.itemId}"
v-for="(item, index2) in filter.itemList" :key="item.itemId"
@click="selectChange({ filterId: filter.filterId, itemId: item.itemId, row: rowIndex})"
>
{{item.itemName}}
</div>
</template>
<template v-if="filter.filterId === 'storage_id'">
<div
class="item-text item-size item-text-selected"
v-for="(item) in filter.itemList" :key="item.itemId"
>
{{item.itemName}}
</div>
</template>
<template v-if="filter.filterId === 'brand_id'">
<div
class="item-img-content item-img-selected"
v-for="(item) in filter.itemList" :key="item.itemId"
>
<div class="item-img">
<img
:src="(item.itemUrl || '').replace('http://', '//').replace('{width}', 280).replace('{height}', 140)"/>
</div>
<div class="item-img-text">{{item.itemName}}</div>
</div>
</template>
</div>
</div>
</div>
<div class="bottom">
<div
class="clear"
@click="clear()"
>清除</div>
<div
class="submit"
@click="submit"
>确定</div>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
// const filterData = [
// {
// "filterId": "sort",
// "filterName": "品类",
// "itemList": [
// {
// "itemId": "44",
// "itemName": "运动鞋"
// }
// ],
// "multiSelect": false
// },
// {
// "filterId": "size",
// "filterName": "尺码",
// "itemList": [
// {
// "itemId": "353,557",
// "itemName": "XXXXL"
// }
// ],
// "multiSelect": false
// },
// {
// "filterId": "brand",
// "filterName": "品牌",
// "itemList": [
// {
// "itemId": "3",
// "itemName": "NIKE",
// "itemUrl": "http://img11.static.yhbimg.com/brandLogo/2018/12/26/14/01184d5c4485f4dcd91498fa4acb8282fc.png?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80"
// }
// ],
// "multiSelect": false
// }
// ]
export default {
name: 'Filtrate',
components: {},
props: {
storage_id: Number,
visible: false
},
data() {
return {
selected: {
// second_type: null // 注意:null是默认值, 点击对应item 会根据 filterData[x].filterId值 动态创建此对象
}
};
},
async activated() {
await this.fetchFilterData({ storage_id: this.storage_id });
},
computed: {
...mapState('second', ['filterData']),
},
methods: {
...mapActions('second', ['fetchFilterData']),
/*
* @ description: 重置 vm.selected 对象下 所有value
* @ author: huzhiming
* @ date: 2019-11-25 14:47:01
* @ version: v1.0.0
*/
clear() {
for (const key of Object.keys(this.selected)) {
this.$set(this.selected, key, null);
this.$emit('submit', this.selected);
}
this.$emit('update:visible', !this.visible);
},
/*
* @ description: 筛选 选中处理, 动态创建对象并赋值,重复点击某一项可开关切换 状态
* @ author: huzhiming
* @ date: 2019-11-25 15:00:25
* @ version: v1.0.0
*/
selectChange({ filterId, itemId, row }) {
// console.log(JSON.stringify(this.selected), filterId);
/*
* @ description: 动态构造 this.selected对象的key 并且赋 选中值;若有值则清空value,实现点击开关切换效果
* @ author: huzhiming
* @ date: 2019-11-25 15:08:42
* @ version: v1.0.0
*/
if (this.selected[filterId] !== itemId) {
this.$set(this.selected, filterId, itemId);
} else {
this.$set(this.selected, filterId, null);
}
},
/*
* @ description: 点击提交按钮
* @ author: huzhiming
* @ date: 2019-11-25 17:10:12
* @ version: v1.0.0
*/
submit() {
if (Object.keys(this.selected).length !== 0) {
this.$emit('submit', this.selected);
}
this.$emit('update:visible', !this.visible);
}
}
};
</script>
<style lang="scss" scoped>
.magrin-right {
margin-right: 14px;
}
.header {
width: 100%;
height: 45px;
padding-left: 20px;
padding-right: 20px;
background-color: #fff;
display: flex;
align-items: stretch;
box-sizing: border-box;
}
.container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #fff;
z-index: 999;
}
.title {
font-size: 68px;
font-weight: bold;
color: #000;
height: 82px;
line-height: 82px;
margin: 40px;
flex-direction: row;
display: flex;
justify-content: space-between;
.cancel {
font-size: 36px;
color: #888;
}
}
.content-search {
margin-top: 80px;
.item {
margin-bottom: 60px;
}
.item-title {
margin-left: 40px;
margin-bottom: 20px;
font-size: 40px;
color: #000;
letter-spacing: 0;
font-weight: 500;
}
.item-list {
overflow-y: hidden;
overflow-x: scroll;
display: -webkit-box;
width: 100%;
margin-left: 1rem;
}
.item-list ::-webkit-scrollbar {
display: none;
}
.item-text {
background: #f5f5f5;
font-size: 28px;
color: #222;
text-align: center;
width: 200px;
height: 80px;
line-height: 80px;
margin-right: 20px;
border-radius: 40px;
}
.item-text-selected {
background: #08304b;
color: #fff;
}
.item-size {
width: 130px;
height: 80px;
line-height: 80px;
}
.item-img {
width: 140px;
height: 70px;
margin-bottom: 22px;
& > img {
width: 100%;
height: 100%;
}
}
.item-img-selected {
opacity: 1 !important;
}
.item-img-content {
opacity: 0.2;
width: 160px;
height: 140px;
display: flex;
justify-content: center;
flex-direction: column;
margin-right: 20px;
}
.item-img-text {
font-size: 20px;
color: #000;
letter-spacing: 0;
text-align: center;
width: 140px;
height: 64px;
}
}
.bottom {
height: 120px;
position: fixed;
bottom: 0;
z-index: 9;
width: 100%;
background: #fff;
display: flex;
border-top: 1px solid rgba(0, 0, 0, 0.12);
justify-content: space-between;
align-items: center;
}
.clear {
margin-left: 32px;
line-height: 80px;
text-align: center;
width: 330px;
height: 80px;
color: #000;
font-size: 32px;
border: 1px solid rgba(0, 0, 0, 0.12);
border-radius: 40px;
}
.submit {
line-height: 80px;
background-color: #08304b;
width: 330px;
height: 80px;
text-align: center;
color: #fff;
font-size: 32px;
border-radius: 40px;
margin-right: 32px;
}
.back {
margin-top: 24px;
width: 24px;
height: 24px;
background: url(~statics/image/order/back@3x.png) no-repeat;
background-size: cover;
}
</style>
... ...
<template>
<div class="bg" v-if="list.length">
<div class="second-list-item" v-for="(product,index) in list" @click="goDetail(product, index)"
:key="index" :class="(index) % 2 === 0 && 'magrin-right'">
<div class="second-image-div">
<image-format
:class="product.status === 100 ? 'dim-imge' : 'second-image'"
:src="product.secondhand_image"
:width="180"
:height="180"/>
<div v-if="product.status === 100" class="dim-out"></div>
</div>
<div class="item-name">
<p>
<span v-if="product.secondhandTypeName">{{product.secondhandTypeName}}</span>
<span>{{product.product_name}}</span>
</p>
</div>
<div class="item-bottom">
<div class="price-flag">
<span>{{product.skup_price && '¥'}}</span><span>{{product.skup_price || ' '}}</span>
<span class='save-flag'>{{product.save_price && '已省¥'}}{{product.save_price || ' '}}</span>
</div>
<div v-if="product.size_name" class="size-flag">{{product.size_name}}码</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
list: Array,
yasParams: Object,
yasEventName: {
type: String,
default: 'XY_UFO_PRD_LIST_C'
},
},
data: function() {
return {
yasFirstId: 0,
itemHeight: 0
};
},
methods: {
goDetail(product, index) {
if (this.yasParams && Object.keys(this.yasParams).length) {
this.yasDetail({ item: product, index });
}
this.$router.push({
name: 'SecondProductDetail',
params: {
skup: product.skup,
yasParams: { ...this.yasParams, PRD_ID: product.product_id },
}
});
},
yasShowEvent: function(height) {
if (!this.itemHeight) {
let item = document.querySelector('.second-list-item');
this.itemHeight = item && item.offsetHeight || 0;
}
if (!this.itemHeight) {
return;
}
// 获取列表单个元素高度
let index = 0;
if (Number(height) > 0) {
// 获取第一个曝光元素
let row = Math.floor((height - 12) / this.itemHeight) + 1;
index = row * 2 - 2;
}
// 获取曝光列表
let list = [];
for (let i = 0; i < 6; i++) {
if (this.list[i + index]) {
list.push(this.list[i + index]);
}
}
// 判断是否是重复曝光
if (list.length && list[0].product_id !== this.yasFirstId) {
this.yasFirstId = list[0].product_id;
let DATA = [];
list.forEach((value, i) => {
const prdType = value.pre_sale_flag === 5 ? 2 : 1; // flag=5全新瑕疵,6二手
DATA.push({ ...this.yasParams, I_INDEX: i + index + 1, PRD_SKUP: value.skup, PRD_ID: value.product_id, PRD_TYPE: prdType });
});
this.$store.dispatch('reportYas', {
params: {
param: { DATA },
appop: 'XY_UFO_SHOW_EVENT'
}
});
}
},
yasDetail({ item, index }) {
this.$store.dispatch('reportYas', {
params: {
param: { ...this.yasParams, I_INDEX: index + 1, PRD_ID: item.product_id, PRD_SKUP: item.skup },
appop: this.yasEventName
}
});
}
}
};
</script>
<style lang="scss" scoped>
.magrin-right {
margin-right: 14px;
}
.second-list-item {
border-radius: 16px;
width: 344px;
background: #fff;
margin-bottom: 16px;
}
.second-image-div {
display: flex;
overflow: hidden;
align-items: center;
justify-content: center;
position: relative;
}
.second-image {
height: 344px;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
}
.dim-imge {
height: 344px;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
opacity: 0.4;
filter: alpha(opacity=40);
}
.dim-out {
width: 200px;
height: 200px;
background: url(~statics/image/list/Group@2x.png) no-repeat;
background-size: contain;
position: absolute;
top: 71.5px;
left: 71.5px;
}
.item-name {
padding: 20px 14px 30px 20px;
align-items: center;
& > p {
height: 80px;
color: #000;
font-size: 24px;
line-height: 40px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
overflow: hidden;
& > :first-child {
color: #fff;
background: #000;
padding: 4px 8px;
text-align: center;
font-size: 24px;
line-height: 30px;
display: inline-block;
border-radius: 4px;
}
}
}
.item-bottom {
padding: 0 14px 30px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.price-flag {
color: #000;
font-size: 32px;
vertical-align: center;
display: flex;
flex-direction: row;
align-items: center;
@include num;
}
.save-flag {
margin-left: 10px;
color: #d0021d;
font-size: 26px;
}
.size-flag {
font-size: 22px;
color: #000;
line-height: 26px;
letter-spacing: 0;
}
.bg {
padding: 24px 24px 8px;
display: flex;
flex-wrap: wrap;
}
</style>
... ...
<template>
<div class="bg" v-if="list.length">
<div class="second-list-item" v-for="(item,index) in list" @click="goDetail(item, index)"
:key="index" :class="(index) % 2 === 0 && 'magrin-right'">
<div class="second-image-div">
<image-format
:class="item.status === 100 ? 'dim-imge' : 'second-image'"
:src="item.image"
:width="180"
:height="180"/>
<div v-if="item.status === 100" class="dim-out"></div>
</div>
<div class="item-bottom">
<div class='flag-wrapper'>
<span class='price-flag'>{{item.price && '¥'}}{{item.price || ' '}}</span>
<span class='type-flag' v-if="item.sechondHandTypeName">{{item.sechondHandTypeName}}</span>
</div>
<div class="size-flag">{{item.sizeName}}</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
list: Array,
yasParams: Object,
yasEventName: {
type: String,
default: 'XY_UFO_PRD_LIST_C'
},
},
data: function() {
return {
yasFirstId: 0,
itemHeight: 0
};
},
methods: {
goDetail(item, index) {
if (this.yasParams && Object.keys(this.yasParams).length) {
this.yasDetail({ item, index });
}
this.$router.push({
name: 'SecondProductDetail',
params: {
skup: item.skup,
yasParams: { ...this.yasParams },
}
});
},
yasShowEvent: function(height) {
if (!this.itemHeight) {
let item = document.querySelector('.second-list-item');
this.itemHeight = item && item.offsetHeight || 0;
}
if (!this.itemHeight) {
return;
}
// 获取列表单个元素高度
let index = 0;
if (Number(height) > 0) {
// 获取第一个曝光元素
let row = Math.floor((height - 12) / this.itemHeight) + 1;
index = row * 2 - 2;
}
// 获取曝光列表
let list = [];
for (let i = 0; i < 6; i++) {
if (this.list[i + index]) {
list.push(this.list[i + index]);
}
}
// 判断是否是重复曝光
if (list.length && list[0].skup !== this.yasFirstId) {
this.yasFirstId = list[0].skup;
let DATA = [];
list.forEach((value, i) => {
const prdType = value.pre_sale_flag === 5 ? 2 : 1; // flag=5全新瑕疵,6二手
// DATA.push({ ...this.yasParams, I_INDEX: i + index + 1, PRD_ID: value.skup, PRD_TYPE: prdType });
DATA.push({ ...this.yasParams, I_INDEX: i + index + 1, PRD_SKUP: value.skup, PRD_TYPE: prdType });
});
this.$store.dispatch('reportYas', {
params: {
param: { DATA },
appop: 'XY_UFO_SHOW_EVENT'
}
});
}
},
yasDetail({ item, index }) {
this.$store.dispatch('reportYas', {
params: {
param: { ...this.yasParams, I_INDEX: index + 1, PRD_SKUP: item.skup },
appop: this.yasEventName
}
});
}
}
};
</script>
<style lang="scss" scoped>
.magrin-right {
margin-right: 14px;
}
.second-list-item {
border-radius: 16px;
width: 344px;
background: #fff;
margin-bottom: 16px;
}
.second-image-div {
display: flex;
overflow: hidden;
align-items: center;
justify-content: center;
position: relative;
}
.second-image {
height: 344px;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
}
.dim-imge {
height: 344px;
border-top-left-radius: 16px;
border-top-right-radius: 16px;
opacity: 0.4;
filter: alpha(opacity=40);
}
.dim-out {
width: 200px;
height: 200px;
background: url(~statics/image/list/Group@2x.png) no-repeat;
background-size: contain;
position: absolute;
top: 71.5px;
left: 71.5px;
}
.item-bottom {
padding: 20px 14px 30px 20px;
display: flex;
justify-content: space-between;
align-items: center;
vertical-align: middle;
}
.flag-wrapper {
display: flex;
flex-direction: row;
align-items: center;
}
.price-flag {
color: #000;
font-size: 32px;
font-weight: bold;
line-height: 40px;
@include num;
}
.type-flag {
margin-left: 5px;
color: #fff;
background: #000;
text-align: center;
font-size: 22px;
line-height: 40px;
padding: 4px 8px;
line-height: 30px;
border-radius: 4px;
}
.size-flag {
font-size: 22px;
color: #000;
line-height: 40px;
letter-spacing: 0;
}
.bg {
padding: 24px 24px 8px;
display: flex;
flex-wrap: wrap;
}
</style>
... ...
<!--
* @description: 二手商品详情
* @fileName: detail.vue
* @author: huzhiming
* @date: 2019-11-21 10:28:52
* @后台人员:
* @version: v1.0.0
* @path: 页面访问路径及参数说明 http://m.yohobuy.com:6001/xianyu/second/product/商品id.html
!-->
<template>
<LayoutApp :show-back="true" title="商品详情" class="second-detail-wrap">
<LayoutScroll
ref="pageScroll"
@scroll-end="false"
:loading="{hide: true, noMore: true}">
<cube-slide ref="slide" :options="slideOptions" :data="info.imageList">
<cube-slide-item v-for="(item, index) in info.imageList" :key="index">
<image-format
:lazy="false"
alt="图片加载失败"
:src="item"
:width="375"
:height="375"
/>
<div class="pole-dot-area"></div>
</cube-slide-item>
<template slot="dots" slot-scope="props">
<div class="dot-wrap">
<span class="cube-dot" :class="{active: props.current === index}" v-for="(item, index) in props.dots"
:key="item">&bull;</span>
</div>
</template>
</cube-slide>
<div class="primary">
<div>
<p class="price">¥{{info.price}} <span v-if="info.newProductPrice">全新¥{{info.newProductPrice}}</span><span
class="red" v-if="info.savePrice"> 省¥{{info.savePrice}}</span></p>
<p class="size">{{info.sizeName}}</p>
</div>
<p class="name">{{info.productName}} {{info.colorName}} {{info.productCode}}</p>
</div>
<div class="other-info">
<p>{{info.gender}} {{info.brandName}}</p>
<p>状况:{{ info.shoeQualityDesc }}</p>
<p>鞋盒:{{ info.shoeBoxDesc }}</p>
<p v-show="info.soldTime">售出时间:{{ info.soldTime }}</p>
<p>{{ info.describeInfo }}</p>
</div>
<div class="extra-card" @click="$router.push({name: 'ProductDetail', params: { productId: info.productId } })">
<image-format
class="image"
:alt="info.image"
:src="info.image"
:width="70"
:height="70"
/>
<div class="middle" @click="'productId'">
<p class="name ellipsis">{{ info.productName }}</p>
<p class="number ellipsis">货号 {{ info.productCode }}</p>
</div>
<i class="cubeic-arrow"></i>
</div>
</LayoutScroll>
<div class="fixed-footer">
<cube-button v-if="info.status==1" class="active" @click="buy">购买 <span
class="price">¥{{info.price}}</span></cube-button>
<cube-button v-else class="disable">商品已售出</cube-button>
</div>
</LayoutApp>
</template>
<script>
import { Button, Slide } from 'cube-ui';
import { mapState, mapActions } from 'vuex';
export default {
name: 'UfoSecondProductDetail',
mixins: [],
props: {
skup: Number,
yasParams: {
P_NAME: '',
TAB_ID: '',
TAB_NAME: '',
PRD_ID: ''
}
},
asyncData({ store, router }) {
return store.dispatch('second/fetchDetailById', { skup: router.params.skup });
},
data() {
return {
slideOptions: {
eventPassthrough: 'vertical'
},
};
},
created() {
},
mounted() {
},
activated() {
this.fetchDetailById({ skup: this.skup });
this.refresh();
/*
[二手商品详情页打开时]
1.FP_NAME:来源页面名称;eg:XY_UFO...
2.FP_PARAM:来源页面参数;搜索关键词,系列ID,品牌ID,商品池ID;
3.TAB_ID:tab切id,1-人气,2-价格,3-新品;
4.TAB_NAME:tab切名称,人气,价格,新品;
5.PRD_ID:商品id;
6.PRD_TYPE:1-二手,2-全新瑕疵;
*/
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_UFO_PRD_DT_INFO',
param: {
P_NAME: `XY_UFO${this.$route.name}`,
FP_NAME: this.yasParams?.P_NAME,
FP_PARAM: this.skup,
TAB_ID: this.yasParams?.TAB_ID,
TAB_NAME: this.yasParams?.TAB_NAME,
PRD_ID: this.yasParams?.PRD_ID,
PRD_TYPE: this.info.sechondHandTypeName === '全新瑕疵' ? 2 : 1
}
}
});
},
methods: {
...mapActions('second', ['fetchDetailById']),
...mapActions('product', ['payment']),
refresh() {
this.$refs.slide && this.$refs.slide.refresh && this.$refs.slide.refresh();
},
// 购买
async buy() {
try {
const info = await this.payment({
skup: this.skup,
});
/**
* {
* "message": "您有未支付的订单,支付或取消后可提交新的订单",
* "code": 512
* }
*/
if (info.code === 512) {
return this.$createDialog({
type: 'confirm',
content: info.message,
confirmBtn: {
text: '查看订单',
active: true,
disabled: false,
href: 'javascript:;'
},
cancelBtn: {
text: '取消',
active: false,
disabled: false,
href: 'javascript:;'
},
onConfirm: () => {
this.$router.push({
name: 'OrderList',
params: {
owner: 'buy',
status: 2, // 待付款
},
});
},
}).show();
}
} catch (e) {
// e
}
this.$router.push({
name: 'OrderBuyConfirm',
query: {
type: 'second',
skup: this.skup
}
});
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_UFO_SECOND_PRD_DT_BUY_C',
param: {
PRD_ID: this.info.productId,
PRD_SKU: this.skup,
PRD_SIZE: this.info.size_name,
ORD_TYPE: this.info.sechondHandTypeName === '全新瑕疵' ? 2 : 1
},
},
});
}
},
computed: {
...mapState('second', ['info'])
},
watch: {},
components: {
'cube-button': Button,
'cube-slide': Slide,
'cube-slide-item': Slide.Item,
}
};
</script>
/* 定义局部样式,添加外围容器,scss嵌套尽量不要超过三层,会影响查找器性能 */
<style rel='stylesheet/scss' lang='scss' scoped>
@import '@/pages/product/product-detail.scss';
.second-detail-wrap {
display: flex;
}
/deep/ .layout-context {
margin-bottom: 130px;
}
.ellipsis {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
/* banner */
.cube-slide {
height: 396px;
max-height: 396px;
/deep/ .cube-slide-dots {
padding: 0;
}
.cube-slide-item {
img {
display: flex;
height: 375px;
min-height: 375px;
max-height: 375px;
margin: 0 auto;
max-width: max-content;
}
}
.pole-dot-area {
width: inherit;
height: 21px;
}
.cube-dot {
display: inline-block;
width: 4px;
height: 4px;
margin: 0 5px;
background: rgba(0, 0, 0, 0.15);
border-radius: 50%;
&.active {
transform-origin: 50% 50%;
transform: scale(1.5);
background: rgba(0, 0, 0, 1);
}
}
}
// product information display
.primary {
margin: 0 40px 40px;
padding: 9px 0 0 0;
border-bottom: 1px solid #eee;
& > div:first-child {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
}
.price {
@include num;
font-size: 48px;
line-height: 56px;
color: #d0021b;
&::first-letter {
font-size: 36px;
letter-spacing: 2px;
}
& > span {
font-size: 24px;
color: #000;
&.red {
color: #d0021b;
}
}
}
.size {
font-size: 28px;
color: #000;
line-height: 38px;
}
.name {
margin-bottom: 16px;
font-size: 28px;
color: #999;
}
}
// other-info
.other-info {
margin: 0 40px 40px;
color: #000;
font-size: 28px;
& > p:not(:last-child) {
margin-bottom: 5px;
}
}
// extra-card
.extra-card {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 40px 0;
border: 1px solid #f2f2f2;
border-radius: 4px;
.image {
min-width: 140px;
min-height: 140px;
max-width: 140px;
max-height: 140px;
margin: 0 20px;
}
.middle {
flex: 1 1 auto;
display: flex;
flex-direction: column;
justify-content: space-between;
max-width: 420px;
height: 82px;
font-size: 28px;
line-height: 32px;
.number {
font-size: 24px;
color: #999;
line-height: 28px;
}
}
.cubeic-arrow {
font-size: 34px;
color: #999;
margin-right: 20px;
}
}
// fixed-footer
.fixed-footer {
position: fixed;
bottom: 0;
display: flex;
width: 100%;
height: 112px;
padding: 16px 30px;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.08);
text-align: center;
background: #fff;
z-index: 5;
@include cube-ufo-btn;
.price {
font-size: 28px;
line-height: 32px;
color: #65ab85;
&::first-letter {
font-size: 20px;
letter-spacing: 2px;
}
}
.disable {
background: #ddd;
color: #999;
border-color: #ddd;
font-size: 32px;
}
}
</style>
... ...
export default [{
name: 'SecondProductDetail',
path: '/xianyu/second/product/:skup.html',
component: () => import(/* webpackChunkName: "second" */ './detail'),
props({params}) {
return {
...params,
skup: parseInt(params.skup, 10),
};
},
}, {
name: 'SecondList',
path: '/xianyu/second/list.html',
component: () => import(/* webpackChunkName: "second" */ './list')
}, {
name: 'SecondSellList', // 筛选页
path: '/xianyu/second/product/:id/selllist.html',
component: () => import(/* webpackChunkName: "second" */ './sell-list'),
props({params}) {
return {
storage_id: parseInt(params.id, 10),
};
},
}];
... ...
<template>
<LayoutApp :show-back="true" title="二手" class="list-wrapper">
<LayoutScroll
ref="scrolllist"
@scroll="onScroll"
@scroll-end="scrollEndHandler"
@pulling-up="fetchSkupList(isMore)"
v-if="skupList.list.length"
class="list-scroll-bg"
>
<SecondList ref="second" :list="skupList.list" :yasParams="yasParams"></SecondList>
</LayoutScroll>
<UfoNoItem class="empty" :tip="`暂无数据`" v-else></UfoNoItem>
</LayoutApp>
</template>
<script>
import SecondList from './components/second-list';
import UfoNoItem from '../../components/ufo-no-item';
import { createNamespacedHelpers } from 'vuex';
const { mapState, mapActions } = createNamespacedHelpers('second/skupList');
export default {
name: 'UfoSecondListPage',
components: {
SecondList,
UfoNoItem
},
data() {
return {
isFetch: true,
scrollY: 0,
yasParams: {
P_NAME: 'XY_UFOSecondList',
TYPE_ID: 5,
TAB_ID: '',
TAB_NAME: '',
P_PARAM: [].toString()
}
};
},
computed: {
...mapState(['skupList', 'isMore'])
},
methods: {
...mapActions(['fetchSecondSkupList']),
async fetchSkupList(isMore) {
if (this.isMore) {
await this.fetchSecondSkupList({ isReset: false });
}
},
onScroll({ y }) {
this.scrollY = -y;
},
scrollEndHandler({ y }) {
this.scrollY = -y;
this.$refs.second.yasShowEvent(-y);
},
yasShowPage() {
let { total, list } = this.skupList;
let PRD_LIST = [];
for (let item of list) {
PRD_LIST.push(item.product_id);
}
PRD_LIST = PRD_LIST.toString();
this.$store.dispatch('reportYas', {
params: {
param: { ...this.yasParams, TOTAL: total, PRD_LIST },
appop: 'XY_UFO_PRD_LIST_L'
}
});
},
},
beforeRouteLeave (to, from, next) {
this.isFetch = to.name !== 'SecondProductDetail'
next();
},
activated: function() {
if(this.isFetch) {
this.scrollY = 0;
this.fetchSecondSkupList({ isReset: true });
}
this.$nextTick(()=> {
this.$refs.scrolllist.scrollTo(this.scrollY);
this.$refs.second.yasShowEvent(this.scrollY);
this.yasShowPage();
})
}
};
</script>
<style lang="scss" scoped>
.list-wrapper {
/deep/ .layout-context {
display: flex;
flex-direction: column;
}
.empty {
flex: 1;
}
}
.list-scroll-bg {
background-color: #f5f5f5;
flex: 1;
}
</style>
... ...