Merge branch 'master' into deploy

This commit is contained in:
Pan 2018-09-07 17:58:59 +08:00
commit 0a65f69cef
42 changed files with 150 additions and 114 deletions

View File

@ -50,6 +50,7 @@ It is a magical vue admin based on the newest development stack of vue, built-in
- Base template recommends using: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
@ -128,7 +129,7 @@ Understanding and learning this knowledge in advance will greatly help the use o
- Error Log
- Dashboard
- Guide Page
- Echarts
- ECharts
- Clipboard
- Markdown to html
```

View File

@ -50,6 +50,7 @@
- 模板建议使用: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript版: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西,或者加入[qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602)
@ -140,7 +141,7 @@
- 错误日志
- Dashboard
- 引导页
- Echarts 图表
- ECharts 图表
- Clipboard(剪贴复制)
- Markdown2html
```
@ -154,7 +155,7 @@ git clone https://github.com/PanJiaChen/vue-element-admin.git
# 安装依赖
npm install
# 建议不要用cnpm安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
# 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 启动服务

View File

@ -116,7 +116,7 @@ const webpackConfig = merge(baseWebpackConfig, {
test: /[\\/]node_modules[\\/]element-ui[\\/]/
},
commons: {
name: 'chunk-comomns',
name: 'chunk-commons',
test: resolve('src/components'), // 可自定义拓展你的规则
minChunks: 3, // 最小公用次数
priority: 5,

View File

@ -50,7 +50,7 @@
"mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"screenfull": "3.3.2",
"screenfull": "3.3.3",
"showdown": "1.8.6",
"simplemde": "1.11.2",
"sortablejs": "1.7.0",
@ -86,7 +86,7 @@
"file-loader": "1.1.11",
"friendly-errors-webpack-plugin": "1.7.0",
"hash-sum": "1.0.2",
"html-webpack-plugin": "^4.0.0-alpha",
"html-webpack-plugin": "4.0.0-alpha",
"husky": "0.14.3",
"lint-staged": "7.2.2",
"mini-css-extract-plugin": "0.4.1",

View File

@ -56,6 +56,10 @@ export default {
this.chart.setOption(
{
backgroundColor: '#08263a',
grid: {
left: '5%',
right: '5%'
},
xAxis: [{
show: false,
data: xAxisData

View File

@ -80,8 +80,8 @@ export default {
},
grid: {
top: 100,
left: '3%',
right: '4%',
left: '2%',
right: '2%',
bottom: '2%',
containLabel: true
},

View File

@ -75,8 +75,10 @@ export default {
}
},
grid: {
left: '5%',
right: '5%',
borderWidth: 0,
top: 110,
top: 150,
bottom: 95,
textStyle: {
color: '#fff'

View File

@ -2,14 +2,27 @@ import { debounce } from '@/utils'
export default {
mounted() {
this.__resizeHanlder = debounce(() => {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
window.addEventListener('resize', this.__resizeHandler)
const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.__resizeHanlder)
window.removeEventListener('resize', this.__resizeHandler)
const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
},
methods: {
sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}
}
}
}

View File

@ -49,7 +49,6 @@ export default {
this.$message('请等待所有图片上传成功 或 出现了网络问题,请刷新页面重新上传!')
return
}
console.log(arr)
this.$emit('successCBK', arr)
this.listObj = {}
this.fileList = []

View File

@ -26,7 +26,7 @@ export default {
}
},
methods: {
generateDate({ header, results }) {
generateData({ header, results }) {
this.excelData.header = header
this.excelData.results = results
this.onSuccess && this.onSuccess(this.excelData)
@ -82,20 +82,20 @@ export default {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const fixedData = this.fixdata(data)
const fixedData = this.fixData(data)
const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
const header = this.get_header_row(worksheet)
const header = this.getHeaderRow(worksheet)
const results = XLSX.utils.sheet_to_json(worksheet)
this.generateDate({ header, results })
this.generateData({ header, results })
this.loading = false
resolve()
}
reader.readAsArrayBuffer(rawFile)
})
},
fixdata(data) {
fixData(data) {
let o = ''
let l = 0
const w = 10240
@ -103,14 +103,16 @@ export default {
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
return o
},
get_header_row(sheet) {
getHeaderRow(sheet) {
const headers = []
const range = XLSX.utils.decode_range(sheet['!ref'])
let C
const R = range.s.r /* start in the first row */
const R = range.s.r
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
var cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })] /* find the cell in the first row */
var hdr = 'UNKNOWN ' + C // <-- replace with your desired default
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
/* find the cell in the first row */
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
headers.push(hdr)
}

View File

@ -1,7 +1,7 @@
// Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard')
if (!Clipboard) {
throw new Error('you shold npm install `clipboard` --save at first ')
throw new Error('you should npm install `clipboard` --save at first ')
}
export default {

View File

@ -20,7 +20,7 @@ export default{
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomheight = dragDom.offsetHeight
const dragDomHeight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
@ -29,7 +29,7 @@ export default{
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')

View File

@ -6,7 +6,7 @@ export default{
const customOpts = Object.assign({}, binding.value)
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
type: 'hit', // hit 点击位置扩散 center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts)
const target = opts.ele

View File

@ -37,6 +37,6 @@ export function numberFormatter(num, digits) {
return num.toString()
}
export function toThousandslsFilter(num) {
export function toThousandFilter(num) {
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

View File

@ -1,12 +1,9 @@
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg组件
import generateIconsView from '@/views/svg-icons/generateIconsView.js'// just for @/views/icons , you can delete it
// register globally
Vue.component('svg-icon', SvgIcon)
const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
const iconMap = requireAll(req)
generateIconsView.generate(iconMap) // just for @/views/icons , you can delete it
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

View File

@ -14,7 +14,7 @@ function hasPermission(roles, permissionRoles) {
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', '/authredirect']// no redirect whitelist
const whiteList = ['/login', '/auth-redirect']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
@ -52,7 +52,7 @@ router.beforeEach((to, from, next) => {
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next('/login') // 否则全部重定向到登录页
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}

View File

@ -12,7 +12,7 @@ import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
/** note: submenu only apppear when children.length>=1
/** note: Submenu only appear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
**/
@ -21,7 +21,7 @@ import nestedRouter from './modules/nested'
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
@ -48,7 +48,7 @@ export const constantRouterMap = [
hidden: true
},
{
path: '/authredirect',
path: '/auth-redirect',
component: () => import('@/views/login/authredirect'),
hidden: true
},

View File

@ -23,7 +23,6 @@ const tagsView = {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1)
console.log('1')
break
}
}

View File

@ -28,6 +28,9 @@
height: 100%;
}
}
.el-scrollbar__bar.is-vertical{
right: 0px;
}
.is-horizontal {
display: none;
}

View File

@ -169,7 +169,6 @@ export function scrollTo(element, to, duration) {
const difference = to - element.scrollTop
const perTick = (difference / duration) * 10
setTimeout(() => {
console.log(new Date())
element.scrollTop = element.scrollTop + perTick
if (element.scrollTop === to) return
scrollTo(element, to, duration - 10)

View File

@ -5,7 +5,7 @@ import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.BASE_API, // api的base_url
baseURL: process.env.BASE_API, // api base_url
timeout: 5000 // request timeout
})
@ -26,13 +26,13 @@ service.interceptors.request.use(
}
)
// respone interceptor
// response interceptor
service.interceptors.response.use(
response => response,
/**
* 下面的注释为通过在response里自定义code来标示请求状态
* 当code返回如下情况则说明权限有问题登出并返回到登录页
* 如想通过xmlhttprequest来状态码标识 逻辑可写在下面error中
* 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
* 以下代码均为样例请结合自生需求加以修改若不需要则可删除
*/
// response => {

View File

@ -16,9 +16,8 @@ export default {
<style scoped>
.chart-container{
position: relative;
padding: 20px;
width: 100%;
height: 85vh;
height: calc(100vh - 84px);
}
</style>

View File

@ -16,9 +16,8 @@ export default {
<style scoped>
.chart-container{
position: relative;
padding:20px;
width: 100%;
height:85vh;
height: calc(100vh - 84px);
}
</style>

View File

@ -16,9 +16,8 @@ export default {
<style scoped>
.chart-container{
position: relative;
padding: 20px;
width: 100%;
height:85vh;
height: calc(100vh - 84px);
}
</style>

View File

@ -2,11 +2,11 @@
<div class="app-container">
<el-tabs v-model="activeName">
<el-tab-pane label="use clipboard directly" name="directly">
<el-input v-model="inputData" placeholder="Please input" style="width:400px;"/>
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;"/>
<el-button type="primary" icon="document" @click="handleCopy(inputData,$event)">copy</el-button>
</el-tab-pane>
<el-tab-pane label="use clipboard by v-directive" name="v-directive">
<el-input v-model="inputData" placeholder="Please input" style="width:400px;"/>
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;"/>
<el-button v-clipboard:copy="inputData" v-clipboard:success="clipboardSuccess" type="primary" icon="document">copy</el-button>
</el-tab-pane>
</el-tabs>

View File

@ -31,18 +31,18 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHanlder = debounce(() => {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
window.addEventListener('resize', this.__resizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
window.removeEventListener('resize', this.__resizeHanlder)
window.removeEventListener('resize', this.__resizeHandler)
this.chart.dispose()
this.chart = null
},

View File

@ -46,33 +46,38 @@ export default {
mounted() {
this.initChart()
if (this.autoResize) {
this.__resizeHanlder = debounce(() => {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
window.addEventListener('resize', this.__resizeHandler)
}
//
const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
sidebarElm.addEventListener('transitionend', this.__resizeHanlder)
sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
if (this.autoResize) {
window.removeEventListener('resize', this.__resizeHanlder)
window.removeEventListener('resize', this.__resizeHandler)
}
const sidebarElm = document.getElementsByClassName('sidebar-container')[0]
sidebarElm.removeEventListener('transitionend', this.__resizeHanlder)
sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
this.chart.dispose()
this.chart = null
},
methods: {
sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}
},
setOptions({ expectedData, actualData } = {}) {
this.chart.setOption({
xAxis: {

View File

@ -29,18 +29,18 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHanlder = debounce(() => {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
window.addEventListener('resize', this.__resizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
window.removeEventListener('resize', this.__resizeHanlder)
window.removeEventListener('resize', this.__resizeHandler)
this.chart.dispose()
this.chart = null
},

View File

@ -31,18 +31,18 @@ export default {
},
mounted() {
this.initChart()
this.__resizeHanlder = debounce(() => {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
window.addEventListener('resize', this.__resizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
window.removeEventListener('resize', this.__resizeHanlder)
window.removeEventListener('resize', this.__resizeHandler)
this.chart.dispose()
this.chart = null
},

View File

@ -7,7 +7,7 @@
</el-table-column>
<el-table-column label="Price" width="195" align="center">
<template slot-scope="scope">
¥{{ scope.row.price | toThousandslsFilter }}
¥{{ scope.row.price | toThousandFilter }}
</template>
</el-table-column>
<el-table-column label="Status" width="100" align="center">

View File

@ -35,7 +35,6 @@ export default {
.document-btn {
float: left;
margin-left: 50px;
vertical-align: middle;
display: block;
cursor: pointer;
background: black;

View File

@ -53,6 +53,7 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;

View File

@ -15,7 +15,7 @@
</el-card>
<el-row :gutter="20" style="margin:100px 15px 50px;">
<el-col :span="12">
<el-col :span="12" :xs="24">
<div class="block">
<el-date-picker v-model="date" :placeholder="$t('i18nView.datePlaceholder')" type="date"/>
</div>
@ -37,7 +37,7 @@
<el-button class="item-btn" size="small" type="danger">{{ $t('i18nView.danger') }}</el-button>
</div>
</el-col>
<el-col :span="12">
<el-col :span="12" :xs="24">
<el-table :data="tableData" fit highlight-current-row border style="width: 100%">
<el-table-column :label="$t('i18nView.tableName')" prop="name" width="100" align="center"/>
<el-table-column :label="$t('i18nView.tableDate')" prop="date" width="120" align="center"/>
@ -103,6 +103,7 @@ export default {
<style scoped>
.box-card {
width: 600px;
max-width: 100%;
margin: 20px auto;
}
.item-btn{

View File

@ -26,6 +26,7 @@ export default {
.app-main {
/*84 = navbar + tags-view = 50 +34 */
min-height: calc(100vh - 84px);
width: 100%;
position: relative;
overflow: hidden;
}

View File

@ -1,10 +1,11 @@
<template>
<el-menu class="navbar" mode="horizontal">
<div class="navbar">
<hamburger :toggle-click="toggleSideBar" :is-active="sidebar.opened" class="hamburger-container"/>
<breadcrumb class="breadcrumb-container"/>
<div class="right-menu">
<template v-if="device!=='mobile'">
<error-log class="errLog-container right-menu-item"/>
<el-tooltip :content="$t('navbar.screenfull')" effect="dark" placement="bottom">
@ -20,6 +21,7 @@
<el-tooltip :content="$t('navbar.theme')" effect="dark" placement="bottom">
<theme-picker class="theme-switch right-menu-item"/>
</el-tooltip>
</template>
<el-dropdown class="avatar-container right-menu-item" trigger="click">
<div class="avatar-wrapper">
@ -43,7 +45,7 @@
</el-dropdown-menu>
</el-dropdown>
</div>
</el-menu>
</div>
</template>
<script>
@ -70,7 +72,8 @@ export default {
...mapGetters([
'sidebar',
'name',
'avatar'
'avatar',
'device'
])
},
methods: {

View File

@ -1,6 +1,6 @@
<script>
export default {
name: 'Authredirect',
name: 'AuthRedirect',
created() {
const hash = window.location.search.slice(1)
window.opener.location.href = window.location.origin + '/login#' + hash

View File

@ -96,9 +96,19 @@ export default {
},
passwordType: 'password',
loading: false,
showDialog: false
showDialog: false,
redirect: undefined
}
},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect
},
immediate: true
}
},
created() {
// window.addEventListener('hashchange', this.afterQRScan)
},
@ -119,7 +129,7 @@ export default {
this.loading = true
this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
this.loading = false
this.$router.push({ path: '/' })
this.$router.push({ path: this.redirect || '/' })
}).catch(() => {
this.loading = false
})
@ -213,6 +223,7 @@ $light_gray:#eee;
left: 0;
right: 0;
width: 520px;
max-width: 100%;
padding: 35px 35px 15px 35px;
margin: 120px auto;
}

View File

@ -10,24 +10,26 @@
</template>
<script>
import openWindow from '@/utils/openWindow'
// import openWindow from '@/utils/openWindow'
export default {
name: 'SocialSignin',
methods: {
wechatHandleClick(thirdpart) {
this.$store.commit('SET_AUTH_TYPE', thirdpart)
const appid = 'xxxxx'
const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/authredirect')
const url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&response_type=code&scope=snsapi_login#wechat_redirect'
openWindow(url, thirdpart, 540, 540)
alert('ok')
// this.$store.commit('SET_AUTH_TYPE', thirdpart)
// const appid = 'xxxxx'
// const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect')
// const url = 'https://open.weixin.qq.com/connect/qrconnect?appid=' + appid + '&redirect_uri=' + redirect_uri + '&response_type=code&scope=snsapi_login#wechat_redirect'
// openWindow(url, thirdpart, 540, 540)
},
tencentHandleClick(thirdpart) {
this.$store.commit('SET_AUTH_TYPE', thirdpart)
const client_id = 'xxxxx'
const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/authredirect')
const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri
openWindow(url, thirdpart, 540, 540)
alert('ok')
// this.$store.commit('SET_AUTH_TYPE', thirdpart)
// const client_id = 'xxxxx'
// const redirect_uri = encodeURIComponent('xxx/redirect?redirect=' + window.location.origin + '/auth-redirect')
// const url = 'https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=' + client_id + '&redirect_uri=' + redirect_uri
// openWindow(url, thirdpart, 540, 540)
}
}
}

View File

@ -1,10 +0,0 @@
const data = {
state: {
iconsMap: []
},
generate(iconsMap) {
this.state.iconsMap = iconsMap
}
}
export default data

View File

@ -21,22 +21,16 @@
</template>
<script>
import icons from './generateIconsView'
import icons from './requireIcons'
import clipboard from '@/utils/clipboard'
export default {
name: 'Icons',
data() {
return {
iconsMap: []
iconsMap: icons
}
},
mounted() {
const iconsMap = icons.state.iconsMap.map((i) => {
return i.default.id.split('-')[1]
})
this.iconsMap = iconsMap
},
methods: {
generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`

View File

@ -0,0 +1,11 @@
const req = require.context('../../icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()
const re = /\.\/(.*)\.svg/
const icons = requireAll(req).map(i => {
return i.match(re)[1]
})
export default icons

View File

@ -87,6 +87,7 @@ export default {
}
.box-card {
width: 400px;
max-width: 100%;
margin: 20px auto;
}