作者 朱兆平

预配模块

此 diff 太大无法显示。
... ... @@ -10,16 +10,18 @@
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.15.3",
"axios": "^0.19.0",
"echarts": "^3.3.2",
"element-ui": "^1.2.8",
"eslint": "^5.14.1",
"font-awesome": "^4.7.0",
"install": "^0.12.2",
"js-cookie": "^2.2.1",
"moment": "^2.24.0",
"npm": "^6.8.0",
"nprogress": "^0.2.0",
"vue": "^2.2.2",
"vue-i18n": "^8.14.0",
"vue-router": "^2.3.0",
"vuex": "^2.0.0-rc.6"
},
... ... @@ -45,7 +47,7 @@
"friendly-errors-webpack-plugin": "^1.1.3",
"function-bind": "^1.0.2",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.17.3",
"http-proxy-middleware": "^0.19.1",
"json-loader": "^0.5.4",
"mockjs": "^1.0.1-beta3",
"node-sass": "^4.5.0",
... ... @@ -62,7 +64,7 @@
"vue-style-loader": "^2.0.0",
"vue-template-compiler": "^2.2.4",
"webpack": "^2.2.1",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.16.1",
"webpack-merge": "^2.6.1"
... ... @@ -75,5 +77,11 @@
"> 1%",
"last 2 versions",
"not ie <= 8"
]
],
"main": ".eslintrc.js",
"repository": {
"type": "git",
"url": "git@118.31.66.166:zp260/vue_cli.git"
},
"keywords": []
}
... ...
import axios from 'axios'
import qs from 'qs'
axios.defaults.baseURL = '/api';
export default {
post(url, data) {
return axios({
... ...
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"/>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scrollTo'
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>
... ...
/**
* @Author: jianglei
* @Date: 2017-10-12 12:06:49
*/
'use strict'
import Vue from 'vue'
export default function treeToArray(data, expandAll, parent = null, level = null) {
let tmp = []
Array.from(data).forEach(function(record) {
if (record._expanded === undefined) {
Vue.set(record, '_expanded', expandAll)
}
let _level = 1
if (level !== undefined && level !== null) {
_level = level + 1
}
Vue.set(record, '_level', _level)
// 如果有父元素
if (parent) {
Vue.set(record, 'parent', parent)
}
tmp.push(record)
if (record.children && record.children.length > 0) {
const children = treeToArray(record.children, expandAll, record, _level)
tmp = tmp.concat(children)
}
})
return tmp
}
... ...
<template>
<el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
<el-table-column type="selection" width="30" align="center"/>
<el-table-column v-if="columns.length===0" width="30" align="center">
<template slot-scope="scope">
<span v-for="space in scope.row._level" :key="space" class="ms-tree-space"/>
<span v-if="iconShow(0,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
<i v-else class="el-icon-minus"/>
</span>
<!--{{ scope.$index }}-->
</template>
</el-table-column>
<el-table-column v-for="(column, index) in columns" v-else :key="column.value" :label="column.text" :width="column.width">
<template slot-scope="scope">
<!-- Todo -->
<!-- eslint-disable-next-line vue/no-confusing-v-for-v-if -->
<span v-for="space in scope.row._level" v-if="index === 0" :key="space" class="ms-tree-space"/>
<span v-if="iconShow(index,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
<i v-else class="el-icon-minus"/>
</span>
{{ scope.row[column.value] }}
</template>
</el-table-column>
<slot/>
</el-table>
</template>
<script>
/**
Auth: Lei.j1ang
Created: 2018/1/19-13:59
*/
import treeToArray from './eval'
export default {
name: 'TreeTable',
props: {
/* eslint-disable */
data: {
type: [Array, Object],
required: true
},
columns: {
type: Array,
default: () => []
},
evalFunc: Function,
evalArgs: Array,
expandAll: {
type: Boolean,
default: true
}
},
computed: {
// 格式化数据源
formatData: function() {
let tmp
if (!Array.isArray(this.data)) {
tmp = [this.data]
} else {
tmp = this.data
}
const func = this.evalFunc || treeToArray
const args = this.evalArgs ? Array.concat([tmp, this.expandAll], this.evalArgs) : [tmp, this.expandAll]
return func.apply(null, args)
}
},
methods: {
showRow: function(row) {
const show = (row.row.parent ? (row.row.parent._expanded && row.row.parent._show) : true)
row.row._show = show
return show ? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;' : 'display:none;'
},
// 切换下级是否展开
toggleExpanded: function(trIndex) {
const record = this.formatData[trIndex]
record._expanded = !record._expanded
},
// 图标显示
iconShow(index, record) {
return (index === 0 && record.children && record.children.length > 0)
}
}
}
</script>
<style rel="stylesheet/css">
@keyframes treeTableShow {
from {opacity: 0;}
to {opacity: 1;}
}
@-webkit-keyframes treeTableShow {
from {opacity: 0;}
to {opacity: 1;}
}
</style>
<style lang="scss" rel="stylesheet/scss" scoped>
$color-blue: #2196F3;
$space-width: 18px;
.ms-tree-space {
position: relative;
top: 1px;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1;
width: $space-width;
height: 14px;
&::before {
content: ""
}
}
.processContainer{
width: 100%;
height: 100%;
}
table td {
line-height: 26px;
}
.tree-ctrl{
position: relative;
cursor: pointer;
color: $color-blue;
margin-left: -$space-width;
}
</style>
... ...
## 写在前面
此组件仅提供一个创建TreeTable的解决思路
## prop说明
#### *data*
**必填**
原始数据,要求是一个数组或者对象
```javascript
[{
key1: value1,
key2: value2,
children: [{
key1: value1
},
{
key1: value1
}]
},
{
key1: value1
}]
```
或者
```javascript
{
key1: value1,
key2: value2,
children: [{
key1: value1
},
{
key1: value1
}]
}
```
#### columns
列属性,要求是一个数组
1. text: 显示在表头的文字
2. value: 对应data的key。treeTable将显示相应的value
3. width: 每列的宽度,为一个数字(可选)
如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便
如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文
```javascript
[{
value:string,
text:string,
width:number
},{
value:string,
text:string,
width:number
}]
```
#### expandAll
是否默认全部展开,boolean值,默认为false
#### evalFunc
解析函数,function,非必须
如果不提供,将使用默认的[evalFunc](./eval.js)
如果提供了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)
#### evalArgs
解析函数的参数,是一个数组
**请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开*
如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了
如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了
具体可参考[*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`属性值
## slot
这是一个自定义列的插槽。
默认情况下,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)
`slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示
## 其他
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue
... ...
// Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard')
if (!Clipboard) {
throw new Error('you should npm install `clipboard` --save at first ')
}
export default {
bind(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
const clipboard = new Clipboard(el, {
text() { return binding.value },
action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
})
clipboard.on('success', e => {
const callback = el._v_clipboard_success
callback && callback(e) // eslint-disable-line
})
clipboard.on('error', e => {
const callback = el._v_clipboard_error
callback && callback(e) // eslint-disable-line
})
el._v_clipboard = clipboard
}
},
update(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
el._v_clipboard.text = function() { return binding.value }
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
}
},
unbind(el, binding) {
if (binding.arg === 'success') {
delete el._v_clipboard_success
} else if (binding.arg === 'error') {
delete el._v_clipboard_error
} else {
el._v_clipboard.destroy()
delete el._v_clipboard
}
}
}
... ...
import Clipboard from './clipboard'
const install = function(Vue) {
Vue.directive('Clipboard', Clipboard)
}
if (window.Vue) {
window.clipboard = Clipboard
Vue.use(install); // eslint-disable-line
}
Clipboard.install = install
export default Clipboard
... ...
export default{
bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function() {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomHeight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-(top) > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
// emit onDrag event
vnode.child.$emit('dragDialog')
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
}
... ...
import drag from './drag'
const install = function(Vue) {
Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
window['el-drag-dialog'] = drag
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag
... ...
import permission from './permission'
const install = function(Vue) {
Vue.directive('permission', permission)
}
if (window.Vue) {
window['permission'] = permission
Vue.use(install); // eslint-disable-line
}
permission.install = install
export default permission
... ...
import store from '@/store'
export default{
inserted(el, binding, vnode) {
const { value } = binding
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
}
}
}
... ...
const vueSticky = {}
let listenAction
vueSticky.install = Vue => {
Vue.directive('sticky', {
inserted(el, binding) {
const params = binding.value || {}
const stickyTop = params.stickyTop || 0
const zIndex = params.zIndex || 1000
const elStyle = el.style
elStyle.position = '-webkit-sticky'
elStyle.position = 'sticky'
// if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
// if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex;
// return
// }
const elHeight = el.getBoundingClientRect().height
const elWidth = el.getBoundingClientRect().width
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
const parentElm = el.parentNode || document.documentElement
const placeholder = document.createElement('div')
placeholder.style.display = 'none'
placeholder.style.width = `${elWidth}px`
placeholder.style.height = `${elHeight}px`
parentElm.insertBefore(placeholder, el)
let active = false
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset'
const method = top ? 'scrollTop' : 'scrollLeft'
let ret = target[prop]
if (typeof ret !== 'number') {
ret = window.document.documentElement[method]
}
return ret
}
const sticky = () => {
if (active) {
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
elStyle.position = 'fixed'
elStyle.width = `${elWidth}px`
placeholder.style.display = 'inline-block'
active = true
}
const reset = () => {
if (!active) {
return
}
elStyle.position = ''
placeholder.style.display = 'none'
active = false
}
const check = () => {
const scrollTop = getScroll(window, true)
const offsetTop = el.getBoundingClientRect().top
if (offsetTop < stickyTop) {
sticky()
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
}
}
}
listenAction = () => {
check()
}
window.addEventListener('scroll', listenAction)
},
unbind() {
window.removeEventListener('scroll', listenAction)
}
})
}
export default vueSticky
... ...
import waves from './waves'
const install = function(Vue) {
Vue.directive('waves', waves)
}
if (window.Vue) {
window.waves = waves
Vue.use(install); // eslint-disable-line
}
waves.install = install
export default waves
... ...
.waves-ripple {
position: absolute;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.15);
background-clip: padding-box;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
opacity: 1;
}
.waves-ripple.z-active {
opacity: 0;
-webkit-transform: scale(2);
-ms-transform: scale(2);
transform: scale(2);
-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}
\ No newline at end of file
... ...
import './waves.css'
export default{
bind(el, binding) {
el.addEventListener('click', e => {
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit 点击位置扩散 center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts)
const target = opts.ele
if (target) {
target.style.position = 'relative'
target.style.overflow = 'hidden'
const rect = target.getBoundingClientRect()
let ripple = target.querySelector('.waves-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'waves-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'waves-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
break
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop || document.body.scrollTop) + 'px'
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft || document.body.scrollLeft) + 'px'
}
ripple.style.backgroundColor = opts.color
ripple.className = 'waves-ripple z-active'
return false
}
}, false)
}
}
... ...
export default {
route: {
dashboard: 'Dashboard',
introduction: 'Introduction',
documentation: 'Documentation',
guide: 'Guide',
permission: 'Permission',
pagePermission: 'Page Permission',
directivePermission: 'Directive Permission',
icons: 'Icons',
components: 'Components',
componentIndex: 'Introduction',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'JSON Editor',
dndList: 'Dnd List',
splitPane: 'SplitPane',
avatarUpload: 'Avatar Upload',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'CountTo',
componentMixin: 'Mixin',
backToTop: 'BackToTop',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban',
charts: 'Charts',
keyboardChart: 'Keyboard Chart',
lineChart: 'Line Chart',
mixChart: 'Mix Chart',
example: 'Example',
nested: 'Nested Routes',
menu1: 'Menu 1',
'menu1-1': 'Menu 1-1',
'menu1-2': 'Menu 1-2',
'menu1-2-1': 'Menu 1-2-1',
'menu1-2-2': 'Menu 1-2-2',
'menu1-3': 'Menu 1-3',
menu2: 'Menu 2',
Table: 'Table',
dynamicTable: 'Dynamic Table',
dragTable: 'Drag Table',
inlineEditTable: 'Inline Edit',
complexTable: 'Complex Table',
treeTable: 'Tree Table',
customTreeTable: 'Custom TreeTable',
tab: 'Tab',
form: 'Form',
createArticle: 'Create Article',
editArticle: 'Edit Article',
articleList: 'Article List',
errorPages: 'Error Pages',
page401: '401',
page404: '404',
errorLog: 'Error Log',
excel: 'Excel',
exportExcel: 'Export Excel',
selectExcel: 'Export Selected',
uploadExcel: 'Upload Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Export Zip',
theme: 'Theme',
clipboardDemo: 'Clipboard',
i18n: 'I18n',
externalLink: 'External Link'
},
navbar: {
logOut: 'Log Out',
dashboard: 'Dashboard',
github: 'Github',
theme: 'Theme',
size: 'Global Size'
},
login: {
title: 'Login Form',
logIn: 'Log in',
username: 'Username',
password: 'Password',
any: 'any',
thirdparty: 'Or connect with',
thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
},
documentation: {
documentation: 'Documentation',
github: 'Github Repository'
},
permission: {
roles: 'Your roles',
switchRoles: 'Switch roles',
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.'
},
guide: {
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 ',
button: 'Show Guide'
},
components: {
documentation: 'Documentation',
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.',
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.',
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
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',
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.'
},
table: {
dynamicTips1: 'Fixed header, sorted by header order',
dynamicTips2: 'Not fixed header, sorted by click order',
dragTips1: 'The default order',
dragTips2: 'The after dragging order',
title: 'Title',
importance: 'Imp',
type: 'Type',
remark: 'Remark',
search: 'Search',
add: 'Add',
export: 'Export',
reviewer: 'reviewer',
id: 'ID',
date: 'Date',
author: 'Author',
readings: 'Readings',
status: 'Status',
actions: 'Actions',
edit: 'Edit',
publish: 'Publish',
draft: 'Draft',
delete: 'Delete',
cancel: 'Cancel',
confirm: 'Confirm'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
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.',
documentation: 'Document introduction'
},
excel: {
export: 'Export',
selectedExport: 'Export Selected Items',
placeholder: 'Please enter the file name(default excel-list)'
},
zip: {
export: 'Export',
placeholder: 'Please enter the file name(default file)'
},
pdf: {
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
},
theme: {
change: 'Change Theme',
documentation: 'Theme documentation',
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.'
},
tagsView: {
refresh: 'Refresh',
close: 'Close',
closeOthers: 'Close Others',
closeAll: 'Close All'
}
}
... ...
export default {
route: {
dashboard: 'Panel de control',
introduction: 'Introducción',
documentation: 'Documentación',
guide: 'Guía',
permission: 'Permisos',
pagePermission: 'Permisos de la página',
directivePermission: 'Permisos de la directiva',
icons: 'Iconos',
components: 'Componentes',
componentIndex: 'Introducción',
tinymce: 'Tinymce',
markdown: 'Markdown',
jsonEditor: 'Editor JSON',
dndList: 'Lista Dnd',
splitPane: 'Panel dividido',
avatarUpload: 'Subir avatar',
dropzone: 'Subir ficheros',
sticky: 'Sticky',
countTo: 'CountTo',
componentMixin: 'Mixin',
backToTop: 'Ir arriba',
dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban',
charts: 'Gráficos',
keyboardChart: 'Keyboard Chart',
lineChart: 'Gráfico de líneas',
mixChart: 'Mix Chart',
example: 'Ejemplo',
nested: 'Rutas anidadass',
menu1: 'Menu 1',
'menu1-1': 'Menu 1-1',
'menu1-2': 'Menu 1-2',
'menu1-2-1': 'Menu 1-2-1',
'menu1-2-2': 'Menu 1-2-2',
'menu1-3': 'Menu 1-3',
menu2: 'Menu 2',
Table: 'Tabla',
dynamicTable: 'Tabla dinámica',
dragTable: 'Arrastrar tabla',
inlineEditTable: 'Editor',
complexTable: 'Complex Table',
treeTable: 'Tree Table',
customTreeTable: 'Custom TreeTable',
tab: 'Pestaña',
form: 'Formulario',
createArticle: 'Crear artículo',
editArticle: 'Editar artículo',
articleList: 'Listado de artículos',
errorPages: 'Páginas de error',
page401: '401',
page404: '404',
errorLog: 'Registro de errores',
excel: 'Excel',
exportExcel: 'Exportar a Excel',
selectExcel: 'Export seleccionado',
uploadExcel: 'Subir Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Exportar a Zip',
theme: 'Tema',
clipboardDemo: 'Clipboard',
i18n: 'I18n',
externalLink: 'Enlace externo'
},
navbar: {
logOut: 'Salir',
dashboard: 'Panel de control',
github: 'Github',
theme: 'Tema',
size: 'Tamaño global'
},
login: {
title: 'Formulario de acceso',
logIn: 'Acceso',
username: 'Usuario',
password: 'Contraseña',
any: 'nada',
thirdparty: 'Conectar con',
thirdpartyTips: 'No se puede simular en local, así que combine su propia simulación de negocios. ! !'
},
documentation: {
documentation: 'Documentación',
github: 'Repositorio Github'
},
permission: {
roles: 'Tus permisos',
switchRoles: 'Cambiar permisos',
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.'
},
guide: {
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 ',
button: 'Ver guía'
},
components: {
documentation: 'Documentación',
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.',
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.',
stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
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',
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.'
},
table: {
dynamicTips1: 'Fixed header, sorted by header order',
dynamicTips2: 'Not fixed header, sorted by click order',
dragTips1: 'Orden por defecto',
dragTips2: 'The after dragging order',
title: 'Título',
importance: 'Importancia',
type: 'Tipo',
remark: 'Remark',
search: 'Buscar',
add: 'Añadir',
export: 'Exportar',
reviewer: 'reviewer',
id: 'ID',
date: 'Fecha',
author: 'Autor',
readings: 'Lector',
status: 'Estado',
actions: 'Acciones',
edit: 'Editar',
publish: 'Publicar',
draft: 'Draft',
delete: 'Eliminar',
cancel: 'Cancelar',
confirm: 'Confirmar'
},
errorLog: {
tips: 'Please click the bug icon in the upper right corner',
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.',
documentation: 'Documento de introducción'
},
excel: {
export: 'Exportar',
selectedExport: 'Exportar seleccionados',
placeholder: 'Por favor escribe un nombre de fichero'
},
zip: {
export: 'Exportar',
placeholder: 'Por favor escribe un nombre de fichero'
},
pdf: {
tips: 'Here we use window.print() to implement the feature of downloading pdf.'
},
theme: {
change: 'Cambiar tema',
documentation: 'Documentación del tema',
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.'
},
tagsView: {
refresh: 'Actualizar',
close: 'Cerrar',
closeOthers: 'Cerrar otros',
closeAll: 'Cerrar todos'
}
}
... ...
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
import elementEsLocale from 'element-ui/lib/locale/lang/es'// element-ui lang
import enLocale from './en'
import zhLocale from './zh'
import esLocale from './es'
Vue.use(VueI18n)
const messages = {
en: {
...enLocale,
...elementEnLocale
},
zh: {
...zhLocale,
...elementZhLocale
},
es: {
...esLocale,
...elementEsLocale
}
}
const i18n = new VueI18n({
// set locale
// options: en | zh | es
locale: Cookies.get('language') || 'en',
// set locale messages
messages
})
export default i18n
... ...
export default {
route: {
dashboard: '首页',
introduction: '简述',
documentation: '文档',
guide: '引导页',
permission: '权限测试页',
pagePermission: '页面权限',
directivePermission: '指令权限',
icons: '图标',
components: '组件',
componentIndex: '介绍',
tinymce: '富文本编辑器',
markdown: 'Markdown',
jsonEditor: 'JSON编辑器',
dndList: '列表拖拽',
splitPane: 'Splitpane',
avatarUpload: '头像上传',
dropzone: 'Dropzone',
sticky: 'Sticky',
countTo: 'CountTo',
componentMixin: '小组件',
backToTop: '返回顶部',
dragDialog: '拖拽 Dialog',
dragSelect: '拖拽 Select',
dragKanban: '可拖拽看板',
charts: '图表',
keyboardChart: '键盘图表',
lineChart: '折线图',
mixChart: '混合图表',
example: '综合实例',
nested: '路由嵌套',
menu1: '菜单1',
'menu1-1': '菜单1-1',
'menu1-2': '菜单1-2',
'menu1-2-1': '菜单1-2-1',
'menu1-2-2': '菜单1-2-2',
'menu1-3': '菜单1-3',
menu2: '菜单2',
Table: 'Table',
dynamicTable: '动态Table',
dragTable: '拖拽Table',
inlineEditTable: 'Table内编辑',
complexTable: '综合Table',
treeTable: '树形表格',
customTreeTable: '自定义树表',
tab: 'Tab',
form: '表单',
createArticle: '创建文章',
editArticle: '编辑文章',
articleList: '文章列表',
errorPages: '错误页面',
page401: '401',
page404: '404',
errorLog: '错误日志',
excel: 'Excel',
exportExcel: 'Export Excel',
selectExcel: 'Export Selected',
uploadExcel: 'Upload Excel',
zip: 'Zip',
pdf: 'PDF',
exportZip: 'Export Zip',
theme: '换肤',
clipboardDemo: 'Clipboard',
i18n: '国际化',
externalLink: '外链'
},
navbar: {
logOut: '退出登录',
dashboard: '首页',
github: '项目地址',
theme: '换肤',
size: '布局大小'
},
login: {
title: '系统登录',
logIn: '登录',
username: '账号',
password: '密码',
any: '随便填',
thirdparty: '第三方登录',
thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
},
documentation: {
documentation: '文档',
github: 'Github 地址'
},
permission: {
roles: '你的权限',
switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如:Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。'
},
guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
button: '打开引导'
},
components: {
documentation: '文档',
tinymceTips: '富文本是管理后台一个核心的功能,但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路,市面上常见的富文本都基本用过了,最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
stickyTips: '当页面滚动到预设的位置会吸附在顶部',
backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示,可在外部使用Element的el-tooltip元素',
imageUploadTips: '由于我在使用时它只有vue@1版本,而且和mockjs不兼容,所以自己改造了一下,如果大家要使用的话,优先还是使用官方版本。'
},
table: {
dynamicTips1: '固定表头, 按照表头顺序排序',
dynamicTips2: '不固定表头, 按照点击顺序排序',
dragTips1: '默认顺序',
dragTips2: '拖拽后顺序',
title: '标题',
importance: '重要性',
type: '类型',
remark: '点评',
search: '搜索',
add: '添加',
export: '导出',
reviewer: '审核人',
id: '序号',
date: '时间',
author: '作者',
readings: '阅读数',
status: '状态',
actions: '操作',
edit: '编辑',
publish: '发布',
draft: '草稿',
delete: '删除',
cancel: '取 消',
confirm: '确 定'
},
errorLog: {
tips: '请点击右上角bug小图标',
description: '现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',
documentation: '文档介绍'
},
excel: {
export: '导出',
selectedExport: '导出已选择项',
placeholder: '请输入文件名(默认excel-list)'
},
zip: {
export: '导出',
placeholder: '请输入文件名(默认file)'
},
pdf: {
tips: '这里使用 window.print() 来实现下载pdf的功能'
},
theme: {
change: '换肤',
documentation: '换肤文档',
tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
},
tagsView: {
refresh: '刷新',
close: '关闭',
closeOthers: '关闭其它',
closeAll: '关闭所有'
}
}
... ...
... ... @@ -12,6 +12,7 @@ import axios from 'axios'
//import 'nprogress/nprogress.css'
import rout from './routes'
import Mock from './mock'
import i18n from './lang'
import 'font-awesome/css/font-awesome.min.css'
//定义一个全局过滤器实现日期格式化
... ... @@ -59,18 +60,13 @@ router.beforeEach((to, from, next) => {
}
});
var message = Vue.prototype.$message;
axios.defaults.baseURL = '/api';
axios.interceptors.response.use(
res => {
//调用ELEMETN的message组件
let msg= Vue.prototype.$message;
//调用route组件
let route = Vue.prototype.$rout;
route.push({
path: "/main"
});
//对响应数据做些事
if (res.data && res.status!==200) {
msg({
message({
// 饿了么的消息弹窗组件,类似toast
showClose: true,
message: 'res.data.error.message.message ? res.data.error.message.message : res.data.error.message',
... ... @@ -82,7 +78,7 @@ axios.interceptors.response.use(
},
error => {
// 用户登录的时候会拿到一个基础信息,比如用户名,token,过期时间戳
// 直接丢localStorage或者sessionStorage
// 直接丢sessionStorage
if (!sessionStorage.getItem("token")) {
// 若是接口访问的时候没有发现有鉴权的基础信息,直接返回登录页
router.push({
... ... @@ -92,15 +88,14 @@ axios.interceptors.response.use(
// 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面
if (error.response.status === 401) {
Vue.prototype.$message({
// 饿了么的消息弹窗组件,类似toast
showClose: true,
message: '未授权,请登录',
router.push({
path: "/login"
});
message({
// 饿了么的消息弹窗组件
message: '授权超时,或未授权,请重新登录',
type: "error"
});
// router.push({
// path: "/login"
// });
}
// 下面是接口回调的satus ,因为我做了一些错误页面,所以都会指向对应的报错页面
... ... @@ -141,6 +136,7 @@ let vue = new Vue({
//template: '<App/>',
router,
store,
i18n,
//components: { App }
render: h => h(App)
}).$mount('#app')
... ...
... ... @@ -6,6 +6,7 @@ import Main from './views/Main.vue'
import Role from './views/nav1/role.vue'
import Perm from './views/nav1/perm.vue'
import LOG from './views/nav1/Log.vue'
import PreManifest from './views/agent/PreManifest.vue'
// import Form from './views/nav1/Form.vue'
import User from './views/nav1/user.vue'
... ... @@ -51,6 +52,16 @@ let routes = [
]
},
{
path: '/agent',
component: Home,
name: '代理人',
iconCls: 'fa fa-id-card-o',
children: [
{ path: '/pre', component: PreManifest, name: '预配' },
{ path: '/page5', component: Page5, name: '页面5' }
]
},
{
path: '/',
component: Home,
name: '导航二',
... ...
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
... ...
import Vue from 'vue'
import Clipboard from 'clipboard'
function clipboardSuccess() {
Vue.prototype.$message({
message: 'Copy successfully',
type: 'success',
duration: 1500
})
}
function clipboardError() {
Vue.prototype.$message({
message: 'Copy failed',
type: 'error'
})
}
export default function handleClipboard(text, event) {
const clipboard = new Clipboard(event.target, {
text: () => text
})
clipboard.on('success', () => {
clipboardSuccess()
clipboard.off('error')
clipboard.off('success')
clipboard.destroy()
})
clipboard.on('error', () => {
clipboardError()
clipboard.off('error')
clipboard.off('success')
clipboard.destroy()
})
clipboard.onClick(event)
}
... ...
const type = [{
value: 'CN',
label: 'USCI'
}, {
value: 'CN',
label: 'ID'
}, {
value: 'CN',
label: 'PASSPORT'
}, {
value: 'CN',
label: 'OC'
}, {
value: 'CN',
label: '9999'
}]
export default type
... ...
const country = [{
value: 'CN',
label: '中国'
}, {
value: 'US',
label: '美国'
}, {
value: 'JP',
label: '日本'
}, {
value: 'UN',
label: '英国'
}, {
value: 'EL',
label: '欧盟'
}]
export default country
... ...
const dangerGoods = [
{ value: 'RFX', label: '禁燃物' },
{ value: 'EXP', label: '快件' },
{ value: 'FRO', label: '冷藏货物' },
{ value: 'ELI', label: '锂电池' },
{ value: 'ELM', label: '锂金属电池' },
{ value: 'WET', label: '没有密水包装的湿' },
{ value: 'LHO', label: '人体器官血液' },
{ value: 'PEM', label: '肉类' },
{ value: 'HUM', label: '尸体' },
{ value: 'SPF', label: '实验动物' },
{ value: 'EAT', label: '食品' },
{ value: 'PEA', label: '兽皮等皮制品' },
{ value: 'PEP', label: '水果蔬菜' },
{ value: 'DIP', label: '外交邮袋' },
{ value: 'DGR', label: '危险品' },
{ value: 'PEF', label: '鲜花' },
{ value: 'PER', label: '鲜活易腐物品' },
{ value: 'XPS', label: '小件货物' },
{ value: 'PIL', label: '药品' },
{ value: 'FRI', label: '用于动植物检疫的' },
{ value: 'MAL', label: '邮件' },
{ value: 'ECM', label: '邮件' }
]
export default dangerGoods
... ...
const goodsPackage = [
{ value: '1', label: '纸箱' },
{ value: '2', label: '木箱' },
{ value: '3', label: '桶装' },
{ value: '4', label: '纸托' },
{ value: '5', label: '木托' },
{ value: '6', label: '塑料箱' },
{ value: '7', label: '金属托' },
{ value: '8', label: '散装' },
{ value: '9', label: '包' },
{ value: '10', label: '其他' }
]
export default goodsPackage
... ...
// translate router.meta.title, be used in breadcrumb sidebar tagsview
export function generateTitle(title) {
const hasKey = this.$te('route.' + title)
if (hasKey) {
// $t :this method from vue-i18n, inject in @/lang/index.js
const translatedTitle = this.$t('route.' + title)
return translatedTitle
}
return title
}
... ...
/**
* Created by jiachenpan on 16/11/18.
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
export function formatTime(time, option) {
time = +time * 1000
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
// less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return (
d.getMonth() +
1 +
'月' +
d.getDate() +
'日' +
d.getHours() +
'时' +
d.getMinutes() +
'分'
)
}
}
// 格式化时间
export function getQueryObject(url) {
url = url == null ? window.location.href : url
const search = url.substring(url.lastIndexOf('?') + 1)
const obj = {}
const reg = /([^?&=]+)=([^?&=]*)/g
search.replace(reg, (rs, $1, $2) => {
const name = decodeURIComponent($1)
let val = decodeURIComponent($2)
val = String(val)
obj[name] = val
return rs
})
return obj
}
/**
*get getByteLen
* @param {Sting} val input value
* @returns {number} output value
*/
export function getByteLen(val) {
let len = 0
for (let i = 0; i < val.length; i++) {
if (val[i].match(/[^\x00-\xff]/gi) != null) {
len += 1
} else {
len += 0.5
}
}
return Math.floor(len)
}
export function cleanArray(actual) {
const newArray = []
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i])
}
}
return newArray
}
export function param(json) {
if (!json) return ''
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return ''
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
})
).join('&')
}
export function param2Obj(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
)
}
export function html2Text(val) {
const div = document.createElement('div')
div.innerHTML = val
return div.textContent || div.innerText
}
export function objectMerge(target, source) {
/* Merges two objects,
giving the last one precedence */
if (typeof target !== 'object') {
target = {}
}
if (Array.isArray(source)) {
return source.slice()
}
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = objectMerge(target[property], sourceProperty)
} else {
target[property] = sourceProperty
}
})
return target
}
export function toggleClass(element, className) {
if (!element || !className) {
return
}
let classString = element.className
const nameIndex = classString.indexOf(className)
if (nameIndex === -1) {
classString += '' + className
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length)
}
element.className = classString
}
export const pickerOptions = [
{
text: '今天',
onClick(picker) {
const end = new Date()
const start = new Date(new Date().toDateString())
end.setTime(start.getTime())
picker.$emit('pick', [start, end])
}
},
{
text: '最近一周',
onClick(picker) {
const end = new Date(new Date().toDateString())
const start = new Date()
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
},
{
text: '最近一个月',
onClick(picker) {
const end = new Date(new Date().toDateString())
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
},
{
text: '最近三个月',
onClick(picker) {
const end = new Date(new Date().toDateString())
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}
]
export function getTime(type) {
if (type === 'start') {
return new Date().getTime() - 3600 * 1000 * 24 * 90
} else {
return new Date(new Date().toDateString())
}
}
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
/**
* This is just a simple version of deep copy
* Has a lot of edge cases bug
* If you want to use a perfect deep copy, use lodash's _.cloneDeep
*/
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'shallowClone')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
export function uniqueArr(arr) {
return Array.from(new Set(arr))
}
export function createUniqueString() {
const timestamp = +new Date() + ''
const randomNum = parseInt((1 + Math.random()) * 65536) + ''
return (+(randomNum + timestamp)).toString(32)
}
... ...
/**
*Created by jiachenpan on 16/11/29.
* @param {Sting} url
* @param {Sting} title
* @param {Number} w
* @param {Number} h
*/
export default function openWindow(url, title, w, h) {
// Fixes dual-screen position Most browsers Firefox
const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width
const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height
const left = ((width / 2) - (w / 2)) + dualScreenLeft
const top = ((height / 2) - (h / 2)) + dualScreenTop
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)
// Puts focus on the newWindow
if (window.focus) {
newWindow.focus()
}
}
... ...
import store from '@/store'
/**
* @param {Array} value
* @returns {Boolean}
* @example see @/views/permission/directive.vue
*/
export default function checkPermission(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
return false
}
return true
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
... ...
import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.BASE_API, // api 的 base_url
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// Do something before request is sent
if (store.getters.token) {
// 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
config.headers['X-Token'] = getToken()
}
return config
},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
response => response,
/**
* 下面的注释为通过在response里,自定义code来标示请求状态
* 当code返回如下情况则说明权限有问题,登出并返回到登录页
* 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
* 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
*/
// response => {
// const res = response.data
// if (res.code !== 20000) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// })
// // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// // 请自行在引入 MessageBox
// // import { Message, MessageBox } from 'element-ui'
// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// confirmButtonText: '重新登录',
// cancelButtonText: '取消',
// type: 'warning'
// }).then(() => {
// store.dispatch('FedLogOut').then(() => {
// location.reload() // 为了重新实例化vue-router对象 避免bug
// })
// })
// }
// return Promise.reject('error')
// } else {
// return response.data
// }
// },
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
... ...
Math.easeInOutQuad = function(t, b, c, d) {
t /= d / 2
if (t < 1) {
return c / 2 * t * t + b
}
t--
return -c / 2 * (t * (t - 2) - 1) + b
}
// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
var requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
})()
// because it's so fucking difficult to detect the scrolling element, just move them all
function move(amount) {
document.documentElement.scrollTop = amount
document.body.parentNode.scrollTop = amount
document.body.scrollTop = amount
}
function position() {
return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
}
export function scrollTo(to, duration, callback) {
const start = position()
const change = to - start
const increment = 20
let currentTime = 0
duration = (typeof (duration) === 'undefined') ? 500 : duration
var animateScroll = function() {
// increment the time
currentTime += increment
// find the value with the quadratic in-out easing function
var val = Math.easeInOutQuad(currentTime, start, change, duration)
// move the document.body
move(val)
// do the animation unless its over
if (currentTime < duration) {
requestAnimFrame(animateScroll)
} else {
if (callback && typeof (callback) === 'function') {
// the animation is done so lets callback
callback()
}
}
}
animateScroll()
}
... ...
/**
* 板箱类型
*
*/
const uldType = ['AKE','AVE','DPE','DQF','PMC','PAG','PLA','PKC']
export default uldType
... ...
/**
* Created by jiachenpan on 16/11/18.
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
/* 合法uri*/
export function validURL(url) {
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.,?'\\+&%$#=~_-]+))*$/
return reg.test(url)
}
/* 小写字母*/
export function validLowerCase(str) {
const reg = /^[a-z]+$/
return reg.test(str)
}
/* 大写字母*/
export function validUpperCase(str) {
const reg = /^[A-Z]+$/
return reg.test(str)
}
/* 大小写字母*/
export function validAlphabets(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
/**
* validate email
* @param email
* @returns {boolean}
*/
export function validEmail(email) {
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,}))$/
return re.test(email)
}
... ...
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.title" style="width: 200px;" class="filter-item" placeholder="运单号"/>
<el-input v-model="listQuery.title" style="width: 200px;" class="filter-item" placeholder="航班号"/>
<el-input v-model="listQuery.title" style="width: 200px;" class="filter-item" placeholder="航班日期"/>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">{{ $t('table.search') }}</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">{{ $t('table.add') }}</el-button>
<el-button v-waves :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">{{ $t('table.export') }}</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-message" @click="handleCreate">批量发送</el-button>
</div>
<tree-table :data="data" :eval-func="func" :eval-args="args" :expand-all="expandAll" stripe="true" style="font-size: 13px" border @selection-change="handleSelectionChange">
<el-table-column prop="waybillNo" label="主单号" width="110px" align="center" sortable>
<template slot-scope="scope">
<span>{{ scope.row.waybillNo }}</span>
</template>
</el-table-column>
<el-table-column label="分单号" width="120px" align="center" sortable>
<template slot-scope="scope">
<span>{{ scope.row.houseWaybillNo }}</span>
</template>
</el-table-column>
<el-table-column label="航班号" width="90px" align="center" sortable>
<template slot-scope="scope">
<span>{{ scope.row.flight }}</span>
</template>
</el-table-column>
<el-table-column label="航班日期" width="100px" align="center" sortable>
<template slot-scope="scope">
<span>{{ scope.row.flightDate | parseTime('{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="起始站" width="60px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.oriStation }}</span>
</template>
</el-table-column>
<el-table-column label="目的站" width="60px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.desStation }}</span>
</template>
</el-table-column>
<el-table-column label="预配件数" width="80px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.maniPiece }}</span>
</template>
</el-table-column>
<el-table-column label="预配重量" width="80px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.maniWeight }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('table.status')" class-name="status-col" width="90" sortable>
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="回执内容" width="230px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.customText }}</span>
<div>
<el-progress :percentage="scope.row.customComplate" :status="scope.row.status | statusFilter" />
</div>
</template>
</el-table-column>
<el-table-column :label="$t('table.actions')" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleUpdate(scope.row)">快速编辑</el-button>
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">{{ $t('table.edit') }}</el-button>
<el-button v-if="scope.row.status!='41301'" size="mini" type="success" @click="handleModifyStatus(scope.row,'41301')">{{ $t('table.publish') }}</el-button>
<el-button v-if="scope.row.status!='10002'" size="small" @click="handleModifyStatus(scope.row,'10002')">客服反馈</el-button>
<el-button v-if="scope.row.status=='10002'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">{{ $t('table.delete') }}</el-button>
<el-switch
v-model="resend"
active-color="#13ce66"
inactive-color="#ff4949" />
</template>
</el-table-column>
</tree-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" fullscreen="true">
<el-form ref="" :inline="true" :label-position="left" :rules="rules" :model="temp" label-width="100px" style="font-size: 13px">
<div>
<el-tag type="info" effect="plain">
航班信息
</el-tag>
</div>
<el-row type="flex" class="row-bg bg-purple">
<el-col :span="24">
<div class="grid-content">
<el-form-item label="航班号" prop="flight">
<el-input v-model="temp.flight"/>
</el-form-item>
<el-form-item label="航班日期" prop="flightDate">
<el-date-picker :picker-options="pickerOptions" v-model="temp.flightDate" align="right" type="date" placeholder="请输入航班日期" />
</el-form-item>
</div>
</el-col>
</el-row>
<div>
<el-tag type="info" effect="plain">
运单信息
</el-tag>
</div>
<el-form-item label="主单号" prop="waybillNo">
<el-input v-model="temp.waybillNo"/>
</el-form-item>
<el-form-item label="分单号" prop="houseWaybillNo">
<el-input v-model="temp.houseWaybillNo"/>
</el-form-item>
<el-form-item label="关区代码" prop="custom">
<el-select v-model="temp.custom" class="filter-item" placeholder="请选择关区代码">
<el-option v-for="item in manifestCustoms" :key="item" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item label="起始站" prop="oriStation">
<el-input v-model="temp.oriStation"/>
</el-form-item>
<el-form-item label="目的站" prop="desStation">
<el-input v-model="temp.desStation"/>
</el-form-item>
<el-form-item label="预配件数" prop="maniPiece">
<el-input v-model.number="temp.maniPiece"/>
</el-form-item>
<el-form-item label="预配重量" prop="maniWeight">
<el-input v-model.number="temp.maniWeight"/>
</el-form-item>
<el-form-item label="付费方式" prop="payMode">
<el-select v-model="temp.payMode" class="filter-item" placeholder="付费方式">
<el-option v-for="item in manifestCustoms" :key="item" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item label="海关状态" prop="goodsType">
<el-select v-model="temp.goodsType" class="filter-item" placeholder="请录入货物类型">
<el-option v-for="item in manifestCustoms" :key="item" :label="item" :value="item"/>
</el-select>
</el-form-item>
<div>
<el-tag type="info" effect="plain">
发货人信息
</el-tag>
</div>
<el-row type="flex" class="row-bg bg-purple">
<el-col :span="24">
<div class="grid-content">
<el-form-item label="公司" prop="shpCompany">
<el-input v-model="temp.shpCompany"/>
</el-form-item>
<el-form-item label="地址" prop="shpAddress">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="城市" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="邮编" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="电话" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="传真" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
</div>
<div>
<el-form-item label="国家" prop="shpCountry">
<el-select
:remote-method="remoteMethod"
:loading="loading"
v-model="temp.country"
filterable
remote
placeholder="请选择" >
<el-option
v-for="item in countryOption"
:key="item.value"
:label="item.label"
:value="item.value">
<span style="float: left">{{ item.label }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="企业代码类型" prop="shpCompnyType">
<el-select
v-model="temp.shpCompnyType"
filterable
placeholder="请选择企业代码类型" >
<el-option
v-for="item in shpCompnyTypeOption"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="shpCity">
<el-input v-model="temp.shpAddress" placeholder="企业代码"/>
</el-form-item>
</div>
</el-col>
</el-row>
<div>
<el-tag type="info" effect="plain">
收货人信息
</el-tag>
</div>
<el-row type="flex" class="row-bg bg-purple">
<el-col :span="24">
<div class="grid-content">
<el-form-item label="公司" prop="shpCompany">
<el-input v-model="temp.shpCompany"/>
</el-form-item>
<el-form-item label="地址" prop="shpAddress">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="城市" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="邮编" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="电话" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
<el-form-item label="传真" prop="shpCity">
<el-input v-model="temp.shpAddress"/>
</el-form-item>
</div>
<div>
<el-form-item label="国家" prop="shpCountry">
<el-select
:remote-method="remoteMethod"
:loading="loading"
v-model="temp.country"
filterable
remote
placeholder="请选择" >
<el-option
v-for="item in countryOption"
:key="item.value"
:label="item.label"
:value="item.value">
<span style="float: left">{{ item.label }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="企业代码类型" prop="shpCompnyType">
<el-select
v-model="temp.shpCompnyType"
filterable
placeholder="请选择企业代码类型" >
<el-option
v-for="item in shpCompnyTypeOption"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="shpCity">
<el-input v-model="temp.shpAddress" placeholder="企业代码"/>
</el-form-item>
</div>
</el-col>
</el-row>
<div>
<el-tag type="info" effect="plain">
危险品信息
</el-tag>
</div>
<el-row type="flex" class="row-bg bg-purple">
<el-col :span="24">
<el-form-item label="危险品信息" prop="shpCompnyType">
<el-select
v-model="temp.dangerGoods"
placeholder="请选择危险品类型" >
<el-option
v-for="item in dangerGoods"
:key="item.value"
:label="item.label"
:value="item.value" >
<span style="float: left">{{ item.label }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.value }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="危险品类别" prop="shpCity">
<el-input v-model="temp.shpAddress" placeholder="危险品类别"/>
</el-form-item>
</el-col>
</el-row>
<div>
<el-tag type="info" effect="plain">
交运信息
</el-tag>
</div>
<el-row type="flex" class="row-bg bg-purple">
<el-col :span="24">
<el-form-item label="包装" prop="shpCompnyType">
<el-select
v-model="temp.goodsPackage"
placeholder="请选择包装类型" >
<el-option
v-for="item in goodsPackage"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="尺寸" prop="shpCity">
<el-input v-model="temp.shpAddress" placeholder="请输入尺寸"/>
</el-form-item>
<el-form-item label="计费重量" prop="shpCity">
<el-input v-model="temp.shpAddress" placeholder="请输入计费重量"/>
</el-form-item>
<el-form-item label="体积" prop="shpCity">
<el-input v-model="temp.shpAddress" placeholder="请输入体积"/>
</el-form-item>
</el-col>
</el-row>
<el-row type="flex" class="row-bg bg-purple">
<el-col :span="24">
<div class="remark">
<el-form-item label="备注" prop="shpCity" inline-message="false">
<el-input
v-model="temp.shpAddress"
type="textarea"
placeholder="其他需要备注的信息"
maxlength="30"
show-word-limit
/>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">{{ $t('table.cancel') }}</el-button>
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">{{ $t('table.confirm') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/**
Auth: Lei.j1ang
Created: 2018/1/19-14:54
*/
import treeTable from '@/components/TreeTable'
import treeToArray from './customEval'
import Pagination from '@/components/Pagination'
import waves from '@/directive/waves' // Waves directive
import country from '@/utils/country.js'
import companyType from '@/utils/companyType.js'
import dangerGoods from '@/utils/dangerGoods.js'
import goodsPackage from '@/utils/goodsPackage.js'
const calendarTypeOptions = [
{ key: 'CN', display_name: 'China' },
{ key: 'US', display_name: 'USA' },
{ key: 'JP', display_name: 'Japan' },
{ key: 'EU', display_name: 'Eurozone' }
]
// arr to obj ,such as { CN : "China", US : "USA" }
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name
return acc
}, {})
treeTable.expandAll = {
type: Boolean,
default: false
}
export default {
name: 'CustomTreeTableDemo',
components: { treeTable, Pagination },
directives: { waves },
filters: {
statusFilter(status) {
const statusMap = {
10003: '',
41301: 'success',
10002: 'info',
10000: 'danger',
45103: 'warning',
wrong: 'exception'
}
return statusMap[status]
},
typeFilter(type) {
return calendarTypeKeyValue[type]
}
},
data() {
return {
func: treeToArray,
expandAll: true,
total: 1,
resend: true,
listLoading: true,
multipleSelection: [],
dialogFormVisible: false,
dialogStatus: '',
country: country,
countryOption: [],
shpCompnyTypeOption: [],
dangerGoods: dangerGoods,
goodsPackage: goodsPackage,
textMap: {
update: '编辑',
create: '添加'
},
listQuery: {
page: 1,
limit: 20,
waybillNo: '',
flight: '',
flightDate: '',
custom: ''
},
temp: {
id: undefined,
waybillNo: '',
houseWaybillNo: '',
custom: '4604',
flight: '',
flightDate: '',
oriStation: 'CGO',
desStation: '',
maniPiece: undefined,
maniWeight: undefined
},
manifestCustoms: ['4604', '4620', '4613'],
data: [
{
id: 0,
waybillNo: '580-20728399',
houseWaybillNo: '',
custom: '4604',
flight: 'CV9733',
flightDate: '2019-06-21',
oriStation: 'LUX',
desStation: 'CGO',
maniPiece: 50,
maniWeight: 21321,
status: '41301',
customText: ' 预配舱单主要数据传输成功。',
customComplate: 25
},
{
id: 1,
waybillNo: '580-20728396',
houseWaybillNo: '',
custom: '4604',
flight: 'CV9731',
flightDate: '2019-06-21',
oriStation: 'LUX',
desStation: 'CGO',
maniPiece: 50,
maniWeight: 21321,
status: 'wrong',
customText: ' 预配回执异常 ',
customComplate: 25,
children: [
{
id: 2,
waybillNo: '580-20728396',
houseWaybillNo: 'ADDSS21231',
custom: '4604',
flight: 'CV9731',
flightDate: '2019-06-21',
oriStation: 'LUX',
desStation: 'CGO',
maniPiece: 50,
maniWeight: 21321,
status: '10002',
customText: ' 已暂存',
customComplate: 0
},
{
id: 3,
waybillNo: '580-20728396',
houseWaybillNo: 'SDE3411',
custom: '4604',
flight: 'CV9731',
flightDate: '2019-06-21',
oriStation: 'LUX',
desStation: 'CGO',
maniPiece: 50,
maniWeight: 21321,
status: '45103',
customText: ' 45103 该提(运)单的运抵报告重复申报,海关审核不通过。 关区代码:4604。',
customComplate: 100
},
{
id: 4,
waybillNo: '580-20728396',
houseWaybillNo: 'SDE3411',
custom: '4604',
flight: 'CV9731',
flightDate: '2019-06-21',
oriStation: 'LUX',
desStation: 'CGO',
maniPiece: 50,
maniWeight: 21321,
status: '10000',
customText: ' 已删除 ',
customComplate: 50
}
]
},
{
id: 4,
waybillNo: '580-20728391',
houseWaybillNo: '',
custom: '4604',
flight: 'KA740',
flightDate: '2019-06-29',
oriStation: 'LUX',
desStation: 'CGO',
maniPiece: 50,
maniWeight: 21321,
status: '10003',
customText: ' 已发送预配舱单 '
}
],
pickerOptions: {
shortcuts: [
{
text: '今天',
onClick(picker) {
picker.$emit('pick', new Date())
}
},
{
text: '昨天',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24)
picker.$emit('pick', date)
}
},
{
text: '明天',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() + 3600 * 1000 * 24)
picker.$emit('pick', date)
}
},
{
text: '一周前',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', date)
}
}
]
},
args: [null, null, 'timeLine'],
rules: {
maniWeight: [{ type: 'number', required: true, message: '重量必须为数字', trigger: 'blur' }],
maniPiece: [{ type: 'number', required: true, message: '件数必须为数字', trigger: 'blur' }],
desStation: [{ required: true, message: '运单目的地必须输入', trigger: 'blur' }],
oriStation: [{ required: true, message: '运单起始站必须数据', trigger: 'change' }],
flightDate: [{ type: 'date', required: true, message: '航班日期必须输入', trigger: 'change' }],
flight: [{ required: true, message: '航班号必须输入', trigger: 'blur' }],
custom: [{ required: true, message: '关区代码必须选择', trigger: 'change' }],
waybillNo: [{ required: true, message: '运单号必须输入', trigger: 'blur' }]
}
}
},
methods: {
message(row) {
this.$message.info(row.event)
},
sortChange(data) {
const { prop, order } = data
if (prop === 'id') {
this.sortByID(order)
}
},
sortByID(order) {
if (order === 'ascending') {
this.listQuery.sort = '+id'
} else {
this.listQuery.sort = '-id'
}
this.handleFilter()
},
handleUpdate(row) {
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs[''].clearValidate()
})
},
handleModifyStatus(row, status) {
this.$message({
message: '操作成功',
type: 'success'
})
row.status = status
},
getList() {
},
handleSelectionChange(val) {
this.multipleSelection = val
},
resetTemp() {
this.temp = {
id: undefined,
waybillNo: '',
houseWaybillNo: '',
custom: '4604',
flight: '',
flightDate: '',
oriStation: 'CGO',
desStation: '',
maniPiece: undefined,
maniWeight: undefined
}
},
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs[''].clearValidate()
})
},
remoteMethod(query) {
if (query !== '') {
this.loading = true
setTimeout(() => {
this.loading = false
this.countryOption = this.country.filter(item => {
return item.value.toUpperCase()
.indexOf(query.toUpperCase()) > -1
})
this.shpCompnyTypeOption = companyType.filter(item => {
return item.value.toUpperCase()
.indexOf(query.toUpperCase()) > -1
})
}, 200)
} else {
this.countryOption = []
}
}
}
}
</script>
<style>
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
.el-progress.is-warning .el-progress-bar__inner{
background-color:#E6A23C
}
.remark textarea{
width: 600px;
}
</style>
... ...
/**
* @Author: jianglei
* @Date: 2017-10-12 12:06:49
*/
'use strict'
import Vue from 'vue'
export default function treeToArray(data, expandAll, parent, level, item) {
const marLTemp = []
let tmp = []
Array.from(data).forEach(function(record) {
if (record._expanded === undefined) {
Vue.set(record, '_expanded', expandAll)
}
let _level = 1
if (level !== undefined && level !== null) {
_level = level + 1
}
Vue.set(record, '_level', _level)
// 如果有父元素
if (parent) {
Vue.set(record, 'parent', parent)
// 如果父元素有偏移量,需要计算在this的偏移量中
// 偏移量还与前面同级元素有关,需要加上前面所有元素的长度和
if (!marLTemp[_level]) {
marLTemp[_level] = 0
}
Vue.set(record, '_marginLeft', marLTemp[_level] + parent._marginLeft)
Vue.set(record, '_width', record[item] / parent[item] * parent._width)
// 在本次计算过偏移量后加上自己长度,以供下一个元素使用
marLTemp[_level] += record._width
} else {
// 如果为根
// 初始化偏移量存储map
marLTemp[record.id] = []
// map中是一个数组,存储的是每级的长度和
// 初始情况下为0
marLTemp[record.id][_level] = 0
Vue.set(record, '_marginLeft', 0)
Vue.set(record, '_width', 1)
}
tmp.push(record)
if (record.children && record.children.length > 0) {
const children = treeToArray(record.children, expandAll, record, _level, item)
tmp = tmp.concat(children)
}
})
return tmp
}
... ...
... ... @@ -274,14 +274,14 @@
};
this.listLoading = true;
getUserList(para).then((res) => {
this.total = res.data.total;
this.users = res.data.list;
let response = res.data.data;
this.total = response.total;
this.users = response.list;
this.listLoading = false;
//NProgress.done();
}).catch((error) => {
this.listLoading = false;
alert(error);
this.$message.error(error.toString());
});
},
... ...