Authored by 李奇

项目初始化、完成统计订单数据

Showing 86 changed files with 4670 additions and 0 deletions

Too many changes to show.

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

{
"presets": [
"es2015"
],
"plugins": [
"transform-runtime",
"transform-vue-jsx",
"transform-object-rest-spread",
"syntax-dynamic-import"
]
}
... ...
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
... ...
build/*
/*.js
app/statics/js/*.js
\ No newline at end of file
... ...
.DS_Store
.idea
app/build/bundle
node_modules/
*.log
.eslintcache
.stylelintcache
app/bundle
/public/
.vscode/
.happypack/
build/dll/
server/bundle/*.json
server/bundle/**/
package-lock.json
public/
... ...
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
registry=http://npm.yohops.com
package-lock=false
... ...
{
"extends": "stylelint-config-yoho",
"processors": ["stylelint-processor-html"],
"rules": {
"string-quotes": "double",
"no-empty-source": null
}
}
... ...
############################################################
# Dockerfile to build hystrix + turbin Installed Containers
# Based on centos 6.7
# How to build new image: docker build -t yoho-hystrix-qcloud .
# the hystrix alert need to post events to influxdb.yohoops.org.
# nginx version: 1.12.0
############################################################
#base image : ccr.ccs.tencentyun.com/yoho-base/nodejs:8.9.1
FROM ccr.ccs.tencentyun.com/yoho-base/nodejs:8.9.1
MAINTAINER feng.chen <feng.chen@yoho.cn>
ENV NODE_ENV=production \
NODE_HOME=/home
COPY ufo-data-platform.tar.gz /home
RUN cd /home && \
mkdir -p /home/ufo-data-platform && \
mkdir -p /Data/log/ufo-data-platform && \
tar -xzf ufo-data-platform.tar.gz -C /home/ufo-data-platform && \
rm -rf ufo-data-platform.tar.gz
WORKDIR /home/ufo-data-platform
#expose port
EXPOSE 6013
CMD ["node","/home/ufo-data-platform/server/app.js"]
... ...
# 基于Vue2.0 SPA结构的有货前端开发框架
## 开发文档 (试行版本)
### 框架技术
Vue@2.0 + vue-router + iView + axios
### 目录结构
```nginx
-app #前端开发目录
---build #webpack配置文件
---components #组件目录
------global #全局注册的公用组件
------[modules] #业务模块用的组件,在模块下已功能类别划分文件夹,比如filter-select(列表查询组件)、table-cell(单元格组件),具体组件明明建议以select-[component]、cell-[component]命名
---config #前端配置文件
---directives #指令文件夹
---filters #过滤器
---images #图片
---pages #业务代码
-----[modules] #已业务模块划分文件夹
-------[pages] #已业务模块中页面划分文件夹
---------index.js #页面的路由注册入口
-------index.js #业务模块的路由注册入口
-----app.vue #最外层组件
-----index.js #路由注册入口
-----layout.vue #layout文件和全局样式
---plugins #插件
---servies #业务数据获取、数据处理、业务逻辑等
-----[modules]-service
---app.js #入口文件
---index.html #模板页
-server #后端开发目录
```
### 代码规范
#### 1.文件、组件命名规范
1.文件命名小写,使用-分割单词
2.组件name大驼峰式命名
3.变量名小驼峰命名
#### 2.页面规范:
template
``` html
<layout-body>
<layout-filter>
...列表筛选区域
<filter-item :label="">
...筛选查询项
<Input v-model="skn" placeholder=""></Input>
</filter-item>
</layout-filter>
<layout-action>
...批量操作区域
<Button type="error" @click="batchSetOffSale">下架</Button>
</layout-action>
<layout-list>
...表格列表区域
<Table></Table>
</layout-list>
</layout-body>
```
script
``` js
import {SelectBrand, SelectCategory} from 'components/select'; // 建议以{}的形式引入组件
export default {
data() {
return {};
},
created() { // 需要在页面初始化执行的周期事件
},
beforeDestory() { // 需要在页面销毁时执行的周期事件
},
methods() {
xxx() {
}
}
components: {}
}
```
style
lang=scss
``` html
<style lang="scss">
.list-page {
float: right;
}
</style>
```
#### 3.路由规范:
用webpack的code split lazy方式加载页面模块,chunkname为[module].[page]
路由meta元信息:
``` js
{
pageName: '[页面名称]'
}
```
``` js
export default {
path: '/onsale.html',
name: 'onsale',
component: () => import(/* webpackChunkName: "product.onsale" */'./onsale),
meta: {
pageName: '在售商品'
}
};
```
#### 4.组件划分规范
1.全局公共组件目录为components/global,该文件夹内组件会在Vue实例化前自动注册
2.模块组件在components/[modules]中创建
3.页面级无复用性组件在app/pages/[modules]/[pages]/components文件夹创建
### 开发流程
#### 1.创建页面及入口文件
在apps/pages/[modules]中创建页面文件夹,创建[page].vue及其入口index.js文件,[page].vue为页面业务代码,index.js为入口和路由代码并在[modules]文件夹下的入口文件添加页面的引用。
#### 2.数据获取操作
在页面业务中需要获取数据的逻辑或者需要做数据处理的统一在apps/services下创建service文件处理,涉及到需要调用的接口需要在server/common/api-domain注册:
``` js
// api调用列表
let domainApis = {
erp: {
login: '/erp-gateway-web/account/profile/login',
getPurview: '/erp-gateway-web/account/menu/query_by_pid'
},
platform: {
queryShopsByAdminPid: '/SellerShopController/queryShopsByAdminPid'
}
};
```
前端调用接口路径为domainApis的属性名,并且加上前缀/Api,例如: '/Api/platform/queryShopsByAdminPid'
#### 3.组件创建
全局组件创建在components/global
业务组件创建在components/[modules]
页面级无复用性组件在app/pages/[modules]/[pages]/components文件夹创建
## CLI
### 项目初始化,执行npm i和生成dll的操作(clone项目之后执行)
```
npm run init
```
### 启动node dev开发模式
```
npm run dev
```
### 启动前端dev开发模式
```
npm run static
```
### 编译静态资源文件
```
npm run build
```
### 代码生成器(生成基础带筛选条件的基础模板页面)
```
npm run code -module=[modulename] -page=[pagename]
```
### 全局js语法检查和css语法检查
```
npm run lint-js
```
```
npm run lint-css
```
\ No newline at end of file
... ...
import Vue from 'vue';
import App from './pages/app';
import yohoPluginCore from './plugins/yoho-plugin-core';
import yohoPluginRouter from './plugins/yoho-plugin-router';
import yohoPluginCache from './plugins/yoho-plugin-cache';
import './statics/scss/base.scss';
import './filters';
import './directives';
Vue.use(yohoPluginCore);
Vue.use(yohoPluginRouter);
Vue.use(yohoPluginCache);
Vue.render({
el: '#app',
template: '<App/>',
components: {App}
});
... ...
<template>
<div class="action-btn-row">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'action-group'
};
</script>
<style lang="scss">
.action-btn-row {
button {
margin-top: 10px;
margin-right: 10px;
margin-bottom: 10px;
}
}
</style>
... ...
<template>
<div :class="classes" :style="styles" @click="back">
<slot>
<div class="back-top-dom-inner">
<i class="ivu-icon ivu-icon-chevron-up"></i>
</div>
</slot>
</div>
</template>
<script>
const prefixCls = 'back-top-dom';
export default {
name: 'back-top-dom',
props: {
height: {
type: Number,
default: 400
},
bottom: {
type: Number,
default: 30
},
right: {
type: Number,
default: 30
},
duration: {
type: Number,
default: 1000
}
},
data() {
return {
backTop: false
};
},
mounted() {
document.getElementsByClassName('layout-content')[0].addEventListener('scroll', this.handleScroll, true);
document.getElementsByClassName('layout-content')[0].addEventListener('resize', this.handleScroll, true);
},
beforeDestroy() {
document.getElementsByClassName('layout-content')[0].removeEventListener('scroll', this.handleScroll, false);
document.getElementsByClassName('layout-content')[0].removeEventListener('resize', this.handleScroll, false);
},
computed: {
classes() {
return [
`${prefixCls}`,
{
[`${prefixCls}-show`]: this.backTop
}
];
},
styles() {
return {
bottom: `${this.bottom}px`,
right: `${this.right}px`
};
},
},
methods: {
handleScroll() {
this.backTop = document.getElementsByClassName('layout-content')[0].scrollTop >= document.getElementsByClassName('layout-content')[0].clientHeight + 92;
},
back() {
const sTop = document.getElementsByClassName('layout-content')[0].scrollTop;
this.scrollTop(document.getElementsByClassName('layout-content')[0], sTop, 0, this.duration);
this.$emit('on-click');
},
scrollTop(el, from = 0, to, duration = 5000) {
const difference = Math.abs(from - to);
const step = Math.ceil(difference / duration * 50);
function scroll(start, end) {
if (start === end) {
return;
}
el.scrollTop = 0;
}
scroll(from, to, step);
}
}
};
</script>
<style lang="scss" scoped>
.back-top-dom {
position: fixed;
z-index: 1002;
cursor: pointer;
display: none;
&.back-top-dom-show {
display: block;
}
.back-top-dom-inner {
background-color: rgba(0, 0, 0, 0.6);
border-radius: 2px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease-in-out;
.ivu-icon {
display: inline-block;
font-family: Ionicons, serif;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: auto;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #fff;
font-size: 24px;
padding: 8px 12px;
}
.ivu-icon-chevron-up:before {
content: "\F126";
}
}
}
</style>
... ...
<template>
<Breadcrumb class="brand-curmb" v-if="showBreadcrumb">
<i class="iconfont icon-alignjustify" aria-hidden="true" @click="$emit('menu-trigger')"></i>
<Breadcrumb-item v-bind:href="item.path" v-for="item in items" :key="item.name">{{item.name}}</Breadcrumb-item>
</Breadcrumb>
</template>
<script>
import _ from 'lodash';
export default {
name: 'breadcrumbs',
data() {
return {
items: [],
activeName: '',
showBreadcrumb: false
};
},
created() {
this.render(_.get(this.$router, 'history.current'));
},
methods: {
render(to) {
this.showBreadcrumb = true;
this.items = [{name: '首页', path: '/'}];
this.items = this.items.concat(_.map(_.get(to, 'matched', []), match => {
return {
name: match.meta.pageName
};
}).filter(m => m.name));
}
},
watch: {
$route(newRoute) {
this.showBreadcrumb = false;
this.$nextTick(() => { // Breadcrumb更新子组件的separator,如果不手动重建Breadcrumb周期,子组件separator不会得到更新
this.render(newRoute);
});
}
}
};
</script>
<style lang="scss">
.brand-curmb {
&.bread-collapse {
.iconfont {
display: initial !important;
}
}
.iconfont {
cursor: pointer;
display: none !important;
}
}
</style>
... ...
<template>
<div v-if="show">
<image-purview
v-if="uploadList[0]"
:status="uploadList[0].status"
:url="uploadList[0].url"
:progress="uploadList[0].showProgress"
:percentage="uploadList[0].percentage"
:remove="true"
@remove-image="handleRemove"></image-purview>
<div class="upload-box" v-show="!uploadList[0]">
<Upload ref="upload"
:show-upload-list="false"
:data="{bucket: bucket}"
:on-success="handleSuccess"
:on-error="handleError"
:default-file-list="defaultList"
:format="['jpg','jpeg','png']"
:max-size="2048"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
action="/Api/upload/image">
<Icon type="ios-cloud-upload-outline" title="上传图片"></Icon>
</Upload>
<Icon type="ios-cloud-outline" v-if="skn" title="调用图片服务" @click.native="browseOnline"></Icon>
</div>
<modal-skn-image v-model="sknImageModel" :skn="skn" @selected="selected"></modal-skn-image>
</div>
</template>
<script>
import _ from 'lodash';
export default {
name: 'drag-file-upload',
props: {
id: {
type: Object
},
defaultFile: {
type: String
},
skn: {
type: [String, Number],
},
bucket: {
type: String,
default() {
return 'goodsimg';
}
}
},
data() {
let _this = this;
return {
imgUrl: '',
visible: false,
show: true,
sknImageModel: false,
uploadList: [],
defaultList: _this.defaultFile ? [{url: _this.defaultFile}] : []
};
},
methods: {
handleView(url) {
this.imgUrl = url;
this.visible = true;
},
handleRemove() {
let file = this.uploadList[0];
let files = this.$refs.upload.fileList;
this.$refs.upload.fileList.splice(files.indexOf(file), 1);
this.uploadList = this.$refs.upload.fileList;
this.$emit('remove', this.id);
},
handleSuccess(response, file, files) {
if (_.get(response, 'data.imagesList.length', 0)) {
file.url = response.data.imagesList[0];
}
this.uploadList = files;
this.$emit('success', this.id, file);
},
handleError() {
this.$Notice.error('上传失败');
this.$emit('error', this.id);
},
handleFormatError(file) {
this.$Notice.warning({
title: '文件格式不正确',
desc: '文件 ' + file.name + ' 格式不正确,请上传 jpg 或 png 格式的图片。'
});
},
handleMaxSize(file) {
this.$Notice.warning({
title: '超出文件大小限制',
desc: '文件 ' + file.name + ' 太大,不能超过 2M。'
});
},
browseOnline() {
this.sknImageModel = true;
},
selected(url) {
this.uploadList = this.defaultList = [{url}];
this.$emit('success', this.id, {url});
},
},
mounted() {
this.uploadList = this.$refs.upload.fileList;
},
watch: {
defaultFile: function(newValue) {
if (newValue) {
this.defaultList = [{url: newValue}];
}
}
}
};
</script>
<style lang="scss">
.upload-box {
width: 100px;
height: 100px;
background: #fff;
border: 1px dashed #dddee1;
border-radius: 4px;
text-align: center;
cursor: pointer;
position: relative;
overflow: hidden;
transition: border-color 0.2s ease;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
&:hover {
border-color: #2d8cf0;
}
.ivu-upload {
height: 30px;
}
.ivu-icon {
font-size: 30px;
margin: 0 2px;
color: #000;
transition: color 0.2s ease;
&:hover {
color: #2d8cf0;
}
}
}
</style>
... ...
<template>
<div :class="className" class="echart">
</div>
</template>
<script>
let ECharts = require('echarts-lib');
import _ from 'lodash';
require('echarts-tooltip');
require('echarts-legend');
require('echarts-scatter');
require('echarts-pie');
require('echarts/map/js/china');
export default {
name: 'EChartMap',
props: ['className', 'echartParams', 'option', 'showEchart'],
data() {
return {
geoCoordMap: {
'海门': [121.15, 31.89],
'鄂尔多斯': [109.781327, 39.608266],
'招远': [120.38, 37.35],
'舟山': [122.207216, 29.985295],
'齐齐哈尔': [123.97, 47.33],
'盐城': [120.13, 33.38],
'赤峰': [118.87, 42.28],
'青岛': [120.33, 36.07],
'乳山': [121.52, 36.89],
'金昌': [102.188043, 38.520089],
'泉州': [118.58, 24.93],
'莱西': [120.53, 36.86],
'日照': [119.46, 35.42],
'胶南': [119.97, 35.88],
'南通': [121.05, 32.08],
'拉萨': [91.11, 29.97],
'云浮': [112.02, 22.93],
'梅州': [116.1, 24.55],
'文登': [122.05, 37.2],
'上海': [121.48, 31.22],
'攀枝花': [101.718637, 26.582347],
'威海': [122.1, 37.5],
'承德': [117.93, 40.97],
'厦门': [118.1, 24.46],
'汕尾': [115.375279, 22.786211],
'潮州': [116.63, 23.68],
'丹东': [124.37, 40.13],
'太仓': [121.1, 31.45],
'曲靖': [103.79, 25.51],
'烟台': [121.39, 37.52],
'福州': [119.3, 26.08],
'瓦房店': [121.979603, 39.627114],
'即墨': [120.45, 36.38],
'抚顺': [123.97, 41.97],
'玉溪': [102.52, 24.35],
'张家口': [114.87, 40.82],
'阳泉': [113.57, 37.85],
'莱州': [119.942327, 37.177017],
'湖州': [120.1, 30.86],
'汕头': [116.69, 23.39],
'昆山': [120.95, 31.39],
'宁波': [121.56, 29.86],
'湛江': [110.359377, 21.270708],
'揭阳': [116.35, 23.55],
'荣成': [122.41, 37.16],
'连云港': [119.16, 34.59],
'葫芦岛': [120.836932, 40.711052],
'常熟': [120.74, 31.64],
'东莞': [113.75, 23.04],
'河源': [114.68, 23.73],
'淮安': [119.15, 33.5],
'泰州': [119.9, 32.49],
'南宁': [108.33, 22.84],
'营口': [122.18, 40.65],
'惠州': [114.4, 23.09],
'江阴': [120.26, 31.91],
'蓬莱': [120.75, 37.8],
'韶关': [113.62, 24.84],
'嘉峪关': [98.289152, 39.77313],
'广州': [113.23, 23.16],
'延安': [109.47, 36.6],
'太原': [112.53, 37.87],
'清远': [113.01, 23.7],
'中山': [113.38, 22.52],
'昆明': [102.73, 25.04],
'寿光': [118.73, 36.86],
'盘锦': [122.070714, 41.119997],
'长治': [113.08, 36.18],
'深圳': [114.07, 22.62],
'珠海': [113.52, 22.3],
'宿迁': [118.3, 33.96],
'咸阳': [108.72, 34.36],
'铜川': [109.11, 35.09],
'平度': [119.97, 36.77],
'佛山': [113.11, 23.05],
'海口': [110.35, 20.02],
'江门': [113.06, 22.61],
'章丘': [117.53, 36.72],
'肇庆': [112.44, 23.05],
'大连': [121.62, 38.92],
'临汾': [111.5, 36.08],
'吴江': [120.63, 31.16],
'石嘴山': [106.39, 39.04],
'沈阳': [123.38, 41.8],
'苏州': [120.62, 31.32],
'茂名': [110.88, 21.68],
'嘉兴': [120.76, 30.77],
'长春': [125.35, 43.88],
'胶州': [120.03336, 36.264622],
'银川': [106.27, 38.47],
'张家港': [120.555821, 31.875428],
'三门峡': [111.19, 34.76],
'锦州': [121.15, 41.13],
'南昌': [115.89, 28.68],
'柳州': [109.4, 24.33],
'三亚': [109.511909, 18.252847],
'自贡': [104.778442, 29.33903],
'吉林': [126.57, 43.87],
'阳江': [111.95, 21.85],
'泸州': [105.39, 28.91],
'西宁': [101.74, 36.56],
'宜宾': [104.56, 29.77],
'呼和浩特': [111.65, 40.82],
'成都': [104.06, 30.67],
'大同': [113.3, 40.12],
'镇江': [119.44, 32.2],
'桂林': [110.28, 25.29],
'张家界': [110.479191, 29.117096],
'宜兴': [119.82, 31.36],
'北海': [109.12, 21.49],
'西安': [108.95, 34.27],
'金坛': [119.56, 31.74],
'东营': [118.49, 37.46],
'牡丹江': [129.58, 44.6],
'遵义': [106.9, 27.7],
'绍兴': [120.58, 30.01],
'扬州': [119.42, 32.39],
'常州': [119.95, 31.79],
'潍坊': [119.1, 36.62],
'重庆': [106.54, 29.59],
'台州': [121.420757, 28.656386],
'南京': [118.78, 32.04],
'滨州': [118.03, 37.36],
'贵阳': [106.71, 26.57],
'无锡': [120.29, 31.59],
'本溪': [123.73, 41.3],
'克拉玛依': [84.77, 45.59],
'渭南': [109.5, 34.52],
'马鞍山': [118.48, 31.56],
'宝鸡': [107.15, 34.38],
'焦作': [113.21, 35.24],
'句容': [119.16, 31.95],
'北京': [116.46, 39.92],
'徐州': [117.2, 34.26],
'衡水': [115.72, 37.72],
'包头': [110, 40.58],
'绵阳': [104.73, 31.48],
'乌鲁木齐': [87.68, 43.77],
'枣庄': [117.57, 34.86],
'杭州': [120.19, 30.26],
'淄博': [118.05, 36.78],
'鞍山': [122.85, 41.12],
'溧阳': [119.48, 31.43],
'库尔勒': [86.06, 41.68],
'安阳': [114.35, 36.1],
'开封': [114.35, 34.79],
'济南': [117, 36.65],
'德阳': [104.37, 31.13],
'温州': [120.65, 28.01],
'九江': [115.97, 29.71],
'邯郸': [114.47, 36.6],
'临安': [119.72, 30.23],
'兰州': [103.73, 36.03],
'沧州': [116.83, 38.33],
'临沂': [118.35, 35.05],
'南充': [106.110698, 30.837793],
'天津': [117.2, 39.13],
'富阳': [119.95, 30.07],
'泰安': [117.13, 36.18],
'诸暨': [120.23, 29.71],
'郑州': [113.65, 34.76],
'哈尔滨': [126.63, 45.75],
'聊城': [115.97, 36.45],
'芜湖': [118.38, 31.33],
'唐山': [118.02, 39.63],
'平顶山': [113.29, 33.75],
'邢台': [114.48, 37.05],
'德州': [116.29, 37.45],
'济宁': [116.59, 35.38],
'荆州': [112.239741, 30.335165],
'宜昌': [111.3, 30.7],
'义乌': [120.06, 29.32],
'丽水': [119.92, 28.45],
'洛阳': [112.44, 34.7],
'秦皇岛': [119.57, 39.95],
'株洲': [113.16, 27.83],
'石家庄': [114.48, 38.03],
'莱芜': [117.67, 36.19],
'常德': [111.69, 29.05],
'保定': [115.48, 38.85],
'湘潭': [112.91, 27.87],
'金华': [119.64, 29.12],
'岳阳': [113.09, 29.37],
'长沙': [113, 28.21],
'衢州': [118.88, 28.97],
'廊坊': [116.7, 39.53],
'菏泽': [115.480656, 35.23375],
'合肥': [117.27, 31.86],
'武汉': [114.31, 30.52],
'大庆': [125.03, 46.58]
}
};
},
mounted() {
setTimeout(() => {
this.initChart();
}, 1000);
},
watch: {
echartParams: {
handler() {
this.initChart();
},
deep: true
},
showEchart: {
handler() {
if (this.myEchart) {
this.myEchart.resize();
}
},
deep: true
}
},
methods: {
convertData(data) {
let res = [];
for (let i = 0; i < data.length; i++) {
let geoCoord = this.geoCoordMap[data[i].name];
if (geoCoord) {
res.push({
name: data[i].name,
value: geoCoord.concat(data[i].value)
});
}
}
return res;
},
initChart() {
let option = {}, max;
let self = this;
if (!this.myEchart && document.querySelector(`.${this.className}`)) {
this.myEchart = ECharts.init(document.querySelector(`.${this.className}`));
}
this.$nextTick(() => {
if (this.myEchart) {
this.myEchart.resize();
}
});
if (!this.echartParams.data) {
return false;
}
max = _.max(_.map(this.echartParams.data, 'value'));
option = {
title: {
text: this.echartParams.title,
left: 'center',
textStyle: {
color: '#323c48'
},
y: '15'
},
legend: {
orient: 'vertical',
type: 'scroll',
y: 'bottom',
x: 'right',
data: (function() {
var list = [], i;
for (i = 0; i <= self.echartParams.legend.length; i++) {
list.push(self.echartParams.legend[i] + '');
}
return list;
})(),
textStyle: {
color: '#323c48'
}
}
};
option = _.assign(option, this.option);
if (this.echartParams.type === '省份') {
option = _.assign({
tooltip: {
trigger: 'item'
},
visualMap: {
min: 0,
max: max,
calculable: true,
text: ['高', '低'],
inRange: {
color: ['#4CAF50', '#eac736', '#d94e5d']
},
textStyle: {
color: '#323c48'
}
},
series: [{
name: this.echartParams.legend,
type: 'map',
mapType: 'china',
roam: false,
label: {
normal: {
show: true
},
emphasis: {
show: true
}
},
data: this.echartParams.data
}]
}, option);
} else if (this.echartParams.type === '城市') {
option = _.assign({
geo: {
map: 'china',
label: {
emphasis: {
show: false
}
},
roam: true,
itemStyle: {
normal: {
areaColor: '#323c48',
borderColor: '#111'
},
emphasis: {
areaColor: '#2a333d'
}
}
},
tooltip: {
trigger: 'item',
formatter: function(params) {
return params.name + ' : ' + params.value[2];
}
},
visualMap: {
min: 0,
max: max,
calculable: true,
text: ['高', '低'],
inRange: {
color: ['#50a3ba', '#eac736', '#d94e5d']
},
textStyle: {
color: '#323c48'
}
},
series: [{
name: this.echartParams.legend,
type: 'scatter',
coordinateSystem: 'geo',
data: this.echartParams.data ? this.convertData(this.echartParams.data) : '',
symbolSize: function() {},
label: {
normal: {
formatter: '{b}',
position: 'right',
show: false
},
emphasis: {
show: true
}
},
itemStyle: {
normal: {
color: '#ddb926'
}
}
}]
}, option);
} else if (this.echartParams.type === '饼图') {
option = _.assign({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
calculable: true,
series: [{
name: this.echartParams.title,
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: this.echartParams.data
}]
}, option);
} else if (this.echartParams.type === '饼图2') {
option = _.assign({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : ({d}%)'
},
calculable: true,
series: [{
name: this.echartParams.title,
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: this.echartParams.data
}]
}, option);
} else if (this.echartParams.type === '会员等级') {
option = _.assign({
tooltip: {
trigger: 'item',
formatter: function(params) {
return params.name + '<br/>订单金额:' + params.value + '(' + params.percent + '%)' +
'<br/>订单数:' + params.data.orderCount + '(' + (100 * params.data.orderCount / params.data.totalOrderCount).toFixed(2) + '%)';
}
},
calculable: true,
series: [{
name: this.echartParams.title,
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: this.echartParams.data
}]
}, option);
}
this.myEchart.setOption(option, true);
window.addEventListener('resize', () => {
this.myEchart.resize();
});
}
}
};
</script>
<style lang="scss">
.echart {
width: 100%;
height: 650px;
}
</style>
... ...
const component = (resolve) => {
require.ensure([], () => {
let echart = require('./echart-map');
resolve(echart);
}, 'echart-map');
};
export default [
'EChartMap',
component
];
... ...
<template>
<div :class="className" class="echart" :style="echartWidth">
</div>
</template>
<script>
let ECharts = require('echarts-lib');
import _ from 'lodash';
import { setTimeout } from 'timers';
require('echarts-tooltip');
require('echarts-legend');
require('echarts-line');
require('echarts-pie');
require('echarts/theme/macarons');
export default {
name: 'EChart',
props: ['className', 'echartParams', 'bigFlag', 'echartWidth', 'showEchart'],
data() {
return {
};
},
mounted() {
setTimeout(() => {
this.initChart();
}, 1000);
},
watch: {
echartParams: {
handler() {
this.initChart();
},
deep: true
},
showEchart: {
handler() {
if (this.myEchart) {
this.myEchart.resize();
}
},
deep: true
}
},
methods: {
seriesData(opt) {
let option = {
name: opt.name,
type: opt.type ? opt.type : 'line',
data: opt.data,
smooth: false,
itemStyle: {
normal: {
label: {
show: opt.isShow ? opt.isShow : false,
position: 'top'
},
labelLine: {
show: true
}
}
}
};
if (opt.areaStyle) {
option.itemStyle.normal = _.assign(option.itemStyle.normal, opt.areaStyle);
}
if (opt.yAxisIndex) {
option.yAxisIndex = opt.yAxisIndex;
}
return option;
},
initChart() {
let yAxis = [], series = [], opt = {};
let data = [];
let self = this;
if (!this.myEchart && document.querySelector(`.${this.className}`)) {
this.myEchart = ECharts.init(document.querySelector(`.${this.className}`), 'macarons');
}
this.$nextTick(() => {
if (this.myEchart) {
this.myEchart.resize();
}
});
if (!this.echartParams.series) {
return;
}
_.each(this.echartParams.series, (item) => {
data = data.concat(item.data);
series.push(this.seriesData(item));
});
_.each(this.echartParams.yAxis, (item) => {
yAxis.push({
type: 'value',
name: item,
min: 0,
// max: _.max(data),
axisLine: {
lineStyle: {
color: '#2d2d2d',
width: 1
}
},
splitLine: {
show: false
},
axisLabel: {
formatter: '{value}' + (this.echartParams.v || ''),
lineStyle: {
color: '#2d2d2d',
width: 1
}
}
});
});
opt = {
title: {
text: this.echartParams.title,
x: 'center',
textStyle: {
color: this.echartParams.color
},
y: 'top'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
}
},
toolbox: {
borderColor: '#2d2d2d',
feature: {
}
},
legend: {
type: 'scroll',
top: 35,
data: (function() {
var list = [], i;
for (i = 0; i <= self.echartParams.legend.length; i++) {
list.push(self.echartParams.legend[i] + '');
}
return list;
})()
},
xAxis: [
{
type: 'category',
data: this.echartParams.xAxis,
axisPointer: {
type: 'shadow',
lineStyle: {
color: '#2d2d2d',
width: 1
}
}
}
],
yAxis: yAxis,
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '70',
containLabel: true
},
series: series,
textStyle: {
color: '#2d2d2d'
}
};
this.myEchart.setOption(opt, true);
window.addEventListener('resize', () => {
this.myEchart.resize();
});
setTimeout(() => {
this.myEchart.resize();
}, 500);
}
}
};
</script>
<style lang="scss">
.echart {
width: 100%;
height: 650px !important;
margin: 0 auto;
}
</style>
... ...
const component = (resolve) => {
require.ensure([], () => {
let echart = require('./echart');
resolve(echart);
}, 'echart');
};
export default [
'EChart',
component
];
... ...
// 懒加载VueHtml5Editor
import _ from 'lodash';
import MultiImage from './multi-image';
import './editor.scss';
export default [
'editor',
() => ({
component: import(/* webpackChunkName: "components.editor" */'vue-html5-editor').then(VueHtml5Editor => {
const editorInstance = new VueHtml5Editor({
hiddenModules: ['info', 'image'],
visibleModules: [
'text',
'color',
'font',
'align',
'list',
'link',
'unlink',
'tabulation',
'multi-image',
'hr',
'eraser',
'undo',
'full-screen',
'info',
],
icons: {
text: 'iconfont icon-pencil',
color: 'iconfont icon-paintbrush',
font: 'iconfont icon-font',
align: 'iconfont icon-alignjustify',
list: 'iconfont icon-list',
link: 'iconfont icon-chain',
unlink: 'iconfont icon-chainbroken',
tabulation: 'iconfont icon-table',
image: 'iconfont icon-images',
'multi-image': 'iconfont icon-images',
hr: 'iconfont icon-minus',
eraser: 'iconfont icon-eraser',
undo: 'iconfont icon-undo',
'full-screen': 'iconfont icon-arrows-alt',
},
image: {
sizeLimit: 512 * 1024,
upload: {
url: '/Api/upload/image',
headers: {},
params: {
bucket: 'goodsimg'
},
fieldName: 'file'
},
compress: null,
uploadHandler(responseText) {
let json = JSON.parse(responseText);
if (json.code === 200) {
return _.get(json, 'data.imagesList[0]', '');
}
return json.data;
}
},
language: 'zh-cn',
modules: [{
name: 'multi-image',
icon: 'fa fa-file-image-o',
i18n: '多图片上传',
show: true,
dashboard: MultiImage
}]
});
return editorInstance;
}),
})
];
... ...
.vue-html5-editor:not(.full-screen) > .content {
max-height: 500px;
}
.vue-html5-editor {
position: relative;
&.full-screen {
z-index: 1003 !important;
}
}
... ...
import Editor from './editor';
export {
Editor
};
... ...
<template>
<div>
<Modal
width="800"
v-model="showModal"
title="图片"
@on-ok="insertImage"
ok-text="保存"
class="multi-image">
<Row :gutter="16">
<Col span="10">
<Input v-model="file" placeholder="请输入地址" @on-enter="addFile"/>
</Col>
<Col span="8">
<Button type="primary" @click="addFile">确定</Button>
</Col>
</Row>
<br />
<Upload
ref="upload"
multiple
type="drag"
:data="{bucket: 'goodsimg'}"
:max-size="2048"
:format="['jpg','jpeg','png']"
:show-upload-list="false"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize"
:on-success="handleSuccess"
:on-error="handleError"
action="/Api/upload/image">
<div style="padding: 20px 0">
<Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
<p>点击或将文件拖拽到这里上传</p>
</div>
</Upload>
<div class="upload-list" v-for="item in fileList">
<template v-if="item.status === 'finished'">
<img :src="item.url">
<div class="upload-list-cover">
<Icon type="ios-eye-outline" @click.native="handleView(item)"></Icon>
<Icon type="ios-trash-outline" @click.native="handleRemove(item)"></Icon>
</div>
</template>
<template v-else>
<Progress v-if="item.showProgress" :percent="item.percentage" hide-info></Progress>
</template>
</div>
</Modal>
<Modal title="查看图片" v-model="visible">
<img :src="imgUrl" v-if="visible" style="width: 100%">
</Modal>
</div>
</template>
<script>
import _ from 'lodash';
export default {
name: 'MultiImage',
created() {
this.$watch('$parent.dashboard', () => {
this.$parent.dashboard === 'dashboard-multi-image' && (this.showModal = true);
});
},
data() {
return {
showModal: true,
fileList: [],
imgUrl: '',
file: '',
visible: false
};
},
methods: {
handleView(item) {
this.imgUrl = item.url;
this.visible = true;
},
handleRemove(file) {
// 从 upload 实例删除数据
let fileList = this.$refs.upload.fileList;
this.$refs.upload.fileList.splice(fileList.indexOf(file), 1);
},
handleFormatError(file) {
this.$Notice.warning({
title: '文件格式不正确',
desc: '文件 ' + file.name + ' 格式不正确,请上传 jpg 或 png 格式的图片。'
});
},
handleMaxSize(file) {
this.$Notice.warning({
title: '超出文件大小限制',
desc: '文件 ' + file.name + ' 太大,不能超过 2M。'
});
},
handleSuccess(response, file) {
let url = _.get(response, 'data.imagesList[0]');
if (url) {
file.url = url;
}
},
handleError(error) {
this.$Notice.error(error);
},
insertImage() {
_.each(_.reverse(this.fileList), file => {
this.$parent.execCommand('insertImage', file.url);
});
},
addFile() {
if (this.file) {
this.fileList.push({
name: '',
status: 'finished',
url: this.file,
uid: ''
});
this.file = '';
}
}
},
watch: {
showModal(val) {
if (!val) {
this.fileList = this.$refs.upload.fileList = [];
this.$parent.dashboard = null;
}
}
},
mounted() {
this.fileList = this.$refs.upload.fileList;
}
};
</script>
<style lang="scss">
.multi-image {
.upload-list {
margin-top: 20px;
display: inline-block;
width: 60px;
height: 60px;
text-align: center;
line-height: 60px;
border: 1px solid transparent;
border-radius: 4px;
overflow: hidden;
background: #fff;
position: relative;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
margin-right: 4px;
}
.upload-list img {
width: 100%;
height: 100%;
}
.upload-list-cover {
display: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.6);
}
.upload-list:hover .upload-list-cover {
display: block;
}
.upload-list-cover i {
color: #fff;
font-size: 20px;
cursor: pointer;
margin: 0 2px;
}
}
</style>
... ...
<template>
<Upload ref="upload"
:id="id"
:action="action"
:data="data"
:accept="accept"
:maxSize="maxSize"
:on-success="success"
:on-error="error"
:before-upload="beforeUpload">
<Button type="ghost" icon="ios-cloud-upload-outline">上传文件</Button>
</Upload>
</template>
<script>
import _ from 'lodash';
export default {
name: 'file-upload',
props: {
action: {
type: String,
default: '/Api/upload/image'
},
data: {
type: Object,
default: () => {
return {
bucket: 'goodsimg'
};
}
},
accept: {
type: String,
default: 'image/png,image/jpeg,image/gif,image/jpg'
},
maxSize: {
type: Number,
default: 512 * 1024
},
id: {
type: Object,
default: () => {
return {};
}
}
},
data() {
return {};
},
methods: {
success(response) {
let files = [];
if (_.get(response, 'data.imagesList.length', 0)) {
files = response.data.imagesList;
}
this.$emit('on-success', this.id, files);
},
error(error) {
this.$Message.error('上传失败');
this.$emit('on-error', this.id, error);
},
beforeUpload() {
this.$refs.upload.clearFiles();
return true;
}
}
};
</script>
... ...
<template>
<Alert class="filter-tips" v-bind="$attrs" v-on="$listeners">
<slot></slot>
</Alert>
</template>
<script>
export default {
name: 'filter-tips'
};
</script>
<style lang="scss">
.filter-tips {
display: inline-block;
margin-left: 30px;
}
</style>
... ...
<template>
<div class="iframe-box">
<iframe :src="src" frameborder="0"></iframe>
</div>
</template>
<script>
export default {
name: 'iFrame',
props: {
src: {
type: String,
default: ''
}
},
data() {
return {
};
}
};
</script>
<style lang="scss">
.iframe-box {
width: 100%;
height: 100%;
position: absolute;
top: 25px;
left: 25px;
right: 25px;
bottom: 25px;
iframe {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
</style>
... ...
import Breadcrumb from './breadcrumbs';
import Menus from './menus';
import FilterItem from './layout-filter-item';
import LayoutBody from './layout-body';
import LayoutAction from './layout-action';
import LayoutList from './layout-list';
import LayoutTab from './layout-tab';
import LayoutFilter from './layout-filter';
import LayoutPrint from './layout-print';
import ActionGroup from './action-group';
import EChart from './echart';
import EChartMap from './echart-map';
import {Editor} from './editor';
import FileUpload from './file-upload';
import DragFileUpload from './drag-file-upload';
import IFrame from './iframe';
import UploadXlsx from './upload-xlsx';
import BackTopDom from './back-top-dom';
import FilterTips from './filter-tips';
export default {
Menus,
Breadcrumb,
FilterItem,
LayoutBody,
LayoutAction,
LayoutList,
LayoutTab,
LayoutFilter,
LayoutPrint,
ActionGroup,
EChart,
Editor,
FileUpload,
DragFileUpload,
IFrame,
UploadXlsx,
BackTopDom,
FilterTips,
EChartMap
};
... ...
<template>
<div class="layout-action">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'layout-action',
data() {
return {
};
}
};
</script>
<style lang="scss">
.layout-action {
margin-bottom: 20px;
}
</style>
... ...
<template>
<div class="layout-body">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'layout-body',
};
</script>
<style lang="scss">
.layout-body {
position: relative;
width: 100%;
height: 100%;
}
</style>
... ...
<template>
<Row class="filter-item">
<Col class="label-col" span="8" v-if="label">
<label>{{label}}:</label>
</Col>
<Col class="item-col" :span="label ? 16 : 24">
<slot></slot>
</Col>
</Row>
</template>
<script>
export default {
name: 'filter-item',
props: {
label: {
type: String,
default: ''
}
},
data() {
return {
};
}
};
</script>
<style lang="scss">
.label-col {
line-height: 32px;
max-width: 80px;
padding: 0 !important;
}
.item-col {
padding: 0 !important;
input,
select,
.ivu-select,
.ivu-cascader {
max-width: 300px;
}
.ivu-date-picker {
width: 100%;
}
}
</style>
... ...
<script>
import _ from 'lodash';
export default {
name: 'layout-filter',
props: {
model: {
type: Object,
default() {
return {};
}
},
inline: {
type: Boolean,
default: false
},
gutter: {
type: Number,
default: 24
},
col: {
type: Number,
default: 4
},
noLine: {
type: Boolean,
default: false
}
},
data() {
return {
cloneModel: _.cloneDeep(this.model)
};
},
methods: {
reset() {
_.each(this.model, (val, key) => {
this.model[key] = this.cloneModel[key];
});
}
},
render(h) {
let slotChunks = _.chunk(this.$slots.default.filter(slot =>
slot.data && (slot.componentOptions.propsData.label || this.inline)), this.col);
let submitSlots = this.$slots.default.filter(slot =>
slot.data && (!slot.componentOptions.propsData.label && !this.inline));
let rowElements = _.map(slotChunks, slotChunk => {
return h('Row', {
class: {
'filter-row': true
},
props: {
gutter: this.gutter
}
}, _.map(slotChunk, slot => {
return h('Col', {
props: {
span: Math.floor(24 / this.col)
}
}, [slot]);
}));
});
_.each(submitSlots, slot => {
rowElements.push(h('Row', {
class: {
'filter-row': true
},
props: {
gutter: this.gutter
}
}, [h('Col', {
props: {
span: 24
}
}, [slot])]));
});
if (!this.noLine) {
rowElements.push(h('div', {
class: {
line: true
}
}));
}
return h('div', {
class: {
'layout-filter': true
}
}, rowElements);
}
};
</script>
<style lang="scss">
.layout-filter {
.filter-row {
margin-bottom: 20px;
}
.line {
border-top: 1px solid #eee;
margin-bottom: 20px;
}
}
</style>
... ...
<template>
<div class="layout-list">
<slot></slot>
<div class="clear-fixed"></div>
</div>
</template>
<script>
export default {
name: 'layout-list',
data() {
return {
};
}
};
</script>
<style lang="scss">
.layout-list {
.ivu-page {
float: right;
margin-top: 20px;
}
}
</style>
... ...
<template>
<div class="layout-print">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'layout-print',
data() {
return {
};
},
created() {
this.$root.$emit('layout-print');
}
};
</script>
<style lang="scss">
.layout-print {
position: relative;
width: 100%;
height: 100%;
}
</style>
... ...
<template>
<div class="layout-tab">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'layout-tab',
data() {
return {
};
}
};
</script>
<style lang="scss">
</style>
... ...
<template>
<Row class="layout-header">
<Col :span="12" class="layout-header-left">
<div class="title">
<i class="iconfont icon-alignjustify" aria-hidden="true" @click="$emit('menu-trigger')"></i>
<span v-if="!showLoading" class="name">{{userInfo.currentShop.shopName}}</span>
</div>
<ul class="notice">
<li><span>|<a href="/rules.VOL.04.pdf" target="_blank">平台规则</a></span></li>
<li><span>|<a href="/user-guide.pdf" target="_blank">使用手册</a></span></li>
<li><span>|<a href="/kpgg.docx" target="_blank">开票公告(重要)</a></span></li>
<li><span>|</span></li>
</ul>
</Col>
<Col :span="12" class="layout-header-right">
<span>你好,{{userInfo.name}}</span>
<router-link to="/user/password.html" class="update-pwd">[修改密码]</router-link>
<span v-if="showLoading" class="loading">切换中...</span>
<span>|</span>
<Dropdown @on-click="switchShop" trigger="click">
<a class="swtich-shop" href="javascript:void(0)">
[切换店铺]
</a>
<Dropdown-menu slot="list">
<Dropdown-item v-for="shop in userInfo.shops" :key="shop.shopsId" :name="shop.shopsId" :selected="userInfo.currentShop.shopsId === shop.shopsId">{{shop.shopName}}</Dropdown-item>
</Dropdown-menu>
</Dropdown>
<a class="logout" href="javascript:;" @click="logout">[退出]</a>
</Col>
</Row>
</template>
<script>
import Vue from 'vue';
import UserService from 'services/user/user-service';
export default {
name: 'layout-user-info',
data() {
return {
userInfo: this.$user,
showLoading: false
};
},
created() {
this.userService = new UserService();
},
methods: {
logout() {
Vue.logout();
},
switchShop(id) {
if (this.userInfo.currentShop.shopsId !== id) {
this.showLoading = true;
this.userService.switchShop(id).then(res => {
this.showLoading = false;
if (res.code === 200) {
this.userInfo.currentShop = this.userInfo.shops.find(shop => shop.shopsId === id);
Vue.switchShop(id);
this.$emit('shop-change', this.userInfo.currentShop);
this.$Message.success(`当前店铺切换为:${this.userInfo.currentShop.shopName}`);
} else {
this.$Message.error(`切换失败:${res.message}`);
}
});
}
}
}
};
</script>
<style lang="scss">
.layout-header {
height: 50px !important;
background: #fff;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
font-size: 14px;
line-height: 20px;
padding: 15px;
position: relative;
&.user-collapse {
height: 0 !important;
padding: 0 !important;
overflow: hidden;
.fa-bars {
position: absolute;
color: #000;
}
}
&.print-hide {
display: none;
}
.fa {
font-size: 20px;
vertical-align: middle;
margin-right: 10px;
cursor: pointer;
}
.layout-header-right {
text-align: right;
padding-right: 20px;
.name {
margin-right: 5px;
}
.loading {
color: #ccc;
}
.swtich-shop {
margin-left: 5px;
font-size: 12px;
color: #444;
}
.logout {
color: #f44545;
font-size: 12px;
margin-left: 5px;
}
}
.layout-header-left {
.title {
float: left;
min-width: 90px;
}
.notice {
float: left;
margin-left: 30px;
li {
float: left;
font-size: 15px;
}
span {
color: #ccc;
}
a {
color: #444;
margin-left: 10px;
margin-right: 10px;
}
}
}
.update-pwd {
margin-left: 5px;
font-size: 12px;
color: #444;
}
}
</style>
... ...
<template>
<div>
<Menu theme="dark" width="auto" :accordion="true" :open-names="openNames" @on-select="open" :active-name="activeName">
<Submenu :name="purview.id" v-for="purview in purviews" :key="purview.id" >
<template slot="title">
<div class="nor">
<Icon type="ios-navigate"></Icon>
{{purview.menu_name}}
</div>
<div class="collapse">
{{purview.menu_name[0]}}
</div>
</template>
<Menu-item :name="subPurview.id" v-for="subPurview in purview.sub" :key="subPurview.id">{{subPurview.menu_name}}</Menu-item>
</Submenu>
</Menu>
</div>
</template>
<script>
import _ from 'lodash';
export default {
name: 'purview-menu',
data() {
return {
purviews: [
{
menu_name: 'UFO订单',
id: '1',
sub: [
{
menu_name: '订单查询',
id: '1-1',
menu_url: '/order.html'
}
]
}
],
$purviews: [],
openNames: [],
activeName: ''
};
},
created() {
this.$purviews = this.deepList(this.purviews);
this.render(_.get(this.$router, 'history.current'));
},
methods: {
render(to) {
let path = to.path;
let menu = _.find(this.$purviews, purview => purview.menu_url === path);
if (menu) {
this.activeName = menu.id;
if (menu.pMenu) {
this.openNames = [menu.pMenu.id];
}
}
},
open(menuId) {
let menu = _.find(this.$purviews, purview => purview.id === menuId);
if (menu) {
this.$router.push(menu.menu_url);
}
},
deepList(purviews) {
let purs = [];
_.each(purviews, pur => {
let sub = pur.sub;
purs.push(pur);
if (sub && sub.length) {
let subs = this.deepList(sub);
_.each(subs, s => {
s.pMenu = pur;
});
purs = purs.concat(...subs);
}
});
return purs;
}
},
watch: {
$route(newRoute) {
this.render(newRoute);
}
}
};
</script>
<style lang="scss">
$menuBg: #495060;
$collapseMenuBg: #313540;
$collapseMenuWidth: 200px;
$collapseWidth: 50px;
$collapseLeftBorder: 2px;
@mixin submenuHover {
.ivu-menu-submenu-title {
margin-left: 0;
border-left: solid 2px #fff;
background-color: $collapseMenuBg !important;
transition: border 0s;
}
}
.ivu-menu-submenu-title {
.collapse {
display: none;
}
.nor {
display: inline-block;
}
}
.layout-menu-foot {
padding-left: 34px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 30px;
line-height: 30px;
text-align: left;
font-size: 13px;
color: #fff;
a {
color: #fff;
}
}
.menu-collapse {
.layout-logo-left {
background: none;
color: #fff;
font-size: 18px;
text-align: center;
font-style: italic;
&:before {
content: "Yoho!";
}
}
.ivu-menu-submenu {
position: relative;
margin-left: 2px;
&:hover {
.ivu-menu {
display: block !important;
}
.nor {
display: block;
}
}
&:hover,
&.ivu-menu-item-active {
@include submenuHover;
}
.ivu-menu {
background: $collapseMenuBg !important;
display: none;
position: absolute;
top: 49px;
left: $collapseWidth - $collapseLeftBorder;
z-index: 999;
width: $collapseMenuWidth;
}
}
.ivu-menu-submenu-title {
position: initial !important;
padding: 0 !important;
width: 100%;
height: $collapseWidth - $collapseLeftBorder;
justify-content: center;
align-items: center;
display: flex;
.ivu-icon-ios-arrow-down {
display: none;
}
.nor {
display: none;
position: absolute;
height: 49px;
width: $collapseMenuWidth;
left: $collapseWidth - $collapseLeftBorder;
top: 0;
background-color: $collapseMenuBg;
z-index: 2;
line-height: 49px;
padding-left: 43px;
color: #fff !important;
cursor: initial;
}
.collapse {
width: 30px;
height: 30px;
margin: 0 auto;
display: block;
line-height: 30px;
text-align: center;
font-size: 20px;
background-color: #fff;
color: #464c5b;
border-radius: 50%;
overflow: hidden;
}
}
}
</style>
... ...
<template>
<div @click="handleClick" style="display: inline-block;">
<input ref="input" type="file" accept=".xlsx" @change="handleChange">
<slot></slot>
</div>
</template>
<script>
import request from 'axios';
import _ from 'lodash';
export default {
name: 'upload-xlsx',
props: {
action: {
type: String,
default() {
return '/upload/xlsx';
}
},
onSuccess: {
type: Function,
default() {
return {};
}
},
onError: {
type: Function,
default() {
return {};
}
},
onProgress: {
type: Function,
default() {
return {};
}
},
onChange: {
type: Function,
default() {
return {};
}
},
data: {
type: Object,
default() {
return {};
}
}
},
data() {
return {
files: null
};
},
methods: {
handleClick() {
this.$refs.input.click();
},
handleChange(e) {
this.files = e.target.files;
this.onChange(this.files);
},
upload(data) {
let files = this.files;
if (!files) {
return Promise.reject();
}
let postFiles = Array.prototype.slice.call(files);
postFiles = postFiles.slice(0, 1);
if (postFiles.length === 0) {
return Promise.reject();
}
Object.assign(this.data, data);
return this.post(postFiles[0], this.data);
},
clear() {
this.files = null;
this.$refs.input.value = '';
},
post(file, params) {
let _this = this;
let config = {
onUploadProgress(processEvent) {
_this.onProgress(processEvent);
}
};
let data = new FormData();
data.append('file', file);
if (!_.isEmpty(params)) {
_.each(params, (v, k) => {
data.append(k, v);
});
}
return request.post(this.action, data, config)
.then((result) => {
_this.onSuccess(result.data);
})
.catch((err) => {
_this.onError(err);
});
}
}
};
</script>
<style lang="scss" scoped>
input[type="file"] {
display: none;
}
</style>
... ...
import Global from './global';
export default {
Global
};
... ...
export default {
dev: {
historyMode: 'history',
axiosBaseUrl: '',
axiosResponseType: 'json',
homePage: 'orders.search',
storeKeys: {
user: '_user',
}
},
production: {
historyMode: 'history',
axiosBaseUrl: '',
axiosResponseType: 'json',
homePage: 'orders.search',
storeKeys: {
user: '_user',
}
}
};
... ...
import Vue from 'vue';
import purview from './purview';
import prodImage from './prod-image';
Vue.directive('purview', purview);
Vue.directive('prod-img', prodImage);
... ...
/**
* Created by TaoHuang on 2017/5/12.
*/
import _ from 'lodash';
function sknImage({skn, sku, size}) {
let baseUrl = '/Api/platform/getRemoteImageUrlBySku';
let params = [];
if (skn) {
params.push(`skn_id=${skn}`);
}
if (sku) {
params.push(`sku_id=${sku}`);
}
params.push(size ? `size=${size}` : 'size=80x80');
params.push(`t=${new Date().getTime()}`);
return `${baseUrl}?${params.join('&')}`;
}
export default {
bind(el, binding) {
// v-prod-img.skn="{ val: 'fdsa', size: '121X123'}" skn
// v-prod-img.sku="{ val: 'fdsa', size: '121X123'}" sku
// v-prod-img.sku="row.productSkn" sku
// v-prod-img.skn="row.productSkn" skn
// v-prod-img="row.productSkn" skn
let data = null;
let key = null;
if (!_.isEmpty(binding.modifiers)) {
key = _.first(_.keys(binding.modifiers));
} else {
key = 'skn';
}
if (_.isPlainObject(binding.value)) {
data = {
[key]: binding.value.val,
size: binding.value.size
};
} else {
data = {
[key]: binding.value
};
}
el.src = sknImage(data);
}
};
... ...
import Vue from 'vue';
import _ from 'lodash';
const getPurview = name => {
let url = `/${_.split(name, '.').join('/')}`;
return _.find(Vue.$purviews, p => p.menu_url === url);
};
export default {
bind(el, binding) {
let pur = getPurview(binding.value);
if (!pur) {
let routePath = _.get(Vue.$router, 'history.current.name', '');
pur = getPurview(`${routePath}.${binding.value}`);
if (!pur) {
el.remove();
}
}
}
};
... ...
<template>
<router-view></router-view>
</template>
<script>
export default {
name: 'app'
};
</script>
... ...
<template>
<div class="captcha-box">
<div class="captcha-box-header">
<span>请将下列图片点击翻转至正确方向</span>
<a href="javascript:;" class="img-check-refresh" @click="captchaRefresh">换一批</a>
</div>
<div class="captcha-box-content">
<div class="item"
v-for="block in blocks"
:key="block.posX"
:style="{
'background-image': `url(${imgSrc})`,
'background-position': `${block.posX}px ${block.posY}px`
}"
@click="captchaClick(block)"></div>
</div>
</div>
</template>
<script>
import _ from 'lodash';
export default {
name: 'captcha-box',
data() {
return {
captchaSrc: '/Api/captcha.jpg',
random: Math.random(),
blocks: []
};
},
created() {
this.blocks = [{
val: 0,
posX: 0,
posY: 0
}, {
val: 0,
posX: -60,
posY: 0
}, {
val: 0,
posX: -120,
posY: 0
}, {
val: 0,
posX: -180,
posY: 0
}];
},
computed: {
imgSrc() {
return `${this.captchaSrc}?r=${this.random}`;
}
},
methods: {
captchaClick(block) {
block.val = (block.val + 1) % 4;
block.posY = (block.posY - 60) % 240;
let vals = _.map(this.blocks, b => {
return b.val;
});
this.$emit('change', vals);
},
captchaRefresh() {
this.random = Math.random();
}
}
};
</script>
<style lang="scss">
.captcha-box {
margin: 0 auto;
width: 270px;
}
.captcha-box-header {
color: #b0b0b0;
a {
color: #ff1901;
float: right;
}
}
.captcha-box-content {
display: flex;
.item {
flex: auto;
cursor: pointer;
margin-right: 10px;
width: 60px;
height: 60px;
overflow: hidden;
border: solid 1px #ccc;
background-size: 240px;
&:last-child {
margin-right: 0;
}
}
}
</style>
... ...
<template>
<div id="img-check-main"></div>
</template>
<script>
import UserService from 'services/user/user-service';
import 'statics/js/gt';
export default {
name: 'gee-captcha-box',
props: ['failNum'],
data() {
return {
};
},
created() {
this.userService = new UserService();
this.captchaInit();
},
watch: {
failNum() {
this._captchObj.reset();
}
},
methods: {
captchaInit() {
let that = this;
this.userService.geeCaptcha().then(function(result) {
if (result.code === 500) {
this.$Message.error('验证码加载异常');
return;
}
initGeetest && initGeetest({ // eslint-disable-line
gt: result.data.gt,
challenge: result.data.challenge,
width: '100%',
product: 'float', // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
new_captcha: result.data.new_captcha,
offline: !result.data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
}, that.initCallback);
});
},
initCallback: function(captchaObj) {
var _this = this;
_this._captchObj = captchaObj;
captchaObj.onSuccess(function() {
var validate = captchaObj.getValidate();
_this._result = [
validate.geetest_challenge,
validate.geetest_validate,
validate.geetest_seccode
];
_this.$emit('change', _this._result);
});
captchaObj.onError(function() {
_this._result = [];
});
captchaObj.onClose(function() {
_this._result = [];
});
captchaObj.appendTo(document.getElementById('img-check-main'));
}
}
};
</script>
<style lang="scss">
</style>
... ...
import Captcha from './captcha';
export {
Captcha
};
... ...
<template>
<div>
<div class="err err-401" v-if="errType === 'error.401'">没权限</div>
<div class="err err-404" v-if="errType === 'error.404'">404</div>
<div class="err err-500" v-if="errType === 'error.500'">服务器错误</div>
</div>
</template>
<script>
export default {
mounted() {
this.errType = this.$router.history.current.name;
},
data() {
return {
errType: '500'
};
}
};
</script>
<style lang="scss" scoped>
.err {
font-size: 30px;
}
</style>
... ...
const router = [{
path: '/login.html',
name: 'login',
component: () => import(/* webpackChunkName: "common" */'./login'),
meta: {
authPass: true
}
}, {
path: '/401.html',
name: 'error.401',
component: () => import(/* webpackChunkName: "common" */'./error'),
meta: {
authPass: true
}
}, {
path: '*',
name: 'error.404',
component: () => import(/* webpackChunkName: "common" */'./error'),
meta: {
authPass: true
}
}, {
path: '/500.html',
name: 'error.500',
component: () => import(/* webpackChunkName: "common" */'./error'),
meta: {
authPass: true
}
}];
export default router;
... ...
<template>
<div class="login-layout">
<div class="login-content">
<p class="login-title">
YOHO WebAPM 平台
</p>
<Card class="login-card">
<Form ref="formInline" :model="formInline" :rules="ruleInline">
<Form-item prop="user">
<Input type="text" size="large" v-model="formInline.user" placeholder="用户名" @on-enter="handleSubmit('formInline')">
<Icon type="ios-person-outline" slot="prepend"></Icon>
</Input>
</Form-item>
<Form-item prop="password">
<input type="hidden" name="pwd" id="">
<Input type="password" size="large" name="pwd" v-model="formInline.password" placeholder="密码" @on-enter="handleSubmit('formInline')">
<Icon type="ios-locked-outline" slot="prepend"></Icon>
</Input>
</Form-item>
<Form-item>
<gee-captcha v-if="isCaptcha" :failNum="failNum" @change="captchaChange"></gee-captcha>
</Form-item>
<!-- <Form-item class="login-btn">
<Button type="primary" :loading="loading" @click="handleSubmit('formInline')">
登录
</Button>
</Form-item> -->
</Form>
</Card>
</div>
</div>
</template>
<script>
import Vue from 'vue';
import {GeeCaptcha} from './components';
export default {
name: 'login',
data() {
return {
loading: false,
isCaptcha: false,
captcha: '',
failNum: 0,
formInline: {
user: '',
password: ''
},
ruleInline: {
user: [
{ required: true, message: '请填写用户名', trigger: 'blur' }
],
password: [
{ required: true, message: '请填写密码', trigger: 'blur' },
{ type: 'string', min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
]
}
};
},
created() {
this.isCaptcha = true;
// this.isCaptcha = this.$cookie.get('_captcha');
},
methods: {
handleSubmit(name) {
this.$refs[name].validate((valid) => {
if (valid) {
// if (this.isCaptcha && this.captcha.length === 0) {
// this.$Message.error('请将图形验证码拖动至正确位置');
// return;
// }
this.login(this.formInline.user, this.formInline.password, this.captcha);
} else {
this.$Message.error('表单验证失败!');
this.failNum++;
}
});
},
captchaChange(vals) {
this.captcha = vals;
this.handleSubmit('formInline');
},
login(username, password, captcha) {
this.$Loading.start();
Vue.passport.local(username, password, captcha).then((ret) => {
if (ret.needUpdate) {
this.$Message.error('你的密码过于简单,请务必修改密码');
this.$router.push('/user/password.html');
} else {
if (ret.overdueInfo) {
this.$Message.warning(ret.overdueInfo);
}
this.$router.push('/');
}
this.$Loading.finish();
}, (error) => {
this.$Loading.error();
this.$Message.error(error.message);
this.failNum++;
});
}
},
components: {
GeeCaptcha
}
};
</script>
<style lang="scss" scoped>
.login-layout {
background-color: rgb(70, 76, 91);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
.login-content {
width: 350px;
margin: 200px auto;
}
.login-title {
width: 100%;
text-align: center;
font-size: 30px;
color: #fff;
line-height: 50px;
padding-bottom: 20px;
}
.ivu-form {
width: 90%;
margin: 20px auto;
}
.login-btn {
text-align: right;
button {
width: 100%;
height: 36px;
font-size: 14px;
}
}
.login-card {
min-height: 250px;
}
}
</style>
... ...
import orders from './orders';
export default {
orders
};
... ...
<template>
<Row type="flex" class-name="layout" :class="layoutClass">
<Col :span="6" class="layout-menu-left" :class="{'print-hide': printHide}">
<purview-menu :class="menuClass"></purview-menu>
</Col>
<Col :span="18" class="layout-menu-right">
<div class="layout-breadcrumb" :class="{'print-hide': printHide}">
<breadcrumbs @menu-trigger="menuTrigger" :class="breadClass"></breadcrumbs>
</div>
<div class="layout-content" >
<router-view v-if="reload"></router-view>
</div>
<div class="layout-copy" :class="{'print-hide': printHide}">
Copyright &copy; 2018-2020 YOHO集团 All rights reserved
</div>
</Col>
</Row>
</template>
<script>
import cache from 'util/cache';
export default {
name: 'layout',
data() {
return {
printHide: false,
layoutClass: {
collapse: false
},
menuClass: {
'menu-collapse': false
},
breadClass: {
'bread-collapse': false
},
userClass: {
'user-collapse': false,
'print-hide': false
},
reload: true
};
},
created() {
this.$root.$on('layout-print', () => {
this.printHide = true;
this.userClass['print-hide'] = true;
this.layoutClass.print = true;
});
},
methods: {
menuTrigger() {
this.menuClass['menu-collapse'] = !this.menuClass['menu-collapse'];
this.userClass['user-collapse'] = !this.userClass['user-collapse'];
this.breadClass['bread-collapse'] = !this.breadClass['bread-collapse'];
this.layoutClass.collapse = !this.layoutClass.collapse;
},
shopChange() {
cache.clear();
this.reload = false;
this.$nextTick(() => {
this.reload = true;
});
},
},
watch: {
$route() {
document.querySelector('.layout-content').scrollTop = 0;
}
}
};
</script>
<style lang="scss">
$menuBg: #495060;
body {
color: #444;
}
.layout {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
min-width: 1200px;
.print-hide {
display: none;
}
&.print {
.layout-content {
overflow: auto;
}
}
&.collapse {
.layout-menu-left {
width: 50px;
min-width: initial;
}
}
.layout-menu-left {
background: $menuBg;
min-width: 170px;
max-width: 200px;
}
.layout-menu-right {
flex: auto;
display: flex;
flex-flow: column;
.layout-breadcrumb {
padding: 10px 15px;
}
.layout-content {
min-height: 200px;
padding: 25px;
padding-top: 10px;
overflow-y: scroll;
background: #fff;
border-radius: 4px;
flex: auto;
position: relative;
}
.layout-copy {
text-align: center;
padding: 5px;
color: #9ea7b4;
}
}
.clear-fixed {
clear: both;
}
}
.layout-menu-left {
transition: width 0.1s ease-in-out;
}
.layout-logo-left {
width: 90%;
height: 30px;
border-radius: 3px;
background-image: url(../statics/images/logo.png);
background-repeat: no-repeat;
margin: 15px auto;
}
.layout-ceiling-main a {
color: #9ba7b5;
}
.layout-hide-text .layout-text {
display: none;
}
.iconfont {
display: initial !important;
}
div[contenteditable] {
img {
vertical-align: bottom;
}
}
.table-header {
border-bottom: 1px solid #e3e8ee;
background-color: #f5f7f9;
td {
background-color: #f5f7f9;
white-space: nowrap;
overflow: hidden;
font-weight: bold;
text-align: center;
}
}
</style>
... ...
import search from './search';
export default {
search
};
... ...
export default {
path: '/search.html',
name: 'search',
component: () => import('./search'),
meta: {
pageName: '订单统计'
}
};
... ...
<template>
<layout-body>
<layout-filter ref="filter" :model="query">
<filter-item label="统计时间">
<Select v-model="filter.type">
<Option :value="item.value" v-for="item in query.type" :key="item.value">{{item.label}}</Option>
</Select>
</filter-item>
<filter-item label="时间选择" v-if="filter.type == 4">
<DatePicker type="datetimerange" @on-change="timeRangeChange" format="yyyy-MM-dd HH:mm:ss"
placeholder="开始时间-结束时间"
style="width: 260px"></DatePicker>
</filter-item>
</layout-filter>
<layout-list>
<Row :gutter="26">
<Col span="26">
<Table border :columns="orderColumns" :data="orderData"></Table>
</Col>
</Row>
</layout-list>
</layout-body>
</template>
<script>
import moment from 'moment';
import {orderService} from 'services/orders';
export default {
data() {
let self = this;
return {
query: {
type: [
{ value: 1, label: '当天', selected: false },
{ value: 2, label: '昨天', selected: false },
{ value: 3, label: '最近一周', selected: false },
{ value: 4, label: '自定义', selected: false },
{ value: 5, label: '全部', selected: false },
]
},
filter: {
type: 1
},
timeRange: {
startTime: 0,
endTime: 0
},
orderColumns: [
{
title: 'UID',
key: 'uid'
},
{
title: '用户名',
key: 'user_name'
},
{
title: '总计',
key: 'total'
},
{
title: '瑕疵确认',
key: 'quality_mini_fault'
},
{
title: '质检不通过',
key: 'quality_not_pass'
},
{
title: '鉴定通过(未发货)',
key: 'only_judge_pass'
},
{
title: '鉴定不通过(未发货)',
key: 'only_judge_reject'
}
],
orderData: []
};
},
created() {
this.orderService = new orderService();
},
mounted() {
this.getData();
},
watch: {
'filter.type': function(newVal) {
this.orderData = [];
this.timeRange = {};
if (newVal) {
this.getData();
}
}
},
methods: {
getData() {
//1: 全部 2: 今天 3: 昨天 4: 最近一周 5: 自定义
let params = {type: this.filter.type};
if (params.type === 4) {
let {startTime, endTime} = this.timeRange;
if (!startTime ||
!endTime ||
startTime > endTime) {
this.orderData = [];
return;
}
params.startTime = startTime;
params.endTime = endTime;
}
this.$Loading.start();
this.orderService
.getOptions(params)
.then(result => {
this.$Loading.finish();
if (result.code !== 200) {
return;
}
this.orderData = result.data || [];
});
},
timeRangeChange(range) {
if (!range[0] || !range[1]) {
this.orderData = [];
}
this.timeRange.startTime = moment(range[0]).unix();
this.timeRange.endTime = moment(range[1]).unix();
this.getData();
}
}
};
</script>
<style lang="scss" scoped>
.card-style {
margin-bottom: 20px;
min-height: 370px;
}
</style>
... ...
import axios from 'axios';
import settle from 'axios/lib/core/settle';
import cache from 'util/cache';
import crypto from 'util/crypto';
export default {
defaultAdapter: axios.defaults.adapter,
install() {
axios.defaults.adapter = config => {
if (config.cache) {
config.id = crypto.md5(`${config.url}|${JSON.stringify(config.params)}|${config.data}`);
let res = cache.get(config.id);
if (res) {
return new Promise(function(resolve, reject) {
settle(resolve, reject, res);
});
}
}
return this.defaultAdapter(config).then(res => {
if (config.cache && res.status === 200 && res.data) {
cache.set(config.id, res);
}
return res;
});
};
}
};
... ...
/**
* 插件
*/
import Router from 'vue-router';
import Promise from 'promise-polyfill';
import iView from 'iview';
import 'iview/dist/styles/iview.css';
import 'iview/dist/styles/fonts/ionicons.eot';
import 'iview/dist/styles/fonts/ionicons.svg';
import 'iview/dist/styles/fonts/ionicons.ttf';
import 'iview/dist/styles/fonts/ionicons.woff';
import '../statics/fonts/iconfont.css';
import store from 'yoho-store';
import cookie from 'yoho-cookie';
import components from '../components';
import axios from 'axios';
import config from 'config';
import _ from 'lodash';
export default {
loadGlobalComponents(Vue) {
_.each(components, componentModules => {
_.each(componentModules, component => {
if (component.length) {
Vue.component(component[0], component[1]);
} else {
Vue.component(component.name, component);
}
});
});
},
defineVueProp(Vue) {
Vue.prop = (key, value) => {
Vue[`$${key}`] = Vue.prototype[`$${key}`] = value;
};
Vue.beforeRenderHooks = [];
Vue.beforeRender = (fn) => {
Vue.beforeRenderHooks.push(fn);
};
Vue.render = opts => {
return new Promise(resolve => {
if (Vue.beforeRenderHooks.length) {
let step = index => {
if (index >= Vue.beforeRenderHooks.length) {
resolve();
} else {
Vue.beforeRenderHooks[index](() => {
step(index + 1);
});
}
};
step(0);
} else {
resolve();
}
}).then(() => {
return new Vue(Object.assign(opts, {
router: Vue.$router
}));
});
};
},
compatible() {
// 兼容IE的Function没有name属性为题,为了修复iView的bug
if (!(function f() {}).name) {
Object.defineProperty(Function.prototype, 'name', { //eslint-disable-line
get: function() {
let name = (this.toString().match(/^function\s*([^\s(]+)/) || [])[1];
Object.defineProperty(this, 'name', { value: name });
return name;
}
});
}
// 使用了webpack code spliting IE下需要promise ployfill
if (!window.Promise) {
window.Promise = Promise;
}
},
install(Vue) {
// 定义Vue全局属性
this.defineVueProp(Vue);
// 加载核心组件
this.loadGlobalComponents(Vue);
// 兼容
this.compatible();
// 加载核心插件
Vue.use(iView);
Vue.use(Router);
// 附加Vue原型属性
Vue.prop('config', config[process.env.NODE_ENV]);
Vue.prop('store', store);
Vue.prop('cookie', cookie);
// 设置axios默认参数
axios.defaults.baseURL = Vue.$config.axiosBaseUrl;
axios.defaults.responseType = Vue.$config.axiosResponseType;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.interceptors.response.use(response => {
if (response.data.code !== 200) {
iView.Message.error(response.data.message);
return Promise.reject({response});
}
if (response.status >= 200 && response.status < 300) {
return response;
}
iView.Message.error('接口异常');
return Promise.reject({response});
}, error => {
iView.Message.error('接口异常');
return Promise.reject(error);
});
}
};
... ...
import Router from 'vue-router';
import pageRoutes from '../pages';
import layout from '../pages/layout';
import common from '../pages/common';
import _ from 'lodash';
export default {
loadRouters(rous, paths, children) {
if (_.has(rous, 'path')) {
let ps = _.flattenDeep(paths).filter(p => p);
if (_.last(ps) === rous.name) {
ps = _.dropRight(ps);
}
if (!children) {
if (rous.path) {
rous.path = _.join(ps, '/') + (rous.path[0] === '/' ? '' : '/') + rous.path;
} else {
rous.path = _.join(ps, '/') + '.html';
}
}
rous.name = _.join(_.concat(ps, [rous.name]), '.');
if (rous.children) {
_.each(rous.children, child => this.loadRouters(child, [paths, child.name], true));
return [rous];
}
return [rous];
}
if (rous.length) {
return _.map(rous, r => {
return this.loadRouters(r, [paths]);
});
} else {
return _.map(rous, (rou, k) => {
return this.loadRouters(rou, [paths, k]);
});
}
},
install(Vue) {
let childRouters = _.flattenDeep(this.loadRouters(pageRoutes));
if (Vue.$config.homePage) {
let homePage = _.find(childRouters, router => router.name === Vue.$config.homePage);
homePage && (homePage.path = '/');
}
let routes = [{
path: '/',
component: layout,
children: childRouters
}];
routes = routes.concat(common);
Vue.$router = new Router({
routes: routes,
mode: Vue.$config.historyMode
});
}
};
... ...
import orderService from './order-service';
export {
orderService
};
... ...
import Service from '../service';
class OrderService extends Service {
getOptions(params) {
return this.get('/api/orders', {params});
}
}
export default OrderService;
... ...
import axios from 'axios';
class Service {
get(url, options) {
return axios.get(url, options).then(res => {
if (res.status === 200) {
return res.data;
}
return {};
});
}
post(url, data) {
return axios.post(url, data).then(res => {
if (res.status === 200) {
return res.data;
}
return {};
});
}
}
export default Service;
... ...
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1495163726758'); /* IE9*/
src: url('iconfont.eot?t=1495163726758#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('iconfont.woff?t=1495163726758') format('woff'), /* chrome, firefox */
url('iconfont.ttf?t=1495163726758') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1495163726758#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-alignjustify:before { content: "\e603"; }
.icon-table:before { content: "\e7b5"; }
.icon-minus:before { content: "\e6b5"; }
.icon-font:before { content: "\e6e2"; }
.icon-list:before { content: "\e728"; }
.icon-paintbrush:before { content: "\e751"; }
.icon-undo:before { content: "\e7f2"; }
.icon-pencil:before { content: "\e604"; }
.icon-images:before { content: "\e7ee"; }
.icon-chain:before { content: "\ea36"; }
.icon-eraser:before { content: "\ea9b"; }
.icon-chainbroken:before { content: "\e679"; }
.icon-arrows-alt:before { content: "\e700"; }
... ...
No preview for this file type
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>
Created by FontForge 20120731 at Fri May 19 11:15:26 2017
By admin
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
panose-1="2 0 6 3 0 0 0 0 0 0"
ascent="896"
descent="-128"
x-height="792"
bbox="0 -212 1096.82 887"
underline-thickness="0"
underline-position="0"
unicode-range="U+0078-EA9B"
/>
<missing-glyph
/>
<glyph glyph-name=".notdef"
/>
<glyph glyph-name=".notdef"
/>
<glyph glyph-name=".null" horiz-adv-x="0"
/>
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="341"
/>
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="alignjustify" unicode="&#xe603;"
d="M960 76v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-832q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h832q13 0 22.5 -9.5t9.5 -22.5zM960 268v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-832q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h832q13 0 22.5 -9.5
t9.5 -22.5zM960 460v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-832q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h832q13 0 22.5 -9.5t9.5 -22.5zM960 652v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-832q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h832q13 0 22.5 -9.5
t9.5 -22.5z" />
<glyph glyph-name="table" unicode="&#xe7b5;"
d="M329 26v109q0 8 -5 13.5t-13 5.5h-183q-8 0 -13 -5.5t-5 -13.5v-109q0 -8 5 -13.5t13 -5.5h183q8 0 13 5.5t5 13.5zM329 245v110q0 8 -5 13t-13 5h-183q-8 0 -13 -5t-5 -13v-110q0 -8 5 -13t13 -5h183q8 0 13 5t5 13zM622 26v109q0 8 -5.5 13.5t-13.5 5.5h-182
q-8 0 -13.5 -5.5t-5.5 -13.5v-109q0 -8 5.5 -13.5t13.5 -5.5h182q8 0 13.5 5.5t5.5 13.5zM329 465v109q0 8 -5 13.5t-13 5.5h-183q-8 0 -13 -5.5t-5 -13.5v-109q0 -8 5 -13.5t13 -5.5h183q8 0 13 5.5t5 13.5zM622 245v110q0 8 -5.5 13t-13.5 5h-182q-8 0 -13.5 -5t-5.5 -13
v-110q0 -8 5.5 -13t13.5 -5h182q8 0 13.5 5t5.5 13zM914 26v109q0 8 -5 13.5t-13 5.5h-183q-8 0 -13 -5.5t-5 -13.5v-109q0 -8 5 -13.5t13 -5.5h183q8 0 13 5.5t5 13.5zM622 465v109q0 8 -5.5 13.5t-13.5 5.5h-182q-8 0 -13.5 -5.5t-5.5 -13.5v-109q0 -8 5.5 -13.5
t13.5 -5.5h182q8 0 13.5 5.5t5.5 13.5zM914 245v110q0 8 -5 13t-13 5h-183q-8 0 -13 -5t-5 -13v-110q0 -8 5 -13t13 -5h183q8 0 13 5t5 13zM914 465v109q0 8 -5 13.5t-13 5.5h-183q-8 0 -13 -5.5t-5 -13.5v-109q0 -8 5 -13.5t13 -5.5h183q8 0 13 5.5t5 13.5zM987 647v-621
q0 -38 -26.5 -65t-64.5 -27h-768q-38 0 -64.5 27t-26.5 65v621q0 38 26.5 65t64.5 27h768q38 0 64.5 -27t26.5 -65z" />
<glyph glyph-name="minus" unicode="&#xe6b5;"
d="M914 358v-110q0 -22 -16 -38t-39 -16h-694q-23 0 -39 16t-16 38v110q0 23 16 39t39 16h694q23 0 39 -16t16 -39z" />
<glyph glyph-name="font" unicode="&#xe6e2;"
d="M451 493l-97 -258q19 0 78 -1t91 -1q11 0 33 1q-50 145 -105 259zM37 -139l1 45q13 4 32 7.5t32.5 6t28 8t25.5 16.5t18 29l135 352l160 414h73q5 -8 7 -12l117 -274q19 -45 60.5 -147.5t64.5 -156.5q9 -20 33.5 -83t41.5 -96q11 -26 20 -33q11 -8 50 -16.5t48 -11.5
q3 -22 3 -33q0 -2 3 -7.5t3 -7.5q-36 0 -108.5 4.5t-109.5 4.5q-43 0 -122.5 -4t-101.5 -4q0 24 2 44l75 16q1 0 7.5 1.5t8.5 2t8 2.5t8.5 4t6.5 4.5t5.5 6t1.5 8.5q0 9 -18 55t-41.5 101.5t-23.5 57.5l-257 1q-15 -33 -44 -112t-29 -93q0 -12 8 -21t25 -14t28 -8t32.5 -5
t23.5 -2v-33q0 -5 -1 -16q-33 0 -99.5 6t-99.5 6q-5 0 -15.5 -2.5t-12.5 -2.5q-45 -8 -107 -8h-6z" />
<glyph glyph-name="list" unicode="&#xe728;"
d="M146 62v-109q0 -8 -5.5 -13.5t-12.5 -5.5h-110q-7 0 -12.5 5.5t-5.5 13.5v109q0 8 5.5 13.5t12.5 5.5h110q7 0 12.5 -5.5t5.5 -13.5zM146 282v-110q0 -7 -5.5 -12.5t-12.5 -5.5h-110q-7 0 -12.5 5.5t-5.5 12.5v110q0 7 5.5 12.5t12.5 5.5h110q7 0 12.5 -5.5t5.5 -12.5z
M146 501v-110q0 -7 -5.5 -12.5t-12.5 -5.5h-110q-7 0 -12.5 5.5t-5.5 12.5v110q0 8 5.5 13t12.5 5h110q7 0 12.5 -5t5.5 -13zM1024 62v-109q0 -8 -5.5 -13.5t-12.5 -5.5h-768q-8 0 -13.5 5.5t-5.5 13.5v109q0 8 5.5 13.5t13.5 5.5h768q7 0 12.5 -5.5t5.5 -13.5zM146 721
v-110q0 -8 -5.5 -13t-12.5 -5h-110q-7 0 -12.5 5t-5.5 13v110q0 7 5.5 12.5t12.5 5.5h110q7 0 12.5 -5.5t5.5 -12.5zM1024 282v-110q0 -7 -5.5 -12.5t-12.5 -5.5h-768q-8 0 -13.5 5.5t-5.5 12.5v110q0 7 5.5 12.5t13.5 5.5h768q7 0 12.5 -5.5t5.5 -12.5zM1024 501v-110
q0 -7 -5.5 -12.5t-12.5 -5.5h-768q-8 0 -13.5 5.5t-5.5 12.5v110q0 8 5.5 13t13.5 5h768q7 0 12.5 -5t5.5 -13zM1024 721v-110q0 -8 -5.5 -13t-12.5 -5h-768q-8 0 -13.5 5t-5.5 13v110q0 7 5.5 12.5t13.5 5.5h768q7 0 12.5 -5.5t5.5 -12.5z" />
<glyph glyph-name="paintbrush" unicode="&#xe751;"
d="M923 812q40 0 70 -26.5t30 -66.5q0 -36 -26 -86q-190 -360 -266 -430q-55 -52 -124 -52q-72 0 -124 53t-52 125q0 73 53 121l365 331q33 31 74 31zM403 221q23 -43 61.5 -74t85.5 -44l1 -40q2 -122 -74 -198.5t-199 -76.5q-71 0 -125 26.5t-87 73t-49.5 104.5t-16.5 126
q4 -3 23.5 -17.5t35.5 -25.5t33.5 -20.5t26.5 -9.5q23 0 31 21q15 37 33.5 64t39.5 43.5t50 27t59 14.5t71 6z" />
<glyph glyph-name="undo" unicode="&#xe7f2;"
d="M951 300q0 -89 -35 -170t-94 -140t-140 -94t-170 -35q-98 0 -186.5 41.5t-151.5 116.5q-4 6 -3.5 13t4.5 12l79 79q5 5 14 5q9 -1 13 -7q42 -54 102.5 -84t128.5 -30q59 0 113 23.5t93.5 63t63 93.5t23.5 113t-23.5 113t-63 93.5t-93.5 63t-113 23.5q-56 0 -107.5 -20.5
t-91.5 -58.5l78 -79q18 -17 8 -39q-9 -23 -33 -23h-256q-15 0 -26 11t-11 26v256q0 24 23 33q22 10 39 -8l75 -73q61 57 139.5 89t162.5 32q89 0 170 -35t140 -94t94 -140t35 -170z" />
<glyph glyph-name="pencil" unicode="&#xe604;"
d="M33 811v-1022v1022zM65 811v-1022v1022zM97 811v-1022v1022zM129 811v-1022v1022zM161 811v-1022v1022zM193 811v-1022v1022zM225 811v-1022v1022zM256 811v-1022v1022zM288 811v-1022v1022zM320 811v-1022v1022zM352 811v-1022v1022zM384 811v-1022v1022zM416 811v-1022
v1022zM448 811v-1022v1022zM480 811v-1022v1022zM512 811v-1022v1022zM544 811v-1022v1022zM576 811v-1022v1022zM608 811v-1022v1022zM640 811v-1022v1022zM672 811v-1022v1022zM704 811v-1022v1022zM736 811v-1022v1022zM768 811v-1022v1022zM799 811v-1022v1022zM831 811
v-1022v1022zM863 811v-1022v1022zM895 811v-1022v1022zM927 811v-1022v1022zM959 811v-1022v1022zM991 811v-1022v1022zM1 779h1022h-1022zM1 747h1022h-1022zM1 715h1022h-1022zM1 683h1022h-1022zM1 651h1022h-1022zM1 619h1022h-1022zM1 587h1022h-1022zM1 556h1022
h-1022zM1 524h1022h-1022zM1 492h1022h-1022zM1 460h1022h-1022zM1 428h1022h-1022zM1 396h1022h-1022zM1 364h1022h-1022zM1 332h1022h-1022zM1 300h1022h-1022zM1 268h1022h-1022zM1 236h1022h-1022zM1 204h1022h-1022zM1 172h1022h-1022zM1 140h1022h-1022zM1 108h1022
h-1022zM1 76h1022h-1022zM1 44h1022h-1022zM1 13h1022h-1022zM1 -19h1022h-1022zM1 -51h1022h-1022zM1 -83h1022h-1022zM1 -115h1022h-1022zM1 -147h1022h-1022zM1 -179h1022h-1022zM863 811q66 0 113 -47t47 -113q0 -53 -32 -95l-64 -64l-223 223l64 64q42 32 95 32zM65 76
l-64 -287l287 64l591 591l-223 223zM715 448l-447 -447l-55 55l447 447z" />
<glyph glyph-name="images" unicode="&#xe7ee;"
d="M915 532l-165 8l-9 107q-2 17 -15 27.5t-31 9.5l-592 -49q-18 -1 -29 -13.5t-10 -29.5l42 -472q2 -17 15.5 -27.5t30.5 -8.5l30 2l-5 -92q0 -18 12.5 -32t32.5 -15l661 -31q19 -1 33.5 11.5t15.5 30.5l28 527q1 19 -12 32.5t-33 14.5zM205 521l-14 -270l-35 -49l-32 356
v1v1q1 8 6 13t13 6l522 43q8 0 14 -4.5t7 -11.5l0.5 -0.5l0.5 -0.5v-0.5v-0.5l6 -61l-438 21q-20 0 -34.5 -12t-15.5 -31zM873 47l-93 111l-55 65q-9 11 -21 11q-10 1 -22 -8l-33 -24q-12 -7 -20 -6q-11 0 -18 7l-3.5 3.5l-3.5 3.5l-86 98q-10 12 -27 13q-18 1 -30 -11
l-202 -218l-14 -14l1 13l13 258l7 126v1v1q5 19 24 18l408 -20l58 -3l116 -5q9 -1 15 -6.5t6 -14.5h0.5t0.5 -0.5v-0.5v-1zM746.5 287q29.5 0 50 21t20.5 50t-20.5 49.5t-50 20.5t-50 -20.5t-20.5 -49.5t20.5 -50t50 -21z" />
<glyph glyph-name="chain" unicode="&#xea36;"
d="M869 201q0 23 -16 39l-119 119q-16 16 -39 16q-24 0 -41 -18q1 -2 10.5 -11t12.5 -12.5t8.5 -11t7.5 -14.5t2 -15q0 -23 -16 -39t-39 -16q-9 0 -16 2t-14.5 7.5t-11 8.5t-12 12t-10.5 11q-19 -18 -19 -42q0 -23 16 -39l118 -118q15 -15 39 -15q22 0 38 14l84 84
q16 16 16 38h1zM467 604q0 23 -16 39l-118 118q-16 16 -39 16q-22 0 -39 -15l-83 -84q-16 -16 -16 -38q0 -23 16 -39l118 -119q16 -15 39 -15q24 0 41 18q-1 1 -10.5 10t-12.5 12.5t-8.5 11t-7.5 14.5t-2 16q0 22 16 38t39 16q9 0 16 -2t14.5 -7t11 -8.5t12 -12.5t10.5 -11
q19 18 19 42v0zM978 201.5q0 -68.5 -48 -116.5l-84 -83q-48 -48 -116 -48q-69 0 -117 49l-118 118q-47 48 -47 116q0 70 50 120l-50 50q-49 -50 -119 -50q-68 0 -116 48l-119 118q-48 48 -48 117t48 116l84 83q48 48 116 48q69 0 117 -49l118 -118q47 -47 47 -116
q0 -70 -50 -119l50 -51q49 51 119 51q68 0 116 -48l119 -119q48 -48 48 -116.5z" />
<glyph glyph-name="eraser" unicode="&#xea9b;" horiz-adv-x="1098"
d="M512 91l192 220h-439l-192 -220h439zM1091 707q8 -20 5 -41t-17 -37l-512 -586q-22 -25 -55 -25h-439q-22 0 -40 12t-27 31q-8 20 -5 41t17 37l512 586q22 25 55 25h439q22 0 40 -12t27 -31z" />
<glyph glyph-name="chainbroken" unicode="&#xe679;"
d="M287 86l-146 -147q-6 -5 -13 -5t-13 5q-5 6 -5 13.5t5 13.5l146 146q6 5 13.5 5t12.5 -5q6 -6 6 -13.5t-6 -12.5zM384 62v-183q0 -8 -5 -13t-13 -5t-13.5 5t-5.5 13v183q0 8 5.5 13.5t13.5 5.5t13 -5.5t5 -13.5zM256 190q0 -8 -5 -13t-13 -5h-183q-8 0 -13 5t-5 13
t5 13.5t13 5.5h183q8 0 13 -5.5t5 -13.5zM978 117q0 -68 -48 -116l-84 -83q-48 -48 -116 -48q-69 0 -117 49l-191 191q-12 12 -24 32l137 11l156 -157q15 -15 38.5 -15.5t39.5 14.5l84 84q16 16 16 38q0 23 -16 39l-157 157l10 137q20 -12 32 -24l192 -192q48 -49 48 -117z
M626 531l-137 -10l-156 156q-16 16 -39 16q-22 0 -39 -15l-84 -84q-16 -16 -16 -38q0 -23 16 -39l157 -156l-10 -138q-20 12 -32 24l-192 192q-48 50 -48 117q0 69 48 116l84 83q48 48 116 48q69 0 117 -49l191 -191q12 -12 24 -32zM987 483q0 -8 -5 -13t-13 -5h-183
q-8 0 -13 5t-5 13t5 13t13 5h183q8 0 13 -5t5 -13zM677 794v-183q0 -8 -5.5 -13t-13.5 -5t-13 5t-5 13v183q0 8 5 13t13 5t13.5 -5t5.5 -13zM909 707l-146 -146q-6 -5 -13 -5t-13 5q-6 6 -6 13.5t6 12.5l146 147q6 5 13 5t13 -5q5 -6 5 -13.5t-5 -13.5z" />
<glyph glyph-name="arrows-alt" unicode="&#xe700;"
d="M806 587l-203 -203l203 -203l83 82q16 18 40 8q22 -9 22 -33v-256q0 -15 -11 -26t-26 -11h-256q-24 0 -33 23q-10 22 8 39l82 83l-203 203l-203 -203l82 -83q18 -17 8 -39q-9 -23 -33 -23h-256q-15 0 -26 11t-11 26v256q0 24 23 33q22 10 39 -8l83 -82l203 203l-203 203
l-83 -82q-10 -11 -25 -11q-7 0 -14 3q-23 9 -23 33v256q0 15 11 26t26 11h256q24 0 33 -23q10 -22 -8 -39l-82 -83l203 -203l203 203l-82 83q-18 17 -8 39q9 23 33 23h256q15 0 26 -11t11 -26v-256q0 -24 -22 -33q-8 -3 -15 -3q-15 0 -25 11z" />
</font>
</defs></svg>
... ...
No preview for this file type
No preview for this file type
"v0.4.6 Geetest Inc.";
(function (window) {
"use strict";
if (typeof window === 'undefined') {
throw new Error('Geetest requires browser environment');
}
var document = window.document;
var Math = window.Math;
var head = document.getElementsByTagName("head")[0];
function _Object(obj) {
this._obj = obj;
}
_Object.prototype = {
_each: function (process) {
var _obj = this._obj;
for (var k in _obj) {
if (_obj.hasOwnProperty(k)) {
process(k, _obj[k]);
}
}
return this;
}
};
function Config(config) {
var self = this;
new _Object(config)._each(function (key, value) {
self[key] = value;
});
}
Config.prototype = {
api_server: 'api.geetest.com',
protocol: 'http://',
typePath: '/gettype.php',
fallback_config: {
slide: {
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
type: 'slide',
slide: '/static/js/geetest.0.0.0.js'
},
fullpage: {
static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
type: 'fullpage',
fullpage: '/static/js/fullpage.0.0.0.js'
}
},
_get_fallback_config: function () {
var self = this;
if (isString(self.type)) {
return self.fallback_config[self.type];
} else if (self.new_captcha) {
return self.fallback_config.fullpage;
} else {
return self.fallback_config.slide;
}
},
_extend: function (obj) {
var self = this;
new _Object(obj)._each(function (key, value) {
self[key] = value;
})
}
};
var isNumber = function (value) {
return (typeof value === 'number');
};
var isString = function (value) {
return (typeof value === 'string');
};
var isBoolean = function (value) {
return (typeof value === 'boolean');
};
var isObject = function (value) {
return (typeof value === 'object' && value !== null);
};
var isFunction = function (value) {
return (typeof value === 'function');
};
var callbacks = {};
var status = {};
var random = function () {
return parseInt(Math.random() * 10000) + (new Date()).valueOf();
};
var loadScript = function (url, cb) {
var script = document.createElement("script");
script.charset = "UTF-8";
script.async = true;
script.onerror = function () {
cb(true);
};
var loaded = false;
script.onload = script.onreadystatechange = function () {
if (!loaded &&
(!script.readyState ||
"loaded" === script.readyState ||
"complete" === script.readyState)) {
loaded = true;
setTimeout(function () {
cb(false);
}, 0);
}
};
script.src = url;
head.appendChild(script);
};
var normalizeDomain = function (domain) {
// special domain: uems.sysu.edu.cn/jwxt/geetest/
// return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
};
var normalizePath = function (path) {
path = path.replace(/\/+/g, '/');
if (path.indexOf('/') !== 0) {
path = '/' + path;
}
return path;
};
var normalizeQuery = function (query) {
if (!query) {
return '';
}
var q = '?';
new _Object(query)._each(function (key, value) {
if (isString(value) || isNumber(value) || isBoolean(value)) {
q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
}
});
if (q === '?') {
q = '';
}
return q.replace(/&$/, '');
};
var makeURL = function (protocol, domain, path, query) {
domain = normalizeDomain(domain);
var url = normalizePath(path) + normalizeQuery(query);
if (domain) {
url = protocol + domain + url;
}
return url;
};
var load = function (protocol, domains, path, query, cb) {
var tryRequest = function (at) {
var url = makeURL(protocol, domains[at], path, query);
loadScript(url, function (err) {
if (err) {
if (at >= domains.length - 1) {
cb(true);
} else {
tryRequest(at + 1);
}
} else {
cb(false);
}
});
};
tryRequest(0);
};
var jsonp = function (domains, path, config, callback) {
if (isObject(config.getLib)) {
config._extend(config.getLib);
callback(config);
return;
}
if (config.offline) {
callback(config._get_fallback_config());
return;
}
var cb = "geetest_" + random();
window[cb] = function (data) {
if (data.status == 'success') {
callback(data.data);
} else if (!data.status) {
callback(data);
} else {
callback(config._get_fallback_config());
}
window[cb] = undefined;
try {
delete window[cb];
} catch (e) {
}
};
load(config.protocol, domains, path, {
gt: config.gt,
callback: cb
}, function (err) {
if (err) {
callback(config._get_fallback_config());
}
});
};
var throwError = function (errorType, config) {
var errors = {
networkError: '网络错误',
gtTypeError: 'gt字段不是字符串类型'
};
if (typeof config.onError === 'function') {
config.onError(errors[errorType]);
} else {
throw new Error(errors[errorType]);
}
};
var detect = function () {
return window.Geetest || document.getElementById("gt_lib");
};
if (detect()) {
status.slide = "loaded";
}
window.initGeetest = function (userConfig, callback) {
var config = new Config(userConfig);
if (userConfig.https) {
config.protocol = 'https://';
} else if (!userConfig.protocol) {
config.protocol = window.location.protocol + '//';
}
// for KFC
if (userConfig.gt === '050cffef4ae57b5d5e529fea9540b0d1' ||
userConfig.gt === '3bd38408ae4af923ed36e13819b14d42') {
config.apiserver = 'yumchina.geetest.com/'; // for old js
config.api_server = 'yumchina.geetest.com';
}
if (isObject(userConfig.getType)) {
config._extend(userConfig.getType);
}
jsonp([config.api_server || config.apiserver], config.typePath, config, function (newConfig) {
var type = newConfig.type;
var init = function () {
config._extend(newConfig);
callback(new window.Geetest(config));
};
callbacks[type] = callbacks[type] || [];
var s = status[type] || 'init';
if (s === 'init') {
status[type] = 'loading';
callbacks[type].push(init);
load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
if (err) {
status[type] = 'fail';
throwError('networkError', config);
} else {
status[type] = 'loaded';
var cbs = callbacks[type];
for (var i = 0, len = cbs.length; i < len; i = i + 1) {
var cb = cbs[i];
if (isFunction(cb)) {
cb();
}
}
callbacks[type] = [];
}
});
} else if (s === "loaded") {
init();
} else if (s === "fail") {
throwError('networkError', config);
} else if (s === "loading") {
callbacks[type].push(init);
}
});
};
})(window);
\ No newline at end of file
... ...
.bg-red {
background-color: #dd4b39 !important;
}
.bg-yellow {
background-color: #f39c12 !important;
}
.bg-aqua {
background-color: #00c0ef !important;
}
.bg-blue {
background-color: #0073b7 !important;
}
.bg-light-blue {
background-color: #3c8dbc !important;
}
.bg-green {
background-color: #00a65a !important;
}
.bg-navy {
background-color: #001f3f !important;
}
.bg-teal {
background-color: #39cccc !important;
}
.bg-olive {
background-color: #3d9970 !important;
}
.bg-lime {
background-color: #01ff70 !important;
}
.bg-orange {
background-color: #ff851b !important;
}
.bg-fuchsia {
background-color: #f012be !important;
}
.bg-purple {
background-color: #605ca8 !important;
}
.bg-maroon {
background-color: #d81b60 !important;
}
.bg-gray-active {
color: #000;
background-color: #b5bbc8 !important;
}
.bg-black-active {
background-color: #000 !important;
}
.bg-red-active {
background-color: #d33724 !important;
}
.bg-yellow-active {
background-color: #db8b0b !important;
}
.bg-aqua-active {
background-color: #00a7d0 !important;
}
.bg-blue-active {
background-color: #005384 !important;
}
.bg-light-blue-active {
background-color: #357ca5 !important;
}
.bg-green-active {
background-color: #008d4c !important;
}
.bg-navy-active {
background-color: #001a35 !important;
}
.bg-teal-active {
background-color: #30bbbb !important;
}
.bg-olive-active {
background-color: #368763 !important;
}
.bg-lime-active {
background-color: #00e765 !important;
}
.bg-orange-active {
background-color: #ff7701 !important;
}
.bg-fuchsia-active {
background-color: #db0ead !important;
}
.bg-purple-active {
background-color: #555299 !important;
}
.bg-maroon-active {
background-color: #ca195a !important;
}
... ...
class DefaultCache {
constructor() {
this.store = {};
}
get(key) {
return this.store[key];
}
remove(key) {
delete this.store[key];
}
set(key, value) {
this.store[key] = value;
}
clear() {
this.store = {};
}
}
export default DefaultCache;
... ...
import DefaultCache from './default-cache';
let DbTypes = {
DEF: 'default'
};
/**
* 缓存适配器
*/
class CacheAdapter {
constructor(dbName) {
this.dbName = dbName;
if (dbName === DbTypes.DEF) {
this.db = new DefaultCache();
}
}
get(key) {
if (this.dbName === DbTypes.DEF) {
return this.db.get(key);
}
}
remove(key) {
if (this.dbName === DbTypes.DEF) {
return this.db.remove(key);
}
}
set(key, value) {
if (this.dbName === DbTypes.DEF) {
return this.db.set(key, value);
}
}
clear() {
if (this.dbName === DbTypes.DEF) {
return this.db.clear();
}
}
}
export default new CacheAdapter(DbTypes.DEF);
... ...
import cryptoAES from 'crypto-js/aes';
import encUtf8 from 'crypto-js/enc-utf8';
import cryptoMd5 from 'crypto-js/md5';
let crypto = {
key: 'd7b7ac4b491fd2b1b9e27bc2ca9bf5d0',
aesEncrypt(plainText) {
if (typeof plainText === 'object') {
plainText = encodeURIComponent(JSON.stringify(plainText));
}
return cryptoAES.encrypt(plainText, this.key).toString();
},
aesDecrypt(cipherText, Type) {
let bytes = cryptoAES.decrypt(cipherText, this.key);
let plainText = bytes.toString(encUtf8);
if (this.getType(Type) === 'Object') {
return JSON.parse(decodeURIComponent(plainText));
}
return plainText;
},
md5(plainText) {
return cryptoMd5(plainText).toString();
},
getType(fn) {
let match = fn && fn.toString().match(/^\s*function (\w+)/);
return match && match[1];
}
};
export default crypto;
... ...
export default {
replaceIllegal: (str) => {
return str.replace(/<[^<>]+>/g, '');
},
replaceScript: (str) => {
return str.replace(/<\/?script>/g, '').replace(/javascript:/g, '').replace(/src=.*?\/\/.*?\.js('|")?/g, '');
}
};
... ...
process.env.NODE_ENV = 'production'
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('./config')
var webpackConfig = require('./webpack.prod.conf')
rm(path.join(config.build.assetsRoot), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
... ...
let _ = require('lodash');
let chalk = require('chalk');
let path = require('path');
let shelljs = require('shelljs');
let fs = require('fs');
let util = require('./util');
let opn = require('opn')
let config = require('../config')
if (!process.env.npm_config_module || !process.env.npm_config_page) {
console.log(chalk.red('缺少-module或者-page参数'));
return;
}
let templateData = {
moduleName: process.env.npm_config_module,
pageName: process.env.npm_config_page
};
let codeMode = process.env.npm_config_mode || 'list';
let restPath = path.join(__dirname, '../../app/pages', `/${templateData.moduleName}/${templateData.pageName}`);
if (fs.existsSync(restPath)) {
console.log(chalk.red(`已经存在/${templateData.moduleName}/${templateData.pageName}目录结构`));
return;
}
shelljs.exec(`mkdir -p ${restPath}`); // 创建页面目录
// 递归编译模板文件并copy到restPath目录
util.complieFile(templateData, '/', codeMode, restPath) && console.log(chalk.green(`模板编译完成`));
// 模块入口文件路径
let moduleIndexPath = path.join(__dirname, '../../app/pages', `/${templateData.moduleName}/index.js`);
// 全局入口文件路径
let indexPath = path.join(__dirname, '../../app/pages', `/index.js`);
// 模块入口中添加page引用
util.complieRef(moduleIndexPath, templateData.pageName) && console.log(chalk.green(`页面引用已添加`));
// 全局入口中添加module的引用
util.complieRef(indexPath, templateData.moduleName) && console.log(chalk.green(`模块引用已添加`));
console.log(chalk.green(`创建完成`));
console.log(chalk.green(`正在打开新页面...`));
setTimeout(() => {
var uri = `http://localhost:${config.dev.port}/${templateData.moduleName}/${templateData.pageName}.html`;
opn(uri)
}, 1000);
... ...
let exportExpress = {
"type": "Property",
"method": false,
"shorthand": true,
"computed": false,
"key": {
"type": "Identifier",
},
"kind": "init",
"value": {
"type": "Identifier",
}
};
module.exports = (name) => {
exportExpress.key.name = name;
exportExpress.value.name = name;
return exportExpress;
};
\ No newline at end of file
... ...
let importExress = {
"type": "ImportDeclaration",
"specifiers": [
{
"type": "ImportDefaultSpecifier",
"local": {
"type": "Identifier",
}
}
],
"source": {
"type": "Literal",
}
};
module.exports = (name) => {
importExress.specifiers[0].local.name = name;
importExress.source.raw = importExress.source.value = `./${name}`;
return importExress;
};
\ No newline at end of file
... ...
<template>
<layout-body>
<Form :label-width="100" ref="form1" :model="modelData" :rules="ruleValidate">
<Form-item label="字段1名称:" prop="field1">
<Input v-model="modelData.field1" placeholder="请输入..." style="width: 400px;" />
</Form-item>
<Form-item label="字段2名称:" prop="field2">
<Input v-model="modelData.field2" placeholder="请输入..." style="width: 400px;" />
</Form-item>
<Form-item label="字段3:" prop="field3">
<radio-season v-model="modelData.field3"></radio-season>
</Form-item>
<Form-item label="字段4:" prop="field4">
<select-season v-model="modelData.field4"></select-season>
</Form-item>
<Form-item label="图片:" prop="img">
<div class="upload-item">
<div class="upload-item-img">
<drag-file-upload bucket="yhb-img01" @success="uploadImageSuccess" @remove="uploadImageRemove">
</drag-file-upload>
</div>
</div>
</Form-item>
<Form-item label="简介:" prop="intro">
<editor :content="modelData.intro" @change="editorChange" :z-index="2">
</editor>
</Form-item>
<Form-item>
<Button type="primary" @click="submit">保存</Button>
</Form-item>
</Form>
</layout-body>
</template>
<script>
export default {
data() {
return {
modelData: {
field1: '',
field2: '',
field3: '',
field4: '',
img: '',
intro: ''
},
ruleValidate: {
field1: [
{ required: true, message: '字段不能为空', trigger: 'blur' }
],
field2: [
{ required: true, message: '字段不能为空', trigger: 'blur' },
],
field3: [
{ required: true, message: '请选择季节', trigger: 'change' }
],
field4: [
{ required: true, message: '请选择季节', trigger: 'change' }
],
img: [
{ required: true, message: '请上传图片' },
],
intro: [
{ required: true, message: '请填写备注', trigger: 'change' }
]
}
};
},
methods: {
editorChange: function(content) {
this.modelData.intro = content;
},
uploadImageSuccess: function(attach, file) {
this.modelData.img = file.url;
},
uploadImageRemove: function() {
this.modelData.img = '';
},
submit: function() {
this.$refs.form1.validate((valid) => {
if (valid) {
this.$Message.success('提交成功!');
} else {
this.$Message.error('表单验证失败!');
}
});
}
}
};
</script>
\ No newline at end of file
... ...
export default {
path: '/#{pageName}.html',
name: '#{pageName}',
component: () => import(/* webpackChunkName: "#{moduleName}.#{pageName}" */'./#{pageName}'),
meta: {
pageName: '新建页面'
}
};
... ...
<template>
<layout-body>
<layout-filter ref="filter" :model="query">
<filter-item label="SKN编码">
<Input v-model.trim="query.sknCode" :maxlength="9"></Input>
</filter-item>
<filter-item label="商家编码">
<Input v-model.trim="query.prodCode"></Input>
</filter-item>
<filter-item label="商品名称">
<Input v-model.trim="query.prodName"></Input>
</filter-item>
<filter-item label="商品条码">
<Input v-model.trim="query.prodBarCode"></Input>
</filter-item>
<filter-item>
<Button type="primary" @click="search">筛选</Button>
<Button @click="reset">清空条件</Button>
</filter-item>
</layout-filter>
<layout-list>
<Demo></Demo>
<Table border :columns="tableCols" :data="tableData"></Table>
<Page :total="pageData.total" :current="pageData.current"
@on-change="pageChange" :page-size="20" show-total></Page>
</layout-list>
</layout-body>
</template>
<script>
import {Demo} from './components';
export default {
data() {
return {
query: {
sknCode: '',
prodCode: '',
prodName: '',
prodBarCode: '',
category: [],
brand: 0,
saleStatus: 0,
stockStatus: 0
},
pageData: {
total: 0,
current: 1,
},
tableData: [{
col1: '1',
col2: '2'
}, {
col1: '3',
col2: '4'
}, {
col1: '5',
col2: '6'
}],
tableCols: [{
title: '列1',
key: 'col1'
}, {
title: '列2',
key: 'col2'
}, {
title: '操作',
render: (h, params) => {
return (
<action-group>
<i-button type="primary" size="small" onClick={() => this.edit(params.row)}>编辑</i-button>
<i-button type="error" size="small" onClick={() => this.del(params.row)}>删除</i-button>
</action-group>
);
}
}]
};
},
methods: {
search() {
// 在这里实现异步查询的方法,建议在service中做
let params = Object.assign(this.query, this.pageData);
console.log(params);
},
reset() {
this.$refs.filter.reset();
},
pageChange() {
this.search();
},
edit() {},
del() {}
},
components: {Demo}
};
</script>
<style lang="scss">
</style>
... ...
<template>
<div>局部组件</div>
</template>
<script>
export default {
name: 'Demo',
};
</script>
... ...
import Demo from './demo';
export {
Demo
};
... ...
export default {
path: '/#{pageName}.html',
name: '#{pageName}',
component: () => import(/* webpackChunkName: "#{moduleName}.#{pageName}" */'./#{pageName}'),
meta: {
pageName: '新建页面'
}
};
... ...