Authored by 邱骏

update

... ... @@ -24,12 +24,12 @@
</template>
<script>
import { get } from "lodash";
import videojs from "video.js";
import { getArticleImageSize, processImage } from "utils/image-handler";
import { get } from 'lodash';
import videojs from 'video.js';
import { getArticleImageSize, processImage } from 'utils/image-handler';
export default {
name: "VideoPlayer",
name: 'VideoPlayer',
props: {
source: String,
cover: String,
... ... @@ -41,7 +41,7 @@ export default {
return {
muted: true,
controls: true,
aspectRatio: "1:1"
aspectRatio: '1:1'
};
}
}
... ... @@ -68,45 +68,45 @@ export default {
2,
imgSize.width,
imgSize.height,
get(this.yoho, "window.supportWebp")
get(this.yoho, 'window.supportWebp')
);
} else {
return this.cover.split("?")[0];
return this.cover.split('?')[0];
}
} else {
return "";
return '';
}
},
sourceType() {
let type = "video/mp4";
let type = 'video/mp4';
if (this.source) {
let source = this.source.split("?")[0];
let source = this.source.split('?')[0];
source = source.split(".");
source = source.split('.');
switch (source[source.length - 1]) {
case "opus":
case "ogv":
type = "video/ogg";
case 'opus':
case 'ogv':
type = 'video/ogg';
break;
case "mkv":
type = "video/x-matroska";
case 'mkv':
type = 'video/x-matroska';
break;
case "m3u8":
type = "application/x-mpegURL";
case 'm3u8':
type = 'application/x-mpegURL';
break;
case "m4a":
type = "audio/mp4";
case 'm4a':
type = 'audio/mp4';
break;
case "mp3":
type = "audio/mpeg";
case 'mp3':
type = 'audio/mpeg';
break;
case "aac":
type = "audio/aac";
case 'aac':
type = 'audio/aac';
break;
case "oga":
type = "audio/ogg";
case 'oga':
type = 'audio/ogg';
break;
default:
break;
... ... @@ -131,7 +131,6 @@ export default {
},
methods: {
async parentHandleclick() {
// await this.delay(1000);
this.player.play();
const timeId = setTimeout(() => {
... ... @@ -155,37 +154,37 @@ export default {
});
},
initPlayer() {
const noVioceClass = "vjs-yoho-novoice";
const noVioceClass = 'vjs-yoho-novoice';
this.player = videojs(this.$refs.videoPlayer, this.options);
this.voiceBtn = this.player.addChild("button");
this.voiceBtn.addClass("vjs-yoho-voice");
this.voiceBtn = this.player.addChild('button');
this.voiceBtn.addClass('vjs-yoho-voice');
this.voiceBtn.addClass(noVioceClass);
this.backBtn = this.player.addChild("button");
this.backBtn.addClass("vjs-yoho-back");
this.voiceBtn.on("touchend", () => {
this.backBtn = this.player.addChild('button');
this.backBtn.addClass('vjs-yoho-back');
this.voiceBtn.on('touchend', () => {
this.player.muted(!this.voiceBtn.hasClass(noVioceClass));
});
this.backBtn.on("touchend", () => {
this.backBtn.on('touchend', () => {
this.player.exitFullscreen();
});
this.player.on("play", () => {
this.player.on('play', () => {
this.player._yohoPlayTime = this.getTime();
});
this.player.on("pause", () => {
this.player.on('pause', () => {
this.player._yohoPauseTime = this.getTime();
});
this.player.on("ended", () => {
this.player.on('ended', () => {
this.player._yohoEndedTime = this.getTime();
});
this.player.on("fullscreenchange", () => {
this.player.on('fullscreenchange', () => {
this.player._yohoPlayTime = this.getTime();
});
this.player.on("volumechange", () => {
this.player.on('volumechange', () => {
const soundOff = this.player.muted() || this.player.volume() === 0;
if (soundOff) {
... ... @@ -205,7 +204,7 @@ export default {
<style lang="scss" scoped>
.video-js {
width: 100%;
height: auto;
height: 750px;
video {
width: 100%;
... ...
<template>
<div class="avatar-wrap">
<ImageFormat class="img-avatar" :lazy="true" :src="imageSrc" :width="width" :height="height" @error="errorHandle"></ImageFormat>
</div>
</template>
<script>
export default {
name: 'WidgetAvatar',
props: {
src: {
type: String,
default: ''
},
width: {
type: Number,
default: 35
},
height: {
type: Number,
default: 35
},
lazy: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
group: [Number, String]
},
data() {
return {
imgSrc: this.src,
defaultSrc: 'https://img12.static.yhbimg.com/article/2019/02/26/16/02456ade977d8dfdbc4ca548b196c1d62b.png?imageView/2/w/{width}/h/{height}'
};
},
computed: {
imageSrc() {
return this.imgSrc || this.defaultSrc;
}
},
watch: {
src(val) {
this.imgSrc = val;
}
},
methods: {
errorHandle() {
this.imgSrc = this.defaultSrc;
}
}
};
</script>
<style lang="css" scoped>
.avatar-wrap {
display: inline-block;
position: relative;
> img {
width: 100%;
height: 100%;
}
}
.img-avatar {
border-radius: 50%;
overflow: hidden;
}
</style>
... ...
<template>
<LayoutApp :show-back="true" title="社区详情">
<div class="detail-container">
<div class="author-container">
<ArticleAuthor :userHeadIco="userHeadIco" :userName="userName"></ArticleAuthor>
</div>
<div class="source-container">
<ArticleVideo v-if="showVideo" :videoUrl="videoUrl" :coverUrl="coverUrl"></ArticleVideo>
<ArticleImage v-else :imageList="imageList"></ArticleImage>
</div>
<div class="slide-container">
<HorizontalSlide :value="imageList"></HorizontalSlide>
</div>
<div class="note-container">
<div class="note-header">Nike 旗下大热鞋款 Air Max 95</div>
<div class="note-content">Nike 旗下大热鞋款 Air Max 95 一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型 @NIKE官方 Max 95 也轻松成为许多鞋迷的心头好紧接「Triple White」之后,备受期待的 Nike 全新鞋款 SF-AF1 Mid 又有一双鞋的「Tiger Camo」配色率先现身网络。</div>
<div class="note-date">2019-01-02</div>
<div class="praise-wrapper" ></div>
</div>
<div class="recommend-container">
<div class="recommend-text">推荐阅读</div>
<div class="recommend-list">list</div>
</div>
</div>
</LayoutApp>
</template>
<script>
import ArticleAuthor from './components/article-author';
import ArticleVideo from './components/article-video';
import ArticleImage from './components/article-image';
import HorizontalSlide from './components/horizontalSlide';
export default {
name: 'articleDetail',
components: {
ArticleAuthor,
ArticleVideo,
ArticleImage,
HorizontalSlide
},
data() {
return {
imageList: [
{image_url: 'http://img11.static.yhbimg.com/goodsimg/2019/07/02/16/0191aa70fd1f46d75e5ff974dfe0cdac1c.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'},
{image_url: 'http://img11.static.yhbimg.com/goodsimg/2018/11/06/14/01bbabe3eb9597d5a36f51a0a29c23e441.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'},
{image_url: 'http://img11.static.yhbimg.com/goodsimg/2018/11/06/14/016c59b642d25a92049003c318ea7b97ff.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'},
{image_url: 'http://img11.static.yhbimg.com/goodsimg/2018/11/06/14/012df38fe6117ea9467a01919a4b4c2f3c.jpg?imageMogr2/thumbnail/{width}x{height}/background/d2hpdGU=/position/center/quality/80'}
],
showVideo: false,
videoUrl: 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4',
coverUrl: 'http://file02.16sucai.com/d/file/2015/0408/779334da99e40adb587d0ba715eca102.jpg',
userHeadIco: 'https://img12.static.yhbimg.com/article/2019/02/26/16/02456ade977d8dfdbc4ca548b196c1d62b.png?imageView/2/w/{width}/h/{height}',
userName: 'MOMO草'
};
},
};
</script>
<style lang="scss" scoped>
.detail-container {
background: #fff;
}
.author-container {
width: 100%;
height: 100px;
}
.source-container {
width: 100%;
height: 750px;
}
.slide-container {
padding: 40px 0 36px 24px;
}
.note-container {
padding: 0 24px 100px;
background: #fff;
}
.note-header {
font-size: 36px;
color: #222;
letter-spacing: 0.34px;
line-height: 56px;
font-weight: bold;
}
.note-content {
font-size: 30px;
color: #000;
letter-spacing: 0.28px;
line-height: 50px;
}
.note-date {
margin-top: 20px;
font-size: 30px;
color: #999;
letter-spacing: 0.28px;
}
.praise-wrapper {
margin-top: 60px;
margin-left: calc((100%-160px)/2);
height: 160px;
width: 160px;
background: url("~statics/image/coupon/used@3x.png");
background-size: contain;
background-repeat: no-repeat;
}
.recommend-container {
background: #F2F2F2;
padding: 36px 24px 0;
}
.recommend-text {
font-size: 32px;
color: #222222;
font-weight: bold;
text-align: left;
}
.recommend-list {
margin-top: 30px;
}
</style>
... ...
<template>
<div class="container">
<WidgetAvatar :lazy="true" class="widget-avatar" :src="userHeadIco" :width="70" :height="70"></WidgetAvatar>
<span>{{userName}}</span>
</div>
</template>
<script>
import WidgetAvatar from '../../../components/widget-avatar';
export default {
name: 'articleAuthor',
components: {
WidgetAvatar
},
props: {
userHeadIco: String,
userName: String
}
};
</script>
<style lang="scss" scoped>
.container {
height: 100px;
padding-left: 24px;
display: flex;
flex-direction: row;
align-items: center;
.widget-avatar {
width: 70px;
height: 70px;
}
span {
margin-left: 20px;
font-size: 24px;
color: #222;
letter-spacing: 0.05px;
}
}
</style>
... ...
<template>
<div class="cover-slide">
<cube-slide ref="slide" :loop="false" :auto-play="false" :data="imageList">
<cube-slide-item v-for="(item, index) in imageList" :key="index" @click.native="clickHandler(item, index)">
<a click="javascript:void 0" class="square-img-container">
<SquareImg :src="item.image_url" :width="600" :height="600"/>
</a>
</cube-slide-item>
<template slot="dots" slot-scope="props">
<div class="dot-wrap">
{{props.current + 1}}/{{imageList.length}}
</div>
</template>
</cube-slide>
</div>
</template>
<script>
import { Slide } from 'cube-ui';
import SquareImg from '../../product/components/square-img';
export default {
name: 'articleImage',
components: {
'cube-slide': Slide,
'cube-slide-item': Slide.Item,
SquareImg
},
props: {
imageList: Array
},
data() {
return {
};
},
methods: {
clickHandler(item, index) {
console.log(item, index);
}
}
};
</script>
<style lang="scss" scoped>
.cover-slide {
flex: 1;
.square-img-container {
display: block;
width: 100%;
height: 750px;
margin: 0 auto;
}
.dot-wrap {
margin-left: 24px;
margin-bottom: 24px;
background: #000;
opacity: 0.5;
width: 80px;
height: 48px;
border-radius: 30px;
color: white;
font-size: 24px;
letter-spacing: 1.96px;
line-height: 48px;
text-align: center;
}
}
.cube-slide-item {
img {
margin: 0 auto;
}
}
</style>
... ...
<template>
<div class="video-container">
<VideoPlayer
ref="videoPlayer"
class="videoWrapper"
:source="videoUrl"
:cover="coverUrl"
></VideoPlayer>
</div>
</template>
<script>
import VideoPlayer from '@/components/video-player';
export default {
name: 'article-video',
props: {
videoUrl: String,
coverUrl: String
},
data() {
return {
};
},
components: {
VideoPlayer
}
};
</script>
<style scoped>
.video-container {
flex: 1;
}
.videoWrapper {
display: block;
width: 100%;
height: 750px;
}
</style>
... ...
<template>
<div class="horizontalSlide">
<ul class="list-warp">
<li class="list-item" v-for="(item, index) in value" :key="index">
<ImageFormat
class="image"
src="//img11.static.yhbimg.com/goodsimg/2018/12/28/15/0160a985f0b5999b77436b7af78a85f3df.jpg" alt="加载失败"
:width="136" :height="180"
@error="imageformatError"/>
<div class="info">
<p class="title">Off-White™ x Nike Air Max 90 全新款「Desert Ore」配色曝光 Off-White™ x Nike Air Max 90 全新款「Desert Ore」配色曝光</p>
<div class="bottom">
<span class='icon-ufo'></span>
<span class="price">¥1299</span>
<span class="icon-goumai"></span>
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'horizontalSlide',
props: ['value'],
data() {
return {};
},
methods: {
imageformatError() {
console.log(6666);
}
},
computed: {},
components: {}
};
</script>
<style lang="scss" scoped>
.horizontalSlide {
overflow: hidden;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
-webkit-overscroll-behavior-x: contain;
scroll-behavior: smooth;
}
.list-warp {
overflow-x: scroll;
width: max-content;
font-size: 0;
}
.list-item {
display: inline-flex;
width: 600px;
height: 180px;
margin-right: 24px;
border-radius: 8px;
.image {
min-width: 136px;
min-height: 180px;
max-width: 136px;
max-height: 180px;
border-radius: 8px 0 0 8px;
filter: contrast(0.9);
}
.title {
font-size: 24px;
color: #999;
line-height: 34px;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.info {
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
border: 1px solid #F0F0F0;
border-radius: 0 8px 8px 0;
}
.price {
margin-left: 16px;
font-size: 28px;
font-family: 'BrownStd-Regular';
vertical-align: middle;
flex: 1 1 auto;
}
.icon-goumai {
float: right;
}
}
</style>
... ...
... ... @@ -7,6 +7,6 @@ export default [
{
name: 'ArticleDetail',
path: '/xianyu/article/detail',
// component: () => import()
component: () => import(/* webpackChunkName: "articleDetail" */ './articleDetail')
}
];
... ...
... ... @@ -99,8 +99,8 @@ export default {
<style lang="scss" scoped>
.progress-box {
position: absolute;
top: 36px;
right: 50px;
top: 0;
right: 90px;
width: 204px;
height: 204px;
}
... ...
... ... @@ -6,8 +6,8 @@
:loading="loadingOptions"
@pulling-up="onPullingUp">
<incomeHeader :data="getAssetSummary"></incomeHeader>
<payAccount></payAccount>
<incomeHeader :data="getAssetSummary" class="header-inner-padding"></incomeHeader>
<payAccount class="inner-padding"></payAccount>
<incomeDetail :data="incomeData">
<template v-for="(item,index) in incomeData.list">
<incomeItem :data="item" :key="index"></incomeItem>
... ... @@ -104,7 +104,19 @@ export default {
height: 100%;
overflow-y: auto;
background-color: white;
padding: 0 40px;
color: #999;
}
.header-inner-padding {
padding: 0 40px;
position: relative;
}
.inner-padding {
padding: 0 40px;
}
/deep/ .income-detail-header {
padding: 0 40px;
}
</style>
... ...
... ... @@ -315,7 +315,7 @@ export default {
});
}
let user = await this.$sdk.getUser();
let user = await this.$sdk.getUser(true);
if (user && user.uid) {
const payInfo = await this.fetchPayment({ skup: this.productDetail.skup });
... ...
... ... @@ -10,13 +10,11 @@
<div class="price"><span class='icon-ufo'></span> {{data.colorName}},{{data.sizeName}}</div>
</div>
</div>
<div v-if="!getLogin" class="no-login-tip">完成“有货”登录授权后将显示商品优惠及运费信息,请先完成登录查看最终支付金额</div>
<div v-if="!isLogin" class="no-login-tip">完成“有货”登录授权后将显示商品优惠及运费信息,请先完成登录查看最终支付金额</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'OrderProductInfo',
props: {
... ... @@ -27,8 +25,20 @@ export default {
}
}
},
computed: {
...mapGetters(['getLogin'])
data() {
return {
isLogin: true
};
},
activated() {
this.getLogin();
},
methods: {
async getLogin() {
const user = await this.$sdk.getUser(true);
this.isLogin = user && !!user.uid;
}
}
};
</script>
... ...
... ... @@ -73,10 +73,10 @@
<!-- <img class="ref-img" v-lazy="prdDetailImage"/> -->
<GrassArtilces :productId="productId" :listdata="articleData"></GrassArtilces>
<!--尺码图片-->
<size-image :brand-id="brandId" :product-id="productId"></size-image>
<!--九宫格社区图-->
<GrassArtilces :productId="productId" :listdata="articleData"></GrassArtilces>
<div class="recommend" v-if="recommend"><h2>相关推荐</h2>
<product-list ref="recommendList" :list="recommend" priceKey="price" :yas-params="recommendYasParams"/>
... ...
... ... @@ -719,3 +719,13 @@ img[lazy=loaded] {
background: url(~statics/image/order/logo@3x.png) no-repeat;
background-size: cover;
}
.icon-goumai {
width: 120px;
height: 50px;
vertical-align: middle;
display: inline-flex;
background: url(~statics/image/order/goumai@3x.png) no-repeat;
background-size: cover;
}
... ...
... ... @@ -61,7 +61,10 @@ export default function() {
{ promotion: '优惠券', promotionAmount: '- ¥0.00' },
{ promotion: '运费券', promotionAmount: '- ¥0.00' },
{ promotion: '实付金额', promotionAmount: '¥0.00' }
]
],
promotionTips: {
promotionIds: ''
}
},
selectedCouponList: [],
selectedPromotion: null,
... ... @@ -124,14 +127,7 @@ export default function() {
state.address = null;
},
[Types.UPDATE_ORDER](state, { amount, couponInfo, couponList, promotionFormulaList, promotionList, promotionTips }) {
state.orderDetail.amount = amount;
state.orderDetail.promotionFormulaList = promotionFormulaList;
state.orderDetail.recommendedCouponInfo = couponInfo;
state.orderDetail.couponList = couponList;
state.orderDetail.promotionList = promotionList;
state.orderDetail.promotionTips = promotionTips;
state.orderDetail = { ...state.orderDetail, promotionTips, promotionList, couponList, amount, promotionFormulaList, recommendedCouponInfo: couponInfo };
},
[Types.CHANGE_SELECT_COUPON_LIST](state, { couponCode, couponType }) {
const item = find(get(state.orderDetail, 'couponList', []), { coupon_code: couponCode });
... ...
... ... @@ -8,7 +8,7 @@ export default {
return state.products[productId];
},
async fetchProductInfo({ commit, state, rootGetters }, { productId }) {
const queryUrls = ['', '/resource', '/activity', '/recommend']
const queryUrls = ['', '/resource', '/activity', '/recommend', '/vedioImage']
if(rootGetters.getLogin) {
queryUrls.push('/limit/info')
}
... ... @@ -29,8 +29,11 @@ export default {
});
});
let [detail, resource, activity, recommend, limitInfo] = await Promise.all(queryTasks);
let [detail, resource, activity, recommend, vedioResource, limitInfo] = await Promise.all(queryTasks);
if(vedioResource) {
}
// 视频资源位
const videoResourceInfo = resource.find(r=> /(\.mp4)/.test(r.data[0].url));
const videoResource = get(videoResourceInfo, 'data[0]', {});
... ... @@ -224,5 +227,5 @@ export default {
});
return result;
}
},
};
... ...
... ... @@ -166,5 +166,15 @@ module.exports = {
product_id: {type: Number},
brand_id: {type: Number}
}
},
// 获取sku配置的视频连接
'/api/ufo/product/vedioImage': {
ufo: true,
auth: false,
api: 'ufo.product.vedioImage',
params: {
product_id: {type: Number},
}
}
};
... ...
{
"name": "xianyu-ufo-app-web",
"version": "1.2.1",
"version": "1.2.5",
"private": true,
"description": "Xianyu Project With Express",
"repository": {
... ...