Commit aec557e9 authored by Junling Bu's avatar Junling Bu
Browse files

chore[litemall-vue]: 参考litemall-admin结构,重新调整litemall-vue的src结构

parent 8d262f8b
......@@ -23,7 +23,7 @@ litemall轻商城,是商城移动版本。
可以阅读3.1
## 3.2 litemall-vue
## 5.2 litemall-vue
这里的代码基于[vant--mobile-mall](https://github.com/qianzhaoy/vant--mobile-mall)
......
......@@ -74,7 +74,7 @@ litemall是一个简单的商场系统,基于现有的开源项目,重新实
* 地址列表、地址添加、地址删除
* 收藏、足迹、关于
### 1.2.1 轻商城功能
### 1.2.2 轻商城功能
**目前还在开发中,不稳定**
......
......@@ -23,6 +23,8 @@
"vant": "^1.4.4",
"vee-validate": "^2.1.4",
"vue": "^2.5.17",
"js-cookie": "2.2.0",
"vuex": "3.0.1",
"vue-router": "^3.0.1",
"vuelidation": "^1.1.0"
},
......
import request from '@/core/utils/request'
import request from '@/utils/request'
// export const GOODS_CATEGORY = '/category';
export const GOODS_CATEGORY = '/wx/catalog/index';
......
// 登录
export const USER_LOGIN = '/wx/auth/login';
export const USER_LOGOUT = '';
// 用户信息
export const USER_PROFILE = '/user-profile';
export const USER_MODIFY_PASSWORD = '';
export const USER_CHANGE_MOBILE = '';
// 验证码
export const USER_SENDCODE = '';
// 地址
export const ADDRESS = '/address';
export const ADDRESS_DEFAULT = '/address-default';
// 收藏
export const GOODS_COLLECT_LIST = '/moreGoods';
// 登录
export const USER_LOGIN = '/wx/auth/login';
export const USER_LOGOUT = '';
// 用户信息
export const USER_PROFILE = '/user-profile';
export const USER_MODIFY_PASSWORD = '';
export const USER_CHANGE_MOBILE = '';
// 验证码
export const USER_SENDCODE = '';
// 地址
export const ADDRESS = '/address';
export const ADDRESS_DEFAULT = '/address-default';
// 收藏
export const GOODS_COLLECT_LIST = '/moreGoods';
import request from '@/utils/request'
export function loginByUsername(data) {
return request({
url: '/wx/auth/login',
method: 'post',
data
})
}
export function logout() {
return request({
url: '/auth/logout',
method: 'post'
})
}
export function getUserInfo(token) {
return request({
url: '/auth/info',
method: 'get',
params: { token }
})
}
\ No newline at end of file
<template>
<van-tabbar v-model="active" style="z-index: 1999">
<van-tabbar-item
v-for="(tab, index) in tabbar"
:icon="tab.icon"
:to="tab.path"
:dot="tab.dot"
:info="tab.info"
:key="index">
{{tab.name}}
</van-tabbar-item>
</van-tabbar>
</template>
<script>
import { Tabbar, TabbarItem } from 'vant';
export default {
data() {
return {
active: 0,
tabbar: [
{
name: '精选',
path: '/',
pathName: 'home',
icon: 'compass-full',
dot: false,
info: ''
},
{
name: '分类',
path: '/items',
pathName: 'class',
icon: 'class-full',
dot: false,
info: ''
},
{
name: '购物车',
path: '/order',
pathName: 'cart',
icon: 'cart-full',
dot: false,
info: ''
},
{
name: '我的',
path: '/user',
pathName: 'user',
icon: 'wode',
dot: false,
info: ''
}
]
};
},
watch: {
$route: 'changeActive'
},
created() {
const toName = this.$route.name;
this.setActive(toName);
},
methods: {
changeActive({ name }) {
this.setActive(name);
},
setActive(name) {
this.tabbar.forEach((tab, i) => {
if (tab.pathName == name) {
this.active = i;
return false;
}
});
}
},
components: {
[Tabbar.name]: Tabbar,
[TabbarItem.name]: TabbarItem
}
};
</script>
<template>
<van-tabbar v-model="active" style="z-index: 1999">
<van-tabbar-item
v-for="(tab, index) in tabbar"
:icon="tab.icon"
:to="tab.path"
:dot="tab.dot"
:info="tab.info"
:key="index">
{{tab.name}}
</van-tabbar-item>
</van-tabbar>
</template>
<script>
import { Tabbar, TabbarItem } from 'vant';
export default {
data() {
return {
active: 0,
tabbar: [
{
name: '精选',
path: '/',
pathName: 'home',
icon: 'compass-full',
dot: false,
info: ''
},
{
name: '分类',
path: '/items',
pathName: 'class',
icon: 'class-full',
dot: false,
info: ''
},
{
name: '购物车',
path: '/order',
pathName: 'cart',
icon: 'cart-full',
dot: false,
info: ''
},
{
name: '我的',
path: '/user',
pathName: 'user',
icon: 'wode',
dot: false,
info: ''
}
]
};
},
watch: {
$route: 'changeActive'
},
created() {
const toName = this.$route.name;
this.setActive(toName);
},
methods: {
changeActive({ name }) {
this.setActive(name);
},
setActive(name) {
this.tabbar.forEach((tab, i) => {
if (tab.pathName == name) {
this.active = i;
return false;
}
});
}
},
components: {
[Tabbar.name]: Tabbar,
[TabbarItem.name]: TabbarItem
}
};
</script>
import { debounce } from 'lodash';
import scroll from 'core/utils/scroll';
const CONTEXT = '$scrollArrow';
const OFFSET = 30;
// 绑定事件
function startBind(el) {
const context = el[CONTEXT];
context.vm.$nextTick(() => {
if (scroll.isAttached(el)) {
doBindEvent.call(el[CONTEXT]);
}
});
}
// 绑定事件到元素上
// 读取基本的控制变量
function doBindEvent() {
if (this.el[CONTEXT].binded) {
return;
}
this.el[CONTEXT].binded = true;
this.scrollEventListener = debounce(handleScrollEvent.bind(this), 100);
// this.scrollEventTarget = this.el;
// var disabledExpr = this.el.getAttribute('waterfall-disabled');
// var disabled = false;
// if (disabledExpr) {
// this.vm.$watch(disabledExpr, (value) => {
// this.disabled = value;
// this.scrollEventListener();
// });
// disabled = Boolean(this.vm[disabledExpr]);
// }
// this.disabled = disabled;
const offset = this.el.getAttribute('scroll-offset');
this.offset = Number(offset) || OFFSET;
this.el.addEventListener('scroll', this.scrollEventListener);
// this.scrollEventListener();
}
// 处理滚动函数
function handleScrollEvent() {
const element = this.el;
// 已被禁止的滚动处理
// if (this.disabled) return;
const targetScrollLeft = scroll.getScrollLeft(element);
const targetVisibleWidth = scroll.getVisibleWidth(element);
// 滚动元素可视区域下边沿到滚动元素元素最顶上 距离
const targetRight = targetScrollLeft + targetVisibleWidth;
// 如果无元素高度,考虑为元素隐藏,直接返回
if (!targetVisibleWidth) return;
// 判断是否到了最右边
const isRightOver = element.scrollWidth - targetRight < this.offset;
// 判断是否到了最左边
const isLeftOver = targetScrollLeft < this.offset;
this.cb &&
this.cb({
target: element,
isRightOver,
isLeftOver
});
// // 判断是否到了顶
// let needLoadMoreToUpper = targetScrollTop < this.offset;
// if (needLoadMoreToUpper) {
// this.cb.upper && this.cb.upper({
// target: scrollEventTarget,
// top: targetScrollTop
// });
// }
}
// 确认何时绑事件监听函数
function doCheckStartBind(el) {
const context = el[CONTEXT];
if (context.vm._isMounted) {
startBind(el);
} else {
context.vm.$on('hook:mounted', () => {
startBind(el);
});
}
}
export default {
bind(el, binding, vnode) {
if (!el[CONTEXT]) {
el[CONTEXT] = {
el,
vm: vnode.context,
cb: {}
};
}
el[CONTEXT].cb = binding.value;
doCheckStartBind(el);
},
update(el) {
const context = el[CONTEXT];
context.scrollEventListener && context.scrollEventListener();
}
};
import { debounce } from 'lodash';
import scroll from '@/utils/scroll';
const CONTEXT = '$scrollArrow';
const OFFSET = 30;
// 绑定事件
function startBind(el) {
const context = el[CONTEXT];
context.vm.$nextTick(() => {
if (scroll.isAttached(el)) {
doBindEvent.call(el[CONTEXT]);
}
});
}
// 绑定事件到元素上
// 读取基本的控制变量
function doBindEvent() {
if (this.el[CONTEXT].binded) {
return;
}
this.el[CONTEXT].binded = true;
this.scrollEventListener = debounce(handleScrollEvent.bind(this), 100);
// this.scrollEventTarget = this.el;
// var disabledExpr = this.el.getAttribute('waterfall-disabled');
// var disabled = false;
// if (disabledExpr) {
// this.vm.$watch(disabledExpr, (value) => {
// this.disabled = value;
// this.scrollEventListener();
// });
// disabled = Boolean(this.vm[disabledExpr]);
// }
// this.disabled = disabled;
const offset = this.el.getAttribute('scroll-offset');
this.offset = Number(offset) || OFFSET;
this.el.addEventListener('scroll', this.scrollEventListener);
// this.scrollEventListener();
}
// 处理滚动函数
function handleScrollEvent() {
const element = this.el;
// 已被禁止的滚动处理
// if (this.disabled) return;
const targetScrollLeft = scroll.getScrollLeft(element);
const targetVisibleWidth = scroll.getVisibleWidth(element);
// 滚动元素可视区域下边沿到滚动元素元素最顶上 距离
const targetRight = targetScrollLeft + targetVisibleWidth;
// 如果无元素高度,考虑为元素隐藏,直接返回
if (!targetVisibleWidth) return;
// 判断是否到了最右边
const isRightOver = element.scrollWidth - targetRight < this.offset;
// 判断是否到了最左边
const isLeftOver = targetScrollLeft < this.offset;
this.cb &&
this.cb({
target: element,
isRightOver,
isLeftOver
});
// // 判断是否到了顶
// let needLoadMoreToUpper = targetScrollTop < this.offset;
// if (needLoadMoreToUpper) {
// this.cb.upper && this.cb.upper({
// target: scrollEventTarget,
// top: targetScrollTop
// });
// }
}
// 确认何时绑事件监听函数
function doCheckStartBind(el) {
const context = el[CONTEXT];
if (context.vm._isMounted) {
startBind(el);
} else {
context.vm.$on('hook:mounted', () => {
startBind(el);
});
}
}
export default {
bind(el, binding, vnode) {
if (!el[CONTEXT]) {
el[CONTEXT] = {
el,
vm: vnode.context,
cb: {}
};
}
el[CONTEXT].cb = binding.value;
doCheckStartBind(el);
},
update(el) {
const context = el[CONTEXT];
context.scrollEventListener && context.scrollEventListener();
}
};
<template>
<div class="field_group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'md-field-group'
};
</script>
<style lang="scss" scoped>
.field_group {
padding-left: 15px;
padding-right: 15px;
> div {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
}
</style>
<template>
<div class="field_group">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'md-field-group'
};
</script>
<style lang="scss" scoped>
.field_group {
padding-left: 15px;
padding-right: 15px;
> div {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
}
</style>
<template>
<div class="md_field" :class="{md_field_hasIcon: !!icon, md_field_isError: isError}">
<van-icon v-if="icon" :name="icon" class="md_feld_icon"/>
<div class="md_field_control">
<input
:type="type"
v-on="listeners"
v-bind="$attrs"
:value="value">
</div>
<div>
<slot name="rightIcon">
<van-icon :name="rightIcon" @click="rightClick" v-show="value" />
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'md-field',
props: {
value: {},
type: {
type: String,
default: 'text'
},
rightIcon: String,
icon: String,
isError: Boolean
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.onInput
};
}
},
methods: {
onInput(event) {
this.$emit('input', event.target.value);
},
rightClick(event) {
this.$emit('right-click', event);
}
}
};
</script>
<style lang="scss" scoped>
.md_field {
position: relative;
border: 1px solid $border-color;
border-radius: 5px;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
display: table;
width: 100%;
box-sizing: border-box;
background-color: #fff;
> div {
display: table-cell;
}
> .md_field_control {
padding-right: 10px;
box-sizing: border-box;
input {
border: 0;
line-height: 14px;
font-size: 14px;
width: 100%;
}
}
.md_feld_icon {
position: absolute;
top: 50%;
left: 10px;
transform: translate(0, -50%);
}
}
.md_field_hasIcon {
padding-left: 40px;
}
.md_field_isError {
color: $red;
background-color: #fcf5f5;
border: 1px solid $red;
input {
color: $red;
background-color: #fcf5f5;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #fcf5f5 inset !important;
}
}
</style>
<template>
<div class="md_field" :class="{md_field_hasIcon: !!icon, md_field_isError: isError}">
<van-icon v-if="icon" :name="icon" class="md_feld_icon"/>
<div class="md_field_control">
<input
:type="type"
v-on="listeners"
v-bind="$attrs"
:value="value">
</div>
<div>
<slot name="rightIcon">
<van-icon :name="rightIcon" @click="rightClick" v-show="value" />
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'md-field',
props: {
value: {},
type: {
type: String,
default: 'text'
},
rightIcon: String,
icon: String,
isError: Boolean
},
computed: {
listeners() {
return {
...this.$listeners,
input: this.onInput
};
}
},
methods: {
onInput(event) {
this.$emit('input', event.target.value);
},
rightClick(event) {
this.$emit('right-click', event);
}
}
};
</script>
<style lang="scss" scoped>
.md_field {
position: relative;
border: 1px solid $border-color;
border-radius: 5px;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
display: table;
width: 100%;
box-sizing: border-box;
background-color: #fff;
> div {
display: table-cell;
}
> .md_field_control {
padding-right: 10px;
box-sizing: border-box;
input {
border: 0;
line-height: 14px;
font-size: 14px;
width: 100%;
}
}
.md_feld_icon {
position: absolute;
top: 50%;
left: 10px;
transform: translate(0, -50%);
}
}
.md_field_hasIcon {
padding-left: 40px;
}
.md_field_isError {
color: $red;
background-color: #fcf5f5;
border: 1px solid $red;
input {
color: $red;
background-color: #fcf5f5;
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #fcf5f5 inset !important;
}
}
</style>
......@@ -17,8 +17,8 @@
<script>
import { List } from 'vant';
import { get } from 'lodash';
import IsEmpty from '@/vue/components/is-empty';
import loadMore from '@/vue/mixin/load-more';
import IsEmpty from '@/components/is-empty';
import loadMore from '@/mixin/load-more';
const DEFAULT_CONFIG = {
params: {},
......
<template>
<div class="is_empty">
<div>
<img src="../../../assets/images/is_empty.png" alt="无商品" width="20%">
<img src="../../assets/images/is_empty.png" alt="无商品" width="20%">
</div>
<div>
<slot></slot>
......
......@@ -41,7 +41,7 @@
<script>
import item_mix from '@/vue/mixin/item-card';
import item_mix from '@/mixin/item-card';
export default {
name: 'item-card-hori',
......
......@@ -25,7 +25,7 @@
<script>
import item_mix from '@/vue/mixin/item-card';
import item_mix from '@/mixin/item-card';
export default {
name: 'item-card-vert',
......
// 使用这个会导致组件内部的 router 导航守卫无法使用, 慎用
/**
* @param { string } chunkPath views 文件夹下的页面路径
* @return { function } 返回 promise<component> 的匿名函数
*/
import spinner from '@/vue/components/spinner';
export default chunkPath => {
const AsyncHandler = () => ({
component: new Promise(resolve => {
setTimeout(() => {
resolve(
import(/* webpackChunkName: "[request]" */ `@/views/${chunkPath}`)
);
}, 1000);
}),
loading: spinner,
error: {
render(h) {
return h('div', {}, ['异步组件加载失败']);
}
},
timeout: 10000
});
return () =>
Promise.resolve({
functional: true,
render(h, { data, children }) {
return h(AsyncHandler, data, children);
}
});
};
// import axios from 'axios'
// import { Message, MessageBox } from 'element-ui'
// import store from '@/store'
// import { getToken } from '@/utils/auth'
// // create an axios instance
// const service = axios.create({
// baseURL: process.env.BASE_API, // api 的 base_url
// timeout: 5000 // request timeout
// })
// // request interceptor
// service.interceptors.request.use(
// config => {
// // Do something before request is sent
// if (store.getters.token) {
// // 让每个请求携带token-- ['X-Litemall-Admin-Token']为自定义key 请根据实际情况自行修改
// config.headers['X-Litemall-Admin-Token'] = getToken()
// }
// return config
// },
// error => {
// // Do something with request error
// console.log(error) // for debug
// Promise.reject(error)
// }
// )
// // response interceptor
// service.interceptors.response.use(
// response => {
// const res = response.data
// if (res.errno === 501) {
// MessageBox.alert('系统未登录,请重新登录', '错误', {
// confirmButtonText: '确定',
// type: 'error'
// }).then(() => {
// store.dispatch('FedLogOut').then(() => {
// location.reload()
// })
// })
// return Promise.reject('error')
// } else if (res.errno === 502) {
// MessageBox.alert('系统内部错误,请联系管理员维护', '错误', {
// confirmButtonText: '确定',
// type: 'error'
// })
// return Promise.reject('error')
// } else if (res.errno === 503) {
// MessageBox.alert('请求业务目前未支持', '警告', {
// confirmButtonText: '确定',
// type: 'error'
// })
// return Promise.reject('error')
// } else if (res.errno === 504) {
// MessageBox.alert('更新数据已经失效,请刷新页面重新操作', '警告', {
// confirmButtonText: '确定',
// type: 'error'
// })
// return Promise.reject('error')
// } else if (res.errno === 505) {
// MessageBox.alert('更新失败,请再尝试一次', '警告', {
// confirmButtonText: '确定',
// type: 'error'
// })
// return Promise.reject('error')
// } else if (res.errno === 506) {
// MessageBox.alert('没有操作权限,请联系管理员授权', '错误', {
// confirmButtonText: '确定',
// type: 'error'
// })
// return Promise.reject('error')
// } else if (res.errno !== 0) {
// // 非5xx的错误属于业务错误,留给具体页面处理
// return Promise.reject(response)
// } else {
// return response
// }
// }, error => {
// console.log('err' + error)// for debug
// Message({
// message: '登录连接超时(后台不能连接,请联系系统管理员)',
// type: 'error',
// duration: 5 * 1000
// })
// return Promise.reject(error)
// })
// export default service
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