Commit 9b217226 authored by Junling Bu's avatar Junling Bu
Browse files

Merge branch 'litemall-vue' of https://github.com/pkwenda/litemall into pkwenda-litemall-vue

parents 3cb559bb ce4ef458
<template>
<div class="popup_wrap">
<van-icon name="clear" class="cancel_popup" @click.native="$parent.value = false"></van-icon>
<div class="popup_header">商品属性</div>
<div class="popup_content">
<van-cell-group>
<van-cell v-for="(str, i) in propsStr" :key="i">
<van-row>
<van-col span="8">{{str[0]}}</van-col>
<van-col span="16">{{str[1]}}</van-col>
</van-row>
</van-cell>
</van-cell-group>
</div>
</div>
</template>
<script>
import { Row, Col } from 'vant';
export default {
name: 'popup-props',
props: {
propsStr: {
type: Array,
default: () => []
}
},
components: {
[Col.name]: Col,
[Row.name]: Row
}
};
</script>
<style lang="scss" scoped>
.popup_wrap {
position: relative;
padding-bottom: 30px;
box-sizing: border-box;
.popup_header {
padding: 15px 0 30px 0;
text-align: center;
}
.popup_content {
min-height: 150px;
max-height: 400px;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: scroll;
padding: 0 10px;
line-height: 30px;
&::-webkit-scrollbar {
background-color: #fff;
width: 5px;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background-color: #bebebe;
}
ol {
padding-left: 15px;
list-style: decimal;
}
}
.cancel_popup {
position: absolute;
right: 15px;
top: 15px;
z-index: 9;
font-size: 18px;
}
}
</style>
<template>
<div class="item_cell_group">
<van-cell-group>
<van-cell title="有效期" value="2017-8-18~2018-8-19"/>
<van-cell title="选择规格" isLink :value="skuComb.sku_str" @click.native="skuClick" />
<van-cell title="文一西路花蒋路交叉口" isLink url="http://m.amap.com/navi/?dest=120.145409,30.238695&key=ab67b14d58d47912a9feb63ba862450c&destName=三潭印月"/>
<van-cell isLink v-if="mobile">
<template slot="title">
<a :href="'tel:' + mobile" class="store_mobile">{{mobile}}</a>
</template>
</van-cell>
<van-cell title="注意事项" @click.native="showPopup = true" isLink />
</van-cell-group>
<van-sku
v-model="showSku"
:showAddCartBtn="showAddCartBtn"
:buyText="buyText"
:sku="sku.sku"
:goods="sku.goods_info"
:goodsId="sku.goods_id"
:disableStepperInput="true"
@buy-clicked="buyGoods"
/>
<van-popup v-model="showPopup" position="bottom" lockOnScroll>
<div class="popup_wrap">
<van-icon name="clear" class="cancel_popup" @click.native="showPopup = false"></van-icon>
<div class="popup_header">注意事项</div>
<div class="popup_content">
<div>这里是注意事项的内容:</div>
<ol>
<li>这里是注意意事项的内容事项的内容</li>
<li>这里是注意事意事项的内容项的内容</li>
<li>这里是注意意事项的内容事项的内容</li>
<li>这里是注意意事项的内容事项的内容</li>
<li>这里是注意意事项的内容事项的内容</li>
<li>这里是注意事项的内容</li>
<li>这里是注意意事项的内容事项的内容</li>
<li>这里是注意意事项的内容事项的内容</li>
<li>这里是注意意事项的内容事项的内容</li>
</ol>
</div>
</div>
</van-popup>
</div>
</template>
<script>
// import sku from "./sku";
import actionMixin from '../mix';
export default {
name: 'virtual-group',
props: {
mobile: String,
skuComb: {
type: Object,
default: () => ({})
},
addressVal: {
type: Object,
default: () => ({})
}
},
mixins: [actionMixin],
data() {
return {
// sku,
};
},
methods: {
addressClick() {}
}
};
</script>
<style lang="scss" scoped>
.popup_wrap {
position: relative;
padding-bottom: 30px;
box-sizing: border-box;
}
.popup_header {
padding: 15px 0 30px 0;
text-align: center;
}
.popup_content {
height: 150px;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: scroll;
padding: 0 10px;
line-height: 30px;
&::-webkit-scrollbar {
background-color: #fff;
width: 5px;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background-color: #bebebe;
}
ol {
padding-left: 15px;
list-style: decimal;
}
}
.store_mobile {
display: block;
}
.cancel_popup {
position: absolute;
right: 15px;
top: 15px;
font-size: 18px;
}
</style>
<template>
<div class="item_detail">
<van-swipe :autoplay="3000">
<van-swipe-item v-for="(image, index) in itemImgs" :key="index">
<!-- <img v-lazy="image" width="100%"> -->
<img :src="image" width="100%">
</van-swipe-item>
</van-swipe>
<van-cell-group class="item_cell_group" v-if="goods">
<van-cell class="item_info">
<div>
<span class="item_price">{{ goods.retailPrice*100 | yuan }}</span>
<span class="item_market_price">{{goods.counterPrice*100 | yuan}}</span>
</div>
<div class="item-title">
<!-- <van-tag plain type="danger" v-if="goods.is_haitao">海淘</van-tag> -->
{{ goods.name }}
</div>
<!-- <div class="item_intro">{{goods.sell_point}}</div> ???-->
<!-- <div class="item_dispatch">发货地: {{}}</div> -->
</van-cell>
</van-cell-group>
<component
v-if="goods"
ref="goodAction"
v-bind:is="'entity-group'"
:selectSku.sync="selectSku"
:addressVal.sync="addressVal"
:mobile="mobile"
:goods-info="goods"
@skuBuy="doBuyNow"
@cart-count="cartEvent"
/>
<div class="item_desc" v-if="goods">
<div class="item_desc_title">商品详情</div>
<div class="item_desc_wrap" v-if="goods.detail.length === 0" style="padding-left: 170px;">
<p>无详情</p>
</div>
<div class="item_desc_wrap" v-html="goods.detail"></div>
</div>
<van-goods-action>
<!-- <van-goods-action-mini-btn @click="doContact" icon="wangwang" iconClass="red afterTag"/> -->
<van-goods-action-mini-btn @click="toCart" icon="cart" :info="cartInfo"/>
<van-goods-action-mini-btn
:style="collectAdd ? 'color: #f7b444;':''"
@click="addCollect"
icon="shoucang"
/>
<van-goods-action-big-btn @click="openSku('cart')" text="加入购物车"/>
<van-goods-action-big-btn primary @click="openSku('buy')" text="立即购买"/>
</van-goods-action>
<van-popup v-model="showContact">
<md-kefu mobile="16454193338"/>
</van-popup>
</div>
</template>
<script>
import { GOODS_DETAIL } from '@/api/goods';
import {
Swipe,
SwipeItem,
GoodsAction,
GoodsActionBigBtn,
GoodsActionMiniBtn,
Popup
} from 'vant';
import md_kefu from '@/vue/components/md-kefu/';
export default {
props: {
itemId: [String, Number]
},
data() {
const isLogin = !!localStorage.getItem('Authorization');
return {
isLogin,
itemImgs: [],
collectAdd: false,
showContact: false,
cartInfo: '0',
mobile: '13454193338',
selectSku: {
selectedNum: 1,
selectedSkuComb: {}
},
addressVal: {
id: null,
area_name: '',
district: '',
city: '',
province: ''
},
goods: null,
productList: []
};
},
computed: {
// itemImgs() {
// debugger;
// return this.goods.info.gallery;
// }
},
created() {
this.initData();
},
methods: {
async initData() {
// let a = this.$route.params.itemId;
this.$reqGet(`/wx/goods/detail?id=${this.itemId}`).then(
res => {
this.goods = res.data.data.info;
this.goods.attribute = res.data.data.attribute;
this.goods.specificationList = res.data.data.specificationList;
this.goods.productList = res.data.data.productList;
this.productList = res.data.data.productList;
this.itemImgs = res.data.data.info.gallery || [];
this.collectAdd = res.data.data.userHasCollect === 1;
}
);
let { data } = await this.$reqGet('/wx/cart/goodscount');
this.cartInfo = data.data;
// this.$reqGet(GOODS_DETAIL).then(res => {
// this.goods = res.data.data;
// });
},
openSku(status) {
const goodAction = this.$refs.goodAction;
goodAction.skuClick(status);
},
cartEvent(count) {
this.cartInfo = ~~this.cartInfo + ~~count + '';
},
doBuyNow() {
// if (
// (this.goods.has_sku && this.selectSku.sku_id) ||
// !this.goods.has_sku
// ) {
// this.$router.push({ name: 'placeOrderEntity' });
// } else {
// const goodAction = this.$refs.goodAction;
// goodAction.showSku = true;
// goodAction.isSkuBuy = true;
// }
},
addCart() {
// debugger;
// if (this.goods.has_sku && this.selectSku.sku_id) {
// this.$reqPost('/wx/cart/add', {
// goodsId: this.itemId,
// number: 1
// }).then(() => {
// this.$toast({
// message: '已添加至购物车',
// duration: 1500
// });
// this.cartInfo = String(parseInt(this.cartInfo) + 1);
// });
// }
},
doContact() {
this.showContact = true;
},
toCart() {
this.$router.push({
name: 'cart'
});
},
async addCollect() {
let { data } = await this.$reqPost(
'/wx/collect/addordelete',
{
valueId: this.itemId,
type: 0
}
);
let type = data.data.type;
this.collectAdd = type === 'add' ? true : false;
this.$toast({
message: this.collectAdd ? '添加成功' : '取消成功',
duration: 1500
});
}
},
components: {
[md_kefu.name]: md_kefu,
[Popup.name]: Popup,
[Swipe.name]: Swipe,
[SwipeItem.name]: SwipeItem,
[GoodsAction.name]: GoodsAction,
[GoodsActionBigBtn.name]: GoodsActionBigBtn,
[GoodsActionMiniBtn.name]: GoodsActionMiniBtn,
'entity-group': () =>
import(/* webpackChunkName: "EntityGroup" */ './EntityGroup/index'),
'virtual-group': () =>
import(/* webpackChunkName: "VirtualGroup" */ './VirtualGroup/index.vue')
}
};
</script>
<style lang="scss" scoped>
.item_detail {
img {
max-width: 100%;
}
}
.item_cell_group {
margin-bottom: 15px;
}
.item_price {
font-size: 20px;
color: $red;
margin-right: 10px;
}
.item_market_price {
color: $font-color-gray;
text-decoration: line-through;
font-size: $font-size-small;
}
.item-title {
line-height: 1.4;
}
.item_dispatch {
font-size: $font-size-small;
color: $font-color-gray;
}
.item_intro {
line-height: 18px;
margin: 5px 0;
font-size: $font-size-small;
color: $font-color-gray;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.item_desc {
background-color: #fff;
p {
padding: 0 10px;
}
/deep/ img {
max-width: 100%;
display: block;
}
}
.item_desc_title {
@include one-border;
padding: 10px 0;
text-align: center;
}
</style>
import { Sku, Popup } from 'vant';
export default {
props: {
selectSku: Object
},
data() {
return {
showSku: false,
showAddCartBtn: false,
isSkuBuy: false,
buyText: '确定'
};
},
methods: {
buyGoods(data) {
data = this.selectSkuData(data);
this.showSku = false;
this.$emit('update:selectSku', data);
this.isSkuBuy && this.$emit('skuBuy', data);
},
selectSkuData(data) {
debugger
if (data.selectedSkuComb) {
data.selectedSkuComb.sku_str = data.selectedSkuComb.props_str_arr
.map(str => str.match(/[^:]*:([^:]*)/)[1])
.join(',');
} else {
data.selectedSkuComb = {};
}
return data;
},
skuClick() {
this.isSkuBuy = false;
this.showSku = true;
}
},
components: {
[Sku.name]: Sku,
[Popup.name]: Popup
}
};
<template>
<div class="item_list over-hide">
<form action="/search">
<van-search
placeholder="请输入商品名称"
v-model="searchVal"
@click="$router.push({ name: 'search' })"
showAction
/>
</form>
<van-tabs v-model="tabActive" @disabled="toggleFilterModal(true)">
<van-tab
v-for="(tab, tabIndex) in tabsItem"
:title="tab.name"
:key="tab.type"
:disabled="tab.sort === false"
>
<InfinityScroll
:ref="'tabScrolls' + tabIndex"
class="full-page scroll-wrap fix-height"
:beforeRequest="beforeRequest"
:apiUrl="listApi"
@onLoad="onLoad(tabIndex, $event)"
>
<item-group>
<item-card-hori
v-for="(item, i) in tab.items"
:key="i"
:goods="item"
@click="itemClick(item.id)"
/>
</item-group>
</InfinityScroll>
</van-tab>
</van-tabs>
<van-popup class="filterItem" v-model="filterItemShow" position="right">
<ul>
<li
v-for="(li, i) in filterItem"
:key="i"
@click="filterMethod(i)"
:class="{filter_active: li.isActive}"
>
{{li.name}}
<van-icon name="success" v-show="li.isActive" class="float-r"/>
</li>
</ul>
</van-popup>
<!-- <transition name="fade">
<van-icon
name="arrowupcircle"
class="backTop"
@click.native="backTop"
v-show="showArrow"
/>
</transition>-->
</div>
</template>
<script>
import { GOODS_SEARCH } from '@/api/goods';
import ItemGroup from '@/vue/components/item-group';
import ItemCardHori from '@/vue/components/item-card-hori/';
import { Search, Tab, Tabs, Popup } from 'vant';
// import { throttle } from 'lodash';
import InfinityScroll from '@/vue/components/infinity-scroll';
export default {
name: 'Item-list',
props: {
keyword: {
type: String,
default: ''
},
itemClass: {
type: [String, Number],
default: ''
}
},
data() {
return {
listApi: GOODS_SEARCH,
shop_id: 1,
tabActive: 0,
tabsItem: [
{ name: '默认', sort: '', items: [] },
{ name: '销量', sort: 'sold_quantity', items: [] },
{ name: '最新', sort: 'created_at', items: [] }
// { name: '筛选', sort: false, items: [] }
],
is_haitao: 0,
filterItem: [
{
name: '全部',
filterType: 2,
isActive: true
},
{
name: '店铺商品',
filterType: 0,
isActive: false
},
{
name: '海淘商品',
filterType: 1,
isActive: false
}
],
isHaitao: 2,
searchVal: '',
filterItemShow: false
// showArrow: false
};
},
computed: {
sortVal() {
const { tabActive: i } = this;
return this.tabsItem[i].sort;
}
},
created() {
// this.scrollShowArrow = throttle(this.scrollShowArrow, 100);
},
methods: {
onLoad(i, items) {
this.tabsItem[i].items.push(...items);
},
beforeRequest() {
return {
params: {
q: this.searchVal,
shop_id: this.shop_id,
cid: this.itemClass,
sort: this.sortVal,
is_haitao: this.isHaitao
}
};
},
// 滚动容器改变, 导致滚动监听失效, 暂时先注释了
// eventListen(isBind = true) {
// if (isBind) {
// this.$el.addEventListener('scroll', this.scrollShowArrow);
// } else {
// this.$el.removeEventListener('scroll', this.scrollShowArrow);
// }
// },
// scrollShowArrow() {
// this.showArrow = this.$el.scrollTop > 120;
// },
activeFilter(i) {
this.filterItem.forEach((item, index) => {
item.isActive = i === index;
});
},
resetActiveTab() {
const { tabActive: i } = this;
this.tabsItem[i].items = [];
const targetScroll = this.$refs[`tabScrolls${i}`][0];
debugger;
targetScroll && targetScroll.resetInit();
},
toggleFilterModal(status) {
this.filterItemShow = status;
},
filterMethod(i) {
const filterType = this.filterItem[i].filterType;
if (filterType === this.isHaitao) return;
this.isHaitao = filterType;
this.activeFilter(i);
this.toggleFilterModal(false);
this.resetActiveTab();
},
// backTop() {
// this.$el.scrollTop = 0;
// },
itemClick(id) {
this.$router.push({ name: 'detail', params: { itemId: id } });
}
},
components: {
InfinityScroll,
[ItemGroup.name]: ItemGroup,
[ItemCardHori.name]: ItemCardHori,
[Tab.name]: Tab,
[Tabs.name]: Tabs,
[Search.name]: Search,
[Popup.name]: Popup
}
};
</script>
<style lang="scss" scoped>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s;
}
.fix-height {
padding-bottom: 88px;
}
.over-hide {
overflow: hidden;
}
.item_list {
background-color: #fff;
padding-bottom: 0;
}
.fixedTop {
position: fixed;
width: 100%;
top: 0;
z-index: 999;
}
.items_loading {
margin: 0 auto;
}
.filterItem {
width: 40%;
height: 100%;
li {
padding: 10px;
&.filter_active {
color: $red;
}
}
}
.backTop {
position: fixed;
right: 20px;
bottom: 20px;
font-size: 24px;
}
</style>
<template>
<div class="item_list">
<form action="/search" class="fixedTop">
<van-search placeholder="请输入商品名称" v-model="searchVal" @search="resetInit" showAction/>
</form>
<van-list
v-model="loading"
:finished="finished"
:immediate-check="false"
:offset="100"
@load="loadMore"
>
<item-group>
<item-card-hori
v-for="(item) in items"
:key="item.id"
:goods="item"
@click="itemClick(item.id)"
/>
</item-group>
</van-list>
<is-empty v-if="items.length === 0">抱歉,没有找到符合条件商品</is-empty>
<transition name="fade">
<van-icon name="arrowupcircle" class="backTop" @click.native="backTop" v-show="showArrow"></van-icon>
</transition>
</div>
</template>
<script>
import { GOODS_SEARCH } from '@/api/goods';
import ItemGroup from '@/vue/components/item-group/';
import IsEmpty from '@/vue/components/is-empty/';
import ItemCardHori from '@/vue/components/item-card-hori/';
import { Search, List } from 'vant';
import _ from 'lodash';
import loadMore from '@/vue/mixin/list-load-more';
import scrollFixed from '@/vue/mixin/scroll-fixed';
export default {
name: 'Item-list',
props: {
keyword: {
type: String,
default: ''
}
},
mixins: [loadMore, scrollFixed],
data() {
return {
isEmpty: false,
shop_id: '',
searchVal: '',
showArrow: false
};
},
activated() {
this.searchVal = this.keyword;
this.resetInit();
this.eventListen();
},
deactivated() {
document
.getElementById('app')
.removeEventListener('scroll', this.scrollShowArrow);
},
created() {
this.initData();
this.scrollShowArrow = _.throttle(this.scrollShowArrow, 100);
},
methods: {
initData() {
debugger;
this.items = [];
return this.$reqGet(
`/wx/goods/list`,
{
keyword: this.searchVal,
page: 1,
size: 100,
sort: 'name',
order: 'desc',
categoryId: 0
},
{
hideLoading: true
}
).then(res => {
const { goodsList, filterCategoryList } = res.data.data;
_.each(goodsList, v => {
v.pic_url = v.picUrl;
});
this.items.push(...goodsList);
// return page;
});
},
eventListen() {
document
.getElementById('app')
.addEventListener('scroll', this.scrollShowArrow);
},
scrollShowArrow() {
this.showArrow = document.getElementById('app').scrollTop > 120;
},
backTop() {
document.getElementById('app').scrollTop = 0;
},
itemClick(i) {
this.$router.push({ name: 'detail', params: { itemId: i } });
}
},
components: {
[ItemGroup.name]: ItemGroup,
[ItemCardHori.name]: ItemCardHori,
[Search.name]: Search,
[List.name]: List,
[IsEmpty.name]: IsEmpty
}
};
</script>
<style lang="scss" scoped>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s;
}
.item_list {
background-color: #fff;
padding-top: 50px;
box-sizing: border-box;
}
.fixedTop {
position: fixed;
width: 100%;
top: 0;
z-index: 999;
}
.items_loading {
margin: 0 auto;
}
.backTop {
position: fixed;
right: 20px;
bottom: 20px;
font-size: 24px;
}
</style>
<template>
<div class="item_search">
<form action="/search" @submit="disabledSubmit">
<van-search placeholder="请输入商品名称" v-model="keyword" @search="enterSearch" autofocus/>
</form>
<div class="item_search_content">
<div class="item_search_text clearfix">
<div class="float-l">历史搜索</div>
<div class="float-r" @click="clearHistory">
<van-icon name="lajitong" style="font-size: 12px;margin-right: 3px" />
清空历史记录
</div>
</div>
<div class="item_search_history">
<word-tag
v-for="(his, i) in wordHistory"
:key="i"
@click="toSearchResult(his)"
>{{his}}</word-tag>
</div>
</div>
</div>
</template>
<script>
import { Search } from 'vant';
import SrarchTag from './search-tag';
export default {
data() {
return {
keyword: '',
focusStatus: true,
wordHistory: []
};
},
methods: {
enterSearch() {
const keyword = this.keyword;
this.pushHistoryTolocal(keyword);
this.toSearchResult(keyword);
},
toSearchResult(word) {
this.keyword = word.trim();
this.$router.push({
name: 'search-result',
query: { keyword: word.trim() }
});
},
pushHistoryTolocal(keyword) {
const wordHistory = this.wordHistory;
const historyKeyWord = this.getKeyWordHistory();
if (!!keyword.trim() && historyKeyWord.indexOf(keyword) < 0) {
wordHistory.push(keyword);
window.localStorage.setItem('keyword', wordHistory.join('|'));
}
},
getKeyWordHistory() {
const listWord = window.localStorage.getItem('keyword');
return listWord ? listWord.split('|') : [];
},
clearHistory() {
this.$dialog
.confirm({
message: '是否清空历史记录'
})
.then(() => {
window.localStorage.setItem('keyword', '');
this.wordHistory = [];
});
},
disabledSubmit() {
return false;
}
},
activated() {
this.wordHistory = this.getKeyWordHistory();
},
components: {
[Search.name]: Search,
[SrarchTag.name]: SrarchTag
}
};
</script>
<style lang="scss" scoped>
.item_search {
background-color: #fff;
}
.item_search_content {
padding: 15px 10px 0;
}
.item_search_text {
font-size: $font-size-normal;
color: $font-color-gray;
margin-bottom: 20px;
}
.item_search_history > span {
margin-right: 10px;
margin-bottom: 10px;
}
</style>
<template>
<span class="search_tag" @click="OnClick">
<slot></slot>
</span>
</template>
<script>
export default {
name: 'word-tag',
methods: {
OnClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" scoped>
.search_tag {
display: inline-block;
font-size: 12px;
background-color: #f4f4f4;
padding: 3px 10px;
border-radius: 11px;
min-width: 20px;
text-align: center;
}
</style>
<template>
<div class="class_tree clearfix">
<ul class="class_tree_nav">
<li
v-for="(item ,index) in list"
:key="item.id"
:class="{active_nav: navActive == index}"
@click="navclick(index)"
>{{item.name}}</li>
</ul>
<div class="class_tree_content">
<div class="class_tree_all">
<img style="width:250px" v-lazy="currentCategory.picUrl">
</div>
<div class="box">
<span>{{currentCategory.desc}}</span>
</div>
<div class="class_tree_items_wrap clearfix">
<div @click="classClick(item.id)" :key="i" v-for="(item, i) in goods">
<div class="class_tree_item_img">
<img :src="item.picUrl" :alt="item.name">
</div>
<div class="class_tree_item_name">{{item.name}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'class-tree',
model: {
prop: 'activeIndex'
},
props: {
activeIndex: {
type: Number,
default: 0
},
list: {
type: Array,
default: () => []
}
},
data() {
const navActive =
this.activeIndex >= this.list.length ? 0 : this.activeIndex;
return {
navActive,
goods: [],
currentCategory: {}
};
},
computed: {},
methods: {
changeList(data) {
this.goods = data.currentSubCategory;
this.currentCategory = data.currentCategory;
console.log(this.goods);
},
allClick() {
this.$emit('all-click');
},
navclick(i) {
this.navActive = i;
this.$emit('nav-click', this.list[i].id);
},
classClick(id) {
this.$emit('class-click', id);
}
}
};
</script>
<style lang="scss" scoped>
@import '../../assets/scss/mixin';
.box {
width: 250px;
height: 20px;
text-align: center;
font-family: PingFangSC-Light, helvetica, 'Heiti SC';
font-size: 13px;
position: absolute;
top: 95px;
}
.box span {
line-height: 20px;
}
.class_tree {
position: relative;
background-color: #fff;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
height: 100%;
box-sizing: border-box;
}
.class_tree_nav {
float: left;
width: 100px;
height: 100%;
background-color: #f8f8f8;
overflow: scroll;
> li {
@include one-border;
height: 40px;
line-height: 40px;
text-align: center;
border-left: 2px solid $bg-color;
}
> li.active_nav {
background-color: #fff;
border-left: 2px solid $red;
color: $red;
}
}
.class_tree_content {
margin-left: 100px;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
.class_tree_all {
text-align: right;
padding-right: 10px;
height: 40px;
line-height: 40px;
color: $font-color-gray;
font-size: $font-size-small;
}
.van-icon-arrow {
font-size: $font-size-small;
}
.class_tree_items_wrap {
padding: 10px 20px;
margin-right: -3%;
margin-top: 70px;
text-align: center;
> div {
float: left;
padding-right: 3%;
box-sizing: border-box;
width: 33.333%;
margin-bottom: 20px;
}
img {
max-width: 100%;
}
.class_tree_item_img {
display: inline-block;
max-width: 100%;
width: 70px;
height: 70px;
}
.class_tree_item_name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
</style>
<template>
<div class="tab_class">
<div class="tal_class_searchBox">
<van-search placeholder="点击前往搜索"/>
<div class="tal_class_searchMask" @click="$router.push({ name: 'search' })"></div>
</div>
<class-tree
ref="classTree"
class="height-fix42"
@nav-click="changeCatalog"
@class-click="toItemList"
@all-click="toItemList"
:list="list"
></class-tree>
<is-empty v-if="isEmpty">抱歉,店主还未上架商品</is-empty>
</div>
</template>
<script>
import { GOODS_CATEGORY, GOODS_CHANNGE_CATEGORY } from '@/api/goods';
import getLocationParam from 'core/utils/location-param';
import { Search } from 'vant';
import classTree from './tabbar-class-tree';
import IsEmpty from '@/vue/components/is-empty';
import _ from 'lodash';
import { async } from 'q';
function getIndex(arr, keyWord) {
let index = 0;
_.each(arr, (v, k) => {
if (v.id === keyWord) {
index = k;
return false;
}
});
return index;
}
export default {
data() {
return {
list: [],
subCategory: [],
isEmpty: false
};
},
created() {
this.initData();
},
methods: {
initData() {
const shop_id = getLocationParam('shop_id');
this.$reqGet(`${GOODS_CATEGORY}`).then(res => {
this.list = res.data.data.categoryList;
this.$refs.classTree.changeList(res.data.data);
this.subCategory = res.data.data.currentSubCategory;
if (this.subCategory.length === 0) this.isEmpty = true;
});
},
removeNoChild(data) {
return data.filter(item => item.children && item.children.length);
},
changeCatalog(id) {
this.$reqGet(`${GOODS_CHANNGE_CATEGORY}${id}`).then(res => {
let index = getIndex(this.list, res.data.data.currentCategory.id);
this.$refs.classTree.changeList(res.data.data);
this.subCategory = res.data.data.currentSubCategory;
if (this.subCategory.length === 0) this.isEmpty = true;
});
},
toItemList(id) {
this.$router.push({
name: 'list',
query: { keyword: '', itemClass: id }
});
}
},
components: {
[Search.name]: Search,
[classTree.name]: classTree,
[IsEmpty.name]: IsEmpty
}
};
</script>
<style scoped>
.tab_class {
overflow: hidden;
background-color: #fff;
}
.height-fix {
padding-bottom: 42px;
}
.tal_class_searchBox {
position: relative;
}
.tal_class_searchMask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9;
}
</style>
<template>
<md-field-group class="foget_view">
<md-field
v-model="password"
icon="lock"
:is-error="isErrow"
placeholder="请输入新密码"/>
<md-field
v-model="passwordRepeat"
type="password"
icon="lock"
:is-error="isErrow"
placeholder="请再次输入密码" />
<div class="red" v-show="isErrow">两次密码输入不一致</div>
<div class="foget_submit">
<van-button size="large" type="danger" @click="submitCode">重置</van-button>
</div>
</md-field-group>
</template>
<script>
import field from '@/vue/components/field/';
import fieldGroup from '@/vue/components/field-group/';
export default {
data() {
return {
isErrow: true,
password: '',
passwordRepeat: ''
};
},
methods: {
submitCode() {}
},
components: {
[field.name]: field,
[fieldGroup.name]: fieldGroup
}
};
</script>
<style lang="scss" scoped>
div.foget_view {
background-color: #fff;
padding-top: 30px;
}
div.foget_submit {
padding-top: 30px;
padding-bottom: 20px;
}
</style>
<template>
<div class="payment_status">
<div class="status_top">
<van-icon :name="statusIcon" :class="statusClass" />
<div>{{statusText}}</div>
</div>
<div class="status_text"><span class="red">3秒</span>后返回到登录页, 您也可以<router-link to="/login" class="red">点此登录</router-link></div>
</div>
</template>
<script>
export default {
name: 'payment-status',
props: {
status: String
},
data() {
return {
isSuccess: true
};
},
computed: {
statusText() {
return this.isSuccess ? '修改成功' : '修改失败';
},
statusIcon() {
return this.isSuccess ? 'checked' : 'fail';
},
statusClass() {
return this.isSuccess ? 'success_icon' : 'fail_icon';
}
},
activated() {
this.isSuccess = this.status === 'success';
}
};
</script>
<style lang="scss" scopd>
.payment_status {
padding-top: 30px;
box-sizing: border-box;
background-color: #fff;
text-align: center;
}
.status_top {
margin-bottom: 15px;
i {
margin-bottom: 5px;
}
> div {
font-size: 18px;
}
}
.status_text {
color: $font-color-gray;
margin-bottom: 50px;
}
.status_icon {
font-size: 80px;
}
i.success_icon {
@extend .status_icon;
color: #06bf04;
}
i.fail_icon {
@extend .status_icon;
color: #f44;
}
</style>
<template>
<md-field-group class="foget_view">
<md-field
v-model="mobile"
icon="mobile"
placeholder="请输入手机号"/>
<md-field
v-model="code"
icon="lock"
placeholder="请输入短信验证码"
>
<div slot="rightIcon" @click="getCode" class="getCode red">
<countdown v-if="counting" :time="60000" @countdownend="countdownend">
<template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
</countdown>
<span v-else>获取验证码</span>
</div>
</md-field >
<div class="foget_submit">
<van-button size="large" type="danger" @click="submitCode">下一步</van-button>
</div>
</md-field-group>
</template>
<script>
import field from '@/vue/components/field/';
import fieldGroup from '@/vue/components/field-group/';
export default {
data() {
return {
counting: false,
mobile: '',
code: ''
};
},
methods: {
submitCode() {
this.$router.push({ name: 'forgetReset' });
},
getCode() {
this.counting = true;
},
countdownend() {
this.counting = false;
}
},
components: {
[field.name]: field,
[fieldGroup.name]: fieldGroup
}
};
</script>
<style lang="scss" scoped>
@import '../../../assets/scss/mixin';
div.foget_view {
background-color: #fff;
padding-top: 30px;
}
div.foget_submit {
padding-top: 30px;
padding-bottom: 20px;
}
.getCode {
@include one-border(left);
text-align: center;
}
.time_down {
color: $red;
}
</style>
<template>
<div class="text-desc text-center bottom_positon">技术支持: liteMall</div>
</template>
<script>
export default {
name: 'login-footer'
};
</script>
<style scoped>
.bottom_positon {
position: absolute;
bottom: 30px;
width: 100%;
}
</style>
<template>
<div class="store_header">
<div class="store_avatar">
<img src="../../assets/images/avatar_default.png" alt="头像" width="55" height="55">
</div>
<div class="store_name">我家的店</div>
</div>
</template>
<script>
export default {
name: 'login-header'
};
</script>
<style lang="scss" scoped>
.store_header {
text-align: center;
padding: 30px 0;
.store_avatar img {
border-radius: 50%;
}
.store_name {
padding-top: 5px;
font-size: 16px;
}
}
</style>
<template>
<div>
<md-field-group>
<md-field
v-model="account"
icon="username"
placeholder="随便输"
right-icon="clear-full"
v-validate="'required'"
name="user"
data-vv-as="帐号"
@right-click="clearText"
/>
<md-field
v-model="password"
icon="lock"
placeholder="随便输"
:type="visiblePass ? 'text' : 'password'"
:right-icon="visiblePass ? 'eye-open' : 'eye-close'"
v-validate="'required'"
data-vv-as="密码"
name="password"
@right-click="visiblePass = !visiblePass"
/>
<div class="clearfix">
<div class="float-r">
<router-link to="/login/forget">忘记密码</router-link>
</div>
</div>
<van-button size="large" type="danger" :loading="isLogining" @click="loginSubmit">登录</van-button>
</md-field-group>
<div class="register clearfix">
<div class="float-l connect">
<!-- <span @click="showKefu = true">联系客服</span> -->
</div>
<div class="float-r">
<router-link to="/login/registerGetCode">免费注册</router-link>
</div>
</div>
<van-popup v-model="showKefu">
<md-kefu mobile="16454193338"/>
</van-popup>
</div>
</template>
<script>
import field from '@/vue/components/field/';
import fieldGroup from '@/vue/components/field-group/';
import md_kefu from '@/vue/components/md-kefu/';
import { USER_LOGIN, USER_PROFILE } from '@/api/user';
import { setLocalStorage } from 'core/utils/local-storage';
import { emailReg, mobileReg } from '@/core/regexp';
import { Popup, Toast } from 'vant';
export default {
name: 'login-request',
data() {
return {
account: '',
password: '',
visiblePass: false,
showKefu: false,
isLogining: false,
userInfo: {}
};
},
methods: {
clearText() {
this.account = '';
},
async validate() {
const result = await this.$validator.validate();
if (!result) {
const errMsg = this.errors.items[0].msg;
Toast(errMsg);
throw new Error(`表单验证: ${errMsg}`);
}
},
async login() {
let loginData = this.getLoginData();
let { data } = await this.$reqPost(USER_LOGIN, loginData);
this.userInfo = data.data.userInfo;
console.log(this.userInfo);
setLocalStorage({
Authorization: data.data.token
});
this.getUserProfile();
},
async loginSubmit() {
this.isLogining = true;
try {
await this.validate();
await this.login();
this.isLogining = false;
} catch (err) {
console.log(err.message);
this.isLogining = false;
}
},
getUserProfile() {
// const {
// data: { data }
// } = await this.$reqGet(USER_PROFILE);
setLocalStorage({
avatar: this.userInfo.avatarUrl,
// user_id: data.user_id,
// background_image: data.background_image,
nickName: this.userInfo.nickName
});
this.routerRedirect();
},
routerRedirect() {
// const { query } = this.$route;
// this.$router.replace({
// name: query.redirect || 'home',
// query: query
// });
window.location = '#/user/';
},
getLoginData() {
const password = this.password;
const username = this.getUserType(this.account);
return {
username: this.account,
password: password
};
},
getUserType(account) {
const accountType = mobileReg.test(account)
? 'mobile'
: emailReg.test(account)
? 'email'
: 'username';
return accountType;
}
},
components: {
[field.name]: field,
[fieldGroup.name]: fieldGroup,
[md_kefu.name]: md_kefu,
[Popup.name]: Popup
}
};
</script>
<style lang="scss" scoped>
@import '../../assets/scss/mixin';
.register {
padding-top: 40px;
color: $font-color-gray;
a {
color: $font-color-gray;
}
> div {
width: 50%;
box-sizing: border-box;
padding: 0 20px;
}
.connect {
@include one-border(right);
text-align: right;
}
}
</style>
<template>
<div class="login">
<login-header />
<login-request />
<login-footer />
</div>
</template>
<script>
import loginHeader from './login-header';
import loginRequest from './login-request';
import loginFooter from './login-footer';
export default {
components: {
[loginHeader.name]: loginHeader,
[loginRequest.name]: loginRequest,
[loginFooter.name]: loginFooter
}
};
</script>
<style lang="scss" scoped>
.login {
position: relative;
background-color: #fff;
}
</style>
<template>
<md-field-group class="register_view">
<div>我们将发送验证码到您的手机</div>
<md-field
v-model="mobile"
icon="mobile"
placeholder="请输入手机号"/>
<div class="register_submit">
<van-button size="large" type="danger" @click="submitCode">下一步</van-button>
</div>
<div class="register_footer">
已有账号?
<router-link to="/login" class="red">登录</router-link>
</div>
</md-field-group>
</template>
<script>
import field from '@/vue/components/field/';
import fieldGroup from '@/vue/components/field-group/';
export default {
data() {
return {
mobile: ''
};
},
methods: {
submitCode() {
this.$router.push({ name: 'registerSubmit' });
}
},
components: {
[field.name]: field,
[fieldGroup.name]: fieldGroup
}
};
</script>
<style lang="scss" scoped>
div.register_view {
background-color: #fff;
padding-top: 30px;
}
div.register_submit {
padding-top: 30px;
padding-bottom: 20px;
}
.register_footer {
text-align: right;
color: $font-color-gray;
}
</style>
<template>
<div class="payment_status">
<div class="status_top">
<van-icon :name="statusIcon" :class="statusClass" />
<div>{{statusText}}</div>
</div>
<div class="status_text"><span class="red">3秒</span>后返回到登录页, 您也可以<router-link to="/login" class="red">点此登录</router-link></div>
</div>
</template>
<script>
export default {
name: 'payment-status',
props: {
status: String
},
data() {
return {
isSuccess: true
};
},
computed: {
statusText() {
return this.isSuccess ? '注册成功' : '注册失败';
},
statusIcon() {
return this.isSuccess ? 'checked' : 'fail';
},
statusClass() {
return this.isSuccess ? 'success_icon' : 'fail_icon';
}
},
activated() {
this.isSuccess = this.status === 'success';
}
};
</script>
<style lang="scss" scopd>
.payment_status {
padding-top: 30px;
box-sizing: border-box;
background-color: #fff;
text-align: center;
}
.status_top {
margin-bottom: 15px;
i {
margin-bottom: 5px;
}
> div {
font-size: 18px;
}
}
.status_text {
color: $font-color-gray;
margin-bottom: 50px;
}
.status_icon {
font-size: 80px;
}
i.success_icon {
@extend .status_icon;
color: #06bf04;
}
i.fail_icon {
@extend .status_icon;
color: #f44;
}
</style>
<template>
<md-field-group class="register_submit">
<md-field v-model="code" icon="mobile" placeholder="请输入验证码">
<div slot="rightIcon" @click="getCode" class="getCode red">
<countdown v-if="counting" :time="60000" @countdownend="countdownend">
<template slot-scope="props">{{ +props.seconds || 60 }}秒后获取</template>
</countdown>
<span v-else>获取验证码</span>
</div>
</md-field>
<md-field v-model="password" icon="lock" placeholder="请输入密码"/>
<md-field v-model="repeatPassword" icon="lock" placeholder="请再次确认密码"/>
<div class="register_submit_btn">
<van-button type="danger" size="large" @click="registerSubmit">确定</van-button>
</div>
</md-field-group>
</template>
<script>
import field from '@/vue/components/field/';
import fieldGroup from '@/vue/components/field-group/';
export default {
data() {
return {
counting: true,
code: '',
password: '',
repeatPassword: ''
};
},
methods: {
registerSubmit() {
this.$router.push({
name: 'registerStatus',
params: { status: 'success' }
});
},
getCode() {
this.counting = true;
},
countdownend() {
this.counting = false;
}
},
components: {
[field.name]: field,
[fieldGroup.name]: fieldGroup
}
};
</script>
<style lang="scss" scoped>
@import '../../../assets/scss/mixin';
.register_submit {
padding-top: 40px;
background-color: #fff;
}
.register_submit_btn {
padding-top: 30px;
}
.getCode {
@include one-border(left);
text-align: center;
}
.time_down {
color: $red;
}
</style>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment