Compare commits

...

46 Commits

Author SHA1 Message Date
Pan
8b41d0fdc9 update readme 2017-07-06 18:15:12 +08:00
Pan
de992c50a6 全局代码格式 优化 2017-07-06 17:56:23 +08:00
Pan
622a8c3a69 refine icon-svg 2017-07-06 17:56:23 +08:00
花裤衩
9342ae6fc3 Update README.md 2017-07-04 16:17:39 +08:00
Pan
540168985d refine code 2017-07-04 16:14:04 +08:00
花裤衩
04261f7347 Update README.md 2017-07-04 15:52:12 +08:00
孙晨光
f0db0697e6 fix: todolist delete icon style 2017-07-03 20:23:23 +08:00
Pan
b3ddf60d04 rm duplicate code 2017-07-03 13:33:26 +08:00
卜木
4fa07aea09 Update index.js 2017-07-03 13:26:57 +08:00
卜木
56a048865d prevent JSON.parse fault when search is undefined 2017-07-03 13:26:57 +08:00
Pan
ffd668fb7d refine dashboard css 2017-07-03 12:45:31 +08:00
Pan
ed803c68b2 fix next bug 2017-07-03 12:45:31 +08:00
Pan
a1a4808c23 fix tabsview bug 2017-07-03 12:45:31 +08:00
Pan
cfa82b0714 fix next bug 2017-07-03 12:45:31 +08:00
Pan
506ecdc0ff split echarts into own file 2017-07-03 12:45:31 +08:00
Pan
8886ab2262 ADD TODOLIST 2017-07-03 12:45:31 +08:00
Pan
e3bc9c7e78 delete redundant code 2017-07-03 12:45:31 +08:00
Pan
54884f50c1 refine css 2017-07-03 12:45:31 +08:00
Pan
f27b16718e add new charts 2017-07-03 12:45:31 +08:00
Pan
4b4bf494b0 refine echarts demo 2017-06-30 15:36:21 +08:00
Pan
b15caf637f fix tabel demo bug 2017-06-30 13:40:18 +08:00
Pan
3466b9feb2 fix postcss bug 2017-06-30 11:31:21 +08:00
Pan
00e06fe44e ignore gifs 2017-06-28 16:04:19 +08:00
Pan
025c6cf038 add tabs gif 2017-06-28 16:00:56 +08:00
Pan
95ef202d20 fix screenfull bug 2017-06-27 14:20:50 +08:00
Pan
cb53ba891c refine code 2017-06-26 17:16:05 +08:00
Pan
805ab1d0ac add github link 2017-06-26 16:50:34 +08:00
Pan
33cf5ef16e refine code 2017-06-26 13:35:43 +08:00
Pan
63ad0fc2f5 refine code 2017-06-26 11:21:53 +08:00
Pan
80a281733a refine assets path config 2017-06-26 09:57:24 +08:00
Pan
f1aa01a233 fix theme example bug 2017-06-23 21:19:30 +08:00
Pan
5cd245ac15 split tabs-view to separate files 2017-06-23 17:56:45 +08:00
Pan
0c3063f46c Release 1.0.3 2017-06-23 17:38:30 +08:00
Pan
91cb0ac5ca add view tabs 2017-06-23 17:36:13 +08:00
Pan
7549eb8044 fix eslint && refine code 2017-06-23 15:39:13 +08:00
孙晨光
1072572ac6 update: 递归过滤异步路由表 2017-06-23 15:25:58 +08:00
Pan
046d1369d2 not use Lazy Loading In dev 2017-06-21 16:59:02 +08:00
Pan
657937c7a5 Revert "refine css"
This reverts commit 6de521d671.
2017-06-21 09:46:47 +08:00
Pan
6de521d671 refine css 2017-06-20 23:14:50 +08:00
Pan
dbf9638922 refine dynamictable example 2017-06-20 23:02:36 +08:00
轩辕Rowboat
aaa64a8ccf update format code
没用编辑器,所以代码没格式化/(ㄒoㄒ)/~~
2017-06-20 15:44:14 +08:00
轩辕Rowboat
ff8d7ada73 千分符考虑小数点 2017-06-20 15:44:14 +08:00
Pan
bf480ca6b4 fix router path bug 2017-06-19 18:14:31 +08:00
Pan
803201af3a fix error link 2017-06-19 18:02:19 +08:00
Pan
e97dbf6115 use babel-preset-env 2017-06-19 17:59:56 +08:00
Pan
cf1fb6cd4d refine demo code 2017-06-15 15:35:14 +08:00
108 changed files with 5408 additions and 5087 deletions

View File

@@ -1,5 +1,8 @@
{
"presets": ["es2015", "stage-2"],
"presets": [
["env", { "modules": false }],
"stage-2"
],
"plugins": ["transform-runtime"],
"comments": false
}

2
.gitignore vendored
View File

@@ -2,7 +2,7 @@
node_modules/
dist/
static/ckeditor
gifs/
npm-debug.log
test/unit/coverage
test/e2e/reports

8
.postcssrc.js Normal file
View File

@@ -0,0 +1,8 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
// to edit target browsers: use "browserlist" field in package.json
"autoprefixer": {}
}
}

View File

@@ -55,6 +55,7 @@ Join the group on QQ 591724180.
- cache tabs example
- screenfull
- markdown2html
- views-tab
## Development
@@ -131,6 +132,10 @@ Only status of user and app configuration is managed by Vuex. Other data are man
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/theme.gif)
#### tabs
![tabs](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/tabs.gif)<br />
#### Collapsing sidebar
![](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/leftmenu.gif)

View File

@@ -5,6 +5,8 @@
[wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
**本项目的定位是后台集成方案,不适合当基础模板来开发,模板建议使用 [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)**
**注意该项目目前使用element-ui@1.3.3版本,所以最低兼容 Vue 2.3.0**
## 前言
@@ -16,6 +18,7 @@
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手带你封装一个vue component](https://segmentfault.com/a/1190000009090836)
相应需求开了一个qq群 591724180 方便大家交流
@@ -57,6 +60,7 @@
- cache tabs example
- screenfull
- markdown2html
- views-tab
## 开发
@@ -130,6 +134,10 @@ Detailed changes for each release are documented in the [release notes](https://
![真正的动态换肤](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/theme.gif)<br />
#### tabs
![tabs](https://github.com/PanJiaChen/vue-element-admin/blob/master/gifs/tabs.gif)<br />
#### 可收起侧边栏

View File

@@ -3,69 +3,69 @@ var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// generate loader string to be used with extract text plugin
function generateLoaders(loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', {indentedSyntax: true}),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

View File

@@ -86,6 +86,14 @@ var webpackConfig = merge(baseWebpackConfig, {
)
}
}),
// split echarts into its own file
new webpack.optimize.CommonsChunkPlugin({
async:'echarts',
minChunks(module) {
var context = module.context;
return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0);
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({

View File

@@ -7,9 +7,9 @@ module.exports = {
prodEnv: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: '',
assetsPublicPath: './', //生产环境assetsPublicPath: '/'
staticPath:'./', //生产环境 staticPath:''
assetsSubDirectory: 'static',
assetsPublicPath: './', //请根据自己路径配置更改
staticPath:'./static/', //请根据自己路径配置更改
productionSourceMap: true,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.

BIN
gifs/tabs.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@@ -1,6 +1,6 @@
{
"name": "juicy",
"version": "1.0.2",
"version": "1.0.3",
"description": "A Vue.js admin",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
@@ -43,7 +43,7 @@
"babel-eslint": "7.2.3",
"babel-loader": "7.0.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-env": "1.5.2",
"babel-preset-stage-2": "6.24.1",
"babel-register": "6.24.1",
"chalk": "1.1.3",

View File

@@ -1,11 +1,15 @@
<template>
<div id="app">
<router-view></router-view>
</div>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default{
name: 'APP'
}
export default{
name: 'APP'
}
</script>
<style lang="scss">
@import './styles/index.scss'; // 全局自定义的css样式
</style>

View File

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,199 @@
/* 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);
}));

View File

@@ -1,104 +0,0 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
// 引入 ECharts 主模块
const echarts = require('echarts/lib/echarts');
// 引入柱状图
require('echarts/lib/chart/bar');
// 引入提示框和标题组件
require('echarts/lib/component/tooltip');
export default {
name: 'barPercent',
props: {
className: {
type: String,
default: 'bar-percent-chart'
},
id: {
type: String,
default: 'bar-percent-chart'
},
width: {
type: String,
default: '100px'
},
height: {
type: String,
default: '80px'
},
dataNum: {
type: Number,
default: 0
}
},
data() {
return {
chart: null
};
},
watch: {
dataNum() {
this.setOptions()
}
},
mounted() {
this.initBar();
},
methods: {
initBar() {
this.chart = echarts.init(document.getElementById(this.id));
this.setOptions();
},
setOptions() {
this.chart.setOption({
tooltip: {
show: true,
formatter(params) {
return '已完成' + params.value + '篇<br/>目标90篇<br/>完成进度' + Math.round((params.value / 90) * 100) + '%'
}
},
grid: {
left: 0,
right: 0,
bottom: 0,
top: 0,
containLabel: false
},
xAxis: [{
type: 'category',
data: ['文章完成比例']
}],
yAxis: [{
type: 'value',
data: [],
show: false
}],
animationDelay: 1000,
series: [{
type: 'bar',
name: '初诊',
itemStyle: {
normal: {
color: '#e5e5e5'
}
},
silent: true,
barGap: '-100%', // Make series be overlap
data: [150]
}, {
type: 'bar',
name: 'app',
itemStyle: {
normal: {
color: '#30b08f'
}
},
z: 10,
data: [this.dataNum]
}]
})
}
}
}
</script>

View File

@@ -1,112 +1,113 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
// 引入 ECharts 主模块
const echarts = require('echarts/lib/echarts');
// 引入柱状图
require('echarts/lib/chart/bar');
require('echarts/lib/chart/line');
// 引入提示框和标题组件
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
import echarts from 'echarts';
require('echarts/lib/component/visualMap');
export default {
name: 'barPercent',
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {};
},
mounted() {
this.initBar();
},
methods: {
initBar() {
this.chart = echarts.init(document.getElementById(this.id));
export default {
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id));
const xAxisData = [];
const data = [];
for (let i = 0; i < 30; i++) {
xAxisData.push(i + '号');
data.push(Math.round(Math.random() * 2 + 3))
}
const xAxisData = [];
const data = [];
for (let i = 0; i < 30; i++) {
xAxisData.push(i + '号');
data.push(Math.round(Math.random() * 2 + 3))
}
this.chart.setOption(
{
backgroundColor: '#08263a',
tooltip: {
trigger: 'axis'
},
xAxis: {
show: false,
data: xAxisData
},
visualMap: {
show: false,
min: 0,
max: 50,
dimension: 0,
inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#08263f'
}
},
axisTick: {}
},
series: [{
type: 'bar',
data,
name: '撸文数',
itemStyle: {
normal: {
barBorderRadius: 5,
shadowBlur: 10,
shadowColor: '#111'
}
},
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20;
},
animationDelayUpdate(idx) {
return idx * 20;
}
}]
})
}
}
}
this.chart.setOption(
{
backgroundColor: '#08263a',
tooltip: {
trigger: 'axis'
},
xAxis: {
show: false,
data: xAxisData
},
visualMap: {
show: false,
min: 0,
max: 50,
dimension: 0,
inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#08263f'
}
},
axisTick: {}
},
series: [{
type: 'bar',
data,
name: '撸文数',
itemStyle: {
normal: {
barBorderRadius: 5,
shadowBlur: 10,
shadowColor: '#111'
}
},
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20;
},
animationDelayUpdate(idx) {
return idx * 20;
}
}]
})
}
}
}
</script>

View File

@@ -1,149 +1,150 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
// 引入 ECharts 主模块
const echarts = require('echarts/lib/echarts');
// 引入柱状图
require('echarts/lib/chart/bar');
require('echarts/lib/chart/line');
// 引入提示框和标题组件
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
import echarts from 'echarts';
require('echarts/lib/component/visualMap');
export default {
name: 'barPercent',
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
export default {
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id));
const xAxisData = [];
const data = [];
const data2 = [];
for (let i = 0; i < 50; i++) {
xAxisData.push(i);
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5);
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3);
}
},
data() {
return {};
},
mounted() {
this.initBar();
},
methods: {
initBar() {
this.chart = echarts.init(document.getElementById(this.id));
const xAxisData = [];
const data = [];
const data2 = [];
for (let i = 0; i < 50; i++) {
xAxisData.push(i);
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5);
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3);
}
this.chart.setOption(
{
backgroundColor: '#08263a',
xAxis: [{
show: false,
data: xAxisData
}, {
show: false,
data: xAxisData
}],
visualMap: {
show: false,
min: 0,
max: 50,
dimension: 0,
inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#08263f'
}
},
axisTick: {
show: false
}
},
series: [{
name: 'back',
type: 'bar',
data: data2,
z: 1,
itemStyle: {
normal: {
opacity: 0.4,
barBorderRadius: 5,
shadowBlur: 3,
shadowColor: '#111'
}
}
}, {
name: 'Simulate Shadow',
type: 'line',
data,
z: 2,
showSymbol: false,
animationDelay: 0,
animationEasing: 'linear',
animationDuration: 1200,
lineStyle: {
normal: {
color: 'transparent'
}
},
areaStyle: {
normal: {
color: '#08263a',
shadowBlur: 50,
shadowColor: '#000'
}
}
}, {
name: 'front',
type: 'bar',
data,
xAxisIndex: 1,
z: 3,
itemStyle: {
normal: {
barBorderRadius: 5
}
}
}],
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20;
},
animationDelayUpdate(idx) {
return idx * 20;
this.chart.setOption(
{
backgroundColor: '#08263a',
xAxis: [{
show: false,
data: xAxisData
}, {
show: false,
data: xAxisData
}],
visualMap: {
show: false,
min: 0,
max: 50,
dimension: 0,
inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
}
})
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#08263f'
}
},
axisTick: {
show: false
}
},
series: [{
name: 'back',
type: 'bar',
data: data2,
z: 1,
itemStyle: {
normal: {
opacity: 0.4,
barBorderRadius: 5,
shadowBlur: 3,
shadowColor: '#111'
}
}
}, {
name: 'Simulate Shadow',
type: 'line',
data,
z: 2,
showSymbol: false,
animationDelay: 0,
animationEasing: 'linear',
animationDuration: 1200,
lineStyle: {
normal: {
color: 'transparent'
}
},
areaStyle: {
normal: {
color: '#08263a',
shadowBlur: 50,
shadowColor: '#000'
}
}
}, {
name: 'front',
type: 'bar',
data,
xAxisIndex: 1,
z: 3,
itemStyle: {
normal: {
barBorderRadius: 5
}
}
}],
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20;
},
animationDelayUpdate(idx) {
return idx * 20;
}
})
}
}
}
</script>

View File

@@ -1,146 +0,0 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
// 引入 ECharts 主模块
const echarts = require('echarts/lib/echarts');
// 引入图
require('echarts/lib/chart/line');
// 引入提示框和标题组件
require('echarts/lib/component/markLine');
require('echarts/lib/component/markPoint');
require('echarts/lib/component/tooltip');
export default {
name: 'lineChart',
props: {
className: {
type: String,
default: 'line-chart'
},
id: {
type: String,
default: 'line-chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '280px'
},
listData: {
type: Array,
require: true
}
},
data() {
return {
chart: null
};
},
watch: {
listData(dataList) {
this.setLine(dataList)
}
},
mounted() {
this.chart = echarts.init(document.getElementById(this.id));
this.setLine(this.listData);
},
methods: {
setLine(dataList) {
const xAxisData = [];
const data = [];
for (let i = 0; i < dataList.length; i++) {
const item = dataList[i]
xAxisData.push(item.week.substring(item.week.length - 2) + '周');
data.push(item.count)
}
const markLineData = [];
for (let i = 1; i < data.length; i++) {
markLineData.push([{
xAxis: i - 1,
yAxis: data[i - 1],
value: data[i] - data[i - 1]
}, {
xAxis: i,
yAxis: data[i]
}]);
}
this.chart.setOption({
title: {
text: 'Awesome Chart'
},
grid: {
left: 0,
right: 0,
bottom: 20,
containLabel: true
},
tooltip: {
trigger: 'axis'
},
animationDelay: 1000,
xAxis: {
data: xAxisData,
axisLine: {
show: false
},
axisTick: {
show: false
}
// axisLabel:{
// show:false
// },
},
yAxis: {
splitLine: {
show: false
},
show: false
// min: 90
},
series: [{
name: '撸文数',
type: 'line',
data,
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' }
]
},
itemStyle: {
normal: {
color: '#30b08f'
}
},
markLine: {
silent: true,
smooth: true,
effect: {
show: true
},
animationDuration(idx) {
return idx * 100;
},
animationDelay: 1000,
animationEasing: 'quadraticInOut',
distance: 1,
label: {
normal: {
position: 'middle'
}
},
symbol: ['none', 'none'],
data: markLineData
}
}]
})
}
}
}
</script>

View File

@@ -1,219 +1,222 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
// 引入 ECharts 主模块
const echarts = require('echarts/lib/echarts');
import echarts from 'echarts';
require('echarts/lib/chart/line');
// 引入提示框和标题组件
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legend');
export default {
name: 'barPercent',
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
export default {
props: {
className: {
type: String,
default: 'chart'
},
data() {
return {};
id: {
type: String,
default: 'chart'
},
mounted() {
this.initChart();
width: {
type: String,
default: '200px'
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id));
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id));
this.chart.setOption({
backgroundColor: '#394056',
title: {
text: '请求数',
this.chart.setOption({
backgroundColor: '#394056',
title: {
text: '请求数',
textStyle: {
fontWeight: 'normal',
fontSize: 16,
color: '#F1F1F3'
},
left: '6%'
},
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#57617B'
}
}
},
legend: {
icon: 'rect',
itemWidth: 14,
itemHeight: 5,
itemGap: 13,
data: ['移动', '电信', '联通'],
right: '4%',
textStyle: {
fontSize: 12,
color: '#F1F1F3'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#57617B'
}
},
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
}],
yAxis: [{
type: 'value',
name: '单位(%',
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#57617B'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontWeight: 'normal',
fontSize: 16,
color: '#F1F1F3'
},
left: '6%'
},
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#57617B'
}
fontSize: 14
}
},
legend: {
icon: 'rect',
itemWidth: 14,
itemHeight: 5,
itemGap: 13,
data: ['移动', '电信', '联通'],
right: '4%',
textStyle: {
fontSize: 12,
color: '#F1F1F3'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#57617B'
}
},
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
}],
yAxis: [{
type: 'value',
name: '单位(%',
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#57617B'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
},
splitLine: {
lineStyle: {
color: '#57617B'
}
}
}],
series: [{
name: '移动',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
splitLine: {
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(137, 189, 27, 0.3)'
}, {
offset: 0.8,
color: 'rgba(137, 189, 27, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(137,189,27)',
borderColor: 'rgba(137,189,2,0.27)',
borderWidth: 12
color: '#57617B'
}
}
}],
series: [{
name: '移动',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(137, 189, 27, 0.3)'
}, {
offset: 0.8,
color: 'rgba(137, 189, 27, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(137,189,27)',
borderColor: 'rgba(137,189,2,0.27)',
borderWidth: 12
}
},
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
}, {
name: '电信',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 136, 212, 0.3)'
}, {
offset: 0.8,
color: 'rgba(0, 136, 212, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(0,136,212)',
borderColor: 'rgba(0,136,212,0.2)',
borderWidth: 12
}
},
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
}, {
name: '电信',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 136, 212, 0.3)'
}, {
offset: 0.8,
color: 'rgba(0, 136, 212, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(0,136,212)',
borderColor: 'rgba(0,136,212,0.2)',
borderWidth: 12
}
},
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
}, {
name: '联通',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(219, 50, 51, 0.3)'
}, {
offset: 0.8,
color: 'rgba(219, 50, 51, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(219,50,51)',
borderColor: 'rgba(219,50,51,0.2)',
borderWidth: 12
}
},
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
}]
})
}
}
},
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
}, {
name: '联通',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(219, 50, 51, 0.3)'
}, {
offset: 0.8,
color: 'rgba(219, 50, 51, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(219,50,51)',
borderColor: 'rgba(219,50,51,0.2)',
borderWidth: 12
}
},
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
}]
})
}
}
}
</script>

View File

@@ -1,265 +1,268 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
</template>
<script>
// 引入 ECharts 主模块
const echarts = require('echarts/lib/echarts');
require('echarts/lib/chart/bar');
require('echarts/lib/chart/line');
// 引入提示框和标题组件
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legend');
require('echarts/lib/component/dataZoom');
export default {
name: 'barPercent',
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
import echarts from 'echarts';
export default {
props: {
className: {
type: String,
default: 'chart'
},
data() {
return {};
id: {
type: String,
default: 'chart'
},
mounted() {
this.initChart();
width: {
type: String,
default: '200px'
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id));
const xData = (function() {
const data = [];
for (let i = 1; i < 13; i++) {
data.push(i + '月份');
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
this.chart = null;
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id));
const xData = (function() {
const data = [];
for (let i = 1; i < 13; i++) {
data.push(i + '月份');
}
return data;
}());
this.chart.setOption({
backgroundColor: '#344b58',
title: {
text: '统计',
x: '4%',
textStyle: {
color: '#fff',
fontSize: '22'
},
subtextStyle: {
color: '#90979c',
fontSize: '16'
}
return data;
}());
this.chart.setOption({
backgroundColor: '#344b58',
title: {
text: '统计',
x: '4%',
textStyle: {
color: '#fff',
fontSize: '22'
},
subtextStyle: {
color: '#90979c',
fontSize: '16'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
textStyle: {
color: '#fff'
}
}
},
grid: {
borderWidth: 0,
top: 110,
bottom: 95,
},
tooltip: {
trigger: 'axis',
axisPointer: {
textStyle: {
color: '#fff'
}
},
legend: {
x: '15%',
top: '10%',
textStyle: {
color: '#90979c'
},
data: ['女', '男', '平均']
},
calculable: true,
xAxis: [{
type: 'category',
axisLine: {
lineStyle: {
color: '#90979c'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
interval: 0
},
data: xData
}],
yAxis: [{
type: 'value',
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: '#90979c'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0
},
splitArea: {
show: false
}
}],
dataZoom: [{
show: true,
height: 30,
xAxisIndex: [
0
],
bottom: 30,
start: 10,
end: 80,
handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
handleSize: '110%',
handleStyle: {
color: '#d3dee5'
},
textStyle: {
color: '#fff' },
borderColor: '#90979c'
}, {
type: 'inside',
show: true,
height: 15,
start: 1,
end: 35
}],
series: [{
name: '女',
type: 'bar',
stack: '总量',
barMaxWidth: 35,
barGap: '10%',
itemStyle: {
normal: {
color: 'rgba(255,144,128,1)',
label: {
show: true,
textStyle: {
color: '#fff'
},
position: 'insideTop',
formatter(p) {
return p.value > 0 ? p.value : '';
}
}
}
},
data: [
709,
1917,
2455,
2610,
1719,
1433,
1544,
3285,
5208,
3372,
2484,
4078
]
},
{
name: '男',
type: 'bar',
stack: '总量',
itemStyle: {
normal: {
color: 'rgba(0,191,183,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : '';
}
}
}
},
data: [
327,
1776,
507,
1200,
800,
482,
204,
1390,
1001,
951,
381,
220
]
}, {
name: '平均',
type: 'line',
stack: '总量',
symbolSize: 10,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(252,230,48,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : '';
}
}
}
},
data: [
1036,
3693,
2962,
3810,
2519,
1915,
1748,
4675,
6209,
4323,
2865,
4298
]
}
},
grid: {
borderWidth: 0,
top: 110,
bottom: 95,
textStyle: {
color: '#fff'
}
},
legend: {
x: '15%',
top: '10%',
textStyle: {
color: '#90979c'
},
data: ['女', '男', '平均']
},
calculable: true,
xAxis: [{
type: 'category',
axisLine: {
lineStyle: {
color: '#90979c'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
interval: 0
},
data: xData
}],
yAxis: [{
type: 'value',
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: '#90979c'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0
},
splitArea: {
show: false
}
}],
dataZoom: [{
show: true,
height: 30,
xAxisIndex: [
0
],
bottom: 30,
start: 10,
end: 80,
handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
handleSize: '110%',
handleStyle: {
color: '#d3dee5'
},
textStyle: {
color: '#fff' },
borderColor: '#90979c'
}, {
type: 'inside',
show: true,
height: 15,
start: 1,
end: 35
}],
series: [{
name: '女',
type: 'bar',
stack: '总量',
barMaxWidth: 35,
barGap: '10%',
itemStyle: {
normal: {
color: 'rgba(255,144,128,1)',
label: {
show: true,
textStyle: {
color: '#fff'
},
position: 'insideTop',
formatter(p) {
return p.value > 0 ? p.value : '';
}
}
}
},
data: [
709,
1917,
2455,
2610,
1719,
1433,
1544,
3285,
5208,
3372,
2484,
4078
]
})
}
},
{
name: '男',
type: 'bar',
stack: '总量',
itemStyle: {
normal: {
color: 'rgba(0,191,183,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : '';
}
}
}
},
data: [
327,
1776,
507,
1200,
800,
482,
204,
1390,
1001,
951,
381,
220
]
}, {
name: '平均',
type: 'line',
stack: '总量',
symbolSize: 10,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(252,230,48,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : '';
}
}
}
},
data: [
1036,
3693,
2962,
3810,
2519,
1915,
1748,
4675,
6209,
4323,
2865,
4298
]
}
]
})
}
}
}
</script>

View File

@@ -1,62 +1,63 @@
<template>
<div :ref="id" :action="url" class="dropzone" :id="id">
<input type="file" name="file">
</div>
<div :ref="id" :action="url" class="dropzone" :id="id">
<input type="file" name="file">
</div>
</template>
<script>
import Dropzone from 'dropzone';
import 'dropzone/dist/dropzone.css';
import Dropzone from 'dropzone';
import 'dropzone/dist/dropzone.css';
// import { getToken } from 'api/qiniu';
Dropzone.autoDiscover = false;
Dropzone.autoDiscover = false;
export default {
data() {
return {
dropzone: '',
initOnce: true
}
},
mounted() {
const element = document.getElementById(this.id);
const vm = this;
this.dropzone = new Dropzone(element, {
clickable: this.clickable,
thumbnailWidth: this.thumbnailWidth,
thumbnailHeight: this.thumbnailHeight,
maxFiles: this.maxFiles,
maxFilesize: this.maxFilesize,
dictRemoveFile: 'Remove',
addRemoveLinks: this.showRemoveLink,
acceptedFiles: this.acceptedFiles,
autoProcessQueue: this.autoProcessQueue,
dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload',
dictMaxFilesExceeded: '只能一个图',
previewTemplate: '<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div> <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
init() {
const val = vm.defaultImg;
if (!val) return;
if (Array.isArray(val)) {
if (val.length === 0) return;
val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v };
this.options.addedfile.call(this, mockFile);
this.options.thumbnail.call(this, mockFile, v);
mockFile.previewElement.classList.add('dz-success');
mockFile.previewElement.classList.add('dz-complete');
vm.initOnce = false;
return true;
})
} else {
const mockFile = { name: 'name', size: 12345, url: val };
export default {
data() {
return {
dropzone: '',
initOnce: true
}
},
mounted() {
const element = document.getElementById(this.id);
const vm = this;
this.dropzone = new Dropzone(element, {
clickable: this.clickable,
thumbnailWidth: this.thumbnailWidth,
thumbnailHeight: this.thumbnailHeight,
maxFiles: this.maxFiles,
maxFilesize: this.maxFilesize,
dictRemoveFile: 'Remove',
addRemoveLinks: this.showRemoveLink,
acceptedFiles: this.acceptedFiles,
autoProcessQueue: this.autoProcessQueue,
dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload',
dictMaxFilesExceeded: '只能一个图',
previewTemplate: '<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div> <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
init() {
const val = vm.defaultImg;
if (!val) return;
if (Array.isArray(val)) {
if (val.length === 0) return;
val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v };
this.options.addedfile.call(this, mockFile);
this.options.thumbnail.call(this, mockFile, val);
this.options.thumbnail.call(this, mockFile, v);
mockFile.previewElement.classList.add('dz-success');
mockFile.previewElement.classList.add('dz-complete');
vm.initOnce = false;
}
},
accept: (file, done) => {
return true;
})
} else {
const mockFile = { name: 'name', size: 12345, url: val };
this.options.addedfile.call(this, mockFile);
this.options.thumbnail.call(this, mockFile, val);
mockFile.previewElement.classList.add('dz-success');
mockFile.previewElement.classList.add('dz-complete');
vm.initOnce = false;
}
},
accept: (file, done) => {
/* 七牛*/
// const token = this.$store.getters.token;
// getToken(token).then(response => {
@@ -65,142 +66,142 @@
// file.url = response.data.qiniu_url;
// done();
// })
done();
},
sending: (file, xhr, formData) => {
done();
},
sending: (file, xhr, formData) => {
/* 七牛*/
console.log(file, xhr, formData)
console.log(file, xhr, formData)
// formData.append('token', file.token);
// formData.append('key', file.key);
vm.initOnce = false;
}
});
if (this.couldPaste) {
document.addEventListener('paste', this.pasteImg)
vm.initOnce = false;
}
});
this.dropzone.on('success', file => {
vm.$emit('dropzone-success', file, vm.dropzone.element)
});
this.dropzone.on('addedfile', file => {
vm.$emit('dropzone-fileAdded', file)
});
this.dropzone.on('removedfile', file => {
vm.$emit('dropzone-removedFile', file)
});
this.dropzone.on('error', (file, error, xhr) => {
vm.$emit('dropzone-error', file, error, xhr)
});
this.dropzone.on('successmultiple', (file, error, xhr) => {
vm.$emit('dropzone-successmultiple', file, error, xhr)
});
if (this.couldPaste) {
document.addEventListener('paste', this.pasteImg)
}
this.dropzone.on('success', file => {
vm.$emit('dropzone-success', file, vm.dropzone.element)
});
this.dropzone.on('addedfile', file => {
vm.$emit('dropzone-fileAdded', file)
});
this.dropzone.on('removedfile', file => {
vm.$emit('dropzone-removedFile', file)
});
this.dropzone.on('error', (file, error, xhr) => {
vm.$emit('dropzone-error', file, error, xhr)
});
this.dropzone.on('successmultiple', (file, error, xhr) => {
vm.$emit('dropzone-successmultiple', file, error, xhr)
});
},
methods: {
removeAllFiles() {
this.dropzone.removeAllFiles(true)
},
methods: {
removeAllFiles() {
this.dropzone.removeAllFiles(true)
},
processQueue() {
this.dropzone.processQueue()
},
pasteImg(event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
if (items[0].kind === 'file') {
this.dropzone.addFile(items[0].getAsFile())
}
},
initImages(val) {
if (!val) return;
if (Array.isArray(val)) {
val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v };
this.dropzone.options.addedfile.call(this.dropzone, mockFile);
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v);
mockFile.previewElement.classList.add('dz-success');
mockFile.previewElement.classList.add('dz-complete');
return true
})
} else {
const mockFile = { name: 'name', size: 12345, url: val };
processQueue() {
this.dropzone.processQueue()
},
pasteImg(event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
if (items[0].kind === 'file') {
this.dropzone.addFile(items[0].getAsFile())
}
},
initImages(val) {
if (!val) return;
if (Array.isArray(val)) {
val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v };
this.dropzone.options.addedfile.call(this.dropzone, mockFile);
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val);
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v);
mockFile.previewElement.classList.add('dz-success');
mockFile.previewElement.classList.add('dz-complete');
}
}
},
destroyed() {
document.removeEventListener('paste', this.pasteImg);
this.dropzone.destroy();
},
watch: {
defaultImg(val) {
if (val.length === 0) {
this.initOnce = false;
return;
}
if (!this.initOnce) return;
this.initImages(val);
this.initOnce = false;
}
},
props: {
id: {
type: String,
required: true
},
url: {
type: String,
required: true
},
clickable: {
type: Boolean,
default: true
},
defaultMsg: {
type: String,
default: '上传图片'
},
acceptedFiles: {
type: String
},
thumbnailHeight: {
type: Number,
default: 200
},
thumbnailWidth: {
type: Number,
default: 200
},
showRemoveLink: {
type: Boolean,
default: true
},
maxFilesize: {
type: Number,
default: 2
},
maxFiles: {
type: Number,
default: 3
},
autoProcessQueue: {
type: Boolean,
default: true
},
useCustomDropzoneOptions: {
type: Boolean,
default: false
},
defaultImg: {
default: false
},
couldPaste: {
default: false
return true
})
} else {
const mockFile = { name: 'name', size: 12345, url: val };
this.dropzone.options.addedfile.call(this.dropzone, mockFile);
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val);
mockFile.previewElement.classList.add('dz-success');
mockFile.previewElement.classList.add('dz-complete');
}
}
},
destroyed() {
document.removeEventListener('paste', this.pasteImg);
this.dropzone.destroy();
},
watch: {
defaultImg(val) {
if (val.length === 0) {
this.initOnce = false;
return;
}
if (!this.initOnce) return;
this.initImages(val);
this.initOnce = false;
}
},
props: {
id: {
type: String,
required: true
},
url: {
type: String,
required: true
},
clickable: {
type: Boolean,
default: true
},
defaultMsg: {
type: String,
default: '上传图片'
},
acceptedFiles: {
type: String
},
thumbnailHeight: {
type: Number,
default: 200
},
thumbnailWidth: {
type: Number,
default: 200
},
showRemoveLink: {
type: Boolean,
default: true
},
maxFilesize: {
type: Number,
default: 2
},
maxFiles: {
type: Number,
default: 3
},
autoProcessQueue: {
type: Boolean,
default: true
},
useCustomDropzoneOptions: {
type: Boolean,
default: false
},
defaultImg: {
default: false
},
couldPaste: {
default: false
}
}
}
</script>
<style scoped>

View File

@@ -1,51 +1,56 @@
<template>
<div>
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
<el-button size="small" type="primary">
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z" p-id="1864"></path></svg>
</el-button>
</el-badge>
<el-dialog title="bug日志" :visible.sync="dialogTableVisible">
<el-table :data="logsList">
<el-table-column label="message">
<template scope="scope">
<div>msg:{{ scope.row.err.message }}</div>
<br/>
<div>url: {{scope.row.url}}</div>
</template>
</el-table-column>
<el-table-column label="stack">
<template scope="scope">
{{ scope.row.err.stack}}
</template>
</el-table-column>
<div>
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
<el-button size="small" type="primary">
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
p-id="1864"></path>
</svg>
</el-button>
</el-badge>
<el-dialog title="bug日志" :visible.sync="dialogTableVisible">
<el-table :data="logsList">
<el-table-column label="message">
<template scope="scope">
<div>msg:{{ scope.row.err.message }}</div>
<br/>
<div>url: {{scope.row.url}}</div>
</template>
</el-table-column>
<el-table-column label="stack">
<template scope="scope">
{{ scope.row.err.stack}}
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</el-table>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'errLog',
props: {
logsList: {
type: Array
}
},
data() {
return {
dialogTableVisible: false
}
export default {
name: 'errLog',
props: {
logsList: {
type: Array
}
},
data() {
return {
dialogTableVisible: false
}
}
}
</script>
<style scoped>
.bug-svg {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

View File

@@ -1,36 +1,46 @@
<template>
<div>
<svg t="1492500959545" @click="toggleClick" class="wscn-icon hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z" p-id="1692"></path><path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z" p-id="1693"></path><path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z" p-id="1694"></path></svg>
</div>
<div>
<svg t="1492500959545" @click="toggleClick" class="wscn-icon hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
<path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"></path>
<path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"></path>
<path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"></path>
</svg>
</div>
</template>
<script>
export default {
name: 'hamburger',
props: {
isActive: {
type: Boolean,
default: false
},
toggleClick: {
type: Function,
default: null
}
export default {
name: 'hamburger',
props: {
isActive: {
type: Boolean,
default: false
},
toggleClick: {
type: Function,
default: null
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
cursor: pointer;
width: 20px;
height: 20px;
transform: rotate(0deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(90deg);
}
.hamburger {
display: inline-block;
cursor: pointer;
width: 20px;
height: 20px;
transform: rotate(0deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(90deg);
}
</style>

View File

@@ -1,11 +0,0 @@
import Vue from 'vue'
function registerAllComponents(requireContext) {
return requireContext.keys().forEach(comp => {
const vueComp = requireContext(comp)
const compName = vueComp.name ? vueComp.name.toLowerCase() : /\/([\w-]+)\.vue$/.exec(comp)[1]
Vue.component(compName, vueComp)
})
}
registerAllComponents(require.context('./', false, /\.vue$/))

View File

@@ -1,12 +1,12 @@
<template>
<svg class="wscn-icon" aria-hidden="true">
<svg class="svg-icon" aria-hidden="true">
<use :xlink:href="iconName"></use>
</svg>
</template>
<script>
export default {
name: 'wscn-icon-svg',
name: 'icon-svg',
props: {
iconClass: {
type: String,
@@ -20,7 +20,3 @@
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,52 +0,0 @@
<template>
<div class="icon-container" :style="containerStyle">
<slot class="icon"></slot>
</div>
</template>
<script>
export default {
name: 'wscn-icon-stack',
props: {
width: {
type: Number,
default: 20
},
shape: {
type: String,
default: 'circle',
validator: val => {
const validShapes = ['circle', 'square']
return validShapes.indexOf(val) > -1
}
}
},
computed: {
containerStyle() {
return {
width: `${this.width}px`,
height: `${this.width}px`,
fontSize: `${this.width * 0.6}px`,
borderRadius: `${this.shape === 'circle' && '50%'}`
}
}
}
}
</script>
<style lang="scss" scoped>
.icon-container {
display: inline-block;
position: relative;
overflow: hidden;
background: #1482F0;
.icon {
position: absolute;
color: #ffffff;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
</style>

View File

@@ -1,147 +1,147 @@
<template>
<div class="material-input__component" :class="computedClasses">
<input v-if="type === 'email'" type="email" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :required="required" @focus="handleFocus(true)"
@blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'url'" type="url" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :required="required" @focus="handleFocus(true)"
@blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'number'" type="number" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :max="max" :min="min" :minlength="minlength"
:maxlength="maxlength" :required="required" @focus="handleFocus(true)" @blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'password'" type="password" class="material-input" :name="name" :id="id" :placeholder="placeholder"
v-model="valueCopy" :readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :max="max" :min="min"
:required="required" @focus="handleFocus(true)" @blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'tel'" type="tel" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :required="required" @focus="handleFocus(true)"
@blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'text'" type="text" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :minlength="minlength" :maxlength="maxlength"
:required="required" @focus="handleFocus(true)" @blur="handleFocus(false)" @input="handleModelInput">
<div class="material-input__component" :class="computedClasses">
<input v-if="type === 'email'" type="email" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :required="required" @focus="handleFocus(true)"
@blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'url'" type="url" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :required="required" @focus="handleFocus(true)"
@blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'number'" type="number" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :max="max" :min="min" :minlength="minlength" :maxlength="maxlength"
:required="required" @focus="handleFocus(true)" @blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'password'" type="password" class="material-input" :name="name" :id="id" :placeholder="placeholder"
v-model="valueCopy" :readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :max="max" :min="min" :required="required"
@focus="handleFocus(true)" @blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'tel'" type="tel" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :required="required" @focus="handleFocus(true)"
@blur="handleFocus(false)" @input="handleModelInput">
<input v-if="type === 'text'" type="text" class="material-input" :name="name" :id="id" :placeholder="placeholder" v-model="valueCopy"
:readonly="readonly" :disabled="disabled" :autocomplete="autocomplete" :minlength="minlength" :maxlength="maxlength" :required="required"
@focus="handleFocus(true)" @blur="handleFocus(false)" @input="handleModelInput">
<span class="material-input-bar"></span>
<span class="material-input-bar"></span>
<label class="material-label">
<label class="material-label">
<slot></slot>
</label>
<div v-if="errorMessages" class="material-errors">
<div v-for="error in computedErrors" class="material-error">
{{ error }}
</div>
</div>
</div>
<div v-if="errorMessages" class="material-errors">
<div v-for="error in computedErrors" class="material-error" :key='error'>
{{ error }}
</div>
</div>
</div>
</template>
<script>
// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
export default {
name: 'material-input',
computed: {
computedErrors() {
return typeof this.errorMessages === 'string'
// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
export default {
name: 'material-input',
computed: {
computedErrors() {
return typeof this.errorMessages === 'string'
? [this.errorMessages] : this.errorMessages
},
computedClasses() {
return {
'material--active': this.focus,
'material--disabled': this.disabled,
'material--has-errors': Boolean(!this.valid || (this.errorMessages && this.errorMessages.length)),
'material--raised': Boolean(this.focus || this.valueCopy || // has value
},
computedClasses() {
return {
'material--active': this.focus,
'material--disabled': this.disabled,
'material--has-errors': Boolean(!this.valid || (this.errorMessages && this.errorMessages.length)),
'material--raised': Boolean(this.focus || this.valueCopy || // has value
(this.placeholder && !this.valueCopy)) // has placeholder
}
}
},
data() {
return {
valueCopy: null,
focus: false,
valid: true
}
},
beforeMount() {
// Here we are following the Vue2 convention on custom v-model:
// https://github.com/vuejs/vue/issues/2873#issuecomment-223759341
this.copyValue(this.value)
},
methods: {
handleModelInput(event) {
this.$emit('input', event.target.value, event)
this.handleValidation()
},
handleFocus(focused) {
this.focus = focused
},
handleValidation() {
this.valid = this.$el ? this.$el.querySelector('.material-input').validity.valid : this.valid
},
copyValue(value) {
this.valueCopy = value
this.handleValidation()
}
},
watch: {
value(newValue) {
this.copyValue(newValue)
}
},
props: {
id: {
type: String,
default: null
},
name: {
type: String,
default: null
},
type: {
type: String,
default: 'text'
},
value: {
default: null
},
placeholder: {
type: String,
default: null
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
min: {
type: String,
default: null
},
max: {
type: String,
default: null
},
minlength: {
type: Number,
default: null
},
maxlength: {
type: Number,
default: null
},
required: {
type: Boolean,
default: true
},
autocomplete: {
type: String,
default: 'off'
},
errorMessages: {
type: [Array, String],
default: null
}
}
}
},
data() {
return {
valueCopy: null,
focus: false,
valid: true
}
},
beforeMount() {
// Here we are following the Vue2 convention on custom v-model:
// https://github.com/vuejs/vue/issues/2873#issuecomment-223759341
this.copyValue(this.value)
},
methods: {
handleModelInput(event) {
this.$emit('input', event.target.value, event)
this.handleValidation()
},
handleFocus(focused) {
this.focus = focused
},
handleValidation() {
this.valid = this.$el ? this.$el.querySelector('.material-input').validity.valid : this.valid
},
copyValue(value) {
this.valueCopy = value
this.handleValidation()
}
},
watch: {
value(newValue) {
this.copyValue(newValue)
}
},
props: {
id: {
type: String,
default: null
},
name: {
type: String,
default: null
},
type: {
type: String,
default: 'text'
},
value: {
default: null
},
placeholder: {
type: String,
default: null
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
min: {
type: String,
default: null
},
max: {
type: String,
default: null
},
minlength: {
type: Number,
default: null
},
maxlength: {
type: Number,
default: null
},
required: {
type: Boolean,
default: true
},
autocomplete: {
type: String,
default: 'off'
},
errorMessages: {
type: [Array, String],
default: null
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>

View File

@@ -6,106 +6,109 @@
</template>
<script>
import 'simplemde/dist/simplemde.min.css'
import SimpleMDE from 'simplemde'
export default {
name: 'Sticky',
props: {
value: String,
id: {
type: String,
default: 'markdown-editor'
},
autofocus: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ''
},
height: {
type: Number,
default: 150
},
zIndex: {
type: Number,
default: 10
},
toolbar: {
type: Array
}
},
data() {
return {
simplemde: null,
hasChange: false
};
},
watch: {
value(val) {
if (val === this.simplemde.value() && !this.hasChange) return;
this.simplemde.value(val);
}
},
mounted() {
this.simplemde = new SimpleMDE({
element: document.getElementById(this.id),
autofocus: this.autofocus,
toolbar: this.toolbar,
spellChecker: false,
insertTexts: {
link: ['[', ']( )']
import 'simplemde/dist/simplemde.min.css';
import SimpleMDE from 'simplemde';
export default {
name: 'Sticky',
props: {
value: String,
id: {
type: String,
default: 'markdown-editor'
},
// hideIcons: ['guide', 'heading', 'quote', 'image', 'preview', 'side-by-side', 'fullscreen'],
placeholder: this.placeholder
});
if (this.value) {
this.simplemde.value(this.value);
}
this.simplemde.codemirror.on('change', () => {
if (this.hasChange) {
this.hasChange = true
autofocus: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ''
},
height: {
type: Number,
default: 150
},
zIndex: {
type: Number,
default: 10
},
toolbar: {
type: Array
}
this.$emit('input', this.simplemde.value());
});
},
destroyed() {
this.simplemde = null;
}
},
data() {
return {
simplemde: null,
hasChange: false
};
},
watch: {
value(val) {
if (val === this.simplemde.value() && !this.hasChange) return;
this.simplemde.value(val);
}
},
mounted() {
this.simplemde = new SimpleMDE({
element: document.getElementById(this.id),
autofocus: this.autofocus,
toolbar: this.toolbar,
spellChecker: false,
insertTexts: {
link: ['[', ']( )']
},
// hideIcons: ['guide', 'heading', 'quote', 'image', 'preview', 'side-by-side', 'fullscreen'],
placeholder: this.placeholder
});
if (this.value) {
this.simplemde.value(this.value);
}
this.simplemde.codemirror.on('change', () => {
if (this.hasChange) {
this.hasChange = true
}
this.$emit('input', this.simplemde.value());
});
},
destroyed() {
this.simplemde = null;
}
};
</script>
<style>
.simplemde-container .CodeMirror {
/*height: 150px;*/
min-height: 150px;
}
.simplemde-container .CodeMirror-scroll{
min-height: 150px;
}
.simplemde-container .CodeMirror {
/*height: 150px;*/
min-height: 150px;
}
.simplemde-container .CodeMirror-code{
padding-bottom: 40px;
}
.simplemde-container .editor-statusbar {
display: none;
}
.simplemde-container .CodeMirror-scroll {
min-height: 150px;
}
.simplemde-container .CodeMirror .CodeMirror-code .cm-link {
color: #1482F0;
}
.simplemde-container .CodeMirror-code {
padding-bottom: 40px;
}
.simplemde-container .CodeMirror .CodeMirror-code .cm-string.cm-url {
color: #2d3b4d;
font-weight: bold;
}
.simplemde-container .editor-statusbar {
display: none;
}
.simplemde-container .CodeMirror .CodeMirror-code .cm-formatting-link-string.cm-url {
padding: 0 2px;
font-weight: bold;
color: #E61E1E;
}
.simplemde-container .CodeMirror .CodeMirror-code .cm-link {
color: #1482F0;
}
.simplemde-container .CodeMirror .CodeMirror-code .cm-string.cm-url {
color: #2d3b4d;
font-weight: bold;
}
.simplemde-container .CodeMirror .CodeMirror-code .cm-formatting-link-string.cm-url {
padding: 0 2px;
font-weight: bold;
color: #E61E1E;
}
</style>

View File

@@ -1,145 +1,140 @@
<template>
<div class="pan-item" :style="{zIndex:zIndex,height:height,width:width}">
<div class="pan-info">
<div class="pan-info-roles-container">
<slot></slot>
</div>
</div>
<img class="pan-thumb" :src="image">
</div>
<div class="pan-item" :style="{zIndex:zIndex,height:height,width:width}">
<div class="pan-info">
<div class="pan-info-roles-container">
<slot></slot>
</div>
</div>
<img class="pan-thumb" :src="image">
</div>
</template>
<script>
export default {
name: 'PanThumb',
props: {
image: {
type: String,
required: true
},
zIndex: {
type: Number,
default: 100
},
width: {
type: String,
default: '150px'
},
height: {
type: String,
default: '150px'
}
export default {
name: 'PanThumb',
props: {
image: {
type: String,
required: true
},
data() {
return {};
zIndex: {
type: Number,
default: 100
},
width: {
type: String,
default: '150px'
},
height: {
type: String,
default: '150px'
}
};
}
};
</script>
<style scoped>
.pan-item {
width: 200px;
height: 200px;
border-radius: 50%;
display: inline-block;
position: relative;
cursor: default;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.pan-item {
width: 200px;
height: 200px;
border-radius: 50%;
display: inline-block;
position: relative;
cursor: default;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.pan-info-roles-container {
padding: 20px;
text-align: center;
}
.pan-info-roles-container {
padding: 20px;
text-align: center;
}
.pan-thumb {
width: 100%;
height: 100%;
background-size: 100%;
border-radius: 50%;
overflow: hidden;
position: absolute;
transform-origin: 95% 40%;
transition: all 0.3s ease-in-out;
}
.pan-thumb {
width: 100%;
height: 100%;
background-size: 100%;
border-radius: 50%;
overflow: hidden;
position: absolute;
transform-origin: 95% 40%;
transition: all 0.3s ease-in-out;
}
.pan-thumb:after {
content: '';
width: 8px;
height: 8px;
position: absolute;
border-radius: 50%;
top: 40%;
left: 95%;
margin: -4px 0 0 -4px;
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
}
.pan-thumb:after {
content: '';
width: 8px;
height: 8px;
position: absolute;
border-radius: 50%;
top: 40%;
left: 95%;
margin: -4px 0 0 -4px;
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
}
.pan-info {
position: absolute;
width: inherit;
height: inherit;
border-radius: 50%;
overflow: hidden;
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
}
.pan-info {
position: absolute;
width: inherit;
height: inherit;
border-radius: 50%;
overflow: hidden;
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
}
.pan-info h3 {
color: #fff;
text-transform: uppercase;
position: relative;
letter-spacing: 2px;
font-size: 18px;
margin: 0 60px;
padding: 22px 0 0 0;
height: 85px;
font-family: 'Open Sans', Arial, sans-serif;
text-shadow: 0 0 1px #fff,
0 1px 2px rgba(0, 0, 0, 0.3);
}
.pan-info h3 {
color: #fff;
text-transform: uppercase;
position: relative;
letter-spacing: 2px;
font-size: 18px;
margin: 0 60px;
padding: 22px 0 0 0;
height: 85px;
font-family: 'Open Sans', Arial, sans-serif;
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
}
.pan-info p {
color: #fff;
padding: 10px 5px;
font-style: italic;
margin: 0 30px;
font-size: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.5);
}
.pan-info p {
color: #fff;
padding: 10px 5px;
font-style: italic;
margin: 0 30px;
font-size: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.5);
}
.pan-info p a {
display: block;
color: #333;
width: 80px;
height: 80px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
color: #fff;
font-style: normal;
font-weight: 700;
text-transform: uppercase;
font-size: 9px;
letter-spacing: 1px;
padding-top: 24px;
margin: 7px auto 0;
font-family: 'Open Sans', Arial, sans-serif;
opacity: 0;
transition: transform 0.3s ease-in-out 0.2s,
opacity 0.3s ease-in-out 0.2s,
background 0.2s linear 0s;
transform: translateX(60px) rotate(90deg);
}
.pan-info p a {
display: block;
color: #333;
width: 80px;
height: 80px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
color: #fff;
font-style: normal;
font-weight: 700;
text-transform: uppercase;
font-size: 9px;
letter-spacing: 1px;
padding-top: 24px;
margin: 7px auto 0;
font-family: 'Open Sans', Arial, sans-serif;
opacity: 0;
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
transform: translateX(60px) rotate(90deg);
}
.pan-info p a:hover {
background: rgba(255, 255, 255, 0.5);
}
.pan-info p a:hover {
background: rgba(255, 255, 255, 0.5);
}
.pan-item:hover .pan-thumb {
transform: rotate(-110deg);
}
.pan-item:hover .pan-thumb {
transform: rotate(-110deg);
}
.pan-item:hover .pan-info p a {
opacity: 1;
transform: translateX(0px) rotate(0deg);
}
.pan-item:hover .pan-info p a {
opacity: 1;
transform: translateX(0px) rotate(0deg);
}
</style>

View File

@@ -1,57 +1,53 @@
<template>
<svg @click='click' class="icon screenfull" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" t="1497503607356" viewBox="0 0 1024 1024" version="1.1" p-id="4109" :fill='fill' :width="width" :height="height"><path d="M604.157933 512l204.484208 204.484208 82.942037-82.942037c10.364045-10.952446 26.498514-13.83817 40.309054-8.067746 13.249769 5.742794 22.465664 18.99154 22.465664 33.977859l0 258.042008c0 20.168342-16.695241 36.863582-36.863582 36.863582L659.452283 954.357873c-14.986319 0-28.236088-9.215896-33.977859-23.025413-5.770424-13.249769-2.885723-29.384237 8.067746-39.748283l82.942037-82.942037L512 604.157933 307.515792 808.642141l82.942037 82.942037c10.952446 10.364045 13.83817 26.498514 8.067746 39.748283-5.742794 13.809517-18.99154 23.025413-33.977859 23.025413L106.504686 954.357873c-20.168342 0-36.863582-16.695241-36.863582-36.863582L69.641103 659.452283c0-14.986319 9.215896-28.236088 23.025413-33.977859 13.249769-5.770424 29.384237-2.8847 39.748283 8.067746l82.942037 82.942037 204.484208-204.484208L215.357859 307.515792l-82.942037 82.942037c-6.890944 6.918573-16.10684 10.952446-25.911136 10.952446-4.593622 0-9.804297-1.14815-13.83817-2.8847-13.809517-5.742794-23.025413-18.99154-23.025413-33.977859L69.641103 106.504686c0-20.168342 16.695241-36.863582 36.863582-36.863582L364.546693 69.641103c14.986319 0 28.236088 9.215896 33.977859 23.025413 5.770424 13.249769 2.8847 29.384237-8.067746 39.748283l-82.942037 82.942037 204.484208 204.484208L716.484208 215.357859l-82.942037-82.942037c-10.952446-10.364045-13.83817-26.498514-8.067746-39.748283 5.742794-13.809517 18.99154-23.025413 33.977859-23.025413l258.042008 0c20.168342 0 36.863582 16.695241 36.863582 36.863582l0 258.042008c0 14.986319-9.215896 28.236088-22.465664 33.977859-4.593622 1.736551-9.804297 2.8847-14.397918 2.8847-9.804297 0-19.020192-4.033873-25.911136-10.952446l-82.942037-82.942037L604.157933 512z" p-id="4110"/></svg>
<svg @click='click' class="icon screenfull" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
t="1497503607356" viewBox="0 0 1024 1024" version="1.1" p-id="4109" :fill='fill' :width="width" :height="height">
<path d="M604.157933 512l204.484208 204.484208 82.942037-82.942037c10.364045-10.952446 26.498514-13.83817 40.309054-8.067746 13.249769 5.742794 22.465664 18.99154 22.465664 33.977859l0 258.042008c0 20.168342-16.695241 36.863582-36.863582 36.863582L659.452283 954.357873c-14.986319 0-28.236088-9.215896-33.977859-23.025413-5.770424-13.249769-2.885723-29.384237 8.067746-39.748283l82.942037-82.942037L512 604.157933 307.515792 808.642141l82.942037 82.942037c10.952446 10.364045 13.83817 26.498514 8.067746 39.748283-5.742794 13.809517-18.99154 23.025413-33.977859 23.025413L106.504686 954.357873c-20.168342 0-36.863582-16.695241-36.863582-36.863582L69.641103 659.452283c0-14.986319 9.215896-28.236088 23.025413-33.977859 13.249769-5.770424 29.384237-2.8847 39.748283 8.067746l82.942037 82.942037 204.484208-204.484208L215.357859 307.515792l-82.942037 82.942037c-6.890944 6.918573-16.10684 10.952446-25.911136 10.952446-4.593622 0-9.804297-1.14815-13.83817-2.8847-13.809517-5.742794-23.025413-18.99154-23.025413-33.977859L69.641103 106.504686c0-20.168342 16.695241-36.863582 36.863582-36.863582L364.546693 69.641103c14.986319 0 28.236088 9.215896 33.977859 23.025413 5.770424 13.249769 2.8847 29.384237-8.067746 39.748283l-82.942037 82.942037 204.484208 204.484208L716.484208 215.357859l-82.942037-82.942037c-10.952446-10.364045-13.83817-26.498514-8.067746-39.748283 5.742794-13.809517 18.99154-23.025413 33.977859-23.025413l258.042008 0c20.168342 0 36.863582 16.695241 36.863582 36.863582l0 258.042008c0 14.986319-9.215896 28.236088-22.465664 33.977859-4.593622 1.736551-9.804297 2.8847-14.397918 2.8847-9.804297 0-19.020192-4.033873-25.911136-10.952446l-82.942037-82.942037L604.157933 512z"
p-id="4110" />
</svg>
</template>
<script>
import screenfull from 'screenfull';
export default {
name: 'hamburger',
props: {
width: {
type: Number,
default: 22
},
height: {
type: Number,
default: 22
},
fill: {
type: String,
default: '#48576a'
}
import screenfull from 'screenfull';
export default {
name: 'hamburger',
props: {
width: {
type: Number,
default: 22
},
data() {
return {
isFullscreen: false
}
height: {
type: Number,
default: 22
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
});
return false;
}
if (this.isFullscreen) {
screenfull.exit();
this.isFullscreen = false;
} else {
screenfull.request();
this.isFullscreen = true;
}
fill: {
type: String,
default: '#48576a'
}
},
data() {
return {
isFullscreen: false
}
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
});
return false;
}
screenfull.toggle();
}
}
}
</script>
<style scoped>
.screenfull {
display: inline-block;
cursor: pointer;
vertical-align: -0.15em;
}
.screenfull {
display: inline-block;
cursor: pointer;
vertical-align: -0.15em;
}
</style>

View File

@@ -1,41 +1,44 @@
<template>
<div :class="classes">
<slot></slot>
</div>
<div :class="classes">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'Pane',
data() {
const classes = ['Pane', this.$parent.split, 'className'];
return {
classes: classes.join(' '),
percent: 50
}
}
export default {
name: 'Pane',
data() {
const classes = ['Pane', this.$parent.split, 'className'];
return {
classes: classes.join(' '),
percent: 50
}
}
}
</script>
<style>
.splitter-pane.vertical.splitter-paneL{
position: absolute;
left: 0px;
height: 100%;
}
.splitter-pane.vertical.splitter-paneR{
position: absolute;
right: 0px;
height: 100%;
}
.splitter-pane.horizontal.splitter-paneL{
position: absolute;
top: 0px;
width: 100%;
}
.splitter-pane.horizontal.splitter-paneR{
position: absolute;
bottom: 0px;
width: 100%;
}
<style scoped>
.splitter-pane.vertical.splitter-paneL {
position: absolute;
left: 0px;
height: 100%;
}
.splitter-pane.vertical.splitter-paneR {
position: absolute;
right: 0px;
height: 100%;
}
.splitter-pane.horizontal.splitter-paneL {
position: absolute;
top: 0px;
width: 100%;
}
.splitter-pane.horizontal.splitter-paneR {
position: absolute;
bottom: 0px;
width: 100%;
}
</style>

View File

@@ -1,71 +1,72 @@
<template>
<div :class="classes" @mousedown="onMouseDown"></div>
<div :class="classes" @mousedown="onMouseDown"></div>
</template>
<script>
export default {
props: {
split: {
validator(value) {
return ['vertical', 'horizontal'].indexOf(value) >= 0
},
required: true
export default {
props: {
split: {
validator(value) {
return ['vertical', 'horizontal'].indexOf(value) >= 0
},
onMouseDown: {
type: Function,
required: true
}
required: true
},
data() {
const classes = ['Resizer', this.split, 'className'];
return {
classes: classes.join(' ')
}
onMouseDown: {
type: Function,
required: true
}
},
data() {
const classes = ['Resizer', this.split, 'className'];
return {
classes: classes.join(' ')
}
}
}
</script>
<style scoped>
.Resizer {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background: #000;
position: absolute;
opacity: .2;
z-index: 1;
/*-moz-background-clip: padding;*/
/*-webkit-background-clip: padding;*/
/*background-clip: padding-box;*/
}
.Resizer {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background: #000;
position: absolute;
opacity: .2;
z-index: 1;
/*-moz-background-clip: padding;*/
/*-webkit-background-clip: padding;*/
/*background-clip: padding-box;*/
}
/*.Resizer:hover {*/
/*-webkit-transition: all 2s ease;*/
/*transition: all 2s ease;*/
/*}*/
/*.Resizer:hover {*/
/*-webkit-transition: all 2s ease;*/
/*transition: all 2s ease;*/
/*}*/
.Resizer.horizontal {
height: 11px;
margin: -5px 0;
border-top: 5px solid rgba(255, 255, 255, 0);
border-bottom: 5px solid rgba(255, 255, 255, 0);
cursor: row-resize;
width: 100%;
}
.Resizer.horizontal {
height: 11px;
margin: -5px 0;
border-top: 5px solid rgba(255, 255, 255, 0);
border-bottom: 5px solid rgba(255, 255, 255, 0);
cursor: row-resize;
width: 100%;
}
.Resizer.horizontal:hover {
border-top: 5px solid rgba(0, 0, 0, 0.5);
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
}
.Resizer.horizontal:hover {
border-top: 5px solid rgba(0, 0, 0, 0.5);
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
}
.Resizer.vertical {
width: 11px;
height: 100%;
border-left: 5px solid rgba(255, 255, 255, 0);
border-right: 5px solid rgba(255, 255, 255, 0);
cursor: col-resize;
}
.Resizer.vertical {
width: 11px;
height: 100%;
border-left: 5px solid rgba(255, 255, 255, 0);
border-right: 5px solid rgba(255, 255, 255, 0);
cursor: col-resize;
}
.Resizer.vertical:hover {
border-left: 5px solid rgba(0, 0, 0, 0.5);
border-right: 5px solid rgba(0, 0, 0, 0.5);
}
.Resizer.vertical:hover {
border-left: 5px solid rgba(0, 0, 0, 0.5);
border-right: 5px solid rgba(0, 0, 0, 0.5);
}
</style>

View File

@@ -1,111 +1,111 @@
<template>
<div ref :style="{ cursor, userSelect}" class="vue-splitter-container clearfix" @mouseup="onMouseUp"
@mousemove="onMouseMove">
<Pane class="splitter-pane splitter-paneL" :split="split" :style="{ [type]: percent+'%'}">
<slot name="paneL"></slot>
</Pane>
<Resizer :style="{ [resizeType]: percent+'%'}" :split="split" :onMouseDown="onMouseDown"
@click="onClick"></Resizer>
<Pane class="splitter-pane splitter-paneR" :split="split" :style="{ [type]: 100-percent+'%'}">
<slot name="paneR"></slot>
</Pane>
</div>
<div ref :style="{ cursor, userSelect}" class="vue-splitter-container clearfix" @mouseup="onMouseUp" @mousemove="onMouseMove">
<pane class="splitter-pane splitter-paneL" :split="split" :style="{ [type]: percent+'%'}">
<slot name="paneL"></slot>
</pane>
<resizer :style="{ [resizeType]: percent+'%'}" :split="split" :onMouseDown="onMouseDown" @click="onClick"></resizer>
<pane class="splitter-pane splitter-paneR" :split="split" :style="{ [type]: 100-percent+'%'}">
<slot name="paneR"></slot>
</pane>
</div>
</template>
<style scoped>
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.vue-splitter-container {
height: 100%;
/*display: flex;*/
position: relative;
}
</style>
<script>
import Resizer from './Resizer';
import Pane from './Pane';
export default {
name: 'splitPane',
components: { Resizer, Pane },
props: {
margin: {
type: Number,
default: 10
import Resizer from './Resizer';
import Pane from './Pane';
export default {
name: 'splitPane',
components: { Resizer, Pane },
props: {
margin: {
type: Number,
default: 10
},
split: {
validator(value) {
return ['vertical', 'horizontal'].indexOf(value) >= 0
},
split: {
validator(value) {
return ['vertical', 'horizontal'].indexOf(value) >= 0
},
required: true
required: true
}
},
data() {
return {
active: false,
hasMoved: false,
height: null,
percent: 50,
type: this.split === 'vertical' ? 'width' : 'height',
resizeType: this.split === 'vertical' ? 'left' : 'top'
}
},
computed: {
userSelect() {
return this.active ? 'none' : ''
},
cursor() {
return this.active ? 'col-resize' : ''
}
},
methods: {
onClick() {
if (!this.hasMoved) {
this.percent = 50;
this.$emit('resize');
}
},
data() {
return {
active: false,
hasMoved: false,
height: null,
percent: 50,
type: this.split === 'vertical' ? 'width' : 'height',
resizeType: this.split === 'vertical' ? 'left' : 'top'
}
onMouseDown() {
this.active = true;
this.hasMoved = false;
},
computed: {
userSelect() {
return this.active ? 'none' : ''
},
cursor() {
return this.active ? 'col-resize' : ''
}
onMouseUp() {
this.active = false;
},
methods: {
onClick() {
if (!this.hasMoved) {
this.percent = 50;
this.$emit('resize');
}
},
onMouseDown() {
this.active = true;
this.hasMoved = false;
},
onMouseUp() {
onMouseMove(e) {
if (e.buttons === 0 || e.which === 0) {
this.active = false;
},
onMouseMove(e) {
if (e.buttons === 0 || e.which === 0) {
this.active = false;
}
if (this.active) {
let offset = 0;
let target = e.currentTarget;
if (this.split === 'vertical') {
while (target) {
offset += target.offsetLeft;
target = target.offsetParent;
}
} else {
while (target) {
offset += target.offsetTop;
target = target.offsetParent;
}
}
if (this.active) {
let offset = 0;
let target = e.currentTarget;
if (this.split === 'vertical') {
while (target) {
offset += target.offsetLeft;
target = target.offsetParent;
}
} else {
while (target) {
offset += target.offsetTop;
target = target.offsetParent;
}
}
const currentPage = this.split === 'vertical' ? e.pageX : e.pageY;
const targetOffset = this.split === 'vertical' ? e.currentTarget.offsetWidth : e.currentTarget.offsetHeight;
const percent = Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100;
if (percent > this.margin && percent < 100 - this.margin) {
this.percent = percent;
}
this.$emit('resize');
this.hasMoved = true;
const currentPage = this.split === 'vertical' ? e.pageX : e.pageY;
const targetOffset = this.split === 'vertical' ? e.currentTarget.offsetWidth : e.currentTarget.offsetHeight;
const percent = Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100;
if (percent > this.margin && percent < 100 - this.margin) {
this.percent = percent;
}
this.$emit('resize');
this.hasMoved = true;
}
}
}
}
</script>
<style scoped>
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.vue-splitter-container {
height: 100%;
/*display: flex;*/
position: relative;
}
</style>

View File

@@ -1,73 +1,74 @@
<template>
<div :style="{height:height+'px',zIndex:zIndex}">
<div :class="className" :style="{top:stickyTop+'px',zIndex:zIndex,position:position,width:width,height:height+'px'}">
<slot>
<div>sticky</div>
</slot>
</div>
<div :style="{height:height+'px',zIndex:zIndex}">
<div :class="className" :style="{top:stickyTop+'px',zIndex:zIndex,position:position,width:width,height:height+'px'}">
<slot>
<div>sticky</div>
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'Sticky',
props: {
stickyTop: {
type: Number,
default: 0
},
zIndex: {
type: Number,
default: 1
},
className: {
type: String
}
},
data() {
return {
active: false,
position: '',
currentTop: '',
width: undefined,
height: undefined,
child: null,
stickyHeight: 0
};
<script>
export default {
name: 'Sticky',
props: {
stickyTop: {
type: Number,
default: 0
},
methods: {
sticky() {
if (this.active) {
return
}
this.position = 'fixed';
this.active = true;
this.width = this.width + 'px';
},
reset() {
if (!this.active) {
return
}
this.position = '';
this.width = 'auto'
this.active = false
},
handleScroll() {
this.width = this.$el.getBoundingClientRect().width;
const offsetTop = this.$el.getBoundingClientRect().top;
if (offsetTop <= this.stickyTop) {
this.sticky();
return
}
this.reset()
}
zIndex: {
type: Number,
default: 1
},
mounted() {
this.height = this.$el.getBoundingClientRect().height;
window.addEventListener('scroll', this.handleScroll);
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll);
className: {
type: String
}
};
},
data() {
return {
active: false,
position: '',
currentTop: '',
width: undefined,
height: undefined,
child: null,
stickyHeight: 0
};
},
methods: {
sticky() {
if (this.active) {
return
}
this.position = 'fixed';
this.active = true;
this.width = this.width + 'px';
},
reset() {
if (!this.active) {
return
}
this.position = '';
this.width = 'auto'
this.active = false
},
handleScroll() {
this.width = this.$el.getBoundingClientRect().width;
const offsetTop = this.$el.getBoundingClientRect().top;
if (offsetTop <= this.stickyTop) {
this.sticky();
return
}
this.reset()
}
},
mounted() {
this.height = this.$el.getBoundingClientRect().height;
window.addEventListener('scroll', this.handleScroll);
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll);
}
};
</script>

View File

@@ -6,79 +6,79 @@
<script>
// import { getToken, upload } from 'api/qiniu'; // 七牛
export default {
name: 'tinymce',
props: {
id: {
type: String,
default: 'tinymceEditor'
},
value: {
type: String,
default: ''
},
toolbar: {
type: Array,
required: false,
default() {
return ['removeformat undo redo | bullist numlist | outdent indent | forecolor | fullscreen code', 'bold italic blockquote | h2 p media link | alignleft aligncenter alignright']
export default {
name: 'tinymce',
props: {
id: {
type: String,
default: 'tinymceEditor'
},
value: {
type: String,
default: ''
},
toolbar: {
type: Array,
required: false,
default() {
return ['removeformat undo redo | bullist numlist | outdent indent | forecolor | fullscreen code', 'bold italic blockquote | h2 p media link | alignleft aligncenter alignright']
}
},
data() {
return {
hasChange: false,
hasInit: false
}
},
menubar: {
default: ''
},
height: {
type: Number,
required: false,
default: 360
}
},
data() {
return {
hasChange: false,
hasInit: false
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => tinymce.get(this.id).setContent(val))
}
}
},
menubar: {
default: ''
},
height: {
type: Number,
required: false,
default: 360
}
},
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => tinymce.get(this.id).setContent(val))
}
}
},
mounted() {
const _this = this;
tinymce.init({
selector: `#${this.id}`,
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
mounted() {
const _this = this;
tinymce.init({
selector: `#${this.id}`,
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
// language: 'zh_CN',
// language_url: '/static/tinymce/langs/zh_CN.js',
toolbar: this.toolbar,
menubar: this.menubar,
plugins: 'advlist,autolink,code,paste,textcolor, colorpicker,fullscreen,link,lists,media,wordcount, imagetools,watermark',
end_container_on_empty_block: true,
powerpaste_word_import: 'clean',
code_dialog_height: 450,
code_dialog_width: 1000,
advlist_bullet_styles: 'square',
advlist_number_styles: 'default',
block_formats: '普通标签=p;小标题=h2;',
imagetools_cors_hosts: ['wpimg.wallstcn.com', 'wallstreetcn.com'],
imagetools_toolbar: 'watermark',
default_link_target: '_blank',
link_title: false,
init_instance_callback: editor => {
if (_this.value) {
editor.setContent(_this.value)
}
_this.hasInit = true;
editor.on('NodeChange Change KeyUp', () => {
this.hasChange = true;
this.$emit('input', editor.getContent({ format: 'raw' }));
});
},
toolbar: this.toolbar,
menubar: this.menubar,
plugins: 'advlist,autolink,code,paste,textcolor, colorpicker,fullscreen,link,lists,media,wordcount, imagetools,watermark',
end_container_on_empty_block: true,
powerpaste_word_import: 'clean',
code_dialog_height: 450,
code_dialog_width: 1000,
advlist_bullet_styles: 'square',
advlist_number_styles: 'default',
block_formats: '普通标签=p;小标题=h2;',
imagetools_cors_hosts: ['wpimg.wallstcn.com', 'wallstreetcn.com'],
imagetools_toolbar: 'watermark',
default_link_target: '_blank',
link_title: false,
init_instance_callback: editor => {
if (_this.value) {
editor.setContent(_this.value)
}
_this.hasInit = true;
editor.on('NodeChange Change KeyUp', () => {
this.hasChange = true;
this.$emit('input', editor.getContent({ format: 'raw' }));
});
},
// 整合七牛上传
// images_dataimg_filter(img) {
// setTimeout(() => {
@@ -112,44 +112,44 @@
// console.log(err);
// });
// },
setup(editor) {
editor.addButton('h2', {
title: '小标题', // tooltip text seen on mouseover
text: '小标题',
onclick() {
editor.execCommand('mceToggleFormat', false, 'h2');
},
onPostRender() {
const btn = this;
editor.on('init', () => {
editor.formatter.formatChanged('h2', state => {
btn.active(state);
setup(editor) {
editor.addButton('h2', {
title: '小标题', // tooltip text seen on mouseover
text: '小标题',
onclick() {
editor.execCommand('mceToggleFormat', false, 'h2');
},
onPostRender() {
const btn = this;
editor.on('init', () => {
editor.formatter.formatChanged('h2', state => {
btn.active(state);
});
});
});
}
});
editor.addButton('p', {
title: '正文',
text: '正文',
onclick() {
editor.execCommand('mceToggleFormat', false, 'p');
},
onPostRender() {
const btn = this;
editor.on('init', () => {
editor.formatter.formatChanged('p', state => {
btn.active(state);
}
});
editor.addButton('p', {
title: '正文',
text: '正文',
onclick() {
editor.execCommand('mceToggleFormat', false, 'p');
},
onPostRender() {
const btn = this;
editor.on('init', () => {
editor.formatter.formatChanged('p', state => {
btn.active(state);
});
});
});
}
});
}
});
},
destroyed() {
tinymce.get(this.id).destroy();
}
}
}
});
}
});
},
destroyed() {
tinymce.get(this.id).destroy();
}
}
</script>
<style scoped>

View File

@@ -0,0 +1,70 @@
<template>
<li class="todo" :class="{ completed: todo.done, editing: editing }">
<div class="view">
<input class="toggle"
type="checkbox"
:checked="todo.done"
@change="toggleTodo( todo)">
<label v-text="todo.text" @dblclick="editing = true"></label>
<button class="destroy" @click="deleteTodo( todo )"></button>
</div>
<input class="edit"
v-show="editing"
v-focus="editing"
:value="todo.text"
@keyup.enter="doneEdit"
@keyup.esc="cancelEdit"
@blur="doneEdit">
</li>
</template>
<script>
export default {
name: 'Todo',
props: ['todo'],
data() {
return {
editing: false
}
},
directives: {
focus(el, { value }, { context }) {
if (value) {
context.$nextTick(() => {
el.focus()
})
}
}
},
methods: {
deleteTodo(todo) {
this.$emit('deleteTodo', todo)
},
editTodo({ todo, value }) {
this.$emit('editTodo', { todo, value })
},
toggleTodo(todo) {
this.$emit('toggleTodo', todo)
},
doneEdit(e) {
const value = e.target.value.trim()
const { todo } = this
if (!value) {
this.deleteTodo({
todo
})
} else if (this.editing) {
this.editTodo({
todo,
value
})
this.editing = false
}
},
cancelEdit(e) {
e.target.value = this.todo.text
this.editing = false
}
}
}
</script>

View File

@@ -0,0 +1,318 @@
.todoapp {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 40PX auto 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 18px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
text-align: center;
border: none;
/* Mobile Safari */
opacity: 0;
position: absolute;
}
.toggle-all+label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all+label:before {
content: '';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked+label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none;
/* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle+label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
background-size: 36px;
}
.todo-list li .toggle:checked+label {
background-size: 36px;
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 50px;
display: block;
line-height: 1.0;
font-size: 14px;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
transition: color 0.2s ease-out;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
position: relative;
padding: 10px 15px;
height: 40px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: -20px;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
font-size: 12px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}
}

View File

@@ -0,0 +1,116 @@
<template>
<section class="todoapp">
<!-- header -->
<header class="header">
<input class="new-todo" autofocus autocomplete="off" placeholder="TODO LIST?" @keyup.enter="addTodo">
</header>
<!-- main section -->
<section class="main" v-show="todos.length">
<input class="toggle-all" id="toggle-all" type="checkbox" :checked="allChecked" @change="toggleAll({ done: !allChecked })">
<label for="toggle-all"></label>
<ul class="todo-list">
<todo @toggleTodo='toggleTodo' @editTodo='editTodo' @deleteTodo='deleteTodo' v-for="(todo, index) in filteredTodos" :key="index"
:todo="todo"></todo>
</ul>
</section>
<!-- footer -->
<footer class="footer" v-show="todos.length">
<span class="todo-count">
<strong>{{ remaining }}</strong>
{{ remaining | pluralize('item') }} left
</span>
<ul class="filters">
<li v-for="(val, key) in filters" :key="key">
<a :class="{ selected: visibility === key }" @click.prevent="visibility = key">{{ key | capitalize }}</a>
</li>
</ul>
<button class="clear-completed" v-show="todos.length > remaining" @click="clearCompleted">
Clear completed
</button>
</footer>
</section>
</template>
<script>
import Todo from './Todo.vue'
const STORAGE_KEY = 'todos'
const filters = {
all: todos => todos,
active: todos => todos.filter(todo => !todo.done),
completed: todos => todos.filter(todo => todo.done)
}
const defalutList = [
{ text: 'star this repository', done: false },
{ text: 'fork this repository', done: false },
{ text: 'follow author', done: false }
]
export default {
components: { Todo },
data() {
return {
visibility: 'all',
filters,
todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || defalutList
}
},
computed: {
allChecked() {
return this.todos.every(todo => todo.done)
},
filteredTodos() {
return filters[this.visibility](this.todos)
},
remaining() {
return this.todos.filter(todo => !todo.done).length
}
},
methods: {
setLocalStorgae() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos))
},
addTodo(e) {
const text = e.target.value
if (text.trim()) {
this.todos.push({
text,
done: false
})
this.setLocalStorgae()
}
e.target.value = ''
},
toggleTodo(val) {
val.done = !val.done
this.setLocalStorgae()
},
deleteTodo(todo) {
console.log(todo)
this.todos.splice(this.todos.indexOf(todo), 1)
this.setLocalStorgae()
},
editTodo({ todo, value }) {
console.log(todo, value)
todo.text = value
this.setLocalStorgae()
},
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.done)
this.setLocalStorgae()
},
toggleAll({ done }) {
this.todos.forEach(todo => {
todo.done = done
this.setLocalStorgae()
})
}
},
filters: {
pluralize: (n, w) => n === 1 ? w : w + 's',
capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)
}
}
</script>
<style lang="scss">
@import './index.scss';
</style>

View File

@@ -1,14 +1,7 @@
<template>
<div class="upload-container">
<el-upload
class="image-uploader"
:data="dataObj"
drag
:multiple="false"
:show-file-list="false"
action="https://httpbin.org/post"
:on-success="handleImageScucess">
<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
:on-success="handleImageScucess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
@@ -22,53 +15,54 @@
</div>
</div>
</template>
<script>
// 预览效果见付费文章
import { getToken } from 'api/qiniu';
export default {
name: 'singleImageUpload',
props: {
value: String
},
computed: {
imageUrl() {
return this.value
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
};
},
methods: {
rmImage() {
this.emitInput('');
},
emitInput(val) {
this.$emit('input', val);
},
handleImageScucess() {
this.emitInput(this.tempUrl)
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
// 预览效果见付费文章
import { getToken } from 'api/qiniu';
export default {
name: 'singleImageUpload',
props: {
value: String
},
computed: {
imageUrl() {
return this.value
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
};
},
methods: {
rmImage() {
this.emitInput('');
},
emitInput(val) {
this.$emit('input', val);
},
handleImageScucess() {
this.emitInput(this.tempUrl)
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>

View File

@@ -1,126 +1,119 @@
<template>
<div class="singleImageUpload2 upload-container">
<el-upload
class="image-uploader"
:data="dataObj"
drag
:multiple="false"
:show-file-list="false"
action="https://httpbin.org/post"
:on-success="handleImageScucess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drag或<em>点击上传</em></div>
</el-upload>
<div v-show="imageUrl.length>0" class="image-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>
</div>
</div>
</div>
</div>
<div class="singleImageUpload2 upload-container">
<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
:on-success="handleImageScucess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drag或<em>点击上传</em></div>
</el-upload>
<div v-show="imageUrl.length>0" class="image-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>
</div>
</div>
</div>
</div>
</template>
<script>
// 预览效果见专题
import { getToken } from 'api/qiniu';
export default {
name: 'singleImageUpload2',
props: {
value: String
},
computed: {
imageUrl() {
return this.value
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
};
},
methods: {
rmImage() {
this.emitInput('');
},
emitInput(val) {
this.$emit('input', val);
},
handleImageScucess() {
this.emitInput(this.tempUrl)
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
// 预览效果见专题
import { getToken } from 'api/qiniu';
export default {
name: 'singleImageUpload2',
props: {
value: String
},
computed: {
imageUrl() {
return this.value
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
};
},
methods: {
rmImage() {
this.emitInput('');
},
emitInput(val) {
this.$emit('input', val);
},
handleImageScucess() {
this.emitInput(this.tempUrl)
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
width: 100%;
height: 100%;
position: relative;
.image-uploader{
height: 100%;
}
.image-preview {
width: 100%;
height: 100%;
position: absolute;
left: 0px;
top: 0px;
border: 1px dashed #d9d9d9;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
}
.upload-container {
width: 100%;
height: 100%;
position: relative;
.image-uploader {
height: 100%;
}
.image-preview {
width: 100%;
height: 100%;
position: absolute;
left: 0px;
top: 0px;
border: 1px dashed #d9d9d9;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
}
</style>

View File

@@ -1,154 +1,146 @@
<template>
<div class="upload-container">
<el-upload
class="image-uploader"
:data="dataObj"
drag
:multiple="false"
:show-file-list="false"
action="https://httpbin.org/post"
:on-success="handleImageScucess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<div class="image-preview image-app-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<div class='app-fake-conver'>&nbsp&nbsp全球 付费节目单 最热 经济</div>
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>
</div>
</div>
</div>
<div class="image-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>
</div>
</div>
</div>
</div>
<div class="upload-container">
<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
:on-success="handleImageScucess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<div class="image-preview image-app-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<div class='app-fake-conver'>&nbsp&nbsp全球 付费节目单 最热 经济</div>
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>
</div>
</div>
</div>
<div class="image-preview">
<div class="image-preview-wrapper" v-show="imageUrl.length>1">
<img :src="imageUrl">
<div class="image-preview-action">
<i @click="rmImage" class="el-icon-delete"></i>
</div>
</div>
</div>
</div>
</template>
<script>
// 预览效果见文章
import { getToken } from 'api/qiniu';
export default {
name: 'singleImageUpload',
props: {
value: String
},
computed: {
imageUrl() {
return this.value
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
};
},
methods: {
rmImage() {
this.emitInput('');
},
emitInput(val) {
this.$emit('input', val);
},
handleImageScucess(file) {
this.emitInput(file.files.file)
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
// 预览效果见文章
import { getToken } from 'api/qiniu';
export default {
name: 'singleImageUpload',
props: {
value: String
},
computed: {
imageUrl() {
return this.value
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
};
},
methods: {
rmImage() {
this.emitInput('');
},
emitInput(val) {
this.$emit('input', val);
},
handleImageScucess(file) {
this.emitInput(file.files.file)
},
beforeUpload() {
const _self = this;
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key;
const token = response.data.qiniu_token;
_self._data.dataObj.token = token;
_self._data.dataObj.key = key;
this.tempUrl = response.data.qiniu_url;
resolve(true);
}).catch(err => {
console.log(err);
reject(false)
});
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.upload-container {
width: 100%;
position: relative;
@include clearfix;
.image-uploader {
width: 35%;
float: left;
}
.image-preview {
width: 200px;
height: 200px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
.image-app-preview{
width: 320px;
height: 180px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.app-fake-conver{
height: 44px;
position: absolute;
width: 100%;
// background: rgba(0, 0, 0, .1);
text-align: center;
line-height: 64px;
color: #fff;
}
}
}
@import "src/styles/mixin.scss";
.upload-container {
width: 100%;
position: relative;
@include clearfix;
.image-uploader {
width: 35%;
float: left;
}
.image-preview {
width: 200px;
height: 200px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
.image-app-preview {
width: 320px;
height: 180px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.app-fake-conver {
height: 44px;
position: absolute;
width: 100%; // background: rgba(0, 0, 0, .1);
text-align: center;
line-height: 64px;
color: #fff;
}
}
}
</style>

View File

@@ -4,61 +4,61 @@
</div>
</template>
<script>
import CodeMirror from 'codemirror';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/rubyblue.css';
require('script-loader!jsonlint');
import 'codemirror/mode/javascript/javascript'
import 'codemirror/addon/lint/lint'
import 'codemirror/addon/lint/json-lint';
import CodeMirror from 'codemirror';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/rubyblue.css';
require('script-loader!jsonlint');
import 'codemirror/mode/javascript/javascript'
import 'codemirror/addon/lint/lint'
import 'codemirror/addon/lint/json-lint';
export default {
name: 'jsonEditor',
data() {
return {
jsonEditor: false
}
},
props: ['value'],
watch: {
value(value) {
const editor_value = this.jsonEditor.getValue();
if (value !== editor_value) {
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
}
}
},
mounted() {
this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
lineNumbers: true,
mode: 'application/json',
gutters: ['CodeMirror-lint-markers'],
theme: 'rubyblue',
lint: true
});
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
this.jsonEditor.on('change', cm => {
this.$emit('changed', cm.getValue())
this.$emit('input', cm.getValue())
})
},
methods: {
getValue() {
return this.jsonEditor.getValue()
export default {
name: 'jsonEditor',
data() {
return {
jsonEditor: false
}
},
props: ['value'],
watch: {
value(value) {
const editor_value = this.jsonEditor.getValue();
if (value !== editor_value) {
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
}
}
},
mounted() {
this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
lineNumbers: true,
mode: 'application/json',
gutters: ['CodeMirror-lint-markers'],
theme: 'rubyblue',
lint: true
});
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
this.jsonEditor.on('change', cm => {
this.$emit('changed', cm.getValue())
this.$emit('input', cm.getValue())
})
},
methods: {
getValue() {
return this.jsonEditor.getValue()
}
}
}
</script>
<style>
.CodeMirror {
height: 100%;
}
.json-editor .cm-s-rubyblue span.cm-string{
color: #F08047;
}
.CodeMirror {
height: 100%;
}
.json-editor .cm-s-rubyblue span.cm-string {
color: #F08047;
}
</style>

View File

@@ -3,7 +3,7 @@
<div class="twoDndList-list" :style="{width:width1}">
<h3>{{list1Title}}</h3>
<draggable :list="list1" class="dragArea" :options="{group:'article'}">
<div class="list-complete-item" v-for="element in list1">
<div class="list-complete-item" v-for="element in list1" :key='element'>
<div class="list-complete-item-handle">[{{element.author}}] {{element.title}}</div>
<div style="position:absolute;right:0px;">
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
@@ -17,7 +17,7 @@
<div class="twoDndList-list" :style="{width:width2}">
<h3>{{list2Title}}</h3>
<draggable :list="filterList2" class="dragArea" :options="{group:'article'}">
<div class="list-complete-item" v-for="element in filterList2">
<div class="list-complete-item" v-for="element in filterList2" :key='element'>
<div class='list-complete-item-handle2' @click="pushEle(element)"> [{{element.author}}] {{element.title}}</div>
</div>
</draggable>
@@ -26,131 +26,133 @@
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'twoDndList',
components: { draggable },
computed: {
filterList2() {
return this.list2.filter(v => {
if (this.isNotInList1(v)) {
return v
}
return false;
})
import draggable from 'vuedraggable'
export default {
name: 'twoDndList',
components: { draggable },
computed: {
filterList2() {
return this.list2.filter(v => {
if (this.isNotInList1(v)) {
return v
}
return false;
})
}
},
props: {
list1: {
type: Array,
default() {
return []
}
},
props: {
list1: {
type: Array,
default() {
return []
}
},
list2: {
type: Array,
default() {
return []
}
},
list1Title: {
type: String,
default: 'list1'
},
list2Title: {
type: String,
default: 'list2'
},
width1: {
type: String,
default: '48%'
},
width2: {
type: String,
default: '48%'
list2: {
type: Array,
default() {
return []
}
},
methods: {
isNotInList1(v) {
return this.list1.every(k => v.id !== k.id)
},
isNotInList2(v) {
return this.list2.every(k => v.id !== k.id)
},
deleteEle(ele) {
for (const item of this.list1) {
if (item.id === ele.id) {
const index = this.list1.indexOf(item);
this.list1.splice(index, 1)
break
}
list1Title: {
type: String,
default: 'list1'
},
list2Title: {
type: String,
default: 'list2'
},
width1: {
type: String,
default: '48%'
},
width2: {
type: String,
default: '48%'
}
},
methods: {
isNotInList1(v) {
return this.list1.every(k => v.id !== k.id)
},
isNotInList2(v) {
return this.list2.every(k => v.id !== k.id)
},
deleteEle(ele) {
for (const item of this.list1) {
if (item.id === ele.id) {
const index = this.list1.indexOf(item);
this.list1.splice(index, 1)
break
}
if (this.isNotInList2(ele)) {
this.list2.unshift(ele)
}
},
pushEle(ele) {
this.list1.push(ele)
}
if (this.isNotInList2(ele)) {
this.list2.unshift(ele)
}
},
pushEle(ele) {
this.list1.push(ele)
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.twoDndList {
background: #fff;
padding-bottom: 40px;
&:after {
content: "";
display: table;
clear: both;
}
.twoDndList-list {
float: left;
padding-bottom: 30px;
&:first-of-type {
margin-right: 2%;
}
.dragArea {
margin-top: 15px;
min-height: 50px;
padding-bottom: 30px;
}
}
.twoDndList {
background: #fff;
padding-bottom: 40px;
&:after {
content: "";
display: table;
clear: both;
}
.twoDndList-list {
float: left;
padding-bottom: 30px;
&:first-of-type {
margin-right: 2%;
}
.dragArea {
margin-top: 15px;
min-height: 50px;
padding-bottom: 30px;
}
}
}
.list-complete-item {
cursor: pointer;
position: relative;
font-size: 14px;
padding: 5px 12px;
margin-top: 4px;
border: 1px solid #bfcbd9;
transition: all 1s;
}
.list-complete-item {
cursor: pointer;
position: relative;
font-size: 14px;
padding: 5px 12px;
margin-top: 4px;
border: 1px solid #bfcbd9;
transition: all 1s;
}
.list-complete-item-handle {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 50px;
}
.list-complete-item-handle2{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 20px;
}
.list-complete-item-handle {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 50px;
}
.list-complete-item.sortable-chosen {
background: #4AB7BD;
}
.list-complete-item-handle2 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 20px;
}
.list-complete-item.sortable-ghost {
background: #30B08F;
}
.list-complete-item.sortable-chosen {
background: #4AB7BD;
}
.list-complete-enter, .list-complete-leave-active {
opacity: 0;
}
.list-complete-item.sortable-ghost {
background: #30B08F;
}
.list-complete-enter,
.list-complete-leave-active {
opacity: 0;
}
</style>

View File

@@ -1,99 +1,91 @@
(function() {
const vueSticky = {};
let listenAction;
vueSticky.install = Vue => {
Vue.directive('sticky', {
inserted(el, binding) {
const params = binding.value || {},
stickyTop = params.stickyTop || 0,
zIndex = params.zIndex || 1000,
elStyle = el.style;
const vueSticky = {};
let listenAction;
vueSticky.install = Vue => {
Vue.directive('sticky', {
inserted(el, binding) {
const params = binding.value || {},
stickyTop = params.stickyTop || 0,
zIndex = params.zIndex || 1000,
elStyle = el.style;
elStyle.position = '-webkit-sticky';
elStyle.position = 'sticky';
elStyle.position = '-webkit-sticky';
elStyle.position = 'sticky';
// if the browser support css stickyCurrently Safari, Firefox and Chrome Canary
// if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex;
// return
// }
const elHeight = el.getBoundingClientRect().height;
const elWidth = el.getBoundingClientRect().width;
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`;
const elHeight = el.getBoundingClientRect().height;
const elWidth = el.getBoundingClientRect().width;
elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`;
const parentElm = el.parentNode || document.documentElement;
const placeholder = document.createElement('div');
const parentElm = el.parentNode || document.documentElement;
const placeholder = document.createElement('div');
placeholder.style.display = 'none';
placeholder.style.width = `${elWidth}px`;
placeholder.style.height = `${elHeight}px`;
parentElm.insertBefore(placeholder, el)
let active = false;
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset';
const method = top ? 'scrollTop' : 'scrollLeft';
let ret = target[prop];
if (typeof ret !== 'number') {
ret = window.document.documentElement[method];
}
return ret;
};
const sticky = () => {
if (active) {
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
elStyle.position = 'fixed';
elStyle.width = `${elWidth}px`;
placeholder.style.display = 'inline-block';
active = true
};
const reset = () => {
if (!active) {
return
}
elStyle.position = '';
placeholder.style.display = 'none';
placeholder.style.width = `${elWidth}px`;
placeholder.style.height = `${elHeight}px`;
parentElm.insertBefore(placeholder, el)
active = false;
};
let active = false;
const getScroll = (target, top) => {
const prop = top ? 'pageYOffset' : 'pageXOffset';
const method = top ? 'scrollTop' : 'scrollLeft';
let ret = target[prop];
if (typeof ret !== 'number') {
ret = window.document.documentElement[method];
const check = () => {
const scrollTop = getScroll(window, true);
const offsetTop = el.getBoundingClientRect().top;
if (offsetTop < stickyTop) {
sticky();
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
}
return ret;
};
}
};
listenAction = () => {
check()
};
const sticky = () => {
if (active) {
return
}
if (!elStyle.height) {
elStyle.height = `${el.offsetHeight}px`
}
window.addEventListener('scroll', listenAction)
},
elStyle.position = 'fixed';
elStyle.width = `${elWidth}px`;
placeholder.style.display = 'inline-block';
active = true
};
unbind() {
window.removeEventListener('scroll', listenAction)
}
})
};
const reset = () => {
if (!active) {
return
}
elStyle.position = '';
placeholder.style.display = 'none';
active = false;
};
const check = () => {
const scrollTop = getScroll(window, true);
const offsetTop = el.getBoundingClientRect().top;
if (offsetTop < stickyTop) {
sticky();
} else {
if (scrollTop < elHeight + stickyTop) {
reset()
}
}
};
listenAction = () => {
check()
};
window.addEventListener('scroll', listenAction)
},
unbind() {
window.removeEventListener('scroll', listenAction)
}
})
};
if (typeof exports == 'object') {
module.exports = vueSticky
} else if (typeof define == 'function' && define.amd) {
define([], () => vueSticky)
} else if (window.Vue) {
window.vueSticky = vueSticky;
Vue.use(vueSticky)
}
}());
export default vueSticky

View File

@@ -1,54 +1,47 @@
import './waves.css';
(function() {
const vueWaves = {};
vueWaves.install = (Vue, options = {}) => {
Vue.directive('waves', {
bind(el, binding) {
el.addEventListener('click', e => {
const customOpts = Object.assign(options, binding.value);
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts),
target = opts.ele;
if (target) {
target.style.position = 'relative';
target.style.overflow = 'hidden';
const rect = target.getBoundingClientRect();
let ripple = target.querySelector('.waves-ripple');
if (!ripple) {
ripple = document.createElement('span');
ripple.className = 'waves-ripple';
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
target.appendChild(ripple);
} else {
ripple.className = 'waves-ripple';
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px';
break;
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px';
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
}
ripple.style.backgroundColor = opts.color;
ripple.className = 'waves-ripple z-active';
return false;
}
}, false);
}
})
};
if (typeof exports == 'object') {
module.exports = vueWaves
} else if (typeof define == 'function' && define.amd) {
define([], () => vueWaves)
} else if (window.Vue) {
window.vueWaves = vueWaves;
Vue.use(vueWaves)
}
}());
const vueWaves = {};
vueWaves.install = (Vue, options = {}) => {
Vue.directive('waves', {
bind(el, binding) {
el.addEventListener('click', e => {
const customOpts = Object.assign(options, binding.value);
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts),
target = opts.ele;
if (target) {
target.style.position = 'relative';
target.style.overflow = 'hidden';
const rect = target.getBoundingClientRect();
let ripple = target.querySelector('.waves-ripple');
if (!ripple) {
ripple = document.createElement('span');
ripple.className = 'waves-ripple';
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
target.appendChild(ripple);
} else {
ripple.className = 'waves-ripple';
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px';
break;
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px';
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
}
ripple.style.backgroundColor = opts.color;
ripple.className = 'waves-ripple z-active';
return false;
}
}, false);
}
})
};
export default vueWaves;

View File

@@ -24,7 +24,6 @@ export function parseTime(time, cFormat) {
time = +time * 1000
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
let date;
if (typeof time == 'object') {
@@ -75,8 +74,6 @@ export function formatTime(time, option) {
}
}
/* 数字 格式化*/
export function nFormatter(num, digits) {
const si = [
@@ -95,14 +92,12 @@ export function nFormatter(num, digits) {
return num.toString();
}
export function html2Text(val) {
const div = document.createElement('div');
div.innerHTML = val;
return div.textContent || div.innerText;
}
export function toThousandslsFilter(num) {
return (+num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','));
}

View File

@@ -6,17 +6,16 @@ import router from './router';
import store from './store';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-default/index.css';
import 'assets/custom-theme/index.css'; // 换肤版本element-ui css https://github.com/PanJiaChen/custom-element-theme
import 'assets/custom-theme/index.css'; // 换肤版本element-ui css
import NProgress from 'nprogress'; // Progress 进度条
import 'nprogress/nprogress.css';// Progress 进度条 样式
import 'normalize.css/normalize.css';// normalize.css 样式格式化
import 'styles/index.scss'; // 全局自定义的css样式
import 'components/Icon-svg/index'; // 封装的svg组件
import 'assets/iconfont/iconfont'; // iconfont 具体图标见https://github.com/PanJiaChen/vue-element-admin/wiki
import * as filters from './filters'; // 全局vue filter
import Multiselect from 'vue-multiselect';// 使用的一个多选框组件element-ui的select不能满足所有需求
import 'vue-multiselect/dist/vue-multiselect.min.css';// 多选框组件css
import Sticky from 'components/Sticky'; // 粘性header组件
import IconSvg from 'components/Icon-svg';// svg 组件
import vueWaves from './directive/waves';// 水波纹指令
import errLog from 'store/errLog';// error log组件
import './mock/index.js'; // 该项目所有请求使用mockjs模拟
@@ -24,6 +23,7 @@ import './mock/index.js'; // 该项目所有请求使用mockjs模拟
// register globally
Vue.component('multiselect', Multiselect);
Vue.component('Sticky', Sticky);
Vue.component('icon-svg', IconSvg)
Vue.use(ElementUI);
Vue.use(vueWaves);
@@ -52,11 +52,9 @@ router.beforeEach((to, from, next) => {
const roles = res.data.role;
store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next(to.path); // hack方法 确保addRoutes已完成
next({ ...to }); // hack方法 确保addRoutes已完成
})
}).catch(err => {
console.log(err);
});
})
} else {
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.role)) {
@@ -77,15 +75,11 @@ router.beforeEach((to, from, next) => {
}
});
router.afterEach(() => {
NProgress.done(); // 结束Progress
});
// window.onunhandledrejection = e => {
// console.log('unhandled', e.reason, e.promise);
// e.preventDefault()
// };
Vue.config.productionTip = false;
// 生产环境错误日志
if (process.env === 'production') {
@@ -99,22 +93,12 @@ if (process.env === 'production') {
};
}
// window.onerror = function (msg, url, lineNo, columnNo, error) {
// console.log('window')
// };
//
// console.error = (function (origin) {
// return function (errorlog) {
// // handler();//基于业务的日志记录及数据报错
// console.log('console'+errorlog)
// origin.call(console, errorlog);
// }
// })(console.error);
new Vue({
el: '#app',
router,
store,
render: h => h(App)
}).$mount('#app');
template: '<App/>',
components: { App }
})

View File

@@ -6,25 +6,21 @@ const userMap = {
token: 'admin',
introduction: '我是超级管理员',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: '超级管理员小潘',
uid: '001'
name: 'Super Admin'
},
editor: {
role: ['editor'],
token: 'editor',
introduction: '我是编辑',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: '普通编辑小张',
uid: '002'
name: 'Normal Editor'
},
developer: {
role: ['develop'],
token: 'develop',
introduction: '我是开发',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: '工程师小王',
uid: '003'
name: '工程师小王'
}
}

View File

@@ -0,0 +1 @@
module.exports = file => require('@/views/' + file + '.vue')

View File

@@ -0,0 +1 @@
module.exports = file => () => import('@/views/' + file + '.vue')

View File

@@ -1,68 +1,66 @@
import Vue from 'vue';
import Router from 'vue-router';
const _import = require('./_import_' + process.env.NODE_ENV);
// in development env not use Lazy Loading,because Lazy Loading large page will cause webpack hot update too slow.so only in production use Lazy Loading
/* layout */
import Layout from '../views/layout/Layout';
/* login */
import Login from '../views/login/';
const authRedirect = () => import('../views/login/authredirect');
const sendPWD = () => import('../views/login/sendpwd');
const reset = () => import('../views/login/reset');
const Login = _import('login/index');
const authRedirect = _import('login/authredirect');
/* dashboard */
const dashboard = () => import('../views/dashboard/index');
const dashboard = _import('dashboard/index');
/* Introduction */
const Introduction = () => import('../views/introduction/index');
const Introduction = _import('introduction/index');
/* components */
const componentsIndex = () => import('../views/components/index');
const Tinymce = () => import('../views/components/tinymce');
const Markdown = () => import('../views/components/markdown');
const JsonEditor = () => import('../views/components/jsoneditor');
const DndList = () => import('../views/components/dndlist');
const AvatarUpload = () => import('../views/components/avatarUpload');
const Dropzone = () => import('../views/components/dropzone');
const Sticky = () => import('../views/components/sticky');
const SplitPane = () => import('../views/components/splitpane');
const CountTo = () => import('../views/components/countTo');
const Mixin = () => import('../views/components/mixin');
const componentsIndex = _import('components/index');
const Tinymce = _import('components/tinymce');
const Markdown = _import('components/markdown');
const JsonEditor = _import('components/jsoneditor');
const DndList = _import('components/dndlist');
const AvatarUpload = _import('components/avatarUpload');
const Dropzone = _import('components/dropzone');
const Sticky = _import('components/sticky');
const SplitPane = _import('components/splitpane');
const CountTo = _import('components/countTo');
const Mixin = _import('components/mixin');
/* charts */
const chartIndex = () => import('../views/charts/index');
const KeyboardChart = () => import('../views/charts/keyboard');
const KeyboardChart2 = () => import('../views/charts/keyboard2');
const LineMarker = () => import('../views/charts/line');
const MixChart = () => import('../views/charts/mixchart');
const chartIndex = _import('charts/index');
const KeyboardChart = _import('charts/keyboard');
const KeyboardChart2 = _import('charts/keyboard2');
const LineMarker = _import('charts/line');
const MixChart = _import('charts/mixchart');
/* error page */
const Err404 = () => import('../views/error/404');
const Err401 = () => import('../views/error/401');
const Err404 = _import('error/404');
const Err401 = _import('error/401');
/* error log */
const ErrorLog = () => import('../views/errlog/index');
const ErrorLog = _import('errlog/index');
/* excel */
const ExcelDownload = () => import('../views/excel/index');
const ExcelDownload = _import('excel/index');
/* theme */
const Theme = () => import('../views/theme/index');
const Theme = _import('theme/index');
/* example*/
const TableLayout = () => import('../views/example/table/index');
const DynamicTable = () => import('../views/example/table/dynamictable');
const Table = () => import('../views/example/table/table');
const DragTable = () => import('../views/example/table/dragTable');
const InlineEditTable = () => import('../views/example/table/inlineEditTable');
const TableLayout = _import('example/table/index');
const DynamicTable = _import('example/table/dynamictable');
const Table = _import('example/table/table');
const DragTable = _import('example/table/dragTable');
const InlineEditTable = _import('example/table/inlineEditTable');
const Form = () => import('../views/example/form');
const Tab = () => import('../views/example/tab/index');
const Form = _import('example/form');
const Tab = _import('example/tab/index');
/* permission */
const Permission = () => import('../views/permission/index');
const Permission = _import('permission/index');
Vue.use(Router);
@@ -77,8 +75,6 @@ Vue.use(Router);
export const constantRouterMap = [
{ path: '/login', component: Login, hidden: true },
{ path: '/authredirect', component: authRedirect, hidden: true },
{ path: '/sendpwd', component: sendPWD, hidden: true },
{ path: '/reset', component: reset, hidden: true },
{ path: '/404', component: Err404, hidden: true },
{ path: '/401', component: Err401, hidden: true },
{
@@ -196,9 +192,9 @@ export const asyncRouterMap = [
icon: 'zonghe',
children: [
{
path: '/table',
path: '/example/table',
component: TableLayout,
redirect: '/table/table',
redirect: '/example/table/table',
name: 'Table',
children: [
{ path: 'dynamictable', component: DynamicTable, name: '动态table' },

View File

@@ -1,12 +1,10 @@
const getters = {
sidebar: state => state.app.sidebar,
visitedViews: state => state.app.visitedViews,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
uid: state => state.user.uid,
email: state => state.user.email,
introduction: state => state.user.introduction,
auth_type: state => state.user.auth_type,
status: state => state.user.status,
roles: state => state.user.roles,
setting: state => state.user.setting,

View File

@@ -6,7 +6,8 @@ const app = {
opened: !+Cookies.get('sidebarStatus')
},
theme: 'default',
livenewsChannels: Cookies.get('livenewsChannels') || '[]'
livenewsChannels: Cookies.get('livenewsChannels') || '[]',
visitedViews: []
},
mutations: {
TOGGLE_SIDEBAR: state => {
@@ -16,11 +17,25 @@ const app = {
Cookies.set('sidebarStatus', 0);
}
state.sidebar.opened = !state.sidebar.opened;
},
ADD_VISITED_VIEWS: (state, view) => {
if (state.visitedViews.includes(view)) return
state.visitedViews.push(view)
},
DEL_VISITED_VIEWS: (state, view) => {
const index = state.visitedViews.indexOf(view)
state.visitedViews.splice(index, 1)
}
},
actions: {
ToggleSideBar: ({ commit }) => {
commit('TOGGLE_SIDEBAR')
},
addVisitedViews: ({ commit }, view) => {
commit('ADD_VISITED_VIEWS', view)
},
delVisitedViews: ({ commit }, view) => {
commit('DEL_VISITED_VIEWS', view)
}
}
};

View File

@@ -1,5 +1,10 @@
import { asyncRouterMap, constantRouterMap } from 'src/router';
/**
* 通过meta.role判断是否与当前用户权限匹配
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0)
@@ -8,40 +13,45 @@ function hasPermission(roles, route) {
}
}
/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param asyncRouterMap
* @param roles
*/
function filterAsyncRouter(asyncRouterMap, roles) {
const accessedRouters = asyncRouterMap.filter(route => {
if (hasPermission(roles, route)) {
if (route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, roles)
}
return true
}
return false
})
return accessedRouters
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRouterMap.concat(routers);
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { roles } = data;
const accessedRouters = asyncRouterMap.filter(v => {
if (roles.indexOf('admin') >= 0) return true;
if (hasPermission(roles, v)) {
if (v.children && v.children.length > 0) {
v.children = v.children.filter(child => {
if (hasPermission(roles, child)) {
return child
}
return false;
});
return v
} else {
return v
}
}
return false;
});
const { roles } = data
let accessedRouters
if (roles.indexOf('admin') >= 0) {
accessedRouters = asyncRouterMap
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
}
commit('SET_ROUTERS', accessedRouters);
resolve();
})
@@ -49,5 +59,4 @@ const permission = {
}
};
export default permission;

View File

@@ -5,10 +5,7 @@ const user = {
state: {
user: '',
status: '',
email: '',
code: '',
uid: undefined,
auth_type: '',
token: Cookies.get('Admin-Token'),
name: '',
avatar: '',
@@ -20,21 +17,12 @@ const user = {
},
mutations: {
SET_AUTH_TYPE: (state, type) => {
state.auth_type = type;
},
SET_CODE: (state, code) => {
state.code = code;
},
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_UID: (state, uid) => {
state.uid = uid;
},
SET_EMAIL: (state, email) => {
state.email = email;
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction;
},
@@ -70,7 +58,6 @@ const user = {
const data = response.data;
Cookies.set('Admin-Token', response.data.token);
commit('SET_TOKEN', data.token);
commit('SET_EMAIL', email);
resolve();
}).catch(error => {
reject(error);
@@ -78,7 +65,6 @@ const user = {
});
},
// 获取用户信息
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
@@ -87,7 +73,6 @@ const user = {
commit('SET_ROLES', data.role);
commit('SET_NAME', data.name);
commit('SET_AVATAR', data.avatar);
commit('SET_UID', data.uid);
commit('SET_INTRODUCTION', data.introduction);
resolve(response);
}).catch(error => {
@@ -100,7 +85,7 @@ const user = {
LoginByThirdparty({ commit, state }, code) {
return new Promise((resolve, reject) => {
commit('SET_CODE', code);
loginByThirdparty(state.status, state.email, state.code, state.auth_type).then(response => {
loginByThirdparty(state.status, state.email, state.code).then(response => {
commit('SET_TOKEN', response.data.token);
Cookies.set('Admin-Token', response.data.token);
resolve();
@@ -110,7 +95,6 @@ const user = {
});
},
// 登出
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {

View File

@@ -1,6 +1,6 @@
@import './btn.scss';
@import './element-ui.scss';
@import "./mixin.scss";
@import './mixin.scss';
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
@@ -96,6 +96,7 @@ code {
opacity: 0;
}
//main-container全局样式
.app-container {
padding: 20px;
@@ -108,12 +109,14 @@ code {
margin-top: 30px;
}
.editor-container .CodeMirror {
height: 100%!important;
}
.wscn-icon {
.text-center{
text-align: center
}
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;

View File

@@ -24,27 +24,34 @@ service.interceptors.request.use(config => {
// respone拦截器
service.interceptors.response.use(
response => response
response => response,
/**
* 下面的注释为通过response自定义code来标示请求状态当code返回如下情况为权限有问题登出并返回到登录页
* 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
*/
// const code = response.data.code;
// // 50014:Token 过期了 50012:其他客户端登录了 50008:非法的token
// if (code === 50008 || code === 50014 || code === 50012) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// });
// // 登出
// store.dispatch('FedLogOut').then(() => {
// router.push({ path: '/login' })
// });
// } else {
// return response
// }
,
// const res = response.data;
// if (res.code !== 20000) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// });
// // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// confirmButtonText: '重新登录',
// cancelButtonText: '取消',
// type: 'warning'
// }).then(() => {
// store.dispatch('FedLogOut').then(() => {
// location.reload();// 为了重新实例化vue-router对象 避免bug
// });
// })
// }
// return Promise.reject(error);
// } else {
// return response.data;
// }
error => {
console.log('err' + error);// for debug
Message({

View File

@@ -110,7 +110,10 @@
export function param2Obj(url) {
const search = url.split('?')[1];
return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
if (!search) {
return {}
}
return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}');
}
export function html2Text(val) {
@@ -212,3 +215,38 @@
}
}
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result;
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp;
// 上次被包装函数被调用时间间隔last小于设定时间间隔wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
// 如果设定为immediate===true因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function(...args) {
context = this;
timestamp = +new Date();
const callNow = immediate && !timeout;
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
}

View File

@@ -1,16 +1,17 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<keyboardChart height='100%' width='100%' />
<keyboard-chart height='100%' width='100%'></keyboard-chart>
</div>
</div>
</template>
<script>
import keyboardChart from 'components/Charts/keyboard';
export default {
components: { keyboardChart }
};
import keyboardChart from 'components/Charts/keyboard';
export default {
components: { keyboardChart }
};
</script>
<style scoped>

View File

@@ -1,16 +1,17 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<keyboardChart2 id='apple' height='100%' width='100%' />
<keyboard-chart2 id='apple' height='100%' width='100%'></keyboard-chart2>
</div>
</div>
</template>
<script>
import keyboardChart2 from 'components/Charts/keyboard2';
export default {
components: { keyboardChart2 }
};
import keyboardChart2 from 'components/Charts/keyboard2';
export default {
components: { keyboardChart2 }
};
</script>
<style scoped>

View File

@@ -1,16 +1,17 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<lineMarker height='100%' width='100%' />
<line-marker height='100%' width='100%'></line-marker>
</div>
</div>
</template>
<script>
import lineMarker from 'components/Charts/lineMarker';
export default {
components: { lineMarker }
};
import lineMarker from 'components/Charts/lineMarker';
export default {
components: { lineMarker }
};
</script>
<style scoped>

View File

@@ -1,16 +1,17 @@
<template>
<div class="components-container" style='height:100vh'>
<div class='chart-container'>
<mixchart id='apple' height='100%' width='100%' />
<mix-chart id='apple' height='100%' width='100%'></mix-chart>
</div>
</div>
</template>
<script>
import mixchart from 'components/Charts/mixchart';
export default {
components: { mixchart }
};
import mixChart from 'components/Charts/mixChart';
export default {
components: { mixChart }
};
</script>
<style scoped>

View File

@@ -4,45 +4,46 @@
由于我在使用时它只有vue@1版本而且有些业务的需求耦合到七牛等等原因吧自己改造了一下如果大家要使用的话优先还是使用官方component
</code>
<PanThumb :image='image'>
</PanThumb>
<pan-thumb :image='image'></pan-thumb>
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;" @click="imagecropperShow=true">修改头像
</el-button>
<ImageCropper :width="300" :height="300" url="https://httpbin.org/post" @close='close' @crop-upload-success="cropSuccess"
:key="imagecropperKey" v-show="imagecropperShow" />
<image-cropper :width="300" :height="300" url="https://httpbin.org/post" @close='close' @crop-upload-success="cropSuccess" :key="imagecropperKey" v-show="imagecropperShow"></image-cropper>
</div>
</template>
<script>
import ImageCropper from 'components/ImageCropper';
import PanThumb from 'components/PanThumb';
export default {
components: { ImageCropper, PanThumb },
data() {
return {
imagecropperShow: false,
imagecropperKey: 0,
image: 'https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191'
}
},
methods: {
cropSuccess(resData) {
this.imagecropperShow = false;
this.imagecropperKey = this.imagecropperKey + 1;
this.image = resData.files.avatar;
},
close() {
this.imagecropperShow = false;
}
import ImageCropper from 'components/ImageCropper';
import PanThumb from 'components/PanThumb';
export default {
components: { ImageCropper, PanThumb },
data() {
return {
imagecropperShow: false,
imagecropperKey: 0,
image: 'https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191'
}
};
},
methods: {
cropSuccess(resData) {
this.imagecropperShow = false;
this.imagecropperKey = this.imagecropperKey + 1;
this.image = resData.files.avatar;
},
close() {
this.imagecropperShow = false;
}
}
};
</script>
<style scoped>
.avatar{
width: 200px;
height: 200px;
border-radius: 50%;
}
.avatar{
width: 200px;
height: 200px;
border-radius: 50%;
}
</style>

View File

@@ -3,7 +3,7 @@
<code> <a href='https://github.com/PanJiaChen/vue-countTo' target='_blank'>countTo component</a></code>
<count-to ref='example' class='example' :start-val='_startVal' :end-val='_endVal' :duration='_duration' :decimals='_decimals'
:separator='_separator' :prefix='_prefix' :suffix='_suffix' :autoplay='false' />
:separator='_separator' :prefix='_prefix' :suffix='_suffix' :autoplay='false'></count-to>
<div style='margin-left: 25%;margin-top: 40px;'>
<label class="label" for="startValInput">startVal: <input type="number" v-model.number='setStartVal' name='startValInput' /></label>
<label class="label" for="endValInput">endVal: <input type="number" v-model.number='setEndVal' name='endVaInput' /></label>
@@ -22,72 +22,72 @@
</template>
<script>
import countTo from 'vue-count-to';
export default {
components: { countTo },
data() {
return {
setStartVal: 0,
setEndVal: 2017,
setDuration: 4000,
setDecimals: 0,
setSeparator: ',',
setSuffix: ' rmb',
setPrefix: '¥ '
}
},
computed: {
_startVal() {
if (this.setStartVal) {
return this.setStartVal
} else {
return 0
}
},
_endVal() {
if (this.setEndVal) {
return this.setEndVal
} else {
return 0
}
},
_duration() {
if (this.setDuration) {
return this.setDuration
} else {
return 100
}
},
_decimals() {
if (this.setDecimals) {
if (this.setDecimals < 0 || this.setDecimals > 20) {
alert('digits argument must be between 0 and 20')
return 0
}
return this.setDecimals
} else {
return 0
}
},
_separator() {
return this.setSeparator
},
_suffix() {
return this.setSuffix
},
_prefix() {
return this.setPrefix
}
},
methods: {
start() {
this.$refs.example.start();
},
pauseResume() {
this.$refs.example.pauseResume();
}
}
};
import countTo from 'vue-count-to';
export default {
components: { countTo },
data() {
return {
setStartVal: 0,
setEndVal: 2017,
setDuration: 4000,
setDecimals: 0,
setSeparator: ',',
setSuffix: ' rmb',
setPrefix: '¥ '
}
},
computed: {
_startVal() {
if (this.setStartVal) {
return this.setStartVal
} else {
return 0
}
},
_endVal() {
if (this.setEndVal) {
return this.setEndVal
} else {
return 0
}
},
_duration() {
if (this.setDuration) {
return this.setDuration
} else {
return 100
}
},
_decimals() {
if (this.setDecimals) {
if (this.setDecimals < 0 || this.setDecimals > 20) {
alert('digits argument must be between 0 and 20')
return 0
}
return this.setDecimals
} else {
return 0
}
},
_separator() {
return this.setSeparator
},
_suffix() {
return this.setSuffix
},
_prefix() {
return this.setPrefix
}
},
methods: {
start() {
this.$refs.example.start();
},
pauseResume() {
this.$refs.example.pauseResume();
}
}
};
</script>
<style scoped>

View File

@@ -2,35 +2,37 @@
<div class="components-container">
<code>drag-list base on <a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a></code>
<div class="editor-container">
<DndList :list1="list1" :list2="list2" list1Title="头条列表" list2Title="文章池" />
<dnd-list :list1="list1" :list2="list2" list1Title="头条列表" list2Title="文章池"></dnd-list>
</div>
</div>
</template>
<script>
import DndList from 'components/twoDndList'
import { getList } from 'api/article';
export default {
components: { DndList },
data() {
return {
list1: [],
list2: []
}
},
created() {
this.fetchData();
},
methods: {
fetchData() {
this.listLoading = true;
getList(this.listQuery).then(response => {
this.list1 = response.data.splice(0, 5);
this.list2 = response.data;
console.log(this.list1, this.list2)
})
}
import DndList from 'components/twoDndList'
import { getList } from 'api/article';
export default {
components: { DndList },
data() {
return {
list1: [],
list2: []
}
};
},
created() {
this.fetchData();
},
methods: {
fetchData() {
this.listLoading = true;
getList(this.listQuery).then(response => {
this.list1 = response.data.splice(0, 5);
this.list2 = response.data;
console.log(this.list1, this.list2)
})
}
}
};
</script>

View File

@@ -4,27 +4,27 @@
由于我司业务有特殊需求而且要传七牛 所以没用第三方 选择了自己封装
</code>
<div class="editor-container">
<Dropzone v-on:dropzone-removedFile="dropzoneR" v-on:dropzone-success="dropzoneS" id="myVueDropzone"
url="https://httpbin.org/post"></Dropzone>
<dropzone v-on:dropzone-removedFile="dropzoneR" v-on:dropzone-success="dropzoneS" id="myVueDropzone" url="https://httpbin.org/post"></dropzone>
</div>
</div>
</template>
<script>
import Dropzone from 'components/Dropzone';
export default {
components: { Dropzone },
methods: {
dropzoneS(file) {
console.log(file)
this.$message({ message: '上传成功', type: 'success' });
},
dropzoneR(file) {
console.log(file)
this.$message({ message: '删除成功', type: 'success' });
}
<script>
import Dropzone from 'components/Dropzone';
export default {
components: { Dropzone },
methods: {
dropzoneS(file) {
console.log(file)
this.$message({ message: '上传成功', type: 'success' });
},
dropzoneR(file) {
console.log(file)
this.$message({ message: '删除成功', type: 'success' });
}
};
}
};
</script>

View File

@@ -8,16 +8,17 @@
</template>
<script>
import jsonEditor from 'components/jsonEditor';
const jsonData = '[{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"CORN"}],"name":""},{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"XAGUSD"},{"market_type":"forexdata","symbol":"AUTD"},{"market_type":"forexdata","symbol":"AGTD"}],"name":"贵金属"},{"items":[{"market_type":"forexdata","symbol":"CORN"},{"market_type":"forexdata","symbol":"WHEAT"},{"market_type":"forexdata","symbol":"SOYBEAN"},{"market_type":"forexdata","symbol":"SUGAR"}],"name":"农产品"},{"items":[{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"USOIL"},{"market_type":"forexdata","symbol":"NGAS"}],"name":"能源化工"}]';
export default {
components: { jsonEditor },
data() {
return {
value: JSON.parse(jsonData)
}
import jsonEditor from 'components/jsonEditor';
const jsonData = '[{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"CORN"}],"name":""},{"items":[{"market_type":"forexdata","symbol":"XAUUSD"},{"market_type":"forexdata","symbol":"XAGUSD"},{"market_type":"forexdata","symbol":"AUTD"},{"market_type":"forexdata","symbol":"AGTD"}],"name":"贵金属"},{"items":[{"market_type":"forexdata","symbol":"CORN"},{"market_type":"forexdata","symbol":"WHEAT"},{"market_type":"forexdata","symbol":"SOYBEAN"},{"market_type":"forexdata","symbol":"SUGAR"}],"name":"农产品"},{"items":[{"market_type":"forexdata","symbol":"UKOIL"},{"market_type":"forexdata","symbol":"USOIL"},{"market_type":"forexdata","symbol":"NGAS"}],"name":"能源化工"}]';
export default {
components: { jsonEditor },
data() {
return {
value: JSON.parse(jsonData)
}
};
}
};
</script>
<style scoped>

View File

@@ -2,7 +2,7 @@
<div class="components-container">
<code>Markdown 我们这里选用了 <a href="https://github.com/sparksuite/simplemde-markdown-editor" target="_blank">simplemde-markdown-editor</a> 简单的用vue封装了一下<a target='_blank' href='https://segmentfault.com/a/1190000009762198#articleHeader14'> 相关文章 </a></code>
<div class="editor-container">
<MdEditor id='contentEditor' ref="contentEditor" v-model='content' :height="300" :zIndex='20'></MdEditor>
<md-editor id='contentEditor' ref="contentEditor" v-model='content' :height="300" :zIndex='20'></md-editor>
</div>
<el-button @click='markdown2Html' style="margin-top:80px;" type="primary">转为HTML<i class="el-icon-document el-icon--right"></i></el-button>
<div v-html="html"></div>
@@ -10,24 +10,24 @@
</template>
<script>
import MdEditor from 'components/MdEditor';
export default {
components: { MdEditor },
data() {
return {
content: '## Simplemde',
html: ''
}
},
methods: {
markdown2Html() {
import('showdown').then(showdown => {
const converter = new showdown.Converter();
this.html = converter.makeHtml(this.content)
})
}
import MdEditor from 'components/MdEditor';
export default {
components: { MdEditor },
data() {
return {
content: '## Simplemde',
html: ''
}
};
},
methods: {
markdown2Html() {
import('showdown').then(showdown => {
const converter = new showdown.Converter();
this.html = converter.makeHtml(this.content)
})
}
}
};
</script>

View File

@@ -1,16 +1,16 @@
<template>
<div class="components-container">
<div class='component-item'>
<MDinput name="name" v-model="title" required :maxlength="100">
<md-input name="name" v-model="title" required :maxlength="100">
标题
</MDinput>
</md-input>
<code class='code-part'>Material Design 的input</code>
</div>
<div class='component-item'>
<PanThumb image='https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191'>
<pan-thumb image='https://wpimg.wallstcn.com/577965b9-bb9e-4e02-9f0c-095b41417191'>
上海花裤衩
</PanThumb>
</pan-thumb>
<code class='code-part'>图片hover效果</code>
</div>
@@ -18,21 +18,21 @@
<el-button v-waves type="primary">水波纹效果</el-button>
<code class='code-part'>水波纹 v-directive</code>
</div>
</div>
</template>
<script>
import MDinput from 'components/MDinput';
import PanThumb from 'components/PanThumb';
export default {
components: { MDinput, PanThumb },
data() {
return {
title: ''
}
<script>
import MdInput from 'components/MDinput';
import PanThumb from 'components/PanThumb';
export default {
components: { MdInput, PanThumb },
data() {
return {
title: ''
}
};
}
};
</script>
<style scoped>

View File

@@ -1,62 +1,65 @@
<template>
<div class="components-container" >
<code>splitPane 如果你用过<a href='http://codepen.io/' target='_blank'>codepen</a>,<a href='https://jsfiddle.net/' target='_blank'>jsfiddle</a>就不会陌生了
<div class="components-container">
<code>splitPane 如果你用过<a href='http://codepen.io/' target='_blank'>codepen</a>,<a href='https://jsfiddle.net/' target='_blank'>jsfiddle</a>就不会陌生了
暂还没有时间开源封装好日后补上
</code>
<splitPane v-on:resize="resize" split="vertical">
<template slot="paneL">
<div class="left-container"></div>
</template>
<template slot="paneR">
<split-pane split="horizontal">
<template slot="paneL">
<div class="top-container"></div>
</template>
<template slot="paneR">
<div class="bottom-container">
</div>
</template>
</split-pane>
</template>
</splitPane>
</div>
<split-pane v-on:resize="resize" split="vertical">
<template slot="paneL">
<div class="left-container"></div>
</template>
<template slot="paneR">
<split-pane split="horizontal">
<template slot="paneL">
<div class="top-container"></div>
</template>
<template slot="paneR">
<div class="bottom-container">
</div>
</template>
</split-pane>
</template>
</split-pane>
</div>
</template>
<script>
import splitPane from 'components/SplitPane'
export default {
components: { splitPane },
methods: {
resize() {
console.log('resize')
}
import splitPane from 'components/SplitPane'
export default {
components: { splitPane },
methods: {
resize() {
console.log('resize')
}
};
}
};
</script>
<style scoped>
.components-container{
position: relative;
height: 100vh;
}
.left-container {
background-color: #F38181;
height:100%;
}
.components-container {
position: relative;
height: 100vh;
}
.right-container {
background-color: #FCE38A;
height: 200px;
}
.left-container {
background-color: #F38181;
height: 100%;
}
.top-container {
background-color: #FCE38A;
width: 100%;
height: 100%;
}
.right-container {
background-color: #FCE38A;
height: 200px;
}
.bottom-container {
width: 100%;
background-color: #95E1D3;
height: 100%;
}
.top-container {
background-color: #FCE38A;
width: 100%;
height: 100%;
}
.bottom-container {
width: 100%;
background-color: #95E1D3;
height: 100%;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div>
<Sticky className="sub-navbar">
<sticky className="sub-navbar">
<el-dropdown trigger="click">
<el-button>
平台<i class="el-icon-caret-bottom el-icon--right"></i>
@@ -32,7 +32,7 @@
<el-button style="margin-left: 10px;" type="success">发布
</el-button>
</Sticky>
</sticky>
<div class="components-container">
<code>Sticky header 当页面滚动到预设的位置会吸附在顶部</code>
@@ -90,33 +90,34 @@
<div>我是占位</div>
</div>
</div>
</template>
<script>
import Sticky from 'components/Sticky';
export default {
components: { Sticky },
data() {
return {
time: '',
url: '',
platforms: ['a-platform'],
platformsOptions: [
<script>
import Sticky from 'components/Sticky';
export default {
components: { Sticky },
data() {
return {
time: '',
url: '',
platforms: ['a-platform'],
platformsOptions: [
{ key: 'a-platform', name: '平台A' },
{ key: 'b-platform', name: '平台B' },
{ key: 'c-platform', name: '平台C' }
],
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now();
}
],
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now();
}
}
}
};
}
};
</script>
<style scoped>
.time-container {
display: inline-block;

View File

@@ -9,16 +9,16 @@
</template>
<script>
import Tinymce from 'components/Tinymce';
import Tinymce from 'components/Tinymce';
export default {
components: { Tinymce },
data() {
return {
content: 'Tinymce'
}
export default {
components: { Tinymce },
data() {
return {
content: 'Tinymce'
}
};
}
};
</script>
<style scoped>

View File

@@ -1,82 +1,82 @@
<template>
<div class="dashboard-editor-container">
<div class=" clearfix">
<PanThumb style="float: left" :image="avatar"> 你的权限:
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
</PanThumb>
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#4AB7BD; color:#fff; position: absolute; top: 50px; border: 0; right: 0;"
aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"fill="currentColor" class="octo-body"></path>
</svg>
</a>
<div class="info-container">
<span class="display_name">{{name}}</span>
<span style='font-size:20px;padding-top:20px;display:inline-block;'>普通编辑dashboard</span>
</div>
</div>
<div>
<img class='emptyGif' :src="emptyGif" >
</div>
</div>
<div class="dashboard-editor-container">
<div class=" clearfix">
<pan-thumb style="float: left" :image="avatar"> 你的权限:
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
</pan-thumb>
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#4AB7BD; color:#fff; position: absolute; top: 50px; border: 0; right: 0;"
aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" class="octo-body"></path>
</svg>
</a>
<div class="info-container">
<span class="display_name">{{name}}</span>
<span style='font-size:20px;padding-top:20px;display:inline-block;'>普通编辑dashboard</span>
</div>
</div>
<div>
<img class='emptyGif' :src="emptyGif">
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import PanThumb from 'components/PanThumb';
export default {
name: 'dashboard-default',
components: { PanThumb },
data() {
return {
emptyGif: 'https://wpimg.wallstcn.com/0e03b7da-db9e-4819-ba10-9016ddfdaed3'
}
},
computed: {
...mapGetters([
'name',
'avatar',
'email',
'uid',
'introduction',
'roles'
])
import { mapGetters } from 'vuex';
import PanThumb from 'components/PanThumb';
export default {
name: 'dashboard-default',
components: { PanThumb },
data() {
return {
emptyGif: 'https://wpimg.wallstcn.com/0e03b7da-db9e-4819-ba10-9016ddfdaed3'
}
},
computed: {
...mapGetters([
'name',
'avatar',
'roles'
])
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.emptyGif {
display: block;
width: 45%;
margin: 0 auto;
}
.dashboard-editor-container {
background-color: #e3e3e3;
min-height: 100vh;
margin-top: -50px;
padding: 100px 60px 0px;
.pan-info-roles {
font-size: 12px;
font-weight: 700;
color: #333;
display: block;
}
.info-container {
position: relative;
margin-left: 190px;
height: 150px;
line-height: 200px;
.display_name {
font-size: 48px;
line-height: 48px;
color: #212121;
position: absolute;
top: 25px;
}
}
}
.emptyGif {
display: block;
width: 45%;
margin: 0 auto;
}
.dashboard-editor-container {
background-color: #e3e3e3;
min-height: 100vh;
margin-top: -50px;
padding: 100px 60px 0px;
.pan-info-roles {
font-size: 12px;
font-weight: 700;
color: #333;
display: block;
}
.info-container {
position: relative;
margin-left: 190px;
height: 150px;
line-height: 200px;
.display_name {
font-size: 48px;
line-height: 48px;
color: #212121;
position: absolute;
top: 25px;
}
}
}
</style>

View File

@@ -1,34 +0,0 @@
<template>
<div class="articlesChart-container">
<span class="articlesChart-container-title">每天撸文</span>
<line-chart :listData='listData' ></line-chart>
</div>
</template>
<script>
import LineChart from 'components/Charts/line';
export default {
name: 'articlesChart',
components: { LineChart },
props: {
listData: {
type: Array,
default: [],
require: true
}
},
data() {
return {}
}
}
</script>
<style>
.articlesChart-container {
width: 100%;
}
.articlesChart-container-title {
color: #7F8C8D;
font-size: 16px;
display: inline-block;
margin-top: 10px;
}
</style>

View File

@@ -0,0 +1,89 @@
<template>
<div :class="className" :style="{height:height,width:width}"></div>
</template>
<script>
import echarts from 'echarts';
require('echarts/theme/macarons'); // echarts 主题
export default {
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons');
this.chart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
}
}],
yAxis: [{
type: 'value'
}],
series: [{
name: 'pageA',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [79, 52, 200, 334, 390, 330, 220]
}, {
name: 'pageB',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [80, 52, 200, 334, 390, 330, 220]
}, {
name: 'pageC',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [30, 52, 200, 334, 390, 330, 220]
}]
})
}
}
}
</script>

View File

@@ -1,295 +1,141 @@
<template>
<div class="dashboard-editor-container">
<div class=" clearfix">
<PanThumb style="float: left" :image="avatar"> 你的权限:
<span class="pan-info-roles" v-for="item in roles">{{item}}</span>
</PanThumb>
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#4AB7BD; color:#fff; position: absolute; top: 50px; border: 0; right: 0;"
aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"fill="currentColor" class="octo-body"></path>
</svg>
</a>
<div class="info-container">
<span class="display_name">{{name}}</span>
<div class="info-wrapper">
<div class="info-item" :to="'/article/wscnlist?uid='+uid">
<countTo class="info-item-num" :startVal='0' :endVal='statisticsData.article_count' :duration='3400'></countTo>
<span class="info-item-text">文章</span>
<wscn-icon-svg icon-class="a" class="dashboard-editor-icon"/>
</div>
<div class="info-item" style="cursor: auto">
<countTo class="info-item-num" :startVal='0' :endVal='statisticsData.pageviews_count' :duration='3600'></countTo>
<span class="info-item-text">浏览量</span>
<wscn-icon-svg icon-class="b" class="dashboard-editor-icon"/>
</div>
<div class="info-item" :to="'/comment/commentslist?res_author_id='+uid">
<countTo class="info-item-num" ref='countTo3' :startVal='0' :endVal='statisticsData.comment_count' :duration='3800'></countTo>
<span class="info-item-text">评论</span>
<wscn-icon-svg icon-class="c" class="dashboard-editor-icon"/>
</div>
</div>
</div>
</div>
<div class="dashboard-editor-container">
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#4AB7BD; color:#fff; position: absolute; top: 50px; border: 0; right: 0;"
aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" class="octo-body"></path>
</svg>
</a>
<el-row class="btn-group">
<el-col :span="4" class='text-center'>
<router-link class="pan-btn blue-btn" to="/components/index">Components</router-link>
</el-col>
<el-col :span="4" class='text-center'>
<router-link class="pan-btn light-blue-btn" to="/charts/index">Charts</router-link>
</el-col>
<el-col :span="4" class='text-center'>
<router-link class="pan-btn pink-btn" to="/excel/download">Excel</router-link>
</el-col>
<el-col :span="4" class='text-center'>
<router-link class="pan-btn green-btn" to="/example/table/table">Table</router-link>
</el-col>
<el-col :span="4" class='text-center'>
<router-link class="pan-btn tiffany-btn" to="/example/form/edit">Form</router-link>
</el-col>
<el-col :span="4" class='text-center'>
<router-link class="pan-btn yellow-btn" to="/example/form/edit">Theme</router-link>
</el-col>
</el-row>
<div class="btn-group">
<router-link class="pan-btn blue-btn" to="/components/index">组件</router-link>
<router-link class="pan-btn light-blue-btn" to="/charts/index">图表</router-link>
<router-link class="pan-btn red-btn" to="/errorpage/404">错误页面</router-link>
<router-link class="pan-btn pink-btn" to="/excel/download">导出excel</router-link>
<router-link class="pan-btn green-btn" to="/example/table">table</router-link>
<router-link class="pan-btn tiffany-btn" to="/example/form1">form</router-link>
</div>
<el-row>
<el-col :span="6">
<el-card class="box-card">
<div slot="header" class="box-card-header">
<pan-thumb class="panThumb" :image="avatar"> 你的权限:
<span class="pan-info-roles" :key='item' v-for="item in roles">{{item}}</span>
</pan-thumb>
</div>
<span class="display_name">{{name}}</span>
<div class="info-item">
<countTo class="info-item-num" :startVal='0' :endVal='statisticsData.article_count' :duration='3400'></countTo>
<span class="info-item-text">文章</span>
<icon-svg icon-class="a" class="dashboard-editor-icon"></icon-svg>
</div>
<div class="info-item">
<countTo class="info-item-num" :startVal='0' :endVal='statisticsData.pageviews_count' :duration='3600'></countTo>
<span class="info-item-text">浏览量</span>
<icon-svg icon-class="b" class="dashboard-editor-icon"></icon-svg>
</div>
</el-card>
</el-col>
<div class="clearfix main-dashboard-container">
<div class="chart-container">
<MonthKpi style="border-bottom: 1px solid #DEE1E2;"
:articlesComplete='statisticsData.month_article_count'></MonthKpi>
<ArticlesChart :listData='statisticsData.week_article'></ArticlesChart>
</div>
<div class="recent-articles-container">
<div class="recent-articles-title">最近撸了</div>
<div class="recent-articles-wrapper">
<template v-if="recentArticles.length!=0">
<div class="recent-articles-item" v-for="item in recentArticles">
<span class="recent-articles-status">{{item.status | statusFilter}}</span>
<span class="recent-articles-content" :to="'/article/edit/'+item.id">
{{item.title}}
</span>
<span class="recent-articles-time">{{item.author}}</span>
</div>
</template>
<template v-else>
<div class="recent-articles-emptyTitle">你太懒了最近都没有撸</div>
</template>
</div>
</div>
</div>
</div>
<el-col :span="8">
<pie-chart></pie-chart>
</el-col>
<el-col :span="10">
<bar-chart></bar-chart>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="15">
<line-chart></line-chart>
</el-col>
<el-col :span="9">
<todo-list></todo-list>
</el-col>
</el-row>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import PanThumb from 'components/PanThumb';
import MonthKpi from './monthKpi';
import ArticlesChart from './articlesChart';
import { getList } from 'api/article';
import countTo from 'vue-count-to';
export default {
name: 'dashboard-editor',
components: { PanThumb, MonthKpi, ArticlesChart, countTo },
data() {
return {
chart: null,
statisticsData: {
article_count: 1024,
comment_count: 102400,
latest_article: [],
month_article_count: 28,
pageviews_count: 1024,
week_article: [
{ count: 30, week: '201716' },
{ count: 26, week: '201715' },
{ count: 31, week: '201714' },
{ count: 28, week: '201713' },
{ count: 40, week: '201712' },
{ count: 41, week: '201711' },
{ count: 50, week: '201710' },
{ count: 42, week: '201709' },
{ count: 36, week: '201708' },
{ count: 32, week: '201707' },
{ count: 40, week: '201706' },
{ count: 41, week: '201705' }
]
},
list: []
}
},
created() {
this.fetchData();
},
computed: {
...mapGetters([
'name',
'avatar',
'email',
'uid',
'introduction',
'roles'
]),
recentArticles() {
return this.list.slice(0, 7)
}
},
methods: {
fetchData() {
getList(this.listQuery).then(response => {
this.list = response.data;
})
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: '已发布',
draft: '草稿中',
deleted: '已删除'
};
return statusMap[status];
}
import { mapGetters } from 'vuex';
import panThumb from 'components/PanThumb';
import pieChart from './pieChart';
import barChart from './barChart';
import lineChart from './lineChart';
import countTo from 'vue-count-to';
import todoList from 'components/TodoList';
export default {
name: 'dashboard-editor',
components: { panThumb, countTo, pieChart, lineChart, barChart, todoList },
data() {
return {
statisticsData: {
article_count: 1024,
comment_count: 102400,
latest_article: [],
month_article_count: 28,
pageviews_count: 1024
}
}
},
computed: {
...mapGetters([
'name',
'avatar',
'roles'
])
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.recent-articles-emptyTitle {
font-size: 16px;
color: #95A5A6;
padding-top: 20px;
text-align: center;
.dashboard-editor-container {
margin: 30px;
.btn-group {
margin-bottom: 60px;
}
.dashboard-editor-container {
padding: 30px 50px;
.pan-info-roles {
font-size: 12px;
font-weight: 700;
color: #333;
display: block;
}
.info-container {
position: relative;
margin-left: 190px;
height: 150px;
line-height: 200px;
.display_name {
font-size: 48px;
line-height: 48px;
color: #212121;
position: absolute;
top: 25px;
}
.info-wrapper {
line-height: 40px;
position: absolute;
bottom: 0px;
.info-item {
cursor: pointer;
display: inline-block;
margin-right: 95px;
.info-item-num {
color: #212121;
font-size: 24px;
display: inline-block;
padding-right: 5px;
}
.info-item-text {
color: #727272;
font-size: 14px;
padding-right: 5px;
display: inline-block;
}
}
}
.dashboard-editor-icon {
width: 22px;
height: 22px;
}
}
.btn-group {
margin: 30px 36px 30px 0;
}
.main-dashboard-container {
width: 100%;
position: relative;
border: 1px solid #DEE1E2;
padding: 0 10px;
}
.chart-container {
float: left;
position: relative;
padding-right: 10px;
width: 40%;
border-right: 1px solid #DEE1E2;
}
.recent-articles-container {
padding: 12px 12px 0px;
float: left;
width: 60%;
position: relative;
.recent-articles- {
&title {
font-size: 16px;
color: #95A5A6;
letter-spacing: 1px;
padding-left: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #DEE1E2;
}
&more {
color: #2C3E50;
font-size: 12px;
float: right;
margin-right: 25px;
line-height: 40px;
&:hover {
color: #3A71A8;
}
}
&wrapper {
padding: 0 20px 0 22px;
.recent-articles- {
&item {
cursor: pointer;
padding: 16px 100px 16px 16px;
border-bottom: 1px solid #DEE1E2;
position: relative;
&:before {
content: "";
height: 104%;
width: 0px;
background: #30B08F;
display: inline-block;
position: absolute;
opacity: 0;
left: 0px;
top: -2px;
transition: 0.3s ease all;
}
&:hover {
&:before {
opacity: 1;
width: 3px;
}
}
}
&status {
font-size: 12px;
display: inline-block;
color: #9B9B9B;
padding-right: 6px;
}
&content {
font-size: 13px;
color: #2C3E50;
&:hover {
color: #3A71A8;
}
}
&time {
position: absolute;
right: 16px;
top: 16px;
color: #9B9B9B;
}
}
}
}
.box-card-header {
position: relative;
height: 160px;
}
.panThumb {
z-index: 100;
height: 150px;
width: 150px;
position: absolute;
left: 0px;
right: 0px;
margin: auto;
}
.display_name{
font-size: 30px;
display: block;
}
.info-item{
display: inline-block;
margin-top: 10px;
font-size: 14px;
&:last-of-type{
margin-left: 15px;
}
}
}
</style>

View File

@@ -0,0 +1,108 @@
<template>
<div :class="className" :style="{height:height,width:width}"></div>
</template>
<script>
import echarts from 'echarts';
require('echarts/theme/macarons'); // echarts 主题
import { debounce } from 'utils';
export default {
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
},
autoResize: {
type: Boolean,
default: true
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
if (this.autoResize) {
this.__resizeHanlder = debounce(() => {
this.chart.resize()
}, 100)
window.addEventListener('resize', this.__resizeHanlder)
}
},
beforeDestroy() {
if (!this.chart) {
return
}
if (this.autoResize) {
window.removeEventListener('resize', this.__resizeHanlder)
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons');
this.chart.setOption({
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
boundaryGap: false
},
grid: {
left: 10,
right: 10,
bottom: 20,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
yAxis: {},
series: [{
name: 'visitors',
itemStyle: {
normal: {
areaStyle: {}
}
},
smooth: true,
type: 'line',
data: [100, 120, 161, 134, 105, 160, 165]
},
{
name: 'buyers',
smooth: true,
type: 'line',
itemStyle: {
normal: {
color: 'rgba(2, 197, 233, 0.2)',
lineStyle: {
color: 'rgba(2, 197, 233, 0.2)'
},
areaStyle: {
color: 'rgba(99,194,255, 0.6)'
}
}
},
data: [120, 82, 91, 154, 162, 140, 130]
}]
})
}
}
}
</script>

View File

@@ -1,61 +0,0 @@
<template>
<div class="monthKpi-container">
<span class="monthKpi-container-title">{{month}}</span>
<BarPercent class="monthKpi-container-chart" :dataNum='articlesComplete'></BarPercent>
<span class="monthKpi-container-text">文章完成比例</span>
<span class="monthKpi-container-count">{{articlesComplete}}<b></b></span>
</div>
</template>
<script>
import BarPercent from 'components/Charts/barPercent';
export default {
name: 'monthKpi',
components: { BarPercent },
props: {
articlesComplete: {
type: Number
}
},
data() {
return {
month: new Date().getMonth() + 1
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.monthKpi-container{
width: 100%;
}
.monthKpi-container-title {
color: #7F8C8D;
font-size: 16px;
display: inline-block;
margin-top: 10px;
}
.monthKpi-container-chart {
margin-left: 100px;
margin-bottom: 4px;
}
.monthKpi-container-text {
margin-left: 112px;
color: #9EA7B3;
font-size: 12px;
}
.monthKpi-container-count {
color: #30B08F;
font-size: 34px;
position: absolute;
left: 260px;
top: 60px;
b {
padding-left: 10px;
color: #9EA7B3;
font-size: 12px;
}
}
</style>

View File

@@ -0,0 +1,77 @@
<template>
<div :class="className" :style="{height:height,width:width}"></div>
</template>
<script>
import echarts from 'echarts';
require('echarts/theme/macarons'); // echarts 主题
export default {
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '300px'
}
},
data() {
return {
chart: null
};
},
mounted() {
this.initChart();
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose();
this.chart = null;
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons');
this.chart.setOption({
title: {
text: 'WEEKLY WRITE ARTICLES',
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
x: 'center',
y: 'bottom',
data: ['industries', 'technology', 'gold', 'forex', 'forecasts', 'markets']
},
calculable: true,
series: [
{
name: 'WEEKLY WRITE ARTICLES',
type: 'pie',
roseType: 'radius',
data: [
{ value: 320, name: 'industries' },
{ value: 240, name: 'technology' },
{ value: 149, name: 'forex' },
{ value: 100, name: 'gold' },
{ value: 59, name: 'forecastsx' },
{ value: 49, name: 'markets' }
]
}
]
})
}
}
}
</script>

View File

@@ -1,39 +1,36 @@
<template>
<div class="dashboard-container">
<component v-bind:is="currentRole"> </component>
</div>
<div class="dashboard-container">
<component v-bind:is="currentRole"> </component>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import EditorDashboard from './editor/index';
import DefaultDashboard from './default/index';
export default {
name: 'dashboard',
components: { EditorDashboard, DefaultDashboard },
data() {
return {
currentRole: 'EditorDashboard'
}
},
computed: {
...mapGetters([
'name',
'avatar',
'email',
'introduction',
'roles'
])
},
created() {
if (this.roles.indexOf('admin') >= 0) {
return;
}
// const isEditor = this.roles.some(v => v.indexOf('editor') >= 0)
// if (!isEditor) {
// this.currentRole = 'DefaultDashboard';
// }
this.currentRole = 'DefaultDashboard';
import { mapGetters } from 'vuex';
import EditorDashboard from './editor/index';
import DefaultDashboard from './default/index';
export default {
name: 'dashboard',
components: { EditorDashboard, DefaultDashboard },
data() {
return {
currentRole: 'EditorDashboard'
}
},
computed: {
...mapGetters([
'name',
'avatar',
'email',
'introduction',
'roles'
])
},
created() {
if (this.roles.indexOf('admin') >= 0) {
return;
}
this.currentRole = 'DefaultDashboard';
}
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div class="errPage-container">
<err-code/>
<err-code></err-code>
<h3>请点击右上角bug小图表</h3>
<code>
@@ -11,19 +11,15 @@
</template>
<script>
import errCode from './errcode';
export default {
components: { errCode },
methods: {
back() {
this.$router.go(-1)
}
}
};
import errCode from './errcode';
export default {
components: { errCode }
};
</script>
<style scoped>
.errPage-container{
padding: 30px;
}
.errPage-container {
padding: 30px;
}
</style>

View File

@@ -28,25 +28,25 @@
</template>
<script>
import errGif from 'assets/401.gif';
export default {
data() {
return {
errGif: errGif + '?' + +new Date(),
ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
dialogVisible: false
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/dashboard' });
} else {
this.$router.go(-1);
}
import errGif from 'assets/401_images/401.gif';
export default {
data() {
return {
errGif: errGif + '?' + +new Date(),
ewizardClap: 'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
dialogVisible: false
}
},
methods: {
back() {
if (this.$route.query.noGoBack) {
this.$router.push({ path: '/dashboard' });
} else {
this.$router.go(-1);
}
}
};
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scoped>

View File

@@ -2,10 +2,10 @@
<div style="background:#f0f2f5;margin-top: -20px;">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="../../assets/404.png" alt="404">
<img class="pic-404__child left" src="../../assets/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="../../assets/404_cloud.png" alt="404">
<img class="pic-404__child right" src="../../assets/404_cloud.png" alt="404">
<img class="pic-404__parent" :src="img_404" alt="404">
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
@@ -19,7 +19,16 @@
</template>
<script>
import img_404 from '@/assets/404_images/404.png'
import img_404_cloud from '@/assets/404_images/404_cloud.png'
export default {
data() {
return {
img_404,
img_404_cloud
}
},
computed: {
message() {
return '特朗普说这个页面你不能进......'
@@ -29,191 +38,192 @@
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404 {
.wscn-http404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
overflow: hidden;
.pic-404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
&__parent {
width: 100%;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&__child {
position: absolute;
&.left {
width: 80px;
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
transform: translateY(0);
opacity: 1;
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

View File

@@ -120,141 +120,141 @@
</template>
<script>
import Tinymce from 'components/Tinymce'
import Upload from 'components/Upload/singleImage3'
import MDinput from 'components/MDinput';
import { validateURL } from 'utils/validate';
import { getArticle } from 'api/article';
import { userSearch } from 'api/remoteSearch';
import Tinymce from 'components/Tinymce'
import Upload from 'components/Upload/singleImage3'
import MDinput from 'components/MDinput';
import { validateURL } from 'utils/validate';
import { getArticle } from 'api/article';
import { userSearch } from 'api/remoteSearch';
export default {
name: 'articleDetail',
components: { Tinymce, MDinput, Upload },
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
export default {
name: 'articleDetail',
components: { Tinymce, MDinput, Upload },
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
this.$message({
message: rule.field + '为必传项',
type: 'error'
});
callback(null)
} else {
callback()
}
};
const validateSourceUri = (rule, value, callback) => {
if (value) {
if (validateURL(value)) {
callback()
} else {
this.$message({
message: rule.field + '为必传项',
message: '外链url填写不正确',
type: 'error'
});
callback(null)
} else {
callback()
}
};
const validateSourceUri = (rule, value, callback) => {
if (value) {
if (validateURL(value)) {
callback()
} else {
this.$message({
message: '外链url填写不正确',
type: 'error'
});
callback(null)
}
} else {
callback()
}
};
return {
postForm: {
title: '', // 文章题目
content: '', // 文章内容
content_short: '', // 文章摘要
source_uri: '', // 文章外链
image_uri: '', // 文章图片
source_name: '', // 文章外部作者
display_time: undefined, // 前台展示时间
id: undefined,
platforms: ['a-platform']
},
fetchSuccess: true,
loading: false,
userLIstOptions: [],
platformsOptions: [
} else {
callback()
}
};
return {
postForm: {
title: '', // 文章题目
content: '', // 文章内容
content_short: '', // 文章摘要
source_uri: '', // 文章外链
image_uri: '', // 文章图片
source_name: '', // 文章外部作者
display_time: undefined, // 前台展示时间
id: undefined,
platforms: ['a-platform']
},
fetchSuccess: true,
loading: false,
userLIstOptions: [],
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
],
rules: {
image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }],
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
}
}
},
computed: {
contentShortLength() {
return this.postForm.content_short.length
},
isEdit() {
return this.$route.meta.isEdit // 根据meta判断
// return this.$route.path.indexOf('edit') !== -1 // 根据路由判断
}
},
created() {
if (this.isEdit) {
this.fetchData();
}
},
methods: {
fetchData() {
getArticle().then(response => {
this.postForm = response.data;
}).catch(err => {
this.fetchSuccess = false;
console.log(err);
});
},
submitForm() {
this.postForm.display_time = parseInt(this.display_time / 1000);
console.log(this.postForm)
this.$refs.postForm.validate(valid => {
if (valid) {
this.loading = true;
this.$notify({
title: '成功',
message: '发布文章成功',
type: 'success',
duration: 2000
});
this.postForm.status = 'published';
this.loading = false;
} else {
console.log('error submit!!');
return false;
}
});
},
draftForm() {
if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
this.$message({
message: '请填写必要的标题和内容',
type: 'warning'
});
return;
}
this.$message({
message: '保存成功',
type: 'success',
showClose: true,
duration: 1000
});
this.postForm.status = 'draft';
},
getRemoteUserList(query) {
userSearch(query).then(response => {
if (!response.data.items) return;
console.log(response)
this.userLIstOptions = response.data.items.map(v => ({
key: v.name
}));
})
],
rules: {
image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }],
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
}
}
},
computed: {
contentShortLength() {
return this.postForm.content_short.length
},
isEdit() {
return this.$route.meta.isEdit // 根据meta判断
// return this.$route.path.indexOf('edit') !== -1 // 根据路由判断
}
},
created() {
if (this.isEdit) {
this.fetchData();
}
},
methods: {
fetchData() {
getArticle().then(response => {
this.postForm = response.data;
}).catch(err => {
this.fetchSuccess = false;
console.log(err);
});
},
submitForm() {
this.postForm.display_time = parseInt(this.display_time / 1000);
console.log(this.postForm)
this.$refs.postForm.validate(valid => {
if (valid) {
this.loading = true;
this.$notify({
title: '成功',
message: '发布文章成功',
type: 'success',
duration: 2000
});
this.postForm.status = 'published';
this.loading = false;
} else {
console.log('error submit!!');
return false;
}
});
},
draftForm() {
if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
this.$message({
message: '请填写必要的标题和内容',
type: 'warning'
});
return;
}
this.$message({
message: '保存成功',
type: 'success',
showClose: true,
duration: 1000
});
this.postForm.status = 'draft';
},
getRemoteUserList(query) {
userSearch(query).then(response => {
if (!response.data.items) return;
console.log(response)
this.userLIstOptions = response.data.items.map(v => ({
key: v.name
}));
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";

View File

@@ -28,7 +28,7 @@
<el-table-column width="80px" label="重要性">
<template scope="scope">
<wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" />
<icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n"></icon-svg>
</template>
</el-table-column>

View File

@@ -29,7 +29,7 @@
<el-table-column width="80px" label="重要性">
<template scope="scope">
<wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" />
<icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n"></icon-svg>
</template>
</el-table-column>
@@ -47,7 +47,7 @@
<el-table-column align="center" label="拖拽" width="95">
<template scope="scope">
<wscn-icon-svg class='drag-handler' icon-class="tuozhuai" />
<icon-svg class='drag-handler' icon-class="tuozhuai" ></icon-svg>
</template>
</el-table-column>
@@ -60,64 +60,64 @@
</template>
<script>
import { fetchList } from 'api/article_table';
import Sortable from 'sortablejs'
import { fetchList } from 'api/article_table';
import Sortable from 'sortablejs'
export default {
name: 'drag-table_demo',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
},
sortable: null,
olderList: [],
newList: []
}
},
created() {
this.getList();
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
}
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.list = response.data.items;
this.total = response.data.total;
this.listLoading = false;
this.olderList = this.list.map(v => v.id);
this.newList = this.olderList.slice();
this.$nextTick(() => {
this.setSort()
})
})
export default {
name: 'drag-table_demo',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
},
setSort() {
const el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0];
this.sortable = Sortable.create(el, {
sortable: null,
olderList: [],
newList: []
}
},
created() {
this.getList();
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
}
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.list = response.data.items;
this.total = response.data.total;
this.listLoading = false;
this.olderList = this.list.map(v => v.id);
this.newList = this.olderList.slice();
this.$nextTick(() => {
this.setSort()
})
})
},
setSort() {
const el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0];
this.sortable = Sortable.create(el, {
// handle: '.drag-handler',
onEnd: evt => {
const tempIndex = this.newList.splice(evt.oldIndex, 1)[0];
this.newList.splice(evt.newIndex, 0, tempIndex);
}
});
}
onEnd: evt => {
const tempIndex = this.newList.splice(evt.oldIndex, 1)[0];
this.newList.splice(evt.newIndex, 0, tempIndex);
}
});
}
}
}
</script>
<style >

View File

@@ -1,38 +1,19 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="formThead">
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="name" label="名称" width="180">
</el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<template scope="scope">
{{scope.row.list[index].value}}
</template>
</el-table-column>
</el-table>
<div style='margin:0 0 5px 20px'>固定表头 按照表头顺序排序</div>
<fixed-thead></fixed-thead>
<div style='margin:30px 0 5px 20px'>不固定表头 按照点击顺序排序</div>
<unfixed-thead></unfixed-thead>
</div>
</template>
<script>
import fixedThead from './dynamictable/fixedThead'
import unfixedThead from './dynamictable/unfixedThead'
export default {
data() {
return {
tableData: [{
name: '水果',
list: [{ name: 'apple', value: 10 }, { name: 'banana', value: 20 }, { name: 'orange', value: 20 }]
}, {
name: '水果2',
list: [{ name: 'apple2', value: 12 }, { name: 'banana2', value: 22 }, { name: 'orange', value: 20 }]
}],
formThead: ['apple', 'banana']
}
}
components: { fixedThead, unfixedThead }
};
</script>

View File

@@ -0,0 +1,57 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="checkboxVal" >
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" :key='key' style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180"></el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<template scope="scope">
{{scope.row[fruit]}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
const defaultFormThead = ['apple', 'banana']; // 默认选中项
export default {
data() {
return {
tableData: [
{
name: 'fruit-1',
apple: 'apple-10',
banana: 'banana-10',
orange: 'orange-10'
},
{
name: 'fruit-2',
apple: 'apple-20',
banana: 'banana-20',
orange: 'orange-20'
}
],
key: 1, // table key
formTheadOptions: ['apple', 'banana', 'orange'], // 可选择表头
checkboxVal: defaultFormThead, // checkboxVal
formThead: defaultFormThead // 默认表头
}
},
watch: {
checkboxVal(valArr) {
this.formThead = this.formTheadOptions.filter(i => valArr.indexOf(i) >= 0);
this.key = this.key + 1;// 为了保证table 每次都会重渲 (牺牲性能保证效果,当然也可以不用)
}
}
};
</script>

View File

@@ -0,0 +1,47 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="formThead">
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180">
</el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<template scope="scope">
{{scope.row[fruit]}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [
{
name: 'fruit-1',
apple: 'apple-10',
banana: 'banana-10',
orange: 'orange-10'
},
{
name: 'fruit-2',
apple: 'apple-20',
banana: 'banana-20',
orange: 'orange-20'
}
],
formThead: ['apple', 'banana']
}
}
};
</script>

View File

@@ -23,7 +23,7 @@
<el-table-column width="100px" label="重要性">
<template scope="scope">
<wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" />
<icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n"></icon-svg>
</template>
</el-table-column>
@@ -52,44 +52,44 @@
</template>
<script>
import { fetchList } from 'api/article_table';
import { fetchList } from 'api/article_table';
export default {
name: 'inline_edit-table_demo',
data() {
return {
list: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
}
}
},
created() {
this.getList();
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
}
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.list = response.data.items.map(v => {
v.edit = false;
return v
});
this.listLoading = false;
})
export default {
name: 'inline_edit-table_demo',
data() {
return {
list: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
}
}
},
created() {
this.getList();
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
}
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.list = response.data.items.map(v => {
v.edit = false;
return v
});
this.listLoading = false;
})
}
}
}
</script>

View File

@@ -60,7 +60,7 @@
<el-table-column width="80px" label="重要性">
<template scope="scope">
<wscn-icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n" />
<icon-svg v-for="n in +scope.row.importance" icon-class="wujiaoxing" class="meta-item__icon" :key="n"></icon-svg>
</template>
</el-table-column>
@@ -150,199 +150,199 @@
</template>
<script>
import { fetchList, fetchPv } from 'api/article_table';
import { parseTime } from 'utils';
import { fetchList, fetchPv } from 'api/article_table';
import { parseTime } from 'utils';
const calendarTypeOptions = [
const calendarTypeOptions = [
{ key: 'CN', display_name: '中国' },
{ key: 'US', display_name: '美国' },
{ key: 'JP', display_name: '日本' },
{ key: 'EU', display_name: '欧元区' }
];
];
// arr to obj
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name;
return acc
}, {});
// arr to obj
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name;
return acc
}, {});
export default {
name: 'table_demo',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
importance: undefined,
title: undefined,
type: undefined,
sort: '+id'
},
temp: {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
type: '',
status: 'published'
},
importanceOptions: [1, 2, 3],
calendarTypeOptions,
sortOptions: [{ label: '按ID升序列', key: '+id' }, { label: '按ID降序', key: '-id' }],
statusOptions: ['published', 'draft', 'deleted'],
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
},
dialogPvVisible: false,
pvData: [],
showAuditor: false,
tableKey: 0
}
export default {
name: 'table_demo',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
importance: undefined,
title: undefined,
type: undefined,
sort: '+id'
},
temp: {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
type: '',
status: 'published'
},
importanceOptions: [1, 2, 3],
calendarTypeOptions,
sortOptions: [{ label: '按ID升序列', key: '+id' }, { label: '按ID降序', key: '-id' }],
statusOptions: ['published', 'draft', 'deleted'],
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
},
dialogPvVisible: false,
pvData: [],
showAuditor: false,
tableKey: 0
}
},
created() {
this.getList();
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
},
created() {
typeFilter(type) {
return calendarTypeKeyValue[type]
}
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.list = response.data.items;
this.total = response.data.total;
this.listLoading = false;
})
},
handleFilter() {
this.getList();
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'gray',
deleted: 'danger'
};
return statusMap[status]
},
typeFilter(type) {
return calendarTypeKeyValue[type]
}
handleSizeChange(val) {
this.listQuery.limit = val;
this.getList();
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.list = response.data.items;
this.total = response.data.total;
this.listLoading = false;
})
},
handleFilter() {
this.getList();
},
handleSizeChange(val) {
this.listQuery.limit = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.page = val;
this.getList();
},
timeFilter(time) {
if (!time[0]) {
this.listQuery.start = undefined;
this.listQuery.end = undefined;
return;
}
this.listQuery.start = parseInt(+time[0] / 1000);
this.listQuery.end = parseInt((+time[1] + 3600 * 1000 * 24) / 1000);
},
handleModifyStatus(row, status) {
this.$message({
message: '操作成功',
type: 'success'
});
row.status = status;
},
handleCreate() {
this.resetTemp();
this.dialogStatus = 'create';
this.dialogFormVisible = true;
},
handleUpdate(row) {
this.temp = Object.assign({}, row);
this.dialogStatus = 'update';
this.dialogFormVisible = true;
},
handleDelete(row) {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
});
const index = this.list.indexOf(row);
this.list.splice(index, 1);
},
create() {
this.temp.id = parseInt(Math.random() * 100) + 1024;
this.temp.timestamp = +new Date();
this.temp.author = '原创作者';
this.list.unshift(this.temp);
this.dialogFormVisible = false;
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
});
},
update() {
this.temp.timestamp = +this.temp.timestamp;
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v);
this.list.splice(index, 1, this.temp);
break;
}
}
this.dialogFormVisible = false;
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
});
},
resetTemp() {
this.temp = {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
status: 'published',
type: ''
};
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData;
this.dialogPvVisible = true;
})
},
handleDownload() {
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel');
const tHeader = ['时间', '地区', '类型', '标题', '重要性'];
const filterVal = ['timestamp', 'province', 'type', 'title', 'importance'];
const data = this.formatJson(filterVal, this.list);
export_json_to_excel(tHeader, data, 'table数据');
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
handleCurrentChange(val) {
this.listQuery.page = val;
this.getList();
},
timeFilter(time) {
if (!time[0]) {
this.listQuery.start = undefined;
this.listQuery.end = undefined;
return;
}
this.listQuery.start = parseInt(+time[0] / 1000);
this.listQuery.end = parseInt((+time[1] + 3600 * 1000 * 24) / 1000);
},
handleModifyStatus(row, status) {
this.$message({
message: '操作成功',
type: 'success'
});
row.status = status;
},
handleCreate() {
this.resetTemp();
this.dialogStatus = 'create';
this.dialogFormVisible = true;
},
handleUpdate(row) {
this.temp = Object.assign({}, row);
this.dialogStatus = 'update';
this.dialogFormVisible = true;
},
handleDelete(row) {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
});
const index = this.list.indexOf(row);
this.list.splice(index, 1);
},
create() {
this.temp.id = parseInt(Math.random() * 100) + 1024;
this.temp.timestamp = +new Date();
this.temp.author = '原创作者';
this.list.unshift(this.temp);
this.dialogFormVisible = false;
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
});
},
update() {
this.temp.timestamp = +this.temp.timestamp;
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v);
this.list.splice(index, 1, this.temp);
break;
}
}
this.dialogFormVisible = false;
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
});
},
resetTemp() {
this.temp = {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
status: 'published',
type: ''
};
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData;
this.dialogPvVisible = true;
})
},
handleDownload() {
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel');
const tHeader = ['时间', '地区', '类型', '标题', '重要性'];
const filterVal = ['timestamp', 'province', 'type', 'title', 'importance'];
const data = this.formatJson(filterVal, this.list);
export_json_to_excel(tHeader, data, 'table数据');
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
}
}
}
</script>

View File

@@ -34,38 +34,41 @@
</template>
<script>
import { getList } from 'api/article';
export default {
data() {
return {
list: null,
listLoading: true
}
},
created() {
this.fetchData();
},
methods: {
fetchData() {
this.listLoading = true;
getList(this.listQuery).then(response => {
this.list = response.data;
this.listLoading = false;
})
import { getList } from 'api/article';
export default {
data() {
return {
list: null,
listLoading: true
}
},
handleDownload() {
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel');
const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间'];
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time'];
const list = this.list;
const data = this.formatJson(filterVal, list);
export_json_to_excel(tHeader, data, '列表excel');
})
created() {
this.fetchData();
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => v[j]))
methods: {
fetchData() {
this.listLoading = true;
getList(this.listQuery).then(response => {
this.list = response.data;
this.listLoading = false;
})
},
handleDownload() {
require.ensure([], () => {
const {
export_json_to_excel
} = require('vendor/Export2Excel');
const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间'];
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time'];
const list = this.list;
const data = this.formatJson(filterVal, list);
export_json_to_excel(tHeader, data, '列表excel');
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => v[j]))
}
}
}
};
};
</script>

View File

@@ -5,13 +5,15 @@
这半年来一直在用vue写管理后台目前后台已经有百来个个页面十几种权限但维护成本依然很低所以准备开源分享一下后台开发的经验和成果目前的技术栈主要的采用vue+element+axios由webpack2打包.由于是个人项目所以数据请求都是用了mockjs模拟注意在次项目基础上改造开发时请移除mock文件
写了一个系列的教程配套文章如何从零构建后一个完整的后台项目:
<ul>
<li><a target='_blank' class='lin' href="https://github.com/PanJiaChen/vue-element-admin/">项目地址</a></li>
<li><a target='_blank' class='lin' href="https://github.com/PanJiaChen/vue-element-admin/wiki">wiki</a></li>
<li><a target='_blank' href="https://juejin.im/post/59097cd7a22b9d0065fb61d2">手摸手带你用 vue 撸后台 系列一(基础篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/591aa14f570c35006961acac">手摸手带你用 vue 撸后台 系列二(登录权限篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/593121aa0ce4630057f70d35">手摸手带你用 vue 撸后台 系列三 (实战篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/595b4d776fb9a06bbe7dba56">手摸手带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)</a></li>
<li><a target='_blank' href="https://segmentfault.com/a/1190000009090836">手摸手带你封装一个vue component</a></li>
</ul>
</code>
</code>
</div>
</div>
</template>

View File

@@ -1,18 +1,18 @@
<template>
<section class="app-main" style="min-height: 100%">
<transition name="fade" mode="out-in">
<router-view :key="key"></router-view>
</transition>
</section>
<section class="app-main" style="min-height: 100%">
<transition name="fade" mode="out-in">
<router-view :key="key"></router-view>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
key() {
return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
}
}
export default {
name: 'AppMain',
computed: {
key() {
return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
}
}
}
</script>

View File

@@ -1,80 +1,80 @@
<template>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
<div class="sidebar-wrapper">
<Sidebar class="sidebar-container" />
</div>
<div class="main-container">
<Navbar/>
<App-main/>
</div>
</div>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
<div class="sidebar-wrapper">
<sidebar class="sidebar-container"></sidebar>
</div>
<div class="main-container">
<navbar></navbar>
<app-main></app-main>
</div>
</div>
</template>
<script>
import { Navbar, Sidebar, AppMain } from 'views/layout';
import { Navbar, Sidebar, AppMain } from 'views/layout';
export default {
name: 'layout',
components: {
Navbar,
Sidebar,
AppMain
},
computed: {
sidebar() {
return this.$store.state.app.sidebar;
}
export default {
name: 'layout',
components: {
Navbar,
Sidebar,
AppMain
},
computed: {
sidebar() {
return this.$store.state.app.sidebar;
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
&.hideSidebar {
.sidebar-wrapper {
transform: translate(-140px, 0);
.sidebar-container {
transform: translate(132px, 0);
}
&:hover {
transform: translate(0, 0);
.sidebar-container {
transform: translate(0, 0);
}
}
}
.main-container{
margin-left: 40px;
}
}
.sidebar-wrapper {
width: 180px;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
transition: all .28s ease-out;
}
.sidebar-container {
transition: all .28s ease-out;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: -17px;
overflow-y: scroll;
}
.main-container {
min-height: 100%;
transition: all .28s ease-out;
margin-left: 180px;
}
}
@import "src/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
&.hideSidebar {
.sidebar-wrapper {
transform: translate(-140px, 0);
.sidebar-container {
transform: translate(132px, 0);
}
&:hover {
transform: translate(0, 0);
.sidebar-container {
transform: translate(0, 0);
}
}
}
.main-container {
margin-left: 40px;
}
}
.sidebar-wrapper {
width: 180px;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
transition: all .28s ease-out;
}
.sidebar-container {
transition: all .28s ease-out;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: -17px;
overflow-y: scroll;
}
.main-container {
min-height: 100%;
transition: all .28s ease-out;
margin-left: 180px;
}
}
</style>

View File

@@ -8,42 +8,42 @@
</template>
<script>
export default {
created() {
this.getBreadcrumb()
},
data() {
return {
levelList: null
}
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name);
const first = matched[0];
if (first && (first.name !== '首页' || first.path !== '')) {
matched = [{ name: '首页', path: '/' }].concat(matched)
}
this.levelList = matched;
}
},
watch: {
$route() {
this.getBreadcrumb();
export default {
created() {
this.getBreadcrumb()
},
data() {
return {
levelList: null
}
},
methods: {
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name);
const first = matched[0];
if (first && (first.name !== '首页' || first.path !== '')) {
matched = [{ name: '首页', path: '/' }].concat(matched)
}
this.levelList = matched;
}
},
watch: {
$route() {
this.getBreadcrumb();
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-levelbar.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect{
color: #97a8be;
cursor:text;
}
.app-levelbar.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View File

@@ -1,116 +1,119 @@
<template>
<el-menu class="navbar" mode="horizontal">
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<levelbar></levelbar>
<error-log v-if="log.length>0" class="errLog-container" :logsList="log"></error-log>
<screenfull class='screenfull'></screenfull>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'">
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class='inlineBlock' to="/">
<el-dropdown-item>
首页
</el-dropdown-item>
</router-link>
<router-link class='inlineBlock' to="/admin/profile">
<el-dropdown-item>
设置
</el-dropdown-item>
</router-link>
<el-dropdown-item divided><span @click="logout" style="display:block;">退出登录</span></el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu>
<el-menu class="navbar" mode="horizontal">
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<levelbar></levelbar>
<tabs-view></tabs-view>
<error-log v-if="log.length>0" class="errLog-container" :logsList="log"></error-log>
<screenfull class='screenfull'></screenfull>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img class="user-avatar" :src="avatar+'?imageView2/1/w/80/h/80'">
<i class="el-icon-caret-bottom"></i>
</div>
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class='inlineBlock' to="/">
<el-dropdown-item>
首页
</el-dropdown-item>
</router-link>
<a target='_blank' href="https://github.com/PanJiaChen/vue-element-admin/">
<el-dropdown-item>
项目地址
</el-dropdown-item>
</a>
<el-dropdown-item divided><span @click="logout" style="display:block;">退出登录</span></el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu>
</template>
<script>
import { mapGetters } from 'vuex';
import Levelbar from './Levelbar';
import Hamburger from 'components/Hamburger';
import Screenfull from 'components/Screenfull';
import ErrorLog from 'components/ErrLog';
import errLogStore from 'store/errLog';
import { mapGetters } from 'vuex';
import Levelbar from './Levelbar';
import TabsView from './TabsView';
import Hamburger from 'components/Hamburger';
import Screenfull from 'components/Screenfull';
import ErrorLog from 'components/ErrLog';
import errLogStore from 'store/errLog';
export default {
components: {
Levelbar,
Hamburger,
ErrorLog,
Screenfull
export default {
components: {
Levelbar,
TabsView,
Hamburger,
ErrorLog,
Screenfull
},
data() {
return {
log: errLogStore.state.errLog
}
},
computed: {
...mapGetters([
'sidebar',
'name',
'avatar'
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
data() {
return {
log: errLogStore.state.errLog
}
},
computed: {
...mapGetters([
'sidebar',
'name',
'avatar'
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload();// 为了重新实例化vue-router对象 避免bug
});
}
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload();// 为了重新实例化vue-router对象 避免bug
});
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.navbar {
height: 50px;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
}
.errLog-container {
display: inline-block;
position: absolute;
right: 150px;
}
.screenfull{
position: absolute;
right: 90px;
top: 16px;
color: red;
}
.avatar-container {
height: 50px;
display: inline-block;
position: absolute;
right: 35px;
.avatar-wrapper {
cursor: pointer;
margin-top:5px;
position: relative;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
.navbar {
height: 50px;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
}
.errLog-container {
display: inline-block;
position: absolute;
right: 150px;
}
.screenfull {
position: absolute;
right: 90px;
top: 16px;
color: red;
}
.avatar-container {
height: 50px;
display: inline-block;
position: absolute;
right: 35px;
.avatar-wrapper {
cursor: pointer;
margin-top: 5px;
position: relative;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
</style>

View File

@@ -1,24 +1,24 @@
<template>
<el-menu mode="vertical" theme="dark" :default-active="$route.path">
<sidebar-item :routes='permission_routers'></sidebar-item>
</el-menu>
<el-menu mode="vertical" theme="dark" :default-active="$route.path">
<sidebar-item :routes='permission_routers'></sidebar-item>
</el-menu>
</template>
<script>
import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem';
export default {
components: { SidebarItem },
computed: {
...mapGetters([
'permission_routers'
])
}
import { mapGetters } from 'vuex';
import SidebarItem from './SidebarItem';
export default {
components: { SidebarItem },
computed: {
...mapGetters([
'permission_routers'
])
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.el-menu {
min-height: 100%;
}
.el-menu {
min-height: 100%;
}
</style>

View File

@@ -1,47 +1,47 @@
<template>
<div>
<template v-for="item in routes">
<router-link v-if="!item.hidden&&item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
<el-menu-item :index="item.path+'/'+item.children[0].path">
<wscn-icon-svg v-if='item.icon' :icon-class="item.icon" /> {{item.children[0].name}}
</el-menu-item>
</router-link>
<el-submenu :index="item.name" v-if="!item.noDropdown&&!item.hidden">
<template slot="title">
<wscn-icon-svg v-if='item.icon' :icon-class="item.icon" /> {{item.name}}
</template>
<template v-for="child in item.children" v-if='!child.hidden'>
<sidebar-item class='menu-indent' v-if='child.children&&child.children.length>0' :routes='[child]'> </sidebar-item>
<router-link v-else class="menu-indent" :to="item.path+'/'+child.path">
<el-menu-item :index="item.path+'/'+child.path">
{{child.name}}
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
<div>
<template v-for="item in routes">
<router-link v-if="!item.hidden&&item.noDropdown&&item.children.length>0" :to="item.path+'/'+item.children[0].path">
<el-menu-item :index="item.path+'/'+item.children[0].path">
<icon-svg v-if='item.icon' :icon-class="item.icon"></icon-svg>{{item.children[0].name}}
</el-menu-item>
</router-link>
<el-submenu :index="item.name" v-if="!item.noDropdown&&!item.hidden">
<template slot="title">
<icon-svg v-if='item.icon' :icon-class="item.icon"></icon-svg> {{item.name}}
</template>
<template v-for="child in item.children" v-if='!child.hidden'>
<sidebar-item class='menu-indent' v-if='child.children&&child.children.length>0' :routes='[child]'> </sidebar-item>
<router-link v-else class="menu-indent" :to="item.path+'/'+child.path">
<el-menu-item :index="item.path+'/'+child.path">
{{child.name}}
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
}
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-icon {
margin-right: 10px;
}
.hideSidebar .menu-indent{
display: block;
text-indent: 10px;
}
.svg-icon {
margin-right: 10px;
}
.hideSidebar .menu-indent {
display: block;
text-indent: 10px;
}
</style>

Some files were not shown because too many files have changed in this diff Show More