作者 朱兆平

预配模块

此 diff 太大无法显示。
@@ -10,16 +10,18 @@ @@ -10,16 +10,18 @@
10 "build": "node build/build.js" 10 "build": "node build/build.js"
11 }, 11 },
12 "dependencies": { 12 "dependencies": {
13 - "axios": "^0.15.3", 13 + "axios": "^0.19.0",
14 "echarts": "^3.3.2", 14 "echarts": "^3.3.2",
15 "element-ui": "^1.2.8", 15 "element-ui": "^1.2.8",
16 "eslint": "^5.14.1", 16 "eslint": "^5.14.1",
17 "font-awesome": "^4.7.0", 17 "font-awesome": "^4.7.0",
18 "install": "^0.12.2", 18 "install": "^0.12.2",
  19 + "js-cookie": "^2.2.1",
19 "moment": "^2.24.0", 20 "moment": "^2.24.0",
20 "npm": "^6.8.0", 21 "npm": "^6.8.0",
21 "nprogress": "^0.2.0", 22 "nprogress": "^0.2.0",
22 "vue": "^2.2.2", 23 "vue": "^2.2.2",
  24 + "vue-i18n": "^8.14.0",
23 "vue-router": "^2.3.0", 25 "vue-router": "^2.3.0",
24 "vuex": "^2.0.0-rc.6" 26 "vuex": "^2.0.0-rc.6"
25 }, 27 },
@@ -45,7 +47,7 @@ @@ -45,7 +47,7 @@
45 "friendly-errors-webpack-plugin": "^1.1.3", 47 "friendly-errors-webpack-plugin": "^1.1.3",
46 "function-bind": "^1.0.2", 48 "function-bind": "^1.0.2",
47 "html-webpack-plugin": "^2.28.0", 49 "html-webpack-plugin": "^2.28.0",
48 - "http-proxy-middleware": "^0.17.3", 50 + "http-proxy-middleware": "^0.19.1",
49 "json-loader": "^0.5.4", 51 "json-loader": "^0.5.4",
50 "mockjs": "^1.0.1-beta3", 52 "mockjs": "^1.0.1-beta3",
51 "node-sass": "^4.5.0", 53 "node-sass": "^4.5.0",
@@ -62,7 +64,7 @@ @@ -62,7 +64,7 @@
62 "vue-style-loader": "^2.0.0", 64 "vue-style-loader": "^2.0.0",
63 "vue-template-compiler": "^2.2.4", 65 "vue-template-compiler": "^2.2.4",
64 "webpack": "^2.2.1", 66 "webpack": "^2.2.1",
65 - "webpack-bundle-analyzer": "^2.2.1", 67 + "webpack-bundle-analyzer": "^3.4.1",
66 "webpack-dev-middleware": "^1.10.0", 68 "webpack-dev-middleware": "^1.10.0",
67 "webpack-hot-middleware": "^2.16.1", 69 "webpack-hot-middleware": "^2.16.1",
68 "webpack-merge": "^2.6.1" 70 "webpack-merge": "^2.6.1"
@@ -75,5 +77,11 @@ @@ -75,5 +77,11 @@
75 "> 1%", 77 "> 1%",
76 "last 2 versions", 78 "last 2 versions",
77 "not ie <= 8" 79 "not ie <= 8"
78 - ] 80 + ],
  81 + "main": ".eslintrc.js",
  82 + "repository": {
  83 + "type": "git",
  84 + "url": "git@118.31.66.166:zp260/vue_cli.git"
  85 + },
  86 + "keywords": []
79 } 87 }
1 import axios from 'axios' 1 import axios from 'axios'
2 import qs from 'qs' 2 import qs from 'qs'
3 3
4 -  
5 -axios.defaults.baseURL = '/api';  
6 -  
7 -  
8 -  
9 export default { 4 export default {
10 post(url, data) { 5 post(url, data) {
11 return axios({ 6 return axios({
  1 +<template>
  2 + <div :class="{'hidden':hidden}" class="pagination-container">
  3 + <el-pagination
  4 + :background="background"
  5 + :current-page.sync="currentPage"
  6 + :page-size.sync="pageSize"
  7 + :layout="layout"
  8 + :page-sizes="pageSizes"
  9 + :total="total"
  10 + v-bind="$attrs"
  11 + @size-change="handleSizeChange"
  12 + @current-change="handleCurrentChange"/>
  13 + </div>
  14 +</template>
  15 +
  16 +<script>
  17 +import { scrollTo } from '@/utils/scrollTo'
  18 +
  19 +export default {
  20 + name: 'Pagination',
  21 + props: {
  22 + total: {
  23 + required: true,
  24 + type: Number
  25 + },
  26 + page: {
  27 + type: Number,
  28 + default: 1
  29 + },
  30 + limit: {
  31 + type: Number,
  32 + default: 20
  33 + },
  34 + pageSizes: {
  35 + type: Array,
  36 + default() {
  37 + return [10, 20, 30, 50]
  38 + }
  39 + },
  40 + layout: {
  41 + type: String,
  42 + default: 'total, sizes, prev, pager, next, jumper'
  43 + },
  44 + background: {
  45 + type: Boolean,
  46 + default: true
  47 + },
  48 + autoScroll: {
  49 + type: Boolean,
  50 + default: true
  51 + },
  52 + hidden: {
  53 + type: Boolean,
  54 + default: false
  55 + }
  56 + },
  57 + computed: {
  58 + currentPage: {
  59 + get() {
  60 + return this.page
  61 + },
  62 + set(val) {
  63 + this.$emit('update:page', val)
  64 + }
  65 + },
  66 + pageSize: {
  67 + get() {
  68 + return this.limit
  69 + },
  70 + set(val) {
  71 + this.$emit('update:limit', val)
  72 + }
  73 + }
  74 + },
  75 + methods: {
  76 + handleSizeChange(val) {
  77 + this.$emit('pagination', { page: this.currentPage, limit: val })
  78 + if (this.autoScroll) {
  79 + scrollTo(0, 800)
  80 + }
  81 + },
  82 + handleCurrentChange(val) {
  83 + this.$emit('pagination', { page: val, limit: this.pageSize })
  84 + if (this.autoScroll) {
  85 + scrollTo(0, 800)
  86 + }
  87 + }
  88 + }
  89 +}
  90 +</script>
  91 +
  92 +<style scoped>
  93 +.pagination-container {
  94 + background: #fff;
  95 + padding: 32px 16px;
  96 +}
  97 +.pagination-container.hidden {
  98 + display: none;
  99 +}
  100 +</style>
  1 +/**
  2 +* @Author: jianglei
  3 +* @Date: 2017-10-12 12:06:49
  4 +*/
  5 +'use strict'
  6 +import Vue from 'vue'
  7 +export default function treeToArray(data, expandAll, parent = null, level = null) {
  8 + let tmp = []
  9 + Array.from(data).forEach(function(record) {
  10 + if (record._expanded === undefined) {
  11 + Vue.set(record, '_expanded', expandAll)
  12 + }
  13 + let _level = 1
  14 + if (level !== undefined && level !== null) {
  15 + _level = level + 1
  16 + }
  17 + Vue.set(record, '_level', _level)
  18 + // 如果有父元素
  19 + if (parent) {
  20 + Vue.set(record, 'parent', parent)
  21 + }
  22 + tmp.push(record)
  23 + if (record.children && record.children.length > 0) {
  24 + const children = treeToArray(record.children, expandAll, record, _level)
  25 + tmp = tmp.concat(children)
  26 + }
  27 + })
  28 + return tmp
  29 +}
  1 +<template>
  2 + <el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
  3 + <el-table-column type="selection" width="30" align="center"/>
  4 + <el-table-column v-if="columns.length===0" width="30" align="center">
  5 + <template slot-scope="scope">
  6 + <span v-for="space in scope.row._level" :key="space" class="ms-tree-space"/>
  7 + <span v-if="iconShow(0,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
  8 + <i v-if="!scope.row._expanded" class="el-icon-plus"/>
  9 + <i v-else class="el-icon-minus"/>
  10 + </span>
  11 + <!--{{ scope.$index }}-->
  12 + </template>
  13 + </el-table-column>
  14 + <el-table-column v-for="(column, index) in columns" v-else :key="column.value" :label="column.text" :width="column.width">
  15 + <template slot-scope="scope">
  16 + <!-- Todo -->
  17 + <!-- eslint-disable-next-line vue/no-confusing-v-for-v-if -->
  18 + <span v-for="space in scope.row._level" v-if="index === 0" :key="space" class="ms-tree-space"/>
  19 + <span v-if="iconShow(index,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
  20 + <i v-if="!scope.row._expanded" class="el-icon-plus"/>
  21 + <i v-else class="el-icon-minus"/>
  22 + </span>
  23 + {{ scope.row[column.value] }}
  24 + </template>
  25 + </el-table-column>
  26 + <slot/>
  27 + </el-table>
  28 +</template>
  29 +
  30 +<script>
  31 +/**
  32 + Auth: Lei.j1ang
  33 + Created: 2018/1/19-13:59
  34 +*/
  35 +import treeToArray from './eval'
  36 +export default {
  37 + name: 'TreeTable',
  38 + props: {
  39 + /* eslint-disable */
  40 + data: {
  41 + type: [Array, Object],
  42 + required: true
  43 + },
  44 + columns: {
  45 + type: Array,
  46 + default: () => []
  47 + },
  48 + evalFunc: Function,
  49 + evalArgs: Array,
  50 + expandAll: {
  51 + type: Boolean,
  52 + default: true
  53 + }
  54 + },
  55 + computed: {
  56 + // 格式化数据源
  57 + formatData: function() {
  58 + let tmp
  59 + if (!Array.isArray(this.data)) {
  60 + tmp = [this.data]
  61 + } else {
  62 + tmp = this.data
  63 + }
  64 + const func = this.evalFunc || treeToArray
  65 + const args = this.evalArgs ? Array.concat([tmp, this.expandAll], this.evalArgs) : [tmp, this.expandAll]
  66 + return func.apply(null, args)
  67 + }
  68 + },
  69 + methods: {
  70 + showRow: function(row) {
  71 + const show = (row.row.parent ? (row.row.parent._expanded && row.row.parent._show) : true)
  72 + row.row._show = show
  73 + return show ? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;' : 'display:none;'
  74 + },
  75 + // 切换下级是否展开
  76 + toggleExpanded: function(trIndex) {
  77 + const record = this.formatData[trIndex]
  78 + record._expanded = !record._expanded
  79 + },
  80 + // 图标显示
  81 + iconShow(index, record) {
  82 + return (index === 0 && record.children && record.children.length > 0)
  83 + }
  84 + }
  85 +}
  86 +</script>
  87 +<style rel="stylesheet/css">
  88 + @keyframes treeTableShow {
  89 + from {opacity: 0;}
  90 + to {opacity: 1;}
  91 + }
  92 + @-webkit-keyframes treeTableShow {
  93 + from {opacity: 0;}
  94 + to {opacity: 1;}
  95 + }
  96 +</style>
  97 +
  98 +<style lang="scss" rel="stylesheet/scss" scoped>
  99 + $color-blue: #2196F3;
  100 + $space-width: 18px;
  101 + .ms-tree-space {
  102 + position: relative;
  103 + top: 1px;
  104 + display: inline-block;
  105 + font-style: normal;
  106 + font-weight: 400;
  107 + line-height: 1;
  108 + width: $space-width;
  109 + height: 14px;
  110 + &::before {
  111 + content: ""
  112 + }
  113 + }
  114 + .processContainer{
  115 + width: 100%;
  116 + height: 100%;
  117 + }
  118 + table td {
  119 + line-height: 26px;
  120 + }
  121 +
  122 + .tree-ctrl{
  123 + position: relative;
  124 + cursor: pointer;
  125 + color: $color-blue;
  126 + margin-left: -$space-width;
  127 + }
  128 +</style>
  1 +## 写在前面
  2 +此组件仅提供一个创建TreeTable的解决思路
  3 +
  4 +## prop说明
  5 +#### *data*
  6 + **必填**
  7 +
  8 + 原始数据,要求是一个数组或者对象
  9 + ```javascript
  10 + [{
  11 + key1: value1,
  12 + key2: value2,
  13 + children: [{
  14 + key1: value1
  15 + },
  16 + {
  17 + key1: value1
  18 + }]
  19 + },
  20 + {
  21 + key1: value1
  22 + }]
  23 + ```
  24 + 或者
  25 + ```javascript
  26 + {
  27 + key1: value1,
  28 + key2: value2,
  29 + children: [{
  30 + key1: value1
  31 + },
  32 + {
  33 + key1: value1
  34 + }]
  35 + }
  36 + ```
  37 +
  38 +#### columns
  39 + 列属性,要求是一个数组
  40 +
  41 + 1. text: 显示在表头的文字
  42 + 2. value: 对应data的key。treeTable将显示相应的value
  43 + 3. width: 每列的宽度,为一个数字(可选)
  44 +
  45 + 如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便
  46 +
  47 + 如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文
  48 + ```javascript
  49 + [{
  50 + value:string,
  51 + text:string,
  52 + width:number
  53 + },{
  54 + value:string,
  55 + text:string,
  56 + width:number
  57 + }]
  58 + ```
  59 +
  60 +#### expandAll
  61 + 是否默认全部展开,boolean值,默认为false
  62 +
  63 +#### evalFunc
  64 + 解析函数,function,非必须
  65 +
  66 + 如果不提供,将使用默认的[evalFunc](./eval.js)
  67 +
  68 + 如果提供了evalFunc,那么会用提供的evalFunc去解析data,并返回treeTable渲染所需要的值。如何编写一个evalFunc,请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)
  69 +
  70 +#### evalArgs
  71 + 解析函数的参数,是一个数组
  72 +
  73 + **请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开*
  74 +
  75 + 如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了
  76 +
  77 + 如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了
  78 +
  79 + 具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)`evalArgs`属性值
  80 +
  81 + ## slot
  82 + 这是一个自定义列的插槽。
  83 +
  84 + 默认情况下,treeTable只有一行行展示数据的功能。但是一般情况下,我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式,这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)[实例效果](https://panjiachen.github.io/vue-element-admin/#/table/tree-table)
  85 +
  86 + `slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示
  87 +
  88 + ## 其他
  89 + 如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue
  1 +// Inspired by https://github.com/Inndy/vue-clipboard2
  2 +const Clipboard = require('clipboard')
  3 +if (!Clipboard) {
  4 + throw new Error('you should npm install `clipboard` --save at first ')
  5 +}
  6 +
  7 +export default {
  8 + bind(el, binding) {
  9 + if (binding.arg === 'success') {
  10 + el._v_clipboard_success = binding.value
  11 + } else if (binding.arg === 'error') {
  12 + el._v_clipboard_error = binding.value
  13 + } else {
  14 + const clipboard = new Clipboard(el, {
  15 + text() { return binding.value },
  16 + action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
  17 + })
  18 + clipboard.on('success', e => {
  19 + const callback = el._v_clipboard_success
  20 + callback && callback(e) // eslint-disable-line
  21 + })
  22 + clipboard.on('error', e => {
  23 + const callback = el._v_clipboard_error
  24 + callback && callback(e) // eslint-disable-line
  25 + })
  26 + el._v_clipboard = clipboard
  27 + }
  28 + },
  29 + update(el, binding) {
  30 + if (binding.arg === 'success') {
  31 + el._v_clipboard_success = binding.value
  32 + } else if (binding.arg === 'error') {
  33 + el._v_clipboard_error = binding.value
  34 + } else {
  35 + el._v_clipboard.text = function() { return binding.value }
  36 + el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
  37 + }
  38 + },
  39 + unbind(el, binding) {
  40 + if (binding.arg === 'success') {
  41 + delete el._v_clipboard_success
  42 + } else if (binding.arg === 'error') {
  43 + delete el._v_clipboard_error
  44 + } else {
  45 + el._v_clipboard.destroy()
  46 + delete el._v_clipboard
  47 + }
  48 + }
  49 +}
  1 +import Clipboard from './clipboard'
  2 +
  3 +const install = function(Vue) {
  4 + Vue.directive('Clipboard', Clipboard)
  5 +}
  6 +
  7 +if (window.Vue) {
  8 + window.clipboard = Clipboard
  9 + Vue.use(install); // eslint-disable-line
  10 +}
  11 +
  12 +Clipboard.install = install
  13 +export default Clipboard
  1 +export default{
  2 + bind(el, binding, vnode) {
  3 + const dialogHeaderEl = el.querySelector('.el-dialog__header')
  4 + const dragDom = el.querySelector('.el-dialog')
  5 + dialogHeaderEl.style.cssText += ';cursor:move;'
  6 + dragDom.style.cssText += ';top:0px;'
  7 +
  8 + // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
  9 + const getStyle = (function() {
  10 + if (window.document.currentStyle) {
  11 + return (dom, attr) => dom.currentStyle[attr]
  12 + } else {
  13 + return (dom, attr) => getComputedStyle(dom, false)[attr]
  14 + }
  15 + })()
  16 +
  17 + dialogHeaderEl.onmousedown = (e) => {
  18 + // 鼠标按下,计算当前元素距离可视区的距离
  19 + const disX = e.clientX - dialogHeaderEl.offsetLeft
  20 + const disY = e.clientY - dialogHeaderEl.offsetTop
  21 +
  22 + const dragDomWidth = dragDom.offsetWidth
  23 + const dragDomHeight = dragDom.offsetHeight
  24 +
  25 + const screenWidth = document.body.clientWidth
  26 + const screenHeight = document.body.clientHeight
  27 +
  28 + const minDragDomLeft = dragDom.offsetLeft
  29 + const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
  30 +
  31 + const minDragDomTop = dragDom.offsetTop
  32 + const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
  33 +
  34 + // 获取到的值带px 正则匹配替换
  35 + let styL = getStyle(dragDom, 'left')
  36 + let styT = getStyle(dragDom, 'top')
  37 +
  38 + if (styL.includes('%')) {
  39 + styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
  40 + styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
  41 + } else {
  42 + styL = +styL.replace(/\px/g, '')
  43 + styT = +styT.replace(/\px/g, '')
  44 + }
  45 +
  46 + document.onmousemove = function(e) {
  47 + // 通过事件委托,计算移动的距离
  48 + let left = e.clientX - disX
  49 + let top = e.clientY - disY
  50 +
  51 + // 边界处理
  52 + if (-(left) > minDragDomLeft) {
  53 + left = -minDragDomLeft
  54 + } else if (left > maxDragDomLeft) {
  55 + left = maxDragDomLeft
  56 + }
  57 +
  58 + if (-(top) > minDragDomTop) {
  59 + top = -minDragDomTop
  60 + } else if (top > maxDragDomTop) {
  61 + top = maxDragDomTop
  62 + }
  63 +
  64 + // 移动当前元素
  65 + dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
  66 +
  67 + // emit onDrag event
  68 + vnode.child.$emit('dragDialog')
  69 + }
  70 +
  71 + document.onmouseup = function(e) {
  72 + document.onmousemove = null
  73 + document.onmouseup = null
  74 + }
  75 + }
  76 + }
  77 +}
  1 +import drag from './drag'
  2 +
  3 +const install = function(Vue) {
  4 + Vue.directive('el-drag-dialog', drag)
  5 +}
  6 +
  7 +if (window.Vue) {
  8 + window['el-drag-dialog'] = drag
  9 + Vue.use(install); // eslint-disable-line
  10 +}
  11 +
  12 +drag.install = install
  13 +export default drag
  1 +import permission from './permission'
  2 +
  3 +const install = function(Vue) {
  4 + Vue.directive('permission', permission)
  5 +}
  6 +
  7 +if (window.Vue) {
  8 + window['permission'] = permission
  9 + Vue.use(install); // eslint-disable-line
  10 +}
  11 +
  12 +permission.install = install
  13 +export default permission
  1 +
  2 +import store from '@/store'
  3 +
  4 +export default{
  5 + inserted(el, binding, vnode) {
  6 + const { value } = binding
  7 + const roles = store.getters && store.getters.roles
  8 +
  9 + if (value && value instanceof Array && value.length > 0) {
  10 + const permissionRoles = value
  11 +
  12 + const hasPermission = roles.some(role => {
  13 + return permissionRoles.includes(role)
  14 + })
  15 +
  16 + if (!hasPermission) {
  17 + el.parentNode && el.parentNode.removeChild(el)
  18 + }
  19 + } else {
  20 + throw new Error(`need roles! Like v-permission="['admin','editor']"`)
  21 + }
  22 + }
  23 +}
  1 +const vueSticky = {}
  2 +let listenAction
  3 +vueSticky.install = Vue => {
  4 + Vue.directive('sticky', {
  5 + inserted(el, binding) {
  6 + const params = binding.value || {}
  7 + const stickyTop = params.stickyTop || 0
  8 + const zIndex = params.zIndex || 1000
  9 + const elStyle = el.style
  10 +
  11 + elStyle.position = '-webkit-sticky'
  12 + elStyle.position = 'sticky'
  13 + // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
  14 + // if (~elStyle.position.indexOf('sticky')) {
  15 + // elStyle.top = `${stickyTop}px`;
  16 + // elStyle.zIndex = zIndex;
  17 + // return
  18 + // }
  19 + const elHeight = el.getBoundingClientRect().height
  20 + const elWidth = el.getBoundingClientRect().width
  21 + elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
  22 +
  23 + const parentElm = el.parentNode || document.documentElement
  24 + const placeholder = document.createElement('div')
  25 + placeholder.style.display = 'none'
  26 + placeholder.style.width = `${elWidth}px`
  27 + placeholder.style.height = `${elHeight}px`
  28 + parentElm.insertBefore(placeholder, el)
  29 +
  30 + let active = false
  31 +
  32 + const getScroll = (target, top) => {
  33 + const prop = top ? 'pageYOffset' : 'pageXOffset'
  34 + const method = top ? 'scrollTop' : 'scrollLeft'
  35 + let ret = target[prop]
  36 + if (typeof ret !== 'number') {
  37 + ret = window.document.documentElement[method]
  38 + }
  39 + return ret
  40 + }
  41 +
  42 + const sticky = () => {
  43 + if (active) {
  44 + return
  45 + }
  46 + if (!elStyle.height) {
  47 + elStyle.height = `${el.offsetHeight}px`
  48 + }
  49 +
  50 + elStyle.position = 'fixed'
  51 + elStyle.width = `${elWidth}px`
  52 + placeholder.style.display = 'inline-block'
  53 + active = true
  54 + }
  55 +
  56 + const reset = () => {
  57 + if (!active) {
  58 + return
  59 + }
  60 +
  61 + elStyle.position = ''
  62 + placeholder.style.display = 'none'
  63 + active = false
  64 + }
  65 +
  66 + const check = () => {
  67 + const scrollTop = getScroll(window, true)
  68 + const offsetTop = el.getBoundingClientRect().top
  69 + if (offsetTop < stickyTop) {
  70 + sticky()
  71 + } else {
  72 + if (scrollTop < elHeight + stickyTop) {
  73 + reset()
  74 + }
  75 + }
  76 + }
  77 + listenAction = () => {
  78 + check()
  79 + }
  80 +
  81 + window.addEventListener('scroll', listenAction)
  82 + },
  83 +
  84 + unbind() {
  85 + window.removeEventListener('scroll', listenAction)
  86 + }
  87 + })
  88 +}
  89 +
  90 +export default vueSticky
  91 +
  1 +import waves from './waves'
  2 +
  3 +const install = function(Vue) {
  4 + Vue.directive('waves', waves)
  5 +}
  6 +
  7 +if (window.Vue) {
  8 + window.waves = waves
  9 + Vue.use(install); // eslint-disable-line
  10 +}
  11 +
  12 +waves.install = install
  13 +export default waves
  1 +.waves-ripple {
  2 + position: absolute;
  3 + border-radius: 100%;
  4 + background-color: rgba(0, 0, 0, 0.15);
  5 + background-clip: padding-box;
  6 + pointer-events: none;
  7 + -webkit-user-select: none;
  8 + -moz-user-select: none;
  9 + -ms-user-select: none;
  10 + user-select: none;
  11 + -webkit-transform: scale(0);
  12 + -ms-transform: scale(0);
  13 + transform: scale(0);
  14 + opacity: 1;
  15 +}
  16 +
  17 +.waves-ripple.z-active {
  18 + opacity: 0;
  19 + -webkit-transform: scale(2);
  20 + -ms-transform: scale(2);
  21 + transform: scale(2);
  22 + -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
  23 + transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
  24 + transition: opacity 1.2s ease-out, transform 0.6s ease-out;
  25 + transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
  26 +}
  1 +import './waves.css'
  2 +
  3 +export default{
  4 + bind(el, binding) {
  5 + el.addEventListener('click', e => {
  6 + const customOpts = Object.assign({}, binding.value)
  7 + const opts = Object.assign({
  8 + ele: el, // 波纹作用元素
  9 + type: 'hit', // hit 点击位置扩散 center中心点扩展
  10 + color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
  11 + }, customOpts)
  12 + const target = opts.ele
  13 + if (target) {
  14 + target.style.position = 'relative'
  15 + target.style.overflow = 'hidden'
  16 + const rect = target.getBoundingClientRect()
  17 + let ripple = target.querySelector('.waves-ripple')
  18 + if (!ripple) {
  19 + ripple = document.createElement('span')
  20 + ripple.className = 'waves-ripple'
  21 + ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
  22 + target.appendChild(ripple)
  23 + } else {
  24 + ripple.className = 'waves-ripple'
  25 + }
  26 + switch (opts.type) {
  27 + case 'center':
  28 + ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
  29 + ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
  30 + break
  31 + default:
  32 + ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px'
  33 + ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'
  34 + }
  35 + ripple.style.backgroundColor = opts.color
  36 + ripple.className = 'waves-ripple z-active'
  37 + return false
  38 + }
  39 + }, false)
  40 + }
  41 +}
  42 +
  1 +export default {
  2 + route: {
  3 + dashboard: 'Dashboard',
  4 + introduction: 'Introduction',
  5 + documentation: 'Documentation',
  6 + guide: 'Guide',
  7 + permission: 'Permission',
  8 + pagePermission: 'Page Permission',
  9 + directivePermission: 'Directive Permission',
  10 + icons: 'Icons',
  11 + components: 'Components',
  12 + componentIndex: 'Introduction',
  13 + tinymce: 'Tinymce',
  14 + markdown: 'Markdown',
  15 + jsonEditor: 'JSON Editor',
  16 + dndList: 'Dnd List',
  17 + splitPane: 'SplitPane',
  18 + avatarUpload: 'Avatar Upload',
  19 + dropzone: 'Dropzone',
  20 + sticky: 'Sticky',
  21 + countTo: 'CountTo',
  22 + componentMixin: 'Mixin',
  23 + backToTop: 'BackToTop',
  24 + dragDialog: 'Drag Dialog',
  25 + dragSelect: 'Drag Select',
  26 + dragKanban: 'Drag Kanban',
  27 + charts: 'Charts',
  28 + keyboardChart: 'Keyboard Chart',
  29 + lineChart: 'Line Chart',
  30 + mixChart: 'Mix Chart',
  31 + example: 'Example',
  32 + nested: 'Nested Routes',
  33 + menu1: 'Menu 1',
  34 + 'menu1-1': 'Menu 1-1',
  35 + 'menu1-2': 'Menu 1-2',
  36 + 'menu1-2-1': 'Menu 1-2-1',
  37 + 'menu1-2-2': 'Menu 1-2-2',
  38 + 'menu1-3': 'Menu 1-3',
  39 + menu2: 'Menu 2',
  40 + Table: 'Table',
  41 + dynamicTable: 'Dynamic Table',
  42 + dragTable: 'Drag Table',
  43 + inlineEditTable: 'Inline Edit',
  44 + complexTable: 'Complex Table',
  45 + treeTable: 'Tree Table',
  46 + customTreeTable: 'Custom TreeTable',
  47 + tab: 'Tab',
  48 + form: 'Form',
  49 + createArticle: 'Create Article',
  50 + editArticle: 'Edit Article',
  51 + articleList: 'Article List',
  52 + errorPages: 'Error Pages',
  53 + page401: '401',
  54 + page404: '404',
  55 + errorLog: 'Error Log',
  56 + excel: 'Excel',
  57 + exportExcel: 'Export Excel',
  58 + selectExcel: 'Export Selected',
  59 + uploadExcel: 'Upload Excel',
  60 + zip: 'Zip',
  61 + pdf: 'PDF',
  62 + exportZip: 'Export Zip',
  63 + theme: 'Theme',
  64 + clipboardDemo: 'Clipboard',
  65 + i18n: 'I18n',
  66 + externalLink: 'External Link'
  67 + },
  68 + navbar: {
  69 + logOut: 'Log Out',
  70 + dashboard: 'Dashboard',
  71 + github: 'Github',
  72 + theme: 'Theme',
  73 + size: 'Global Size'
  74 + },
  75 + login: {
  76 + title: 'Login Form',
  77 + logIn: 'Log in',
  78 + username: 'Username',
  79 + password: 'Password',
  80 + any: 'any',
  81 + thirdparty: 'Or connect with',
  82 + thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
  83 + },
  84 + documentation: {
  85 + documentation: 'Documentation',
  86 + github: 'Github Repository'
  87 + },
  88 + permission: {
  89 + roles: 'Your roles',
  90 + switchRoles: 'Switch roles',
  91 + tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.'
  92 + },
  93 + guide: {
  94 + description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
  95 + button: 'Show Guide'
  96 + },
  97 + components: {
  98 + documentation: 'Documentation',
  99 + tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
  100 + dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
  101 + stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
  102 + backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
  103 + backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
  104 + imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
  105 + },
  106 + table: {
  107 + dynamicTips1: 'Fixed header, sorted by header order',
  108 + dynamicTips2: 'Not fixed header, sorted by click order',
  109 + dragTips1: 'The default order',
  110 + dragTips2: 'The after dragging order',
  111 + title: 'Title',
  112 + importance: 'Imp',
  113 + type: 'Type',
  114 + remark: 'Remark',
  115 + search: 'Search',
  116 + add: 'Add',
  117 + export: 'Export',
  118 + reviewer: 'reviewer',
  119 + id: 'ID',
  120 + date: 'Date',
  121 + author: 'Author',
  122 + readings: 'Readings',
  123 + status: 'Status',
  124 + actions: 'Actions',
  125 + edit: 'Edit',
  126 + publish: 'Publish',
  127 + draft: 'Draft',
  128 + delete: 'Delete',
  129 + cancel: 'Cancel',
  130 + confirm: 'Confirm'
  131 + },
  132 + errorLog: {
  133 + tips: 'Please click the bug icon in the upper right corner',
  134 + description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
  135 + documentation: 'Document introduction'
  136 + },
  137 + excel: {
  138 + export: 'Export',
  139 + selectedExport: 'Export Selected Items',
  140 + placeholder: 'Please enter the file name(default excel-list)'
  141 + },
  142 + zip: {
  143 + export: 'Export',
  144 + placeholder: 'Please enter the file name(default file)'
  145 + },
  146 + pdf: {
  147 + tips: 'Here we use window.print() to implement the feature of downloading pdf.'
  148 + },
  149 + theme: {
  150 + change: 'Change Theme',
  151 + documentation: 'Theme documentation',
  152 + tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
  153 + },
  154 + tagsView: {
  155 + refresh: 'Refresh',
  156 + close: 'Close',
  157 + closeOthers: 'Close Others',
  158 + closeAll: 'Close All'
  159 + }
  160 +}
  1 +export default {
  2 + route: {
  3 + dashboard: 'Panel de control',
  4 + introduction: 'Introducción',
  5 + documentation: 'Documentación',
  6 + guide: 'Guía',
  7 + permission: 'Permisos',
  8 + pagePermission: 'Permisos de la página',
  9 + directivePermission: 'Permisos de la directiva',
  10 + icons: 'Iconos',
  11 + components: 'Componentes',
  12 + componentIndex: 'Introducción',
  13 + tinymce: 'Tinymce',
  14 + markdown: 'Markdown',
  15 + jsonEditor: 'Editor JSON',
  16 + dndList: 'Lista Dnd',
  17 + splitPane: 'Panel dividido',
  18 + avatarUpload: 'Subir avatar',
  19 + dropzone: 'Subir ficheros',
  20 + sticky: 'Sticky',
  21 + countTo: 'CountTo',
  22 + componentMixin: 'Mixin',
  23 + backToTop: 'Ir arriba',
  24 + dragDialog: 'Drag Dialog',
  25 + dragSelect: 'Drag Select',
  26 + dragKanban: 'Drag Kanban',
  27 + charts: 'Gráficos',
  28 + keyboardChart: 'Keyboard Chart',
  29 + lineChart: 'Gráfico de líneas',
  30 + mixChart: 'Mix Chart',
  31 + example: 'Ejemplo',
  32 + nested: 'Rutas anidadass',
  33 + menu1: 'Menu 1',
  34 + 'menu1-1': 'Menu 1-1',
  35 + 'menu1-2': 'Menu 1-2',
  36 + 'menu1-2-1': 'Menu 1-2-1',
  37 + 'menu1-2-2': 'Menu 1-2-2',
  38 + 'menu1-3': 'Menu 1-3',
  39 + menu2: 'Menu 2',
  40 + Table: 'Tabla',
  41 + dynamicTable: 'Tabla dinámica',
  42 + dragTable: 'Arrastrar tabla',
  43 + inlineEditTable: 'Editor',
  44 + complexTable: 'Complex Table',
  45 + treeTable: 'Tree Table',
  46 + customTreeTable: 'Custom TreeTable',
  47 + tab: 'Pestaña',
  48 + form: 'Formulario',
  49 + createArticle: 'Crear artículo',
  50 + editArticle: 'Editar artículo',
  51 + articleList: 'Listado de artículos',
  52 + errorPages: 'Páginas de error',
  53 + page401: '401',
  54 + page404: '404',
  55 + errorLog: 'Registro de errores',
  56 + excel: 'Excel',
  57 + exportExcel: 'Exportar a Excel',
  58 + selectExcel: 'Export seleccionado',
  59 + uploadExcel: 'Subir Excel',
  60 + zip: 'Zip',
  61 + pdf: 'PDF',
  62 + exportZip: 'Exportar a Zip',
  63 + theme: 'Tema',
  64 + clipboardDemo: 'Clipboard',
  65 + i18n: 'I18n',
  66 + externalLink: 'Enlace externo'
  67 + },
  68 + navbar: {
  69 + logOut: 'Salir',
  70 + dashboard: 'Panel de control',
  71 + github: 'Github',
  72 + theme: 'Tema',
  73 + size: 'Tamaño global'
  74 + },
  75 + login: {
  76 + title: 'Formulario de acceso',
  77 + logIn: 'Acceso',
  78 + username: 'Usuario',
  79 + password: 'Contraseña',
  80 + any: 'nada',
  81 + thirdparty: 'Conectar con',
  82 + thirdpartyTips: 'No se puede simular en local, así que combine su propia simulación de negocios. ! !'
  83 + },
  84 + documentation: {
  85 + documentation: 'Documentación',
  86 + github: 'Repositorio Github'
  87 + },
  88 + permission: {
  89 + roles: 'Tus permisos',
  90 + switchRoles: 'Cambiar permisos',
  91 + tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.'
  92 + },
  93 + guide: {
  94 + description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
  95 + button: 'Ver guía'
  96 + },
  97 + components: {
  98 + documentation: 'Documentación',
  99 + tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
  100 + dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
  101 + stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
  102 + backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
  103 + backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
  104 + imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
  105 + },
  106 + table: {
  107 + dynamicTips1: 'Fixed header, sorted by header order',
  108 + dynamicTips2: 'Not fixed header, sorted by click order',
  109 + dragTips1: 'Orden por defecto',
  110 + dragTips2: 'The after dragging order',
  111 + title: 'Título',
  112 + importance: 'Importancia',
  113 + type: 'Tipo',
  114 + remark: 'Remark',
  115 + search: 'Buscar',
  116 + add: 'Añadir',
  117 + export: 'Exportar',
  118 + reviewer: 'reviewer',
  119 + id: 'ID',
  120 + date: 'Fecha',
  121 + author: 'Autor',
  122 + readings: 'Lector',
  123 + status: 'Estado',
  124 + actions: 'Acciones',
  125 + edit: 'Editar',
  126 + publish: 'Publicar',
  127 + draft: 'Draft',
  128 + delete: 'Eliminar',
  129 + cancel: 'Cancelar',
  130 + confirm: 'Confirmar'
  131 + },
  132 + errorLog: {
  133 + tips: 'Please click the bug icon in the upper right corner',
  134 + description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
  135 + documentation: 'Documento de introducción'
  136 + },
  137 + excel: {
  138 + export: 'Exportar',
  139 + selectedExport: 'Exportar seleccionados',
  140 + placeholder: 'Por favor escribe un nombre de fichero'
  141 + },
  142 + zip: {
  143 + export: 'Exportar',
  144 + placeholder: 'Por favor escribe un nombre de fichero'
  145 + },
  146 + pdf: {
  147 + tips: 'Here we use window.print() to implement the feature of downloading pdf.'
  148 + },
  149 + theme: {
  150 + change: 'Cambiar tema',
  151 + documentation: 'Documentación del tema',
  152 + tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
  153 + },
  154 + tagsView: {
  155 + refresh: 'Actualizar',
  156 + close: 'Cerrar',
  157 + closeOthers: 'Cerrar otros',
  158 + closeAll: 'Cerrar todos'
  159 + }
  160 +}
  1 +import Vue from 'vue'
  2 +import VueI18n from 'vue-i18n'
  3 +import Cookies from 'js-cookie'
  4 +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
  5 +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
  6 +import elementEsLocale from 'element-ui/lib/locale/lang/es'// element-ui lang
  7 +import enLocale from './en'
  8 +import zhLocale from './zh'
  9 +import esLocale from './es'
  10 +
  11 +Vue.use(VueI18n)
  12 +
  13 +const messages = {
  14 + en: {
  15 + ...enLocale,
  16 + ...elementEnLocale
  17 + },
  18 + zh: {
  19 + ...zhLocale,
  20 + ...elementZhLocale
  21 + },
  22 + es: {
  23 + ...esLocale,
  24 + ...elementEsLocale
  25 + }
  26 +}
  27 +
  28 +const i18n = new VueI18n({
  29 + // set locale
  30 + // options: en | zh | es
  31 + locale: Cookies.get('language') || 'en',
  32 + // set locale messages
  33 + messages
  34 +})
  35 +
  36 +export default i18n
  1 +export default {
  2 + route: {
  3 + dashboard: '首页',
  4 + introduction: '简述',
  5 + documentation: '文档',
  6 + guide: '引导页',
  7 + permission: '权限测试页',
  8 + pagePermission: '页面权限',
  9 + directivePermission: '指令权限',
  10 + icons: '图标',
  11 + components: '组件',
  12 + componentIndex: '介绍',
  13 + tinymce: '富文本编辑器',
  14 + markdown: 'Markdown',
  15 + jsonEditor: 'JSON编辑器',
  16 + dndList: '列表拖拽',
  17 + splitPane: 'Splitpane',
  18 + avatarUpload: '头像上传',
  19 + dropzone: 'Dropzone',
  20 + sticky: 'Sticky',
  21 + countTo: 'CountTo',
  22 + componentMixin: '小组件',
  23 + backToTop: '返回顶部',
  24 + dragDialog: '拖拽 Dialog',
  25 + dragSelect: '拖拽 Select',
  26 + dragKanban: '可拖拽看板',
  27 + charts: '图表',
  28 + keyboardChart: '键盘图表',
  29 + lineChart: '折线图',
  30 + mixChart: '混合图表',
  31 + example: '综合实例',
  32 + nested: '路由嵌套',
  33 + menu1: '菜单1',
  34 + 'menu1-1': '菜单1-1',
  35 + 'menu1-2': '菜单1-2',
  36 + 'menu1-2-1': '菜单1-2-1',
  37 + 'menu1-2-2': '菜单1-2-2',
  38 + 'menu1-3': '菜单1-3',
  39 + menu2: '菜单2',
  40 + Table: 'Table',
  41 + dynamicTable: '动态Table',
  42 + dragTable: '拖拽Table',
  43 + inlineEditTable: 'Table内编辑',
  44 + complexTable: '综合Table',
  45 + treeTable: '树形表格',
  46 + customTreeTable: '自定义树表',
  47 + tab: 'Tab',
  48 + form: '表单',
  49 + createArticle: '创建文章',
  50 + editArticle: '编辑文章',
  51 + articleList: '文章列表',
  52 + errorPages: '错误页面',
  53 + page401: '401',
  54 + page404: '404',
  55 + errorLog: '错误日志',
  56 + excel: 'Excel',
  57 + exportExcel: 'Export Excel',
  58 + selectExcel: 'Export Selected',
  59 + uploadExcel: 'Upload Excel',
  60 + zip: 'Zip',
  61 + pdf: 'PDF',
  62 + exportZip: 'Export Zip',
  63 + theme: '换肤',
  64 + clipboardDemo: 'Clipboard',
  65 + i18n: '国际化',
  66 + externalLink: '外链'
  67 + },
  68 + navbar: {
  69 + logOut: '退出登录',
  70 + dashboard: '首页',
  71 + github: '项目地址',
  72 + theme: '换肤',
  73 + size: '布局大小'
  74 + },
  75 + login: {
  76 + title: '系统登录',
  77 + logIn: '登录',
  78 + username: '账号',
  79 + password: '密码',
  80 + any: '随便填',
  81 + thirdparty: '第三方登录',
  82 + thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
  83 + },
  84 + documentation: {
  85 + documentation: '文档',
  86 + github: 'Github 地址'
  87 + },
  88 + permission: {
  89 + roles: '你的权限',
  90 + switchRoles: '切换权限',
  91 + tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。'
  92 + },
  93 + guide: {
  94 + description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
  95 + button: '打开引导'
  96 + },
  97 + components: {
  98 + documentation: '文档',
  99 + tinymceTips: '富文本是管理后台一个核心的功能,但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路,市面上常见的富文本都基本用过了,最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
  100 + dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
  101 + stickyTips: '当页面滚动到预设的位置会吸附在顶部',
  102 + backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
  103 + backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示,可在外部使用Element的el-tooltip元素',
  104 + imageUploadTips: '由于我在使用时它只有vue@1版本,而且和mockjs不兼容,所以自己改造了一下,如果大家要使用的话,优先还是使用官方版本。'
  105 + },
  106 + table: {
  107 + dynamicTips1: '固定表头, 按照表头顺序排序',
  108 + dynamicTips2: '不固定表头, 按照点击顺序排序',
  109 + dragTips1: '默认顺序',
  110 + dragTips2: '拖拽后顺序',
  111 + title: '标题',
  112 + importance: '重要性',
  113 + type: '类型',
  114 + remark: '点评',
  115 + search: '搜索',
  116 + add: '添加',
  117 + export: '导出',
  118 + reviewer: '审核人',
  119 + id: '序号',
  120 + date: '时间',
  121 + author: '作者',
  122 + readings: '阅读数',
  123 + status: '状态',
  124 + actions: '操作',
  125 + edit: '编辑',
  126 + publish: '发布',
  127 + draft: '草稿',
  128 + delete: '删除',
  129 + cancel: '取 消',
  130 + confirm: '确 定'
  131 + },
  132 + errorLog: {
  133 + tips: '请点击右上角bug小图标',
  134 + description: '现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',
  135 + documentation: '文档介绍'
  136 + },
  137 + excel: {
  138 + export: '导出',
  139 + selectedExport: '导出已选择项',
  140 + placeholder: '请输入文件名(默认excel-list)'
  141 + },
  142 + zip: {
  143 + export: '导出',
  144 + placeholder: '请输入文件名(默认file)'
  145 + },
  146 + pdf: {
  147 + tips: '这里使用 window.print() 来实现下载pdf的功能'
  148 + },
  149 + theme: {
  150 + change: '换肤',
  151 + documentation: '换肤文档',
  152 + tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
  153 + },
  154 + tagsView: {
  155 + refresh: '刷新',
  156 + close: '关闭',
  157 + closeOthers: '关闭其它',
  158 + closeAll: '关闭所有'
  159 + }
  160 +}
@@ -12,6 +12,7 @@ import axios from 'axios' @@ -12,6 +12,7 @@ import axios from 'axios'
12 //import 'nprogress/nprogress.css' 12 //import 'nprogress/nprogress.css'
13 import rout from './routes' 13 import rout from './routes'
14 import Mock from './mock' 14 import Mock from './mock'
  15 +import i18n from './lang'
15 import 'font-awesome/css/font-awesome.min.css' 16 import 'font-awesome/css/font-awesome.min.css'
16 17
17 //定义一个全局过滤器实现日期格式化 18 //定义一个全局过滤器实现日期格式化
@@ -59,18 +60,13 @@ router.beforeEach((to, from, next) => { @@ -59,18 +60,13 @@ router.beforeEach((to, from, next) => {
59 } 60 }
60 }); 61 });
61 62
  63 +var message = Vue.prototype.$message;
  64 +axios.defaults.baseURL = '/api';
62 axios.interceptors.response.use( 65 axios.interceptors.response.use(
63 res => { 66 res => {
64 - //调用ELEMETN的message组件  
65 - let msg= Vue.prototype.$message;  
66 - //调用route组件  
67 - let route = Vue.prototype.$rout;  
68 - route.push({  
69 - path: "/main"  
70 - });  
71 //对响应数据做些事 67 //对响应数据做些事
72 if (res.data && res.status!==200) { 68 if (res.data && res.status!==200) {
73 - msg({ 69 + message({
74 // 饿了么的消息弹窗组件,类似toast 70 // 饿了么的消息弹窗组件,类似toast
75 showClose: true, 71 showClose: true,
76 message: 'res.data.error.message.message ? res.data.error.message.message : res.data.error.message', 72 message: 'res.data.error.message.message ? res.data.error.message.message : res.data.error.message',
@@ -82,7 +78,7 @@ axios.interceptors.response.use( @@ -82,7 +78,7 @@ axios.interceptors.response.use(
82 }, 78 },
83 error => { 79 error => {
84 // 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳 80 // 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳
85 - // 直接丢localStorage或者sessionStorage 81 + // 直接丢sessionStorage
86 if (!sessionStorage.getItem("token")) { 82 if (!sessionStorage.getItem("token")) {
87 // 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页 83 // 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页
88 router.push({ 84 router.push({
@@ -92,15 +88,14 @@ axios.interceptors.response.use( @@ -92,15 +88,14 @@ axios.interceptors.response.use(
92 88
93 // 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面 89 // 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面
94 if (error.response.status === 401) { 90 if (error.response.status === 401) {
95 - Vue.prototype.$message({  
96 - // 饿了么的消息弹窗组件,类似toast  
97 - showClose: true,  
98 - message: '未授权,请登录', 91 + router.push({
  92 + path: "/login"
  93 + });
  94 + message({
  95 + // 饿了么的消息弹窗组件
  96 + message: '授权超时,或未授权,请重新登录',
99 type: "error" 97 type: "error"
100 }); 98 });
101 - // router.push({  
102 - // path: "/login"  
103 - // });  
104 } 99 }
105 100
106 // 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面 101 // 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面
@@ -141,6 +136,7 @@ let vue = new Vue({ @@ -141,6 +136,7 @@ let vue = new Vue({
141 //template: '<App/>', 136 //template: '<App/>',
142 router, 137 router,
143 store, 138 store,
  139 + i18n,
144 //components: { App } 140 //components: { App }
145 render: h => h(App) 141 render: h => h(App)
146 }).$mount('#app') 142 }).$mount('#app')
@@ -6,6 +6,7 @@ import Main from './views/Main.vue' @@ -6,6 +6,7 @@ import Main from './views/Main.vue'
6 import Role from './views/nav1/role.vue' 6 import Role from './views/nav1/role.vue'
7 import Perm from './views/nav1/perm.vue' 7 import Perm from './views/nav1/perm.vue'
8 import LOG from './views/nav1/Log.vue' 8 import LOG from './views/nav1/Log.vue'
  9 +import PreManifest from './views/agent/PreManifest.vue'
9 // import Form from './views/nav1/Form.vue' 10 // import Form from './views/nav1/Form.vue'
10 11
11 import User from './views/nav1/user.vue' 12 import User from './views/nav1/user.vue'
@@ -51,6 +52,16 @@ let routes = [ @@ -51,6 +52,16 @@ let routes = [
51 ] 52 ]
52 }, 53 },
53 { 54 {
  55 + path: '/agent',
  56 + component: Home,
  57 + name: '代理人',
  58 + iconCls: 'fa fa-id-card-o',
  59 + children: [
  60 + { path: '/pre', component: PreManifest, name: '预配' },
  61 + { path: '/page5', component: Page5, name: '页面5' }
  62 + ]
  63 + },
  64 + {
54 path: '/', 65 path: '/',
55 component: Home, 66 component: Home,
56 name: '导航二', 67 name: '导航二',
  1 +import Cookies from 'js-cookie'
  2 +
  3 +const TokenKey = 'Admin-Token'
  4 +
  5 +export function getToken() {
  6 + return Cookies.get(TokenKey)
  7 +}
  8 +
  9 +export function setToken(token) {
  10 + return Cookies.set(TokenKey, token)
  11 +}
  12 +
  13 +export function removeToken() {
  14 + return Cookies.remove(TokenKey)
  15 +}
  1 +import Vue from 'vue'
  2 +import Clipboard from 'clipboard'
  3 +
  4 +function clipboardSuccess() {
  5 + Vue.prototype.$message({
  6 + message: 'Copy successfully',
  7 + type: 'success',
  8 + duration: 1500
  9 + })
  10 +}
  11 +
  12 +function clipboardError() {
  13 + Vue.prototype.$message({
  14 + message: 'Copy failed',
  15 + type: 'error'
  16 + })
  17 +}
  18 +
  19 +export default function handleClipboard(text, event) {
  20 + const clipboard = new Clipboard(event.target, {
  21 + text: () => text
  22 + })
  23 + clipboard.on('success', () => {
  24 + clipboardSuccess()
  25 + clipboard.off('error')
  26 + clipboard.off('success')
  27 + clipboard.destroy()
  28 + })
  29 + clipboard.on('error', () => {
  30 + clipboardError()
  31 + clipboard.off('error')
  32 + clipboard.off('success')
  33 + clipboard.destroy()
  34 + })
  35 + clipboard.onClick(event)
  36 +}
  1 +const type = [{
  2 + value: 'CN',
  3 + label: 'USCI'
  4 +}, {
  5 + value: 'CN',
  6 + label: 'ID'
  7 +}, {
  8 + value: 'CN',
  9 + label: 'PASSPORT'
  10 +}, {
  11 + value: 'CN',
  12 + label: 'OC'
  13 +}, {
  14 + value: 'CN',
  15 + label: '9999'
  16 +}]
  17 +export default type
  1 +const country = [{
  2 + value: 'CN',
  3 + label: '中国'
  4 +}, {
  5 + value: 'US',
  6 + label: '美国'
  7 +}, {
  8 + value: 'JP',
  9 + label: '日本'
  10 +}, {
  11 + value: 'UN',
  12 + label: '英国'
  13 +}, {
  14 + value: 'EL',
  15 + label: '欧盟'
  16 +}]
  17 +export default country
  1 +const dangerGoods = [
  2 + { value: 'RFX', label: '禁燃物' },
  3 +
  4 + { value: 'EXP', label: '快件' },
  5 +
  6 + { value: 'FRO', label: '冷藏货物' },
  7 +
  8 + { value: 'ELI', label: '锂电池' },
  9 +
  10 + { value: 'ELM', label: '锂金属电池' },
  11 +
  12 + { value: 'WET', label: '没有密水包装的湿' },
  13 +
  14 + { value: 'LHO', label: '人体器官血液' },
  15 +
  16 + { value: 'PEM', label: '肉类' },
  17 +
  18 + { value: 'HUM', label: '尸体' },
  19 +
  20 + { value: 'SPF', label: '实验动物' },
  21 +
  22 + { value: 'EAT', label: '食品' },
  23 +
  24 + { value: 'PEA', label: '兽皮等皮制品' },
  25 +
  26 + { value: 'PEP', label: '水果蔬菜' },
  27 +
  28 + { value: 'DIP', label: '外交邮袋' },
  29 +
  30 + { value: 'DGR', label: '危险品' },
  31 +
  32 + { value: 'PEF', label: '鲜花' },
  33 +
  34 + { value: 'PER', label: '鲜活易腐物品' },
  35 +
  36 + { value: 'XPS', label: '小件货物' },
  37 +
  38 + { value: 'PIL', label: '药品' },
  39 +
  40 + { value: 'FRI', label: '用于动植物检疫的' },
  41 +
  42 + { value: 'MAL', label: '邮件' },
  43 +
  44 + { value: 'ECM', label: '邮件' }
  45 +
  46 +]
  47 +export default dangerGoods
  1 +const goodsPackage = [
  2 + { value: '1', label: '纸箱' },
  3 +
  4 + { value: '2', label: '木箱' },
  5 +
  6 + { value: '3', label: '桶装' },
  7 +
  8 + { value: '4', label: '纸托' },
  9 +
  10 + { value: '5', label: '木托' },
  11 +
  12 + { value: '6', label: '塑料箱' },
  13 +
  14 + { value: '7', label: '金属托' },
  15 +
  16 + { value: '8', label: '散装' },
  17 +
  18 + { value: '9', label: '包' },
  19 +
  20 + { value: '10', label: '其他' }
  21 +]
  22 +export default goodsPackage
  1 +// translate router.meta.title, be used in breadcrumb sidebar tagsview
  2 +export function generateTitle(title) {
  3 + const hasKey = this.$te('route.' + title)
  4 +
  5 + if (hasKey) {
  6 + // $t :this method from vue-i18n, inject in @/lang/index.js
  7 + const translatedTitle = this.$t('route.' + title)
  8 +
  9 + return translatedTitle
  10 + }
  11 + return title
  12 +}
  1 +/**
  2 + * Created by jiachenpan on 16/11/18.
  3 + */
  4 +
  5 +export function parseTime(time, cFormat) {
  6 + if (arguments.length === 0) {
  7 + return null
  8 + }
  9 + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  10 + let date
  11 + if (typeof time === 'object') {
  12 + date = time
  13 + } else {
  14 + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
  15 + time = parseInt(time)
  16 + }
  17 + if ((typeof time === 'number') && (time.toString().length === 10)) {
  18 + time = time * 1000
  19 + }
  20 + date = new Date(time)
  21 + }
  22 + const formatObj = {
  23 + y: date.getFullYear(),
  24 + m: date.getMonth() + 1,
  25 + d: date.getDate(),
  26 + h: date.getHours(),
  27 + i: date.getMinutes(),
  28 + s: date.getSeconds(),
  29 + a: date.getDay()
  30 + }
  31 + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
  32 + let value = formatObj[key]
  33 + // Note: getDay() returns 0 on Sunday
  34 + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
  35 + if (result.length > 0 && value < 10) {
  36 + value = '0' + value
  37 + }
  38 + return value || 0
  39 + })
  40 + return time_str
  41 +}
  42 +
  43 +export function formatTime(time, option) {
  44 + time = +time * 1000
  45 + const d = new Date(time)
  46 + const now = Date.now()
  47 +
  48 + const diff = (now - d) / 1000
  49 +
  50 + if (diff < 30) {
  51 + return '刚刚'
  52 + } else if (diff < 3600) {
  53 + // less 1 hour
  54 + return Math.ceil(diff / 60) + '分钟前'
  55 + } else if (diff < 3600 * 24) {
  56 + return Math.ceil(diff / 3600) + '小时前'
  57 + } else if (diff < 3600 * 24 * 2) {
  58 + return '1天前'
  59 + }
  60 + if (option) {
  61 + return parseTime(time, option)
  62 + } else {
  63 + return (
  64 + d.getMonth() +
  65 + 1 +
  66 + '月' +
  67 + d.getDate() +
  68 + '日' +
  69 + d.getHours() +
  70 + '时' +
  71 + d.getMinutes() +
  72 + '分'
  73 + )
  74 + }
  75 +}
  76 +
  77 +// 格式化时间
  78 +export function getQueryObject(url) {
  79 + url = url == null ? window.location.href : url
  80 + const search = url.substring(url.lastIndexOf('?') + 1)
  81 + const obj = {}
  82 + const reg = /([^?&=]+)=([^?&=]*)/g
  83 + search.replace(reg, (rs, $1, $2) => {
  84 + const name = decodeURIComponent($1)
  85 + let val = decodeURIComponent($2)
  86 + val = String(val)
  87 + obj[name] = val
  88 + return rs
  89 + })
  90 + return obj
  91 +}
  92 +
  93 +/**
  94 + *get getByteLen
  95 + * @param {Sting} val input value
  96 + * @returns {number} output value
  97 + */
  98 +export function getByteLen(val) {
  99 + let len = 0
  100 + for (let i = 0; i < val.length; i++) {
  101 + if (val[i].match(/[^\x00-\xff]/gi) != null) {
  102 + len += 1
  103 + } else {
  104 + len += 0.5
  105 + }
  106 + }
  107 + return Math.floor(len)
  108 +}
  109 +
  110 +export function cleanArray(actual) {
  111 + const newArray = []
  112 + for (let i = 0; i < actual.length; i++) {
  113 + if (actual[i]) {
  114 + newArray.push(actual[i])
  115 + }
  116 + }
  117 + return newArray
  118 +}
  119 +
  120 +export function param(json) {
  121 + if (!json) return ''
  122 + return cleanArray(
  123 + Object.keys(json).map(key => {
  124 + if (json[key] === undefined) return ''
  125 + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
  126 + })
  127 + ).join('&')
  128 +}
  129 +
  130 +export function param2Obj(url) {
  131 + const search = url.split('?')[1]
  132 + if (!search) {
  133 + return {}
  134 + }
  135 + return JSON.parse(
  136 + '{"' +
  137 + decodeURIComponent(search)
  138 + .replace(/"/g, '\\"')
  139 + .replace(/&/g, '","')
  140 + .replace(/=/g, '":"') +
  141 + '"}'
  142 + )
  143 +}
  144 +
  145 +export function html2Text(val) {
  146 + const div = document.createElement('div')
  147 + div.innerHTML = val
  148 + return div.textContent || div.innerText
  149 +}
  150 +
  151 +export function objectMerge(target, source) {
  152 + /* Merges two objects,
  153 + giving the last one precedence */
  154 +
  155 + if (typeof target !== 'object') {
  156 + target = {}
  157 + }
  158 + if (Array.isArray(source)) {
  159 + return source.slice()
  160 + }
  161 + Object.keys(source).forEach(property => {
  162 + const sourceProperty = source[property]
  163 + if (typeof sourceProperty === 'object') {
  164 + target[property] = objectMerge(target[property], sourceProperty)
  165 + } else {
  166 + target[property] = sourceProperty
  167 + }
  168 + })
  169 + return target
  170 +}
  171 +
  172 +export function toggleClass(element, className) {
  173 + if (!element || !className) {
  174 + return
  175 + }
  176 + let classString = element.className
  177 + const nameIndex = classString.indexOf(className)
  178 + if (nameIndex === -1) {
  179 + classString += '' + className
  180 + } else {
  181 + classString =
  182 + classString.substr(0, nameIndex) +
  183 + classString.substr(nameIndex + className.length)
  184 + }
  185 + element.className = classString
  186 +}
  187 +
  188 +export const pickerOptions = [
  189 + {
  190 + text: '今天',
  191 + onClick(picker) {
  192 + const end = new Date()
  193 + const start = new Date(new Date().toDateString())
  194 + end.setTime(start.getTime())
  195 + picker.$emit('pick', [start, end])
  196 + }
  197 + },
  198 + {
  199 + text: '最近一周',
  200 + onClick(picker) {
  201 + const end = new Date(new Date().toDateString())
  202 + const start = new Date()
  203 + start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
  204 + picker.$emit('pick', [start, end])
  205 + }
  206 + },
  207 + {
  208 + text: '最近一个月',
  209 + onClick(picker) {
  210 + const end = new Date(new Date().toDateString())
  211 + const start = new Date()
  212 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
  213 + picker.$emit('pick', [start, end])
  214 + }
  215 + },
  216 + {
  217 + text: '最近三个月',
  218 + onClick(picker) {
  219 + const end = new Date(new Date().toDateString())
  220 + const start = new Date()
  221 + start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
  222 + picker.$emit('pick', [start, end])
  223 + }
  224 + }
  225 +]
  226 +
  227 +export function getTime(type) {
  228 + if (type === 'start') {
  229 + return new Date().getTime() - 3600 * 1000 * 24 * 90
  230 + } else {
  231 + return new Date(new Date().toDateString())
  232 + }
  233 +}
  234 +
  235 +export function debounce(func, wait, immediate) {
  236 + let timeout, args, context, timestamp, result
  237 +
  238 + const later = function() {
  239 + // 据上一次触发时间间隔
  240 + const last = +new Date() - timestamp
  241 +
  242 + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
  243 + if (last < wait && last > 0) {
  244 + timeout = setTimeout(later, wait - last)
  245 + } else {
  246 + timeout = null
  247 + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
  248 + if (!immediate) {
  249 + result = func.apply(context, args)
  250 + if (!timeout) context = args = null
  251 + }
  252 + }
  253 + }
  254 +
  255 + return function(...args) {
  256 + context = this
  257 + timestamp = +new Date()
  258 + const callNow = immediate && !timeout
  259 + // 如果延时不存在,重新设定延时
  260 + if (!timeout) timeout = setTimeout(later, wait)
  261 + if (callNow) {
  262 + result = func.apply(context, args)
  263 + context = args = null
  264 + }
  265 +
  266 + return result
  267 + }
  268 +}
  269 +
  270 +/**
  271 + * This is just a simple version of deep copy
  272 + * Has a lot of edge cases bug
  273 + * If you want to use a perfect deep copy, use lodash's _.cloneDeep
  274 + */
  275 +export function deepClone(source) {
  276 + if (!source && typeof source !== 'object') {
  277 + throw new Error('error arguments', 'shallowClone')
  278 + }
  279 + const targetObj = source.constructor === Array ? [] : {}
  280 + Object.keys(source).forEach(keys => {
  281 + if (source[keys] && typeof source[keys] === 'object') {
  282 + targetObj[keys] = deepClone(source[keys])
  283 + } else {
  284 + targetObj[keys] = source[keys]
  285 + }
  286 + })
  287 + return targetObj
  288 +}
  289 +
  290 +export function uniqueArr(arr) {
  291 + return Array.from(new Set(arr))
  292 +}
  293 +
  294 +export function createUniqueString() {
  295 + const timestamp = +new Date() + ''
  296 + const randomNum = parseInt((1 + Math.random()) * 65536) + ''
  297 + return (+(randomNum + timestamp)).toString(32)
  298 +}
  1 +/**
  2 + *Created by jiachenpan on 16/11/29.
  3 + * @param {Sting} url
  4 + * @param {Sting} title
  5 + * @param {Number} w
  6 + * @param {Number} h
  7 + */
  8 +
  9 +export default function openWindow(url, title, w, h) {
  10 + // Fixes dual-screen position Most browsers Firefox
  11 + const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
  12 + const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
  13 +
  14 + const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width
  15 + const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height
  16 +
  17 + const left = ((width / 2) - (w / 2)) + dualScreenLeft
  18 + const top = ((height / 2) - (h / 2)) + dualScreenTop
  19 + const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left)
  20 +
  21 + // Puts focus on the newWindow
  22 + if (window.focus) {
  23 + newWindow.focus()
  24 + }
  25 +}
  26 +
  1 +import store from '@/store'
  2 +
  3 +/**
  4 + * @param {Array} value
  5 + * @returns {Boolean}
  6 + * @example see @/views/permission/directive.vue
  7 + */
  8 +export default function checkPermission(value) {
  9 + if (value && value instanceof Array && value.length > 0) {
  10 + const roles = store.getters && store.getters.roles
  11 + const permissionRoles = value
  12 +
  13 + const hasPermission = roles.some(role => {
  14 + return permissionRoles.includes(role)
  15 + })
  16 +
  17 + if (!hasPermission) {
  18 + return false
  19 + }
  20 + return true
  21 + } else {
  22 + console.error(`need roles! Like v-permission="['admin','editor']"`)
  23 + return false
  24 + }
  25 +}
  1 +import axios from 'axios'
  2 +import { Message } from 'element-ui'
  3 +import store from '@/store'
  4 +import { getToken } from '@/utils/auth'
  5 +
  6 +// create an axios instance
  7 +const service = axios.create({
  8 + baseURL: process.env.BASE_API, // api 的 base_url
  9 + timeout: 5000 // request timeout
  10 +})
  11 +
  12 +// request interceptor
  13 +service.interceptors.request.use(
  14 + config => {
  15 + // Do something before request is sent
  16 + if (store.getters.token) {
  17 + // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
  18 + config.headers['X-Token'] = getToken()
  19 + }
  20 + return config
  21 + },
  22 + error => {
  23 + // Do something with request error
  24 + console.log(error) // for debug
  25 + Promise.reject(error)
  26 + }
  27 +)
  28 +
  29 +// response interceptor
  30 +service.interceptors.response.use(
  31 + response => response,
  32 + /**
  33 + * 下面的注释为通过在response里,自定义code来标示请求状态
  34 + * 当code返回如下情况则说明权限有问题,登出并返回到登录页
  35 + * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
  36 + * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
  37 + */
  38 + // response => {
  39 + // const res = response.data
  40 + // if (res.code !== 20000) {
  41 + // Message({
  42 + // message: res.message,
  43 + // type: 'error',
  44 + // duration: 5 * 1000
  45 + // })
  46 + // // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
  47 + // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
  48 + // // 请自行在引入 MessageBox
  49 + // // import { Message, MessageBox } from 'element-ui'
  50 + // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
  51 + // confirmButtonText: '重新登录',
  52 + // cancelButtonText: '取消',
  53 + // type: 'warning'
  54 + // }).then(() => {
  55 + // store.dispatch('FedLogOut').then(() => {
  56 + // location.reload() // 为了重新实例化vue-router对象 避免bug
  57 + // })
  58 + // })
  59 + // }
  60 + // return Promise.reject('error')
  61 + // } else {
  62 + // return response.data
  63 + // }
  64 + // },
  65 + error => {
  66 + console.log('err' + error) // for debug
  67 + Message({
  68 + message: error.message,
  69 + type: 'error',
  70 + duration: 5 * 1000
  71 + })
  72 + return Promise.reject(error)
  73 + }
  74 +)
  75 +
  76 +export default service
  1 +Math.easeInOutQuad = function(t, b, c, d) {
  2 + t /= d / 2
  3 + if (t < 1) {
  4 + return c / 2 * t * t + b
  5 + }
  6 + t--
  7 + return -c / 2 * (t * (t - 2) - 1) + b
  8 +}
  9 +
  10 +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
  11 +var requestAnimFrame = (function() {
  12 + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
  13 +})()
  14 +
  15 +// because it's so fucking difficult to detect the scrolling element, just move them all
  16 +function move(amount) {
  17 + document.documentElement.scrollTop = amount
  18 + document.body.parentNode.scrollTop = amount
  19 + document.body.scrollTop = amount
  20 +}
  21 +
  22 +function position() {
  23 + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
  24 +}
  25 +
  26 +export function scrollTo(to, duration, callback) {
  27 + const start = position()
  28 + const change = to - start
  29 + const increment = 20
  30 + let currentTime = 0
  31 + duration = (typeof (duration) === 'undefined') ? 500 : duration
  32 + var animateScroll = function() {
  33 + // increment the time
  34 + currentTime += increment
  35 + // find the value with the quadratic in-out easing function
  36 + var val = Math.easeInOutQuad(currentTime, start, change, duration)
  37 + // move the document.body
  38 + move(val)
  39 + // do the animation unless its over
  40 + if (currentTime < duration) {
  41 + requestAnimFrame(animateScroll)
  42 + } else {
  43 + if (callback && typeof (callback) === 'function') {
  44 + // the animation is done so lets callback
  45 + callback()
  46 + }
  47 + }
  48 + }
  49 + animateScroll()
  50 +}
  1 +/**
  2 + * 板箱类型
  3 + *
  4 + */
  5 +const uldType = ['AKE','AVE','DPE','DQF','PMC','PAG','PLA','PKC']
  6 +export default uldType
  1 +/**
  2 + * Created by jiachenpan on 16/11/18.
  3 + */
  4 +
  5 +export function isExternal(path) {
  6 + return /^(https?:|mailto:|tel:)/.test(path)
  7 +}
  8 +
  9 +export function validUsername(str) {
  10 + const valid_map = ['admin', 'editor']
  11 + return valid_map.indexOf(str.trim()) >= 0
  12 +}
  13 +
  14 +/* 合法uri*/
  15 +export function validURL(url) {
  16 + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
  17 + return reg.test(url)
  18 +}
  19 +
  20 +/* 小写字母*/
  21 +export function validLowerCase(str) {
  22 + const reg = /^[a-z]+$/
  23 + return reg.test(str)
  24 +}
  25 +
  26 +/* 大写字母*/
  27 +export function validUpperCase(str) {
  28 + const reg = /^[A-Z]+$/
  29 + return reg.test(str)
  30 +}
  31 +
  32 +/* 大小写字母*/
  33 +export function validAlphabets(str) {
  34 + const reg = /^[A-Za-z]+$/
  35 + return reg.test(str)
  36 +}
  37 +
  38 +/**
  39 + * validate email
  40 + * @param email
  41 + * @returns {boolean}
  42 + */
  43 +export function validEmail(email) {
  44 + const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
  45 + return re.test(email)
  46 +}
  1 +<template>
  2 + <div class="app-container">
  3 + <div class="filter-container">
  4 + <el-input v-model="listQuery.title" style="width: 200px;" class="filter-item" placeholder="运单号"/>
  5 + <el-input v-model="listQuery.title" style="width: 200px;" class="filter-item" placeholder="航班号"/>
  6 + <el-input v-model="listQuery.title" style="width: 200px;" class="filter-item" placeholder="航班日期"/>
  7 + <el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">{{ $t('table.search') }}</el-button>
  8 + <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">{{ $t('table.add') }}</el-button>
  9 + <el-button v-waves :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">{{ $t('table.export') }}</el-button>
  10 + <el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-message" @click="handleCreate">批量发送</el-button>
  11 + </div>
  12 + <tree-table :data="data" :eval-func="func" :eval-args="args" :expand-all="expandAll" stripe="true" style="font-size: 13px" border @selection-change="handleSelectionChange">
  13 + <el-table-column prop="waybillNo" label="主单号" width="110px" align="center" sortable>
  14 + <template slot-scope="scope">
  15 + <span>{{ scope.row.waybillNo }}</span>
  16 + </template>
  17 + </el-table-column>
  18 + <el-table-column label="分单号" width="120px" align="center" sortable>
  19 + <template slot-scope="scope">
  20 + <span>{{ scope.row.houseWaybillNo }}</span>
  21 + </template>
  22 + </el-table-column>
  23 + <el-table-column label="航班号" width="90px" align="center" sortable>
  24 + <template slot-scope="scope">
  25 + <span>{{ scope.row.flight }}</span>
  26 + </template>
  27 + </el-table-column>
  28 + <el-table-column label="航班日期" width="100px" align="center" sortable>
  29 + <template slot-scope="scope">
  30 + <span>{{ scope.row.flightDate | parseTime('{y}-{m}-{d}') }}</span>
  31 + </template>
  32 + </el-table-column>
  33 + <el-table-column label="起始站" width="60px" align="center">
  34 + <template slot-scope="scope">
  35 + <span>{{ scope.row.oriStation }}</span>
  36 + </template>
  37 + </el-table-column>
  38 + <el-table-column label="目的站" width="60px" align="center">
  39 + <template slot-scope="scope">
  40 + <span>{{ scope.row.desStation }}</span>
  41 + </template>
  42 + </el-table-column>
  43 + <el-table-column label="预配件数" width="80px" align="center">
  44 + <template slot-scope="scope">
  45 + <span>{{ scope.row.maniPiece }}</span>
  46 + </template>
  47 + </el-table-column>
  48 + <el-table-column label="预配重量" width="80px" align="center">
  49 + <template slot-scope="scope">
  50 + <span>{{ scope.row.maniWeight }}</span>
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column :label="$t('table.status')" class-name="status-col" width="90" sortable>
  54 + <template slot-scope="scope">
  55 + <el-tag :type="scope.row.status | statusFilter">{{ scope.row.status }}</el-tag>
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column label="回执内容" width="230px" align="center">
  59 + <template slot-scope="scope">
  60 + <span>{{ scope.row.customText }}</span>
  61 + <div>
  62 + <el-progress :percentage="scope.row.customComplate" :status="scope.row.status | statusFilter" />
  63 + </div>
  64 + </template>
  65 + </el-table-column>
  66 + <el-table-column :label="$t('table.actions')" align="center" class-name="small-padding fixed-width">
  67 + <template slot-scope="scope">
  68 + <el-button type="primary" size="small" @click="handleUpdate(scope.row)">快速编辑</el-button>
  69 + <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">{{ $t('table.edit') }}</el-button>
  70 + <el-button v-if="scope.row.status!='41301'" size="mini" type="success" @click="handleModifyStatus(scope.row,'41301')">{{ $t('table.publish') }}</el-button>
  71 + <el-button v-if="scope.row.status!='10002'" size="small" @click="handleModifyStatus(scope.row,'10002')">客服反馈</el-button>
  72 + <el-button v-if="scope.row.status=='10002'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">{{ $t('table.delete') }}</el-button>
  73 + <el-switch
  74 + v-model="resend"
  75 + active-color="#13ce66"
  76 + inactive-color="#ff4949" />
  77 + </template>
  78 + </el-table-column>
  79 + </tree-table>
  80 + <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
  81 +
  82 + <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" fullscreen="true">
  83 + <el-form ref="" :inline="true" :label-position="left" :rules="rules" :model="temp" label-width="100px" style="font-size: 13px">
  84 + <div>
  85 + <el-tag type="info" effect="plain">
  86 + 航班信息
  87 + </el-tag>
  88 + </div>
  89 + <el-row type="flex" class="row-bg bg-purple">
  90 + <el-col :span="24">
  91 + <div class="grid-content">
  92 + <el-form-item label="航班号" prop="flight">
  93 + <el-input v-model="temp.flight"/>
  94 + </el-form-item>
  95 + <el-form-item label="航班日期" prop="flightDate">
  96 + <el-date-picker :picker-options="pickerOptions" v-model="temp.flightDate" align="right" type="date" placeholder="请输入航班日期" />
  97 + </el-form-item>
  98 + </div>
  99 + </el-col>
  100 + </el-row>
  101 + <div>
  102 + <el-tag type="info" effect="plain">
  103 + 运单信息
  104 + </el-tag>
  105 + </div>
  106 + <el-form-item label="主单号" prop="waybillNo">
  107 + <el-input v-model="temp.waybillNo"/>
  108 + </el-form-item>
  109 + <el-form-item label="分单号" prop="houseWaybillNo">
  110 + <el-input v-model="temp.houseWaybillNo"/>
  111 + </el-form-item>
  112 + <el-form-item label="关区代码" prop="custom">
  113 + <el-select v-model="temp.custom" class="filter-item" placeholder="请选择关区代码">
  114 + <el-option v-for="item in manifestCustoms" :key="item" :label="item" :value="item"/>
  115 + </el-select>
  116 + </el-form-item>
  117 + <el-form-item label="起始站" prop="oriStation">
  118 + <el-input v-model="temp.oriStation"/>
  119 + </el-form-item>
  120 + <el-form-item label="目的站" prop="desStation">
  121 + <el-input v-model="temp.desStation"/>
  122 + </el-form-item>
  123 + <el-form-item label="预配件数" prop="maniPiece">
  124 + <el-input v-model.number="temp.maniPiece"/>
  125 + </el-form-item>
  126 + <el-form-item label="预配重量" prop="maniWeight">
  127 + <el-input v-model.number="temp.maniWeight"/>
  128 + </el-form-item>
  129 + <el-form-item label="付费方式" prop="payMode">
  130 + <el-select v-model="temp.payMode" class="filter-item" placeholder="付费方式">
  131 + <el-option v-for="item in manifestCustoms" :key="item" :label="item" :value="item"/>
  132 + </el-select>
  133 + </el-form-item>
  134 + <el-form-item label="海关状态" prop="goodsType">
  135 + <el-select v-model="temp.goodsType" class="filter-item" placeholder="请录入货物类型">
  136 + <el-option v-for="item in manifestCustoms" :key="item" :label="item" :value="item"/>
  137 + </el-select>
  138 + </el-form-item>
  139 + <div>
  140 + <el-tag type="info" effect="plain">
  141 + 发货人信息
  142 + </el-tag>
  143 + </div>
  144 + <el-row type="flex" class="row-bg bg-purple">
  145 + <el-col :span="24">
  146 + <div class="grid-content">
  147 + <el-form-item label="公司" prop="shpCompany">
  148 + <el-input v-model="temp.shpCompany"/>
  149 + </el-form-item>
  150 + <el-form-item label="地址" prop="shpAddress">
  151 + <el-input v-model="temp.shpAddress"/>
  152 + </el-form-item>
  153 + <el-form-item label="城市" prop="shpCity">
  154 + <el-input v-model="temp.shpAddress"/>
  155 + </el-form-item>
  156 + <el-form-item label="邮编" prop="shpCity">
  157 + <el-input v-model="temp.shpAddress"/>
  158 + </el-form-item>
  159 + <el-form-item label="电话" prop="shpCity">
  160 + <el-input v-model="temp.shpAddress"/>
  161 + </el-form-item>
  162 + <el-form-item label="传真" prop="shpCity">
  163 + <el-input v-model="temp.shpAddress"/>
  164 + </el-form-item>
  165 + </div>
  166 + <div>
  167 + <el-form-item label="国家" prop="shpCountry">
  168 + <el-select
  169 + :remote-method="remoteMethod"
  170 + :loading="loading"
  171 + v-model="temp.country"
  172 + filterable
  173 + remote
  174 + placeholder="请选择" >
  175 + <el-option
  176 + v-for="item in countryOption"
  177 + :key="item.value"
  178 + :label="item.label"
  179 + :value="item.value">
  180 + <span style="float: left">{{ item.label }}</span>
  181 + <span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
  182 + </el-option>
  183 + </el-select>
  184 + </el-form-item>
  185 + <el-form-item label="企业代码类型" prop="shpCompnyType">
  186 + <el-select
  187 + v-model="temp.shpCompnyType"
  188 + filterable
  189 + placeholder="请选择企业代码类型" >
  190 + <el-option
  191 + v-for="item in shpCompnyTypeOption"
  192 + :key="item.value"
  193 + :label="item.label"
  194 + :value="item.value" />
  195 + </el-select>
  196 + </el-form-item>
  197 + <el-form-item prop="shpCity">
  198 + <el-input v-model="temp.shpAddress" placeholder="企业代码"/>
  199 + </el-form-item>
  200 + </div>
  201 + </el-col>
  202 + </el-row>
  203 + <div>
  204 + <el-tag type="info" effect="plain">
  205 + 收货人信息
  206 + </el-tag>
  207 + </div>
  208 + <el-row type="flex" class="row-bg bg-purple">
  209 + <el-col :span="24">
  210 + <div class="grid-content">
  211 + <el-form-item label="公司" prop="shpCompany">
  212 + <el-input v-model="temp.shpCompany"/>
  213 + </el-form-item>
  214 + <el-form-item label="地址" prop="shpAddress">
  215 + <el-input v-model="temp.shpAddress"/>
  216 + </el-form-item>
  217 + <el-form-item label="城市" prop="shpCity">
  218 + <el-input v-model="temp.shpAddress"/>
  219 + </el-form-item>
  220 + <el-form-item label="邮编" prop="shpCity">
  221 + <el-input v-model="temp.shpAddress"/>
  222 + </el-form-item>
  223 + <el-form-item label="电话" prop="shpCity">
  224 + <el-input v-model="temp.shpAddress"/>
  225 + </el-form-item>
  226 + <el-form-item label="传真" prop="shpCity">
  227 + <el-input v-model="temp.shpAddress"/>
  228 + </el-form-item>
  229 + </div>
  230 + <div>
  231 + <el-form-item label="国家" prop="shpCountry">
  232 + <el-select
  233 + :remote-method="remoteMethod"
  234 + :loading="loading"
  235 + v-model="temp.country"
  236 + filterable
  237 + remote
  238 + placeholder="请选择" >
  239 + <el-option
  240 + v-for="item in countryOption"
  241 + :key="item.value"
  242 + :label="item.label"
  243 + :value="item.value">
  244 + <span style="float: left">{{ item.label }}</span>
  245 + <span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
  246 + </el-option>
  247 + </el-select>
  248 + </el-form-item>
  249 + <el-form-item label="企业代码类型" prop="shpCompnyType">
  250 + <el-select
  251 + v-model="temp.shpCompnyType"
  252 + filterable
  253 + placeholder="请选择企业代码类型" >
  254 + <el-option
  255 + v-for="item in shpCompnyTypeOption"
  256 + :key="item.value"
  257 + :label="item.label"
  258 + :value="item.value" />
  259 + </el-select>
  260 + </el-form-item>
  261 + <el-form-item prop="shpCity">
  262 + <el-input v-model="temp.shpAddress" placeholder="企业代码"/>
  263 + </el-form-item>
  264 + </div>
  265 + </el-col>
  266 + </el-row>
  267 + <div>
  268 + <el-tag type="info" effect="plain">
  269 + 危险品信息
  270 + </el-tag>
  271 + </div>
  272 + <el-row type="flex" class="row-bg bg-purple">
  273 + <el-col :span="24">
  274 + <el-form-item label="危险品信息" prop="shpCompnyType">
  275 + <el-select
  276 + v-model="temp.dangerGoods"
  277 + placeholder="请选择危险品类型" >
  278 + <el-option
  279 + v-for="item in dangerGoods"
  280 + :key="item.value"
  281 + :label="item.label"
  282 + :value="item.value" >
  283 + <span style="float: left">{{ item.label }}</span>
  284 + <span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
  285 + </el-option>
  286 + </el-select>
  287 + </el-form-item>
  288 + <el-form-item label="危险品类别" prop="shpCity">
  289 + <el-input v-model="temp.shpAddress" placeholder="危险品类别"/>
  290 + </el-form-item>
  291 + </el-col>
  292 + </el-row>
  293 + <div>
  294 + <el-tag type="info" effect="plain">
  295 + 交运信息
  296 + </el-tag>
  297 + </div>
  298 + <el-row type="flex" class="row-bg bg-purple">
  299 + <el-col :span="24">
  300 + <el-form-item label="包装" prop="shpCompnyType">
  301 + <el-select
  302 + v-model="temp.goodsPackage"
  303 + placeholder="请选择包装类型" >
  304 + <el-option
  305 + v-for="item in goodsPackage"
  306 + :key="item.value"
  307 + :label="item.label"
  308 + :value="item.value" />
  309 + </el-select>
  310 + </el-form-item>
  311 + <el-form-item label="尺寸" prop="shpCity">
  312 + <el-input v-model="temp.shpAddress" placeholder="请输入尺寸"/>
  313 + </el-form-item>
  314 + <el-form-item label="计费重量" prop="shpCity">
  315 + <el-input v-model="temp.shpAddress" placeholder="请输入计费重量"/>
  316 + </el-form-item>
  317 + <el-form-item label="体积" prop="shpCity">
  318 + <el-input v-model="temp.shpAddress" placeholder="请输入体积"/>
  319 + </el-form-item>
  320 + </el-col>
  321 + </el-row>
  322 + <el-row type="flex" class="row-bg bg-purple">
  323 + <el-col :span="24">
  324 + <div class="remark">
  325 + <el-form-item label="备注" prop="shpCity" inline-message="false">
  326 + <el-input
  327 + v-model="temp.shpAddress"
  328 + type="textarea"
  329 + placeholder="其他需要备注的信息"
  330 + maxlength="30"
  331 + show-word-limit
  332 + />
  333 + </el-form-item>
  334 + </div>
  335 + </el-col>
  336 + </el-row>
  337 + </el-form>
  338 +
  339 + <div slot="footer" class="dialog-footer">
  340 + <el-button @click="dialogFormVisible = false">{{ $t('table.cancel') }}</el-button>
  341 + <el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">{{ $t('table.confirm') }}</el-button>
  342 + </div>
  343 + </el-dialog>
  344 +
  345 + </div>
  346 +</template>
  347 +
  348 +<script>
  349 +/**
  350 + Auth: Lei.j1ang
  351 + Created: 2018/1/19-14:54
  352 +*/
  353 +
  354 +import treeTable from '@/components/TreeTable'
  355 +import treeToArray from './customEval'
  356 +import Pagination from '@/components/Pagination'
  357 +import waves from '@/directive/waves' // Waves directive
  358 +import country from '@/utils/country.js'
  359 +import companyType from '@/utils/companyType.js'
  360 +import dangerGoods from '@/utils/dangerGoods.js'
  361 +import goodsPackage from '@/utils/goodsPackage.js'
  362 +
  363 +const calendarTypeOptions = [
  364 + { key: 'CN', display_name: 'China' },
  365 + { key: 'US', display_name: 'USA' },
  366 + { key: 'JP', display_name: 'Japan' },
  367 + { key: 'EU', display_name: 'Eurozone' }
  368 +]
  369 +
  370 +// arr to obj ,such as { CN : "China", US : "USA" }
  371 +const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
  372 + acc[cur.key] = cur.display_name
  373 + return acc
  374 +}, {})
  375 +
  376 +treeTable.expandAll = {
  377 + type: Boolean,
  378 + default: false
  379 +}
  380 +
  381 +export default {
  382 + name: 'CustomTreeTableDemo',
  383 + components: { treeTable, Pagination },
  384 + directives: { waves },
  385 + filters: {
  386 + statusFilter(status) {
  387 + const statusMap = {
  388 + 10003: '',
  389 + 41301: 'success',
  390 + 10002: 'info',
  391 + 10000: 'danger',
  392 + 45103: 'warning',
  393 + wrong: 'exception'
  394 + }
  395 + return statusMap[status]
  396 + },
  397 + typeFilter(type) {
  398 + return calendarTypeKeyValue[type]
  399 + }
  400 + },
  401 + data() {
  402 + return {
  403 + func: treeToArray,
  404 + expandAll: true,
  405 + total: 1,
  406 + resend: true,
  407 + listLoading: true,
  408 + multipleSelection: [],
  409 + dialogFormVisible: false,
  410 + dialogStatus: '',
  411 + country: country,
  412 + countryOption: [],
  413 + shpCompnyTypeOption: [],
  414 + dangerGoods: dangerGoods,
  415 + goodsPackage: goodsPackage,
  416 + textMap: {
  417 + update: '编辑',
  418 + create: '添加'
  419 + },
  420 + listQuery: {
  421 + page: 1,
  422 + limit: 20,
  423 + waybillNo: '',
  424 + flight: '',
  425 + flightDate: '',
  426 + custom: ''
  427 + },
  428 + temp: {
  429 + id: undefined,
  430 + waybillNo: '',
  431 + houseWaybillNo: '',
  432 + custom: '4604',
  433 + flight: '',
  434 + flightDate: '',
  435 + oriStation: 'CGO',
  436 + desStation: '',
  437 + maniPiece: undefined,
  438 + maniWeight: undefined
  439 + },
  440 + manifestCustoms: ['4604', '4620', '4613'],
  441 + data: [
  442 + {
  443 + id: 0,
  444 + waybillNo: '580-20728399',
  445 + houseWaybillNo: '',
  446 + custom: '4604',
  447 + flight: 'CV9733',
  448 + flightDate: '2019-06-21',
  449 + oriStation: 'LUX',
  450 + desStation: 'CGO',
  451 + maniPiece: 50,
  452 + maniWeight: 21321,
  453 + status: '41301',
  454 + customText: ' 预配舱单主要数据传输成功。',
  455 + customComplate: 25
  456 + },
  457 + {
  458 + id: 1,
  459 + waybillNo: '580-20728396',
  460 + houseWaybillNo: '',
  461 + custom: '4604',
  462 + flight: 'CV9731',
  463 + flightDate: '2019-06-21',
  464 + oriStation: 'LUX',
  465 + desStation: 'CGO',
  466 + maniPiece: 50,
  467 + maniWeight: 21321,
  468 + status: 'wrong',
  469 + customText: ' 预配回执异常 ',
  470 + customComplate: 25,
  471 + children: [
  472 + {
  473 + id: 2,
  474 + waybillNo: '580-20728396',
  475 + houseWaybillNo: 'ADDSS21231',
  476 + custom: '4604',
  477 + flight: 'CV9731',
  478 + flightDate: '2019-06-21',
  479 + oriStation: 'LUX',
  480 + desStation: 'CGO',
  481 + maniPiece: 50,
  482 + maniWeight: 21321,
  483 + status: '10002',
  484 + customText: ' 已暂存',
  485 + customComplate: 0
  486 + },
  487 + {
  488 + id: 3,
  489 + waybillNo: '580-20728396',
  490 + houseWaybillNo: 'SDE3411',
  491 + custom: '4604',
  492 + flight: 'CV9731',
  493 + flightDate: '2019-06-21',
  494 + oriStation: 'LUX',
  495 + desStation: 'CGO',
  496 + maniPiece: 50,
  497 + maniWeight: 21321,
  498 + status: '45103',
  499 + customText: ' 45103 该提(运)单的运抵报告重复申报,海关审核不通过。 关区代码:4604。',
  500 + customComplate: 100
  501 + },
  502 + {
  503 + id: 4,
  504 + waybillNo: '580-20728396',
  505 + houseWaybillNo: 'SDE3411',
  506 + custom: '4604',
  507 + flight: 'CV9731',
  508 + flightDate: '2019-06-21',
  509 + oriStation: 'LUX',
  510 + desStation: 'CGO',
  511 + maniPiece: 50,
  512 + maniWeight: 21321,
  513 + status: '10000',
  514 + customText: ' 已删除 ',
  515 + customComplate: 50
  516 + }
  517 + ]
  518 + },
  519 + {
  520 + id: 4,
  521 + waybillNo: '580-20728391',
  522 + houseWaybillNo: '',
  523 + custom: '4604',
  524 + flight: 'KA740',
  525 + flightDate: '2019-06-29',
  526 + oriStation: 'LUX',
  527 + desStation: 'CGO',
  528 + maniPiece: 50,
  529 + maniWeight: 21321,
  530 + status: '10003',
  531 + customText: ' 已发送预配舱单 '
  532 + }
  533 + ],
  534 + pickerOptions: {
  535 + shortcuts: [
  536 + {
  537 + text: '今天',
  538 + onClick(picker) {
  539 + picker.$emit('pick', new Date())
  540 + }
  541 + },
  542 + {
  543 + text: '昨天',
  544 + onClick(picker) {
  545 + const date = new Date()
  546 + date.setTime(date.getTime() - 3600 * 1000 * 24)
  547 + picker.$emit('pick', date)
  548 + }
  549 + },
  550 + {
  551 + text: '明天',
  552 + onClick(picker) {
  553 + const date = new Date()
  554 + date.setTime(date.getTime() + 3600 * 1000 * 24)
  555 + picker.$emit('pick', date)
  556 + }
  557 + },
  558 + {
  559 + text: '一周前',
  560 + onClick(picker) {
  561 + const date = new Date()
  562 + date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
  563 + picker.$emit('pick', date)
  564 + }
  565 + }
  566 + ]
  567 + },
  568 + args: [null, null, 'timeLine'],
  569 + rules: {
  570 + maniWeight: [{ type: 'number', required: true, message: '重量必须为数字', trigger: 'blur' }],
  571 + maniPiece: [{ type: 'number', required: true, message: '件数必须为数字', trigger: 'blur' }],
  572 + desStation: [{ required: true, message: '运单目的地必须输入', trigger: 'blur' }],
  573 + oriStation: [{ required: true, message: '运单起始站必须数据', trigger: 'change' }],
  574 + flightDate: [{ type: 'date', required: true, message: '航班日期必须输入', trigger: 'change' }],
  575 + flight: [{ required: true, message: '航班号必须输入', trigger: 'blur' }],
  576 + custom: [{ required: true, message: '关区代码必须选择', trigger: 'change' }],
  577 + waybillNo: [{ required: true, message: '运单号必须输入', trigger: 'blur' }]
  578 + }
  579 + }
  580 + },
  581 + methods: {
  582 + message(row) {
  583 + this.$message.info(row.event)
  584 + },
  585 + sortChange(data) {
  586 + const { prop, order } = data
  587 + if (prop === 'id') {
  588 + this.sortByID(order)
  589 + }
  590 + },
  591 + sortByID(order) {
  592 + if (order === 'ascending') {
  593 + this.listQuery.sort = '+id'
  594 + } else {
  595 + this.listQuery.sort = '-id'
  596 + }
  597 + this.handleFilter()
  598 + },
  599 + handleUpdate(row) {
  600 + this.temp = Object.assign({}, row) // copy obj
  601 + this.temp.timestamp = new Date(this.temp.timestamp)
  602 + this.dialogStatus = 'update'
  603 + this.dialogFormVisible = true
  604 + this.$nextTick(() => {
  605 + this.$refs[''].clearValidate()
  606 + })
  607 + },
  608 + handleModifyStatus(row, status) {
  609 + this.$message({
  610 + message: '操作成功',
  611 + type: 'success'
  612 + })
  613 + row.status = status
  614 + },
  615 + getList() {
  616 +
  617 + },
  618 + handleSelectionChange(val) {
  619 + this.multipleSelection = val
  620 + },
  621 + resetTemp() {
  622 + this.temp = {
  623 + id: undefined,
  624 + waybillNo: '',
  625 + houseWaybillNo: '',
  626 + custom: '4604',
  627 + flight: '',
  628 + flightDate: '',
  629 + oriStation: 'CGO',
  630 + desStation: '',
  631 + maniPiece: undefined,
  632 + maniWeight: undefined
  633 + }
  634 + },
  635 + handleCreate() {
  636 + this.resetTemp()
  637 + this.dialogStatus = 'create'
  638 + this.dialogFormVisible = true
  639 + this.$nextTick(() => {
  640 + this.$refs[''].clearValidate()
  641 + })
  642 + },
  643 + remoteMethod(query) {
  644 + if (query !== '') {
  645 + this.loading = true
  646 + setTimeout(() => {
  647 + this.loading = false
  648 + this.countryOption = this.country.filter(item => {
  649 + return item.value.toUpperCase()
  650 + .indexOf(query.toUpperCase()) > -1
  651 + })
  652 + this.shpCompnyTypeOption = companyType.filter(item => {
  653 + return item.value.toUpperCase()
  654 + .indexOf(query.toUpperCase()) > -1
  655 + })
  656 + }, 200)
  657 + } else {
  658 + this.countryOption = []
  659 + }
  660 + }
  661 + }
  662 +}
  663 +</script>
  664 +<style>
  665 + .el-row {
  666 + margin-bottom: 20px;
  667 + &:last-child {
  668 + margin-bottom: 0;
  669 + }
  670 + }
  671 + .el-col {
  672 + border-radius: 4px;
  673 + }
  674 + .bg-purple-dark {
  675 + background: #99a9bf;
  676 + }
  677 + .bg-purple {
  678 + background: #d3dce6;
  679 + }
  680 + .bg-purple-light {
  681 + background: #e5e9f2;
  682 + }
  683 + .grid-content {
  684 + border-radius: 4px;
  685 + min-height: 36px;
  686 + }
  687 + .row-bg {
  688 + padding: 10px 0;
  689 + background-color: #f9fafc;
  690 + }
  691 + .el-progress.is-warning .el-progress-bar__inner{
  692 + background-color:#E6A23C
  693 + }
  694 + .remark textarea{
  695 + width: 600px;
  696 + }
  697 +</style>
  698 +
  1 +/**
  2 +* @Author: jianglei
  3 +* @Date: 2017-10-12 12:06:49
  4 +*/
  5 +'use strict'
  6 +import Vue from 'vue'
  7 +export default function treeToArray(data, expandAll, parent, level, item) {
  8 + const marLTemp = []
  9 + let tmp = []
  10 + Array.from(data).forEach(function(record) {
  11 + if (record._expanded === undefined) {
  12 + Vue.set(record, '_expanded', expandAll)
  13 + }
  14 + let _level = 1
  15 + if (level !== undefined && level !== null) {
  16 + _level = level + 1
  17 + }
  18 + Vue.set(record, '_level', _level)
  19 + // 如果有父元素
  20 + if (parent) {
  21 + Vue.set(record, 'parent', parent)
  22 + // 如果父元素有偏移量,需要计算在this的偏移量中
  23 + // 偏移量还与前面同级元素有关,需要加上前面所有元素的长度和
  24 + if (!marLTemp[_level]) {
  25 + marLTemp[_level] = 0
  26 + }
  27 + Vue.set(record, '_marginLeft', marLTemp[_level] + parent._marginLeft)
  28 + Vue.set(record, '_width', record[item] / parent[item] * parent._width)
  29 + // 在本次计算过偏移量后加上自己长度,以供下一个元素使用
  30 + marLTemp[_level] += record._width
  31 + } else {
  32 + // 如果为根
  33 + // 初始化偏移量存储map
  34 + marLTemp[record.id] = []
  35 + // map中是一个数组,存储的是每级的长度和
  36 + // 初始情况下为0
  37 + marLTemp[record.id][_level] = 0
  38 + Vue.set(record, '_marginLeft', 0)
  39 + Vue.set(record, '_width', 1)
  40 + }
  41 + tmp.push(record)
  42 + if (record.children && record.children.length > 0) {
  43 + const children = treeToArray(record.children, expandAll, record, _level, item)
  44 + tmp = tmp.concat(children)
  45 + }
  46 + })
  47 + return tmp
  48 +}
@@ -274,14 +274,14 @@ @@ -274,14 +274,14 @@
274 }; 274 };
275 this.listLoading = true; 275 this.listLoading = true;
276 getUserList(para).then((res) => { 276 getUserList(para).then((res) => {
277 - this.total = res.data.total;  
278 - this.users = res.data.list; 277 + let response = res.data.data;
  278 + this.total = response.total;
  279 + this.users = response.list;
279 this.listLoading = false; 280 this.listLoading = false;
280 //NProgress.done(); 281 //NProgress.done();
281 }).catch((error) => { 282 }).catch((error) => {
282 this.listLoading = false; 283 this.listLoading = false;
283 -  
284 - alert(error); 284 + this.$message.error(error.toString());
285 }); 285 });
286 286
287 }, 287 },