Merge pull request #1 from PanJiaChen/master

更新到最新
This commit is contained in:
frank10000 2018-11-17 11:16:58 +08:00 committed by GitHub
commit 9afc46ccf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 611 additions and 84 deletions

View File

@ -118,6 +118,7 @@ Understanding and learning this knowledge in advance will greatly help the use o
- Avatar Upload - Avatar Upload
- Back To Top - Back To Top
- Drag Dialog - Drag Dialog
- Drag Select
- Drag Kanban - Drag Kanban
- Drag List - Drag List
- SplitPane - SplitPane
@ -198,6 +199,14 @@ If you find this project useful, you can buy author a glass of juice :tropical_d
[Buy me a coffee](https://www.buymeacoffee.com/Pan) [Buy me a coffee](https://www.buymeacoffee.com/Pan)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License ## License
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE) [MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)

View File

@ -130,6 +130,7 @@
- 头像上传 - 头像上传
- 返回顶部 - 返回顶部
- 拖拽Dialog - 拖拽Dialog
- 拖拽Select
- 拖拽看板 - 拖拽看板
- 列表拖拽 - 列表拖拽
- SplitPane - SplitPane
@ -210,6 +211,14 @@ Detailed changes for each release are documented in the [release notes](https://
[Paypal Me](https://www.paypal.me/panfree23) [Paypal Me](https://www.paypal.me/panfree23)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License ## License
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE) [MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)

View File

@ -1,6 +1,6 @@
{ {
"name": "vue-element-admin", "name": "vue-element-admin",
"version": "3.9.1", "version": "3.9.3",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features", "description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>", "author": "Pan <panfree23@gmail.com>",
"license": "MIT", "license": "MIT",

View File

@ -3,7 +3,7 @@
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" v-if="item.meta.title" :key="item.path"> <el-breadcrumb-item v-for="(item,index) in levelList" v-if="item.meta.title" :key="item.path">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ generateTitle(item.meta.title) }}</span> <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ generateTitle(item.meta.title) }}</span>
<router-link v-else :to="item.redirect||item.path">{{ generateTitle(item.meta.title) }}</router-link> <a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
</el-breadcrumb> </el-breadcrumb>
@ -30,12 +30,8 @@ export default {
methods: { methods: {
generateTitle, generateTitle,
getBreadcrumb() { getBreadcrumb() {
const { params } = this.$route
let matched = this.$route.matched.filter(item => { let matched = this.$route.matched.filter(item => {
if (item.name) { if (item.name) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
var toPath = pathToRegexp.compile(item.path)
item.path = toPath(params)
return true return true
} }
}) })
@ -44,6 +40,20 @@ export default {
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched) matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
} }
this.levelList = matched this.levelList = matched
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
} }
} }
} }

View File

@ -1,6 +1,11 @@
import { debounce } from '@/utils' import { debounce } from '@/utils'
export default { export default {
data() {
return {
sidebarElm: null
}
},
mounted() { mounted() {
this.__resizeHandler = debounce(() => { this.__resizeHandler = debounce(() => {
if (this.chart) { if (this.chart) {
@ -9,14 +14,13 @@ export default {
}, 100) }, 100)
window.addEventListener('resize', this.__resizeHandler) window.addEventListener('resize', this.__resizeHandler)
const sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler) this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.__resizeHandler) window.removeEventListener('resize', this.__resizeHandler)
const sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
}, },
methods: { methods: {
sidebarResizeHandler(e) { sidebarResizeHandler(e) {

View File

@ -0,0 +1,61 @@
<template>
<el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners">
<slot/>
</el-select>
</template>
<script>
import Sortable from 'sortablejs'
export default {
name: 'DragSelect',
props: {
value: {
type: Array,
required: true
}
},
computed: {
selectVal: {
get() {
return [...this.value]
},
set(val) {
this.$emit('input', [...val])
}
}
},
mounted() {
this.setSort()
},
methods: {
setSort() {
const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0]
this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
setData: function(dataTransfer) {
dataTransfer.setData('Text', '')
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
},
onEnd: evt => {
const targetRow = this.value.splice(evt.oldIndex, 1)[0]
this.value.splice(evt.newIndex, 0, targetRow)
}
})
}
}
}
</script>
<style scoped>
.drag-select >>> .sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
.drag-select >>> .el-tag{
cursor: pointer;
}
</style>

View File

@ -6,6 +6,7 @@
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item> <el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item> <el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item>
<el-dropdown-item :disabled="language==='es'" command="es">Español</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</template> </template>

View File

@ -0,0 +1,99 @@
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
: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>

View File

@ -5,7 +5,7 @@
</template> </template>
<script> <script>
const padding = 15 // tag's padding const tagAndTagSpacing = 4 // tagAndTagSpacing
export default { export default {
name: 'ScrollPane', name: 'ScrollPane',
@ -20,18 +20,54 @@ export default {
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
}, },
moveToTarget($target) { moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el const $container = this.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth const $containerWidth = $container.offsetWidth
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
const $targetLeft = $target.offsetLeft const tagList = this.$parent.$refs.tag
const $targetWidth = $target.offsetWidth
if ($targetLeft > $containerWidth) { let firstTag = null
// tag in the right let lastTag = null
$scrollWrapper.scrollLeft = $targetLeft - $containerWidth + $targetWidth + padding let prevTag = null
let nextTag = null
// find first tag and last tag
if (tagList.length > 0) {
firstTag = tagList[0]
lastTag = tagList[tagList.length - 1]
}
// find preTag and nextTag
for (let i = 0; i < tagList.length; i++) {
if (tagList[i] === currentTag) {
if (i === 0) {
nextTag = tagList[i].length > 1 && tagList[i + 1]
} else if (i === tagList.length - 1) {
prevTag = tagList[i].length > 1 && tagList[i - 1]
} else { } else {
// tag in the left prevTag = tagList[i - 1]
$scrollWrapper.scrollLeft = $targetLeft - padding nextTag = tagList[i + 1]
}
break
}
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
} else {
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
}
} }
} }
} }

View File

@ -1,6 +1,6 @@
// Here is a list of the toolbar // Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
const toolbar = ['bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
export default toolbar export default toolbar

View File

@ -65,7 +65,7 @@
如果不提供,将使用默认的[evalFunc](./eval.js) 如果不提供,将使用默认的[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/example/table/treeTable/customEval.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 #### evalArgs
解析函数的参数,是一个数组 解析函数的参数,是一个数组
@ -76,12 +76,12 @@
如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了 如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了
具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customTreeTable.vue)的`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 ## slot
这是一个自定义列的插槽。 这是一个自定义列的插槽。
默认情况下treeTable只有一行行展示数据的功能。但是一般情况下我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/example/table/treeTable/customTreeTable.vue)[实例效果](http://panjiachen.github.io/vue-element-admin/#/example/table/custom-tree-table) 默认情况下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自定义列的左边展示 `slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示

View File

@ -22,6 +22,7 @@ export default {
componentMixin: 'Mixin', componentMixin: 'Mixin',
backToTop: 'BackToTop', backToTop: 'BackToTop',
dragDialog: 'Drag Dialog', dragDialog: 'Drag Dialog',
dragSelect: 'Drag Select',
dragKanban: 'Drag Kanban', dragKanban: 'Drag Kanban',
charts: 'Charts', charts: 'Charts',
keyboardChart: 'Keyboard Chart', keyboardChart: 'Keyboard Chart',

156
src/lang/es.js Executable file
View File

@ -0,0 +1,156 @@
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',
exportZip: 'Exportar a Zip',
theme: 'Tema',
clipboardDemo: 'Clipboard',
i18n: 'I18n',
externalLink: 'Enlace externo'
},
navbar: {
logOut: 'Salir',
dashboard: 'Panel de control',
github: 'Github',
screenfull: 'Pantalla completa',
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'
},
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'
},
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'
}
}

View File

@ -3,8 +3,10 @@ import VueI18n from 'vue-i18n'
import Cookies from 'js-cookie' import Cookies from 'js-cookie'
import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang 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 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 enLocale from './en'
import zhLocale from './zh' import zhLocale from './zh'
import esLocale from './es'
Vue.use(VueI18n) Vue.use(VueI18n)
@ -16,12 +18,16 @@ const messages = {
zh: { zh: {
...zhLocale, ...zhLocale,
...elementZhLocale ...elementZhLocale
},
es: {
...esLocale,
...elementEsLocale
} }
} }
const i18n = new VueI18n({ const i18n = new VueI18n({
// set locale // set locale
// options: en or zh // options: en | zh | es
locale: Cookies.get('language') || 'en', locale: Cookies.get('language') || 'en',
// set locale messages // set locale messages
messages messages

View File

@ -22,6 +22,7 @@ export default {
componentMixin: '小组件', componentMixin: '小组件',
backToTop: '返回顶部', backToTop: '返回顶部',
dragDialog: '拖拽 Dialog', dragDialog: '拖拽 Dialog',
dragSelect: '拖拽 Select',
dragKanban: '可拖拽看板', dragKanban: '可拖拽看板',
charts: '图表', charts: '图表',
keyboardChart: '键盘图表', keyboardChart: '键盘图表',

View File

@ -4,6 +4,16 @@ import articleAPI from './article'
import remoteSearchAPI from './remoteSearch' import remoteSearchAPI from './remoteSearch'
import transactionAPI from './transaction' import transactionAPI from './transaction'
// 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题
// https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
Mock.XHR.prototype.send = function() {
if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false
}
this.proxy_send(...arguments)
}
// Mock.setup({ // Mock.setup({
// timeout: '350-600' // timeout: '350-600'
// }) // })

View File

@ -78,6 +78,12 @@ const componentsRouter = {
name: 'DragDialogDemo', name: 'DragDialogDemo',
meta: { title: 'dragDialog' } meta: { title: 'dragDialog' }
}, },
{
path: 'drag-select',
component: () => import('@/views/components-demo/dragSelect'),
name: 'DragSelectDemo',
meta: { title: 'dragSelect' }
},
{ {
path: 'dnd-list', path: 'dnd-list',
component: () => import('@/views/components-demo/dndList'), component: () => import('@/views/components-demo/dndList'),

View File

@ -164,17 +164,6 @@ export function objectMerge(target, source) {
return target return target
} }
export function scrollTo(element, to, duration) {
if (duration <= 0) return
const difference = to - element.scrollTop
const perTick = (difference / duration) * 10
setTimeout(() => {
element.scrollTop = element.scrollTop + perTick
if (element.scrollTop === to) return
scrollTo(element, to, duration - 10)
}, 10)
}
export function toggleClass(element, className) { export function toggleClass(element, className) {
if (!element || !className) { if (!element || !className) {
return return

50
src/utils/scrollTo.js Normal file
View File

@ -0,0 +1,50 @@
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()
}

View File

@ -0,0 +1,43 @@
<template>
<div class="components-container">
<el-drag-select v-model="value" style="width:500px;" multiple placeholder="请选择">
<el-option v-for="item in options" :label="item.label" :value="item.value" :key="item.value" />
</el-drag-select>
<div style="margin-top:30px;">
<el-tag v-for="item of value" :key="item" style="margin-right:15px;">{{ item }}</el-tag>
</div>
</div>
</template>
<script>
import ElDragSelect from '@/components/DragSelect' // base on element-ui
export default {
name: 'DragSelectDemo',
components: { ElDragSelect },
data() {
return {
value: ['Apple', 'Banana', 'Orange'],
options: [{
value: 'Apple',
label: 'Apple'
}, {
value: 'Banana',
label: 'Banana'
}, {
value: 'Orange',
label: 'Orange'
}, {
value: 'Pear',
label: 'Pear'
}, {
value: 'Strawberry',
label: 'Strawberry'
}]
}
}
}
</script>

View File

@ -32,7 +32,8 @@ export default {
}, },
data() { data() {
return { return {
chart: null chart: null,
sidebarElm: null
} }
}, },
watch: { watch: {
@ -55,8 +56,8 @@ export default {
} }
// //
const sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler) this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
}, },
beforeDestroy() { beforeDestroy() {
if (!this.chart) { if (!this.chart) {
@ -66,8 +67,7 @@ export default {
window.removeEventListener('resize', this.__resizeHandler) window.removeEventListener('resize', this.__resizeHandler)
} }
const sidebarElm = document.getElementsByClassName('sidebar-container')[0] this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
this.chart.dispose() this.chart.dispose()
this.chart = null this.chart = null

View File

@ -143,7 +143,8 @@ export default {
title: [{ validator: validateRequire }], title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }], content: [{ validator: validateRequire }],
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }] source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
} },
tempRoute: {}
} }
}, },
computed: { computed: {
@ -161,6 +162,11 @@ export default {
} else { } else {
this.postForm = Object.assign({}, defaultForm) this.postForm = Object.assign({}, defaultForm)
} }
// Why need to make a copy of this.$route here?
// Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
// https://github.com/PanJiaChen/vue-element-admin/issues/1221
this.tempRoute = Object.assign({}, this.$route)
}, },
methods: { methods: {
fetchData(id) { fetchData(id) {
@ -178,7 +184,7 @@ export default {
}, },
setTagsViewTitle() { setTagsViewTitle() {
const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article' const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article'
const route = Object.assign({}, this.$route, { title: `${title}-${this.postForm.id}` }) const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
this.$store.dispatch('updateVisitedView', route) this.$store.dispatch('updateVisitedView', route)
}, },
submitForm() { submitForm() {

View File

@ -50,26 +50,18 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="pagination-container"> <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
<el-pagination
:current-page="listQuery.page"
:page-sizes="[10,20,30, 50]"
:page-size="listQuery.limit"
:total="total"
background
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"/>
</div>
</div> </div>
</template> </template>
<script> <script>
import { fetchList } from '@/api/article' import { fetchList } from '@/api/article'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
export default { export default {
name: 'ArticleList', name: 'ArticleList',
components: { Pagination },
filters: { filters: {
statusFilter(status) { statusFilter(status) {
const statusMap = { const statusMap = {

View File

@ -9,6 +9,7 @@
<el-radio-group v-model="lang" size="small"> <el-radio-group v-model="lang" size="small">
<el-radio label="zh" border>简体中文</el-radio> <el-radio label="zh" border>简体中文</el-radio>
<el-radio label="en" border>English</el-radio> <el-radio label="en" border>English</el-radio>
<el-radio label="es" border>Español</el-radio>
</el-radio-group> </el-radio-group>
<el-tag style="margin-top:15px;display:block;" type="info">{{ $t('i18nView.note') }}</el-tag> <el-tag style="margin-top:15px;display:block;" type="info">{{ $t('i18nView.note') }}</el-tag>
</div> </div>
@ -101,6 +102,7 @@ export default {
if (!this.$i18n.getLocaleMessage('en')[viewName]) { if (!this.$i18n.getLocaleMessage('en')[viewName]) {
this.$i18n.mergeLocaleMessage('en', local.en) this.$i18n.mergeLocaleMessage('en', local.en)
this.$i18n.mergeLocaleMessage('zh', local.zh) this.$i18n.mergeLocaleMessage('zh', local.zh)
this.$i18n.mergeLocaleMessage('es', local.es)
} }
this.setOptions() // set default select options this.setOptions() // set default select options
}, },

View File

@ -39,5 +39,25 @@ export default {
two: 'Two', two: 'Two',
three: 'Three' three: 'Three'
} }
},
es: {
i18nView: {
title: 'Switch Language',
note: 'The internationalization of this project is based on vue-i18n',
datePlaceholder: 'Pick a day',
selectPlaceholder: 'Select',
tableDate: 'tableDate',
tableName: 'tableName',
tableAddress: 'tableAddress',
default: 'default:',
primary: 'primary',
success: 'success',
info: 'info',
warning: 'warning',
danger: 'danger',
one: 'One',
two: 'Two',
three: 'Three'
}
} }
} }

View File

@ -130,15 +130,16 @@ export default {
height: 50px; height: 50px;
margin-right: 30px; margin-right: 30px;
.avatar-wrapper { .avatar-wrapper {
cursor: pointer;
margin-top: 5px; margin-top: 5px;
position: relative; position: relative;
.user-avatar { .user-avatar {
cursor: pointer;
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 10px; border-radius: 10px;
} }
.el-icon-caret-bottom { .el-icon-caret-bottom {
cursor: pointer;
position: absolute; position: absolute;
right: -20px; right: -20px;
top: 25px; top: 25px;

View File

@ -5,7 +5,7 @@
v-for="tag in visitedViews" v-for="tag in visitedViews"
ref="tag" ref="tag"
:class="isActive(tag)?'active':''" :class="isActive(tag)?'active':''"
:to="tag.fullPath" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
:key="tag.path" :key="tag.path"
tag="span" tag="span"
class="tags-view-item" class="tags-view-item"
@ -76,7 +76,7 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
for (const tag of tags) { for (const tag of tags) {
if (tag.to.path === this.$route.path) { if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag.$el) this.$refs.scrollPane.moveToTarget(tag)
// when query is different then update // when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) { if (tag.to.fullPath !== this.$route.fullPath) {
@ -121,11 +121,21 @@ export default {
this.$router.push('/') this.$router.push('/')
}, },
openMenu(tag, e) { openMenu(tag, e) {
const menuMinWidth = 105
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
const offsetWidth = this.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const left = e.clientX - offsetLeft + 15 // 15: margin right
if (left > maxLeft) {
this.left = maxLeft
} else {
this.left = left
}
this.top = e.clientY
this.visible = true this.visible = true
this.selectedTag = tag this.selectedTag = tag
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
this.left = e.clientX - offsetLeft + 15 // 15: margin right
this.top = e.clientY
}, },
closeMenu() { closeMenu() {
this.visible = false this.visible = false

View File

@ -24,8 +24,9 @@
border border
fit fit
highlight-current-row highlight-current-row
style="width: 100%;"> style="width: 100%;"
<el-table-column :label="$t('table.id')" align="center" width="65"> @sort-change="sortChange">
<el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="65">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.id }}</span> <span>{{ scope.row.id }}</span>
</template> </template>
@ -80,9 +81,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="pagination-container"> <pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
<el-pagination v-show="total>0" :current-page="listQuery.page" :page-sizes="[10,20,30, 50]" :page-size="listQuery.limit" :total="total" background layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange"/>
</div>
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible"> <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;"> <el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
@ -130,8 +129,9 @@
<script> <script>
import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article' import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article'
import waves from '@/directive/waves' // import waves from '@/directive/waves' // Waves directive
import { parseTime } from '@/utils' import { parseTime } from '@/utils'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
const calendarTypeOptions = [ const calendarTypeOptions = [
{ key: 'CN', display_name: 'China' }, { key: 'CN', display_name: 'China' },
@ -148,9 +148,8 @@ const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
export default { export default {
name: 'ComplexTable', name: 'ComplexTable',
directives: { components: { Pagination },
waves directives: { waves },
},
filters: { filters: {
statusFilter(status) { statusFilter(status) {
const statusMap = { const statusMap = {
@ -168,7 +167,7 @@ export default {
return { return {
tableKey: 0, tableKey: 0,
list: null, list: null,
total: null, total: 0,
listLoading: true, listLoading: true,
listQuery: { listQuery: {
page: 1, page: 1,
@ -228,14 +227,6 @@ export default {
this.listQuery.page = 1 this.listQuery.page = 1
this.getList() this.getList()
}, },
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
handleModifyStatus(row, status) { handleModifyStatus(row, status) {
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
@ -243,6 +234,20 @@ export default {
}) })
row.status = status row.status = status
}, },
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()
},
resetTemp() { resetTemp() {
this.temp = { this.temp = {
id: undefined, id: undefined,