Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
baa7172a70 | ||
|
bc003fd78b | ||
|
41a5615ee5 | ||
|
df23405bde | ||
|
f94aa1aca3 | ||
|
96bda5515c | ||
|
0f6d830c19 | ||
|
e8ab82c2c1 | ||
|
a284c1f007 | ||
|
323408f8d9 | ||
|
90d3e314c0 | ||
|
4780ac334f | ||
|
b0bd91cd0f | ||
|
ae2754a568 | ||
|
6e07d18b1d | ||
|
f58db95be5 | ||
|
616b65c238 | ||
|
68cd051bb6 | ||
|
935d9aaba8 | ||
|
616f173aab | ||
|
d1cceb69b7 | ||
|
ff13ee1f27 | ||
|
dc84e7b9fb | ||
|
b82e8e860d | ||
|
98a8277a37 | ||
|
63ff44f8e5 | ||
|
dd5646ffbc |
@@ -198,6 +198,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)
|
||||
|
||||
## 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
|
||||
|
||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
|
@@ -210,6 +210,14 @@ Detailed changes for each release are documented in the [release notes](https://
|
||||
|
||||
[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
|
||||
|
||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
|
@@ -58,8 +58,10 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
inject: true,
|
||||
favicon: resolve('favicon.ico'),
|
||||
title: 'vue-element-admin',
|
||||
path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
|
||||
})
|
||||
templateParameters: {
|
||||
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory,
|
||||
},
|
||||
}),
|
||||
]
|
||||
})
|
||||
|
||||
|
@@ -9,7 +9,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
// const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
|
||||
|
||||
function resolve(dir) {
|
||||
@@ -56,7 +56,9 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||
inject: true,
|
||||
favicon: resolve('favicon.ico'),
|
||||
title: 'vue-element-admin',
|
||||
path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
|
||||
templateParameters: {
|
||||
BASE_URL: config.build.assetsPublicPath + config.build.assetsSubDirectory,
|
||||
},
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
@@ -138,7 +140,7 @@ const webpackConfig = merge(baseWebpackConfig, {
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSAssetsPlugin()
|
||||
// new OptimizeCSSAssetsPlugin()
|
||||
]
|
||||
}
|
||||
})
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<title>vue-element-admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src=<%= htmlWebpackPlugin.options.path %>/tinymce4.7.5/tinymce.min.js></script>
|
||||
<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-element-admin",
|
||||
"version": "3.9.0",
|
||||
"version": "3.9.2",
|
||||
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
|
||||
"author": "Pan <panfree23@gmail.com>",
|
||||
"license": "MIT",
|
||||
@@ -94,6 +94,7 @@
|
||||
"node-sass": "^4.7.2",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.0",
|
||||
"ora": "3.0.0",
|
||||
"path-to-regexp": "2.4.0",
|
||||
"portfinder": "1.0.13",
|
||||
"postcss-import": "11.1.0",
|
||||
"postcss-loader": "2.1.6",
|
||||
|
@@ -1,199 +0,0 @@
|
||||
/* eslint-disable */
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['exports', 'echarts'], factory);
|
||||
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
|
||||
// CommonJS
|
||||
factory(exports, require('echarts'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory({}, root.echarts);
|
||||
}
|
||||
}(this, function (exports, echarts) {
|
||||
var log = function (msg) {
|
||||
if (typeof console !== 'undefined') {
|
||||
console && console.error && console.error(msg);
|
||||
}
|
||||
};
|
||||
if (!echarts) {
|
||||
log('ECharts is not Loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
var colorPalette = [
|
||||
'#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80',
|
||||
'#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
|
||||
'#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
|
||||
'#59678c','#c9ab00','#7eb00a','#6f5553','#c14089'
|
||||
];
|
||||
|
||||
|
||||
var theme = {
|
||||
color: colorPalette,
|
||||
|
||||
title: {
|
||||
textStyle: {
|
||||
fontWeight: 'normal',
|
||||
color: '#008acd'
|
||||
}
|
||||
},
|
||||
|
||||
visualMap: {
|
||||
itemWidth: 15,
|
||||
color: ['#5ab1ef','#e0ffff']
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
iconStyle: {
|
||||
normal: {
|
||||
borderColor: colorPalette[0]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(50,50,50,0.5)',
|
||||
axisPointer : {
|
||||
type : 'line',
|
||||
lineStyle : {
|
||||
color: '#008acd'
|
||||
},
|
||||
crossStyle: {
|
||||
color: '#008acd'
|
||||
},
|
||||
shadowStyle : {
|
||||
color: 'rgba(200,200,200,0.2)'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
dataZoom: {
|
||||
dataBackgroundColor: '#efefff',
|
||||
fillerColor: 'rgba(182,162,222,0.2)',
|
||||
handleColor: '#008acd'
|
||||
},
|
||||
|
||||
grid: {
|
||||
borderColor: '#eee'
|
||||
},
|
||||
|
||||
categoryAxis: {
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#008acd'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: ['#eee']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
valueAxis: {
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#008acd'
|
||||
}
|
||||
},
|
||||
splitArea : {
|
||||
show : true,
|
||||
areaStyle : {
|
||||
color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)']
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: ['#eee']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
timeline : {
|
||||
lineStyle : {
|
||||
color : '#008acd'
|
||||
},
|
||||
controlStyle : {
|
||||
normal : { color : '#008acd'},
|
||||
emphasis : { color : '#008acd'}
|
||||
},
|
||||
symbol : 'emptyCircle',
|
||||
symbolSize : 3
|
||||
},
|
||||
|
||||
line: {
|
||||
smooth : true,
|
||||
symbol: 'emptyCircle',
|
||||
symbolSize: 3
|
||||
},
|
||||
|
||||
candlestick: {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#d87a80',
|
||||
color0: '#2ec7c9',
|
||||
lineStyle: {
|
||||
color: '#d87a80',
|
||||
color0: '#2ec7c9'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
scatter: {
|
||||
symbol: 'circle',
|
||||
symbolSize: 4
|
||||
},
|
||||
|
||||
map: {
|
||||
label: {
|
||||
normal: {
|
||||
textStyle: {
|
||||
color: '#d87a80'
|
||||
}
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
borderColor: '#eee',
|
||||
areaColor: '#ddd'
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#fe994e'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
graph: {
|
||||
color: colorPalette
|
||||
},
|
||||
|
||||
gauge : {
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']],
|
||||
width: 10
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
splitNumber: 10,
|
||||
length :15,
|
||||
lineStyle: {
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
length :22,
|
||||
lineStyle: {
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
pointer : {
|
||||
width : 5
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
echarts.registerTheme('macarons', theme);
|
||||
}));
|
@@ -11,6 +11,7 @@
|
||||
|
||||
<script>
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -29,7 +30,15 @@ export default {
|
||||
methods: {
|
||||
generateTitle,
|
||||
getBreadcrumb() {
|
||||
let matched = this.$route.matched.filter(item => item.name)
|
||||
const { params } = this.$route
|
||||
let matched = this.$route.matched.filter(item => {
|
||||
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
|
||||
}
|
||||
})
|
||||
const first = matched[0]
|
||||
if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) {
|
||||
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
|
||||
|
@@ -6,6 +6,7 @@
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<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==='es'" command="es">Español</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
99
src/components/Pagination/index.vue
Normal file
99
src/components/Pagination/index.vue
Normal 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>
|
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const padding = 15 // tag's padding
|
||||
const tagAndTagSpacing = 4 // tagAndTagSpacing
|
||||
|
||||
export default {
|
||||
name: 'ScrollPane',
|
||||
@@ -20,18 +20,54 @@ export default {
|
||||
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
},
|
||||
moveToTarget($target) {
|
||||
moveToTarget(currentTag) {
|
||||
const $container = this.$refs.scrollContainer.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
|
||||
const $targetLeft = $target.offsetLeft
|
||||
const $targetWidth = $target.offsetWidth
|
||||
if ($targetLeft > $containerWidth) {
|
||||
// tag in the right
|
||||
$scrollWrapper.scrollLeft = $targetLeft - $containerWidth + $targetWidth + padding
|
||||
const tagList = this.$parent.$refs.tag
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
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 {
|
||||
prevTag = tagList[i - 1]
|
||||
nextTag = tagList[i + 1]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
// tag in the left
|
||||
$scrollWrapper.scrollLeft = $targetLeft - padding
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<input id="excel-upload-input" ref="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
|
||||
<div id="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
|
||||
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
|
||||
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
|
||||
Drop excel file here or
|
||||
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">Browse</el-button>
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
},
|
||||
handleUpload() {
|
||||
document.getElementById('excel-upload-input').click()
|
||||
this.$refs['excel-upload-input'].click()
|
||||
},
|
||||
handleClick(e) {
|
||||
const files = e.target.files
|
||||
@@ -126,11 +126,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#excel-upload-input{
|
||||
.excel-upload-input{
|
||||
display: none;
|
||||
z-index: -9999;
|
||||
}
|
||||
#drop{
|
||||
.drop{
|
||||
border: 2px dashed #bbb;
|
||||
width: 600px;
|
||||
height: 160px;
|
||||
|
155
src/lang/es.js
Executable file
155
src/lang/es.js
Executable file
@@ -0,0 +1,155 @@
|
||||
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',
|
||||
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'
|
||||
}
|
||||
}
|
@@ -3,8 +3,10 @@ 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)
|
||||
|
||||
@@ -16,12 +18,16 @@ const messages = {
|
||||
zh: {
|
||||
...zhLocale,
|
||||
...elementZhLocale
|
||||
},
|
||||
es: {
|
||||
...esLocale,
|
||||
...elementEsLocale
|
||||
}
|
||||
}
|
||||
|
||||
const i18n = new VueI18n({
|
||||
// set locale
|
||||
// options: en or zh
|
||||
// options: en | zh | es
|
||||
locale: Cookies.get('language') || 'en',
|
||||
// set locale messages
|
||||
messages
|
||||
|
@@ -4,6 +4,16 @@ import articleAPI from './article'
|
||||
import remoteSearchAPI from './remoteSearch'
|
||||
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({
|
||||
// timeout: '350-600'
|
||||
// })
|
||||
|
@@ -47,6 +47,9 @@
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
.is-active > .el-submenu__title{
|
||||
color: #f4f4f5!important;
|
||||
}
|
||||
}
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
@@ -120,3 +123,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu--vertical{
|
||||
& >.el-menu{
|
||||
.svg-icon{
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
// translate router.meta.title, be used in breadcrumb sidebar tagsview
|
||||
export function generateTitle(title) {
|
||||
const hasKey = this.$te('route.' + title)
|
||||
const translatedTitle = this.$t('route.' + title) // $t :this method from vue-i18n, inject in @/lang/index.js
|
||||
|
||||
if (hasKey) {
|
||||
// $t :this method from vue-i18n, inject in @/lang/index.js
|
||||
const translatedTitle = this.$t('route.' + title)
|
||||
|
||||
return translatedTitle
|
||||
}
|
||||
return title
|
||||
|
@@ -164,17 +164,6 @@ export function objectMerge(target, source) {
|
||||
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) {
|
||||
if (!element || !className) {
|
||||
return
|
||||
@@ -296,3 +285,7 @@ export function deepClone(source) {
|
||||
export function uniqueArr(arr) {
|
||||
return Array.from(new Set(arr))
|
||||
}
|
||||
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
50
src/utils/scrollTo.js
Normal file
50
src/utils/scrollTo.js
Normal 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()
|
||||
}
|
176
src/vendor/Blob.js
vendored
176
src/vendor/Blob.js
vendored
@@ -1,176 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* Blob.js
|
||||
* A Blob implementation.
|
||||
* 2014-05-27
|
||||
*
|
||||
* By Eli Grey, http://eligrey.com
|
||||
* By Devin Samarin, https://github.com/eboyjr
|
||||
* License: X11/MIT
|
||||
* See LICENSE.md
|
||||
*/
|
||||
|
||||
/*global self, unescape */
|
||||
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
||||
plusplus: true */
|
||||
|
||||
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
|
||||
|
||||
(function (view) {
|
||||
"use strict";
|
||||
|
||||
view.URL = view.URL || view.webkitURL;
|
||||
|
||||
if (view.Blob && view.URL) {
|
||||
try {
|
||||
new Blob;
|
||||
return;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Internally we use a BlobBuilder implementation to base Blob off of
|
||||
// in order to support older browsers that only have BlobBuilder
|
||||
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function (view) {
|
||||
var
|
||||
get_class = function (object) {
|
||||
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
||||
},
|
||||
FakeBlobBuilder = function BlobBuilder() {
|
||||
this.data = [];
|
||||
},
|
||||
FakeBlob = function Blob(data, type, encoding) {
|
||||
this.data = data;
|
||||
this.size = data.length;
|
||||
this.type = type;
|
||||
this.encoding = encoding;
|
||||
},
|
||||
FBB_proto = FakeBlobBuilder.prototype,
|
||||
FB_proto = FakeBlob.prototype,
|
||||
FileReaderSync = view.FileReaderSync,
|
||||
FileException = function (type) {
|
||||
this.code = this[this.name = type];
|
||||
},
|
||||
file_ex_codes = (
|
||||
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR " +
|
||||
"NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
|
||||
).split(" "),
|
||||
file_ex_code = file_ex_codes.length,
|
||||
real_URL = view.URL || view.webkitURL || view,
|
||||
real_create_object_URL = real_URL.createObjectURL,
|
||||
real_revoke_object_URL = real_URL.revokeObjectURL,
|
||||
URL = real_URL,
|
||||
btoa = view.btoa,
|
||||
atob = view.atob
|
||||
|
||||
,
|
||||
ArrayBuffer = view.ArrayBuffer,
|
||||
Uint8Array = view.Uint8Array;
|
||||
FakeBlob.fake = FB_proto.fake = true;
|
||||
while (file_ex_code--) {
|
||||
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
||||
}
|
||||
if (!real_URL.createObjectURL) {
|
||||
URL = view.URL = {};
|
||||
}
|
||||
URL.createObjectURL = function (blob) {
|
||||
var
|
||||
type = blob.type,
|
||||
data_URI_header;
|
||||
if (type === null) {
|
||||
type = "application/octet-stream";
|
||||
}
|
||||
if (blob instanceof FakeBlob) {
|
||||
data_URI_header = "data:" + type;
|
||||
if (blob.encoding === "base64") {
|
||||
return data_URI_header + ";base64," + blob.data;
|
||||
} else if (blob.encoding === "URI") {
|
||||
return data_URI_header + "," + decodeURIComponent(blob.data);
|
||||
}
|
||||
if (btoa) {
|
||||
return data_URI_header + ";base64," + btoa(blob.data);
|
||||
} else {
|
||||
return data_URI_header + "," + encodeURIComponent(blob.data);
|
||||
}
|
||||
} else if (real_create_object_URL) {
|
||||
return real_create_object_URL.call(real_URL, blob);
|
||||
}
|
||||
};
|
||||
URL.revokeObjectURL = function (object_URL) {
|
||||
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
|
||||
real_revoke_object_URL.call(real_URL, object_URL);
|
||||
}
|
||||
};
|
||||
FBB_proto.append = function (data /*, endings*/ ) {
|
||||
var bb = this.data;
|
||||
// decode data to a binary string
|
||||
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
|
||||
var
|
||||
str = "",
|
||||
buf = new Uint8Array(data),
|
||||
i = 0,
|
||||
buf_len = buf.length;
|
||||
for (; i < buf_len; i++) {
|
||||
str += String.fromCharCode(buf[i]);
|
||||
}
|
||||
bb.push(str);
|
||||
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
|
||||
if (FileReaderSync) {
|
||||
var fr = new FileReaderSync;
|
||||
bb.push(fr.readAsBinaryString(data));
|
||||
} else {
|
||||
// async FileReader won't work as BlobBuilder is sync
|
||||
throw new FileException("NOT_READABLE_ERR");
|
||||
}
|
||||
} else if (data instanceof FakeBlob) {
|
||||
if (data.encoding === "base64" && atob) {
|
||||
bb.push(atob(data.data));
|
||||
} else if (data.encoding === "URI") {
|
||||
bb.push(decodeURIComponent(data.data));
|
||||
} else if (data.encoding === "raw") {
|
||||
bb.push(data.data);
|
||||
}
|
||||
} else {
|
||||
if (typeof data !== "string") {
|
||||
data += ""; // convert unsupported types to strings
|
||||
}
|
||||
// decode UTF-16 to binary string
|
||||
bb.push(unescape(encodeURIComponent(data)));
|
||||
}
|
||||
};
|
||||
FBB_proto.getBlob = function (type) {
|
||||
if (!arguments.length) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(this.data.join(""), type, "raw");
|
||||
};
|
||||
FBB_proto.toString = function () {
|
||||
return "[object BlobBuilder]";
|
||||
};
|
||||
FB_proto.slice = function (start, end, type) {
|
||||
var args = arguments.length;
|
||||
if (args < 3) {
|
||||
type = null;
|
||||
}
|
||||
return new FakeBlob(
|
||||
this.data.slice(start, args > 1 ? end : this.data.length), type, this.encoding
|
||||
);
|
||||
};
|
||||
FB_proto.toString = function () {
|
||||
return "[object Blob]";
|
||||
};
|
||||
FB_proto.close = function () {
|
||||
this.size = this.data.length = 0;
|
||||
};
|
||||
return FakeBlobBuilder;
|
||||
}(view));
|
||||
|
||||
view.Blob = function Blob(blobParts, options) {
|
||||
var type = options ? (options.type || "") : "";
|
||||
var builder = new BlobBuilder();
|
||||
if (blobParts) {
|
||||
for (var i = 0, len = blobParts.length; i < len; i++) {
|
||||
builder.append(blobParts[i]);
|
||||
}
|
||||
}
|
||||
return builder.getBlob(type);
|
||||
};
|
||||
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
|
8
src/vendor/Export2Excel.js
vendored
8
src/vendor/Export2Excel.js
vendored
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable */
|
||||
require('script-loader!file-saver');
|
||||
require('script-loader!@/vendor/Blob');
|
||||
import XLSX from 'xlsx'
|
||||
|
||||
function generateArray(table) {
|
||||
@@ -149,7 +148,8 @@ export function export_json_to_excel({
|
||||
header,
|
||||
data,
|
||||
filename,
|
||||
autoWidth = true
|
||||
autoWidth = true,
|
||||
bookType= 'xlsx'
|
||||
} = {}) {
|
||||
/* original data */
|
||||
filename = filename || 'excel-list'
|
||||
@@ -196,11 +196,11 @@ export function export_json_to_excel({
|
||||
wb.Sheets[ws_name] = ws;
|
||||
|
||||
var wbout = XLSX.write(wb, {
|
||||
bookType: 'xlsx',
|
||||
bookType: bookType,
|
||||
bookSST: false,
|
||||
type: 'binary'
|
||||
});
|
||||
saveAs(new Blob([s2ab(wbout)], {
|
||||
type: "application/octet-stream"
|
||||
}), filename + ".xlsx");
|
||||
}), `${filename}.${bookType}`);
|
||||
}
|
||||
|
@@ -143,12 +143,16 @@ export default {
|
||||
title: [{ validator: validateRequire }],
|
||||
content: [{ validator: validateRequire }],
|
||||
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
|
||||
}
|
||||
},
|
||||
tempRoute: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
contentShortLength() {
|
||||
return this.postForm.content_short.length
|
||||
},
|
||||
lang() {
|
||||
return this.$store.getters.language
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -158,6 +162,11 @@ export default {
|
||||
} else {
|
||||
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: {
|
||||
fetchData(id) {
|
||||
@@ -166,10 +175,18 @@ export default {
|
||||
// Just for test
|
||||
this.postForm.title += ` Article Id:${this.postForm.id}`
|
||||
this.postForm.content_short += ` Article Id:${this.postForm.id}`
|
||||
|
||||
// Set tagsview title
|
||||
this.setTagsViewTitle()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
setTagsViewTitle() {
|
||||
const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article'
|
||||
const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
|
||||
this.$store.dispatch('updateVisitedView', route)
|
||||
},
|
||||
submitForm() {
|
||||
this.postForm.display_time = parseInt(this.display_time / 1000)
|
||||
console.log(this.postForm)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
|
||||
<el-table v-loading.body="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
|
||||
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
|
||||
<el-table-column align="center" label="ID" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.id }}</span>
|
||||
@@ -50,26 +50,18 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<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>
|
||||
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchList } from '@/api/article'
|
||||
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
|
||||
|
||||
export default {
|
||||
name: 'ArticleList',
|
||||
components: { Pagination },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
@@ -87,7 +79,7 @@ export default {
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
limit: 10
|
||||
limit: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
|
30
src/views/excel/components/AutoWidthOption.vue
Normal file
30
src/views/excel/components/AutoWidthOption.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div style="display:inline-block;">
|
||||
<label class="radio-label">Cell Auto-Width: </label>
|
||||
<el-radio-group v-model="autoWidth">
|
||||
<el-radio :label="true" border>True</el-radio>
|
||||
<el-radio :label="false" border>False</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
autoWidth: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
38
src/views/excel/components/BookTypeOption.vue
Normal file
38
src/views/excel/components/BookTypeOption.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div style="display:inline-block;">
|
||||
<label class="radio-label">Book Type: </label>
|
||||
<el-select v-model="bookType" style="width:120px;" >
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: 'xlsx'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: ['xlsx', 'csv', 'txt']
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bookType: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
28
src/views/excel/components/FilenameOption.vue
Normal file
28
src/views/excel/components/FilenameOption.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div style="display:inline-block;">
|
||||
<!-- $t is vue-i18n global function to translate lang -->
|
||||
<label class="radio-label" style="padding-left:0;">Filename: </label>
|
||||
<el-input :placeholder="$t('excel.placeholder')" v-model="filename" style="width:340px;" prefix-icon="el-icon-document"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filename: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -2,14 +2,15 @@
|
||||
<!-- $t is vue-i18n global function to translate lang -->
|
||||
<div class="app-container">
|
||||
|
||||
<label class="radio-label" style="padding-left:0;">Filename: </label>
|
||||
<el-input :placeholder="$t('excel.placeholder')" v-model="filename" style="width:340px;" prefix-icon="el-icon-document"/>
|
||||
<label class="radio-label">Cell Auto-Width: </label>
|
||||
<el-radio-group v-model="autoWidth">
|
||||
<el-radio :label="true" border>True</el-radio>
|
||||
<el-radio :label="false" border>False</el-radio>
|
||||
</el-radio-group>
|
||||
<el-button :loading="downloadLoading" style="margin:0 0 20px 20px;" type="primary" icon="document" @click="handleDownload">{{ $t('excel.export') }} Excel</el-button>
|
||||
<div>
|
||||
<FilenameOption v-model="filename" />
|
||||
<AutoWidthOption v-model="autoWidth" />
|
||||
<BookTypeOption v-model="bookType" />
|
||||
<el-button :loading="downloadLoading" style="margin:0 0 20px 20px;" type="primary" icon="document" @click="handleDownload">{{ $t('excel.export') }} Excel</el-button>
|
||||
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
|
||||
<el-tag type="info">Documentation</el-tag>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row>
|
||||
<el-table-column align="center" label="Id" width="95">
|
||||
@@ -46,15 +47,22 @@
|
||||
import { fetchList } from '@/api/article'
|
||||
import { parseTime } from '@/utils'
|
||||
|
||||
// options components
|
||||
import FilenameOption from './components/FilenameOption'
|
||||
import AutoWidthOption from './components/AutoWidthOption'
|
||||
import BookTypeOption from './components/BookTypeOption'
|
||||
|
||||
export default {
|
||||
name: 'ExportExcel',
|
||||
components: { FilenameOption, AutoWidthOption, BookTypeOption },
|
||||
data() {
|
||||
return {
|
||||
list: null,
|
||||
listLoading: true,
|
||||
downloadLoading: false,
|
||||
filename: '',
|
||||
autoWidth: true
|
||||
autoWidth: true,
|
||||
bookType: 'xlsx'
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -79,7 +87,8 @@ export default {
|
||||
header: tHeader,
|
||||
data,
|
||||
filename: this.filename,
|
||||
autoWidth: this.autoWidth
|
||||
autoWidth: this.autoWidth,
|
||||
bookType: this.bookType
|
||||
})
|
||||
this.downloadLoading = false
|
||||
})
|
||||
|
@@ -3,6 +3,9 @@
|
||||
<!-- $t is vue-i18n global function to translate lang -->
|
||||
<el-input :placeholder="$t('excel.placeholder')" v-model="filename" style="width:340px;" prefix-icon="el-icon-document"/>
|
||||
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">{{ $t('excel.selectedExport') }}</el-button>
|
||||
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
|
||||
<el-tag type="info">Documentation</el-tag>
|
||||
</a>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
ref="multipleTable"
|
||||
|
@@ -9,6 +9,7 @@
|
||||
<el-radio-group v-model="lang" size="small">
|
||||
<el-radio label="zh" border>简体中文</el-radio>
|
||||
<el-radio label="en" border>English</el-radio>
|
||||
<el-radio label="es" border>Español</el-radio>
|
||||
</el-radio-group>
|
||||
<el-tag style="margin-top:15px;display:block;" type="info">{{ $t('i18nView.note') }}</el-tag>
|
||||
</div>
|
||||
@@ -101,6 +102,7 @@ export default {
|
||||
if (!this.$i18n.getLocaleMessage('en')[viewName]) {
|
||||
this.$i18n.mergeLocaleMessage('en', local.en)
|
||||
this.$i18n.mergeLocaleMessage('zh', local.zh)
|
||||
this.$i18n.mergeLocaleMessage('es', local.es)
|
||||
}
|
||||
this.setOptions() // set default select options
|
||||
},
|
||||
|
@@ -39,5 +39,25 @@ export default {
|
||||
two: 'Two',
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
src/views/layout/components/Sidebar/FixiOSBug.js
Normal file
26
src/views/layout/components/Sidebar/FixiOSBug.js
Normal file
@@ -0,0 +1,26 @@
|
||||
export default {
|
||||
computed: {
|
||||
device() {
|
||||
return this.$store.state.app.device
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// In order to fix the click on menu on the ios device will trigger the mouseeleave bug
|
||||
// https://github.com/PanJiaChen/vue-element-admin/issues/1135
|
||||
this.fixBugIniOS()
|
||||
},
|
||||
methods: {
|
||||
fixBugIniOS() {
|
||||
const $submenu = this.$refs.submenu
|
||||
if ($submenu) {
|
||||
const handleMouseleave = $submenu.handleMouseleave
|
||||
$submenu.handleMouseleave = (e) => {
|
||||
if (this.device === 'mobile') {
|
||||
return
|
||||
}
|
||||
handleMouseleave(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
src/views/layout/components/Sidebar/Link.vue
Normal file
39
src/views/layout/components/Sidebar/Link.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
<template>
|
||||
<!-- eslint-disable vue/require-component-is-->
|
||||
<component v-bind="linkProps(to)">
|
||||
<slot/>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isExternalLink(routePath) {
|
||||
return isExternal(routePath)
|
||||
},
|
||||
linkProps(url) {
|
||||
if (this.isExternalLink(url)) {
|
||||
return {
|
||||
is: 'a',
|
||||
href: url,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
}
|
||||
}
|
||||
return {
|
||||
is: 'router-link',
|
||||
to: url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -2,14 +2,14 @@
|
||||
<div v-if="!item.hidden&&item.children" class="menu-wrapper">
|
||||
|
||||
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
||||
<a :href="onlyOneChild.path" target="_blank" @click="clickLink(onlyOneChild.path,$event)">
|
||||
<app-link :to="resolvePath(onlyOneChild.path)">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
|
||||
<item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||item.meta.icon" :title="generateTitle(onlyOneChild.meta.title)" />
|
||||
</el-menu-item>
|
||||
</a>
|
||||
</app-link>
|
||||
</template>
|
||||
|
||||
<el-submenu v-else :index="item.name||item.path">
|
||||
<el-submenu v-else ref="submenu" :index="resolvePath(item.path)">
|
||||
<template slot="title">
|
||||
<item v-if="item.meta" :icon="item.meta.icon" :title="generateTitle(item.meta.title)" />
|
||||
</template>
|
||||
@@ -23,11 +23,11 @@
|
||||
:base-path="resolvePath(child.path)"
|
||||
class="nest-menu" />
|
||||
|
||||
<a v-else :href="child.path" :key="child.name" target="_blank" @click="clickLink(child.path,$event)">
|
||||
<app-link v-else :to="resolvePath(child.path)" :key="child.name">
|
||||
<el-menu-item :index="resolvePath(child.path)">
|
||||
<item v-if="child.meta" :icon="child.meta.icon" :title="generateTitle(child.meta.title)" />
|
||||
</el-menu-item>
|
||||
</a>
|
||||
</app-link>
|
||||
</template>
|
||||
</el-submenu>
|
||||
|
||||
@@ -37,12 +37,15 @@
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
import { validateURL } from '@/utils/validate'
|
||||
import { isExternal } from '@/utils'
|
||||
import Item from './Item'
|
||||
import AppLink from './Link'
|
||||
import FixiOSBug from './FixiOSBug'
|
||||
|
||||
export default {
|
||||
name: 'SidebarItem',
|
||||
components: { Item },
|
||||
components: { Item, AppLink },
|
||||
mixins: [FixiOSBug],
|
||||
props: {
|
||||
// route object
|
||||
item: {
|
||||
@@ -89,17 +92,13 @@ export default {
|
||||
return false
|
||||
},
|
||||
resolvePath(routePath) {
|
||||
if (this.isExternalLink(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
return path.resolve(this.basePath, routePath)
|
||||
},
|
||||
isExternalLink(routePath) {
|
||||
return validateURL(routePath)
|
||||
},
|
||||
clickLink(routePath, e) {
|
||||
if (!this.isExternalLink(routePath)) {
|
||||
e.preventDefault()
|
||||
const path = this.resolvePath(routePath)
|
||||
this.$router.push(path)
|
||||
}
|
||||
return isExternal(routePath)
|
||||
},
|
||||
generateTitle
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
text-color="#bfcbd9"
|
||||
active-text-color="#409EFF"
|
||||
>
|
||||
<sidebar-item v-for="route in permission_routers" :key="route.name" :item="route" :base-path="route.path"/>
|
||||
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
@@ -2,15 +2,17 @@
|
||||
<div class="tags-view-container">
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
|
||||
<router-link
|
||||
v-for="tag in Array.from(visitedViews)"
|
||||
v-for="tag in visitedViews"
|
||||
ref="tag"
|
||||
:class="isActive(tag)?'active':''"
|
||||
:to="tag"
|
||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||
:key="tag.path"
|
||||
tag="span"
|
||||
class="tags-view-item"
|
||||
@click.middle.native="closeSelectedTag(tag)"
|
||||
@contextmenu.prevent.native="openMenu(tag,$event)">
|
||||
{{ generateTitle(tag.title) }}
|
||||
<span class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)"/>
|
||||
<span class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
||||
@@ -59,28 +61,22 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
generateTitle, // generateTitle by vue-i18n
|
||||
generateRoute() {
|
||||
if (this.$route.name) {
|
||||
return this.$route
|
||||
}
|
||||
return false
|
||||
},
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path
|
||||
},
|
||||
addViewTags() {
|
||||
const route = this.generateRoute()
|
||||
if (!route) {
|
||||
return false
|
||||
const { name } = this.$route
|
||||
if (name) {
|
||||
this.$store.dispatch('addView', this.$route)
|
||||
}
|
||||
this.$store.dispatch('addView', route)
|
||||
return false
|
||||
},
|
||||
moveToCurrentTag() {
|
||||
const tags = this.$refs.tag
|
||||
this.$nextTick(() => {
|
||||
for (const tag of tags) {
|
||||
if (tag.to.path === this.$route.path) {
|
||||
this.$refs.scrollPane.moveToTarget(tag.$el)
|
||||
this.$refs.scrollPane.moveToTarget(tag)
|
||||
|
||||
// when query is different then update
|
||||
if (tag.to.fullPath !== this.$route.fullPath) {
|
||||
@@ -149,6 +145,7 @@ export default {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
|
@@ -80,9 +80,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<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>
|
||||
<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">
|
||||
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
|
||||
@@ -111,8 +109,7 @@
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">{{ $t('table.cancel') }}</el-button>
|
||||
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData">{{ $t('table.confirm') }}</el-button>
|
||||
<el-button v-else type="primary" @click="updateData">{{ $t('table.confirm') }}</el-button>
|
||||
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">{{ $t('table.confirm') }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
@@ -131,8 +128,9 @@
|
||||
|
||||
<script>
|
||||
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 Pagination from '@/components/Pagination' // Secondary package based on el-pagination
|
||||
|
||||
const calendarTypeOptions = [
|
||||
{ key: 'CN', display_name: 'China' },
|
||||
@@ -149,9 +147,8 @@ const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
|
||||
|
||||
export default {
|
||||
name: 'ComplexTable',
|
||||
directives: {
|
||||
waves
|
||||
},
|
||||
components: { Pagination },
|
||||
directives: { waves },
|
||||
filters: {
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
@@ -169,7 +166,7 @@ export default {
|
||||
return {
|
||||
tableKey: 0,
|
||||
list: null,
|
||||
total: null,
|
||||
total: 0,
|
||||
listLoading: true,
|
||||
listQuery: {
|
||||
page: 1,
|
||||
@@ -229,14 +226,6 @@ export default {
|
||||
this.listQuery.page = 1
|
||||
this.getList()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.listQuery.limit = val
|
||||
this.getList()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.listQuery.page = val
|
||||
this.getList()
|
||||
},
|
||||
handleModifyStatus(row, status) {
|
||||
this.$message({
|
||||
message: '操作成功',
|
||||
|
Reference in New Issue
Block a user