Commit 632adc4e authored by 智勇's avatar 智勇

状态码拦截器

parent 725147ee
......@@ -21,7 +21,7 @@ export function logout() {
export function getUserInfo(token) {
return request({
url: '/user/check_token',
url: '/user/check_token_new',
method: 'get',
params: { token }
})
......
......@@ -7,7 +7,7 @@ const dockersRouter = {
redirect: 'noredirect',
name: 'Dockers',
meta: {
title: 'dockers',
title: 'Dockers',
icon: 'chart'
},
children: [
......
import { loginByUsername, getUserInfo } from '@/api/login'
// import { loginByUsername, logout, getUserInfo } from '@/api/login'
import { loginByUsername, logout, getUserInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
const user = {
......@@ -67,7 +66,7 @@ const user = {
// if (response.data.code !== '0000') {
// reject('Verification failed, please login again.')
// }
const data = response
const data = response.data
data.roles = ['admin']
if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
......@@ -101,19 +100,16 @@ const user = {
// 登出
LogOut({ commit, state }) {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
// return new Promise((resolve, reject) => {
// logout(state.token).then(() => {
// commit('SET_TOKEN', '')
// commit('SET_ROLES', [])
// removeToken()
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
}).catch(error => {
reject(error)
})
})
},
// 前端 登出
......
......@@ -39,15 +39,7 @@ service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== '0000') {
Message({
message: res.msg,
type: 'error',
duration: 5 * 1000
})
// 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// 请自行在引入 MessageBox
// import { Message, MessageBox } from 'element-ui'
if (res.status === 401) {
MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
......@@ -57,11 +49,16 @@ service.interceptors.response.use(
location.reload() // 为了重新实例化vue-router对象 避免bug
})
})
} else {
Message({
message: res.msg,
type: 'error',
duration: 5 * 1000
})
}
return Promise.reject('error')
} else {
return response.data
}
return response.data
},
error => {
console.log('err' + error) // for debug
......
<template>
<div class="chart-container">
<chart height="100%" width="100%"/>
</div>
</template>
<script>
import Chart from '@/components/Charts/keyboard'
export default {
name: 'KeyboardChart',
components: { Chart }
}
</script>
<style scoped>
.chart-container{
position: relative;
width: 100%;
height: calc(100vh - 84px);
}
</style>
<template>
<div class="chart-container">
<chart height="100%" width="100%"/>
</div>
</template>
<script>
import Chart from '@/components/Charts/lineMarker'
export default {
name: 'LineChart',
components: { Chart }
}
</script>
<style scoped>
.chart-container{
position: relative;
width: 100%;
height: calc(100vh - 84px);
}
</style>
<template>
<div class="chart-container">
<chart height="100%" width="100%"/>
</div>
</template>
<script>
import Chart from '@/components/Charts/mixChart'
export default {
name: 'MixChart',
components: { Chart }
}
</script>
<style scoped>
.chart-container{
position: relative;
width: 100%;
height: calc(100vh - 84px);
}
</style>
<template>
<div class="app-container">
<el-tabs v-model="activeName">
<el-tab-pane label="use clipboard directly" name="directly">
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;"/>
<el-button type="primary" icon="document" @click="handleCopy(inputData,$event)">copy</el-button>
</el-tab-pane>
<el-tab-pane label="use clipboard by v-directive" name="v-directive">
<el-input v-model="inputData" placeholder="Please input" style="width:400px;max-width:100%;"/>
<el-button v-clipboard:copy="inputData" v-clipboard:success="clipboardSuccess" type="primary" icon="document">copy</el-button>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import clip from '@/utils/clipboard' // use clipboard directly
import clipboard from '@/directive/clipboard/index.js' // use clipboard by v-directive
export default {
name: 'ClipboardDemo',
directives: {
clipboard
},
data() {
return {
activeName: 'directly',
inputData: 'https://github.com/PanJiaChen/vue-element-admin'
}
},
methods: {
handleCopy(text, event) {
clip(text, event)
},
clipboardSuccess() {
this.$message({
message: 'Copy successfully',
type: 'success',
duration: 1500
})
}
}
}
</script>
<template>
<div :class="className" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
const animationDuration = 6000
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()
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
window.removeEventListener('resize', this.__resizeHandler)
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: {
top: 10,
left: '2%',
right: '2%',
bottom: '3%',
containLabel: true
},
xAxis: [{
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
axisTick: {
alignWithLabel: true
}
}],
yAxis: [{
type: 'value',
axisTick: {
show: false
}
}],
series: [{
name: 'pageA',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [79, 52, 200, 334, 390, 330, 220],
animationDuration
}, {
name: 'pageB',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [80, 52, 200, 334, 390, 330, 220],
animationDuration
}, {
name: 'pageC',
type: 'bar',
stack: 'vistors',
barWidth: '60%',
data: [30, 52, 200, 334, 390, 330, 220],
animationDuration
}]
})
}
}
}
</script>
<template>
<el-card class="box-card-component" style="margin-left:8px;">
<div slot="header" class="box-card-header">
<img src="https://wpimg.wallstcn.com/e7d23d71-cf19-4b90-a1cc-f56af8c0903d.png">
</div>
<div style="position:relative;">
<pan-thumb :image="avatar" class="panThumb"/>
<mallki class-name="mallki-text" text="vue-element-admin"/>
<div style="padding-top:35px;" class="progress-item">
<span>Vue</span>
<el-progress :percentage="70"/>
</div>
<div class="progress-item">
<span>JavaScript</span>
<el-progress :percentage="18"/>
</div>
<div class="progress-item">
<span>Css</span>
<el-progress :percentage="12"/>
</div>
<div class="progress-item">
<span>ESLint</span>
<el-progress :percentage="100" status="success"/>
</div>
</div>
</el-card>
</template>
<script>
import { mapGetters } from 'vuex'
import PanThumb from '@/components/PanThumb'
import Mallki from '@/components/TextHoverEffect/Mallki'
export default {
components: { PanThumb, Mallki },
filters: {
statusFilter(status) {
const statusMap = {
success: 'success',
pending: 'danger'
}
return statusMap[status]
}
},
data() {
return {
statisticsData: {
article_count: 1024,
pageviews_count: 1024
}
}
},
computed: {
...mapGetters([
'name',
'avatar',
'roles'
])
}
}
</script>
<style rel="stylesheet/scss" lang="scss" >
.box-card-component{
.el-card__header {
padding: 0px!important;
}
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
.box-card-component {
.box-card-header {
position: relative;
height: 220px;
img {
width: 100%;
height: 100%;
transition: all 0.2s linear;
&:hover {
transform: scale(1.1, 1.1);
filter: contrast(130%);
}
}
}
.mallki-text {
position: absolute;
top: 0px;
right: 0px;
font-size: 20px;
font-weight: bold;
}
.panThumb {
z-index: 100;
height: 70px!important;
width: 70px!important;
position: absolute!important;
top: -45px;
left: 0px;
border: 5px solid #ffffff;
background-color: #fff;
margin: auto;
box-shadow: none!important;
/deep/ .pan-info {
box-shadow: none!important;
}
}
.progress-item {
margin-bottom: 10px;
font-size: 14px;
}
@media only screen and (max-width: 1510px){
.mallki-text{
display: none;
}
}
}
</style>
<template>
<div :class="className" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
export default {
props: {
className: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '350px'
},
autoResize: {
type: Boolean,
default: true
},
chartData: {
type: Object,
required: true
}
},
data() {
return {
chart: null,
sidebarElm: null
}
},
watch: {
chartData: {
deep: true,
handler(val) {
this.setOptions(val)
}
}
},
mounted() {
this.initChart()
if (this.autoResize) {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
}
// 监听侧边栏的变化
this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
if (this.autoResize) {
window.removeEventListener('resize', this.__resizeHandler)
}
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
this.chart.dispose()
this.chart = null
},
methods: {
sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}
},
setOptions({ expectedData, actualData } = {}) {
this.chart.setOption({
xAxis: {
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
boundaryGap: false,
axisTick: {
show: false
}
},
grid: {
left: 10,
right: 10,
bottom: 20,
top: 30,
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
padding: [5, 10]
},
yAxis: {
axisTick: {
show: false
}
},
legend: {
data: ['expected', 'actual']
},
series: [{
name: 'expected', itemStyle: {
normal: {
color: '#FF005A',
lineStyle: {
color: '#FF005A',
width: 2
}
}
},
smooth: true,
type: 'line',
data: expectedData,
animationDuration: 2800,
animationEasing: 'cubicInOut'
},
{
name: 'actual',
smooth: true,
type: 'line',
itemStyle: {
normal: {
color: '#3888fa',
lineStyle: {
color: '#3888fa',
width: 2
},
areaStyle: {
color: '#f3f8ff'
}
}
},
data: actualData,
animationDuration: 2800,
animationEasing: 'quadraticOut'
}]
})
},
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.setOptions(this.chartData)
}
}
}
</script>
<template>
<el-row :gutter="40" class="panel-group">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
<div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">New Visits</div>
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num"/>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('messages')">
<div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="message" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">Messages</div>
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num"/>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('purchases')">
<div class="card-panel-icon-wrapper icon-money">
<svg-icon icon-class="money" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">Purchases</div>
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num"/>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">Shoppings</div>
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num"/>
</div>
</div>
</el-col>
</el-row>
</template>
<script>
import CountTo from 'vue-count-to'
export default {
components: {
CountTo
},
methods: {
handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type)
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.panel-group {
margin-top: 18px;
.card-panel-col{
margin-bottom: 32px;
}
.card-panel {
height: 108px;
cursor: pointer;
font-size: 12px;
position: relative;
overflow: hidden;
color: #666;
background: #fff;
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
border-color: rgba(0, 0, 0, .05);
&:hover {
.card-panel-icon-wrapper {
color: #fff;
}
.icon-people {
background: #40c9c6;
}
.icon-message {
background: #36a3f7;
}
.icon-money {
background: #f4516c;
}
.icon-shopping {
background: #34bfa3
}
}
.icon-people {
color: #40c9c6;
}
.icon-message {
color: #36a3f7;
}
.icon-money {
color: #f4516c;
}
.icon-shopping {
color: #34bfa3
}
.card-panel-icon-wrapper {
float: left;
margin: 14px 0 0 14px;
padding: 16px;
transition: all 0.38s ease-out;
border-radius: 6px;
}
.card-panel-icon {
float: left;
font-size: 48px;
}
.card-panel-description {
float: right;
font-weight: bold;
margin: 26px;
margin-left: 0px;
.card-panel-text {
line-height: 18px;
color: rgba(0, 0, 0, 0.45);
font-size: 16px;
margin-bottom: 12px;
}
.card-panel-num {
font-size: 20px;
}
}
}
}
</style>
<template>
<div :class="className" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
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()
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
window.removeEventListener('resize', this.__resizeHandler)
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(this.$el, 'macarons')
this.chart.setOption({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
left: 'center',
bottom: '10',
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts']
},
calculable: true,
series: [
{
name: 'WEEKLY WRITE ARTICLES',
type: 'pie',
roseType: 'radius',
radius: [15, 95],
center: ['50%', '38%'],
data: [
{ value: 320, name: 'Industries' },
{ value: 240, name: 'Technology' },
{ value: 149, name: 'Forex' },
{ value: 100, name: 'Gold' },
{ value: 59, name: 'Forecasts' }
],
animationEasing: 'cubicInOut',
animationDuration: 2600
}
]
})
}
}
}
</script>
<template>
<div :class="className" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import { debounce } from '@/utils'
const animationDuration = 3000
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()
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
},
beforeDestroy() {
if (!this.chart) {
return
}
window.removeEventListener('resize', this.__resizeHandler)
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'
}
},
radar: {
radius: '66%',
center: ['50%', '42%'],
splitNumber: 8,
splitArea: {
areaStyle: {
color: 'rgba(127,95,132,.3)',
opacity: 1,
shadowBlur: 45,
shadowColor: 'rgba(0,0,0,.5)',
shadowOffsetX: 0,
shadowOffsetY: 15
}
},
indicator: [
{ name: 'Sales', max: 10000 },
{ name: 'Administration', max: 20000 },
{ name: 'Information Techology', max: 20000 },
{ name: 'Customer Support', max: 20000 },
{ name: 'Development', max: 20000 },
{ name: 'Marketing', max: 20000 }
]
},
legend: {
left: 'center',
bottom: '10',
data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']
},
series: [{
type: 'radar',
symbolSize: 0,
areaStyle: {
normal: {
shadowBlur: 13,
shadowColor: 'rgba(0,0,0,.2)',
shadowOffsetX: 0,
shadowOffsetY: 10,
opacity: 1
}
},
data: [
{
value: [5000, 7000, 12000, 11000, 15000, 14000],
name: 'Allocated Budget'
},
{
value: [4000, 9000, 15000, 15000, 13000, 11000],
name: 'Expected Spending'
},
{
value: [5500, 11000, 12000, 15000, 12000, 12000],
name: 'Actual Spending'
}
],
animationDuration: animationDuration
}]
})
}
}
}
</script>
<template>
<li :class="{ completed: todo.done, editing: editing }" class="todo">
<div class="view">
<input
:checked="todo.done"
class="toggle"
type="checkbox"
@change="toggleTodo( todo)">
<label @dblclick="editing = true" v-text="todo.text"/>
<button class="destroy" @click="deleteTodo( todo )"/>
</div>
<input
v-focus="editing"
v-show="editing"
:value="todo.text"
class="edit"
@keyup.enter="doneEdit"
@keyup.esc="cancelEdit"
@blur="doneEdit">
</li>
</template>
<script>
export default {
name: 'Todo',
directives: {
focus(el, { value }, { context }) {
if (value) {
context.$nextTick(() => {
el.focus()
})
}
}
},
props: {
todo: {
type: Object,
default: function() {
return {}
}
}
},
data() {
return {
editing: false
}
},
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>
.todoapp {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
color: #4d4d4d;
min-width: 230px;
max-width: 550px;
margin: 0 auto ;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
background: #fff;
z-index: 1;
position: relative;
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: 10px 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;
cursor: pointer;
}
.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: 40px;
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;
position: relative;
z-index: 1;
list-style: none;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
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;
}
}
}
<template>
<section class="todoapp">
<!-- header -->
<header class="header">
<input class="new-todo" autocomplete="off" placeholder="Todo List" @keyup.enter="addTodo">
</header>
<!-- main section -->
<section v-show="todos.length" class="main">
<input id="toggle-all" :checked="allChecked" class="toggle-all" type="checkbox" @change="toggleAll({ done: !allChecked })">
<label for="toggle-all"/>
<ul class="todo-list">
<todo
v-for="(todo, index) in filteredTodos"
:key="index"
:todo="todo"
@toggleTodo="toggleTodo"
@editTodo="editTodo"
@deleteTodo="deleteTodo"/>
</ul>
</section>
<!-- footer -->
<footer v-show="todos.length" class="footer">
<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 },
{ text: 'vue-element-admin', done: true },
{ text: 'vue', done: true },
{ text: 'element-ui', done: true },
{ text: 'axios', done: true },
{ text: 'webpack', done: true }
]
export default {
components: { Todo },
filters: {
pluralize: (n, w) => n === 1 ? w : w + 's',
capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)
},
data() {
return {
visibility: 'all',
filters,
// todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || defalutList
todos: 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: {
setLocalStorage() {
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.setLocalStorage()
}
e.target.value = ''
},
toggleTodo(val) {
val.done = !val.done
this.setLocalStorage()
},
deleteTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
this.setLocalStorage()
},
editTodo({ todo, value }) {
todo.text = value
this.setLocalStorage()
},
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.done)
this.setLocalStorage()
},
toggleAll({ done }) {
this.todos.forEach(todo => {
todo.done = done
this.setLocalStorage()
})
}
}
}
</script>
<style lang="scss">
@import './index.scss';
</style>
<template>
<el-table :data="list" style="width: 100%;padding-top: 15px;">
<el-table-column label="Order_No" min-width="200">
<template slot-scope="scope">
{{ scope.row.order_no | orderNoFilter }}
</template>
</el-table-column>
<el-table-column label="Price" width="195" align="center">
<template slot-scope="scope">
¥{{ scope.row.price | toThousandFilter }}
</template>
</el-table-column>
<el-table-column label="Status" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter"> {{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { fetchList } from '@/api/transaction'
export default {
filters: {
statusFilter(status) {
const statusMap = {
success: 'success',
pending: 'danger'
}
return statusMap[status]
},
orderNoFilter(str) {
return str.substring(0, 30)
}
},
data() {
return {
list: null
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
fetchList().then(response => {
this.list = response.data.items.slice(0, 8)
})
}
}
}
</script>
<template>
<div class="dashboard-editor-container">
<github-corner style="position: absolute; top: 0px; border: 0; right: 0;"/>
<panel-group @handleSetLineChartData="handleSetLineChartData"/>
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<line-chart :chart-data="lineChartData"/>
</el-row>
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<raddar-chart/>
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<pie-chart/>
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<bar-chart/>
</div>
</el-col>
</el-row>
<el-row :gutter="8">
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<transaction-table/>
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
<todo-list/>
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
<box-card/>
</el-col>
</el-row>
</div>
</template>
<script>
import GithubCorner from '@/components/GithubCorner'
import PanelGroup from './components/PanelGroup'
import LineChart from './components/LineChart'
import RaddarChart from './components/RaddarChart'
import PieChart from './components/PieChart'
import BarChart from './components/BarChart'
import TransactionTable from './components/TransactionTable'
import TodoList from './components/TodoList'
import BoxCard from './components/BoxCard'
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145]
},
messages: {
expectedData: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130]
},
purchases: {
expectedData: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130]
},
shoppings: {
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130]
}
}
export default {
name: 'DashboardAdmin',
components: {
GithubCorner,
PanelGroup,
LineChart,
RaddarChart,
PieChart,
BarChart,
TransactionTable,
TodoList,
BoxCard
},
data() {
return {
lineChartData: lineChartData.newVisitis
}
},
methods: {
handleSetLineChartData(type) {
this.lineChartData = lineChartData[type]
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
</style>
<template>
<div class="dashboard-editor-container">
<div class=" clearfix">
<pan-thumb :image="avatar" style="float: left"> Your roles:
<span v-for="item in roles" :key="item" class="pan-info-roles">{{ item }}</span>
</pan-thumb>
<github-corner style="position: absolute; top: 0px; border: 0; right: 0;"/>
<div class="info-container">
<span class="display_name">{{ name }}</span>
<span style="font-size:20px;padding-top:20px;display:inline-block;">Editor's Dashboard</span>
</div>
</div>
<div>
<img :src="emptyGif" class="emptyGif">
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import PanThumb from '@/components/PanThumb'
import GithubCorner from '@/components/GithubCorner'
export default {
name: 'DashboardEditor',
components: { PanThumb, GithubCorner },
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;
padding: 50px 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>
<template>
<div class="dashboard-container">
<component :is="currentRole"/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import adminDashboard from './admin'
import editorDashboard from './editor'
export default {
name: 'Dashboard',
components: { adminDashboard, editorDashboard },
data() {
return {
currentRole: 'adminDashboard'
}
},
computed: {
...mapGetters([
'roles'
])
},
created() {
if (!this.roles.includes('admin')) {
this.currentRole = 'editorDashboard'
}
}
}
</script>
......@@ -113,7 +113,7 @@ export default {
methods: {
getConfig() {
getConfig(this.listQuery).then(res => {
this.configs = res.data.data
this.configs = res.data
this.listLoading = false
})
},
......@@ -139,22 +139,14 @@ export default {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
saveConfig(this.temp).then((res) => {
if (res.data.code !== '0000') {
this.$notify({
message: `${res.data.msg}`,
type: 'error',
duration: 2000
})
} else {
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '保存成功',
type: 'success',
duration: 2000
})
this.getConfig()
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '保存成功',
type: 'success',
duration: 2000
})
this.getConfig()
})
}
})
......
......@@ -84,13 +84,13 @@ export default {
methods: {
getNamespace() {
getNamespace().then(res => {
this.namespaces = res.data.data.map(item => item.name)
this.namespaces = res.data.map(item => item.name)
})
},
getDBName() {
getDBName().then(res => {
this.dbnames = res.data.data.map(item => item.dbname)
this.dbnames = res.data.map(item => item.dbname)
this.dbnames.unshift('all(no_mall)')
})
},
......@@ -106,7 +106,7 @@ export default {
syncQuery(location) {
dbSyncQuery({ location }).then(res => {
res = res.data.data
res = res.data
if (this.timer > 300) {
this.$notify({
title: '超时',
......@@ -141,7 +141,7 @@ export default {
if (valid) {
dbSync(this.temp).then((res) => {
this.dialogFormVisible = false
this.location = res.data.data.location
this.location = res.data.location
this.$notify({
message: '正在同步',
type: 'success',
......
......@@ -64,7 +64,7 @@ export default {
getAllEnvTemplate() {
getAllEnvTemplate().then(res => {
this.templates = res.data.data
this.templates = res.data
})
},
......
......@@ -302,7 +302,7 @@ export default {
},
created() {
getType().then(res => {
this.typeList = res.data.data
this.typeList = res.data
})
this.namespace = this.$route.params.name
this.getServicelist()
......@@ -310,7 +310,7 @@ export default {
methods: {
getServicelist() {
fetchK8sdetail({ 'namespace': this.namespace }).then(res => {
const servicelist = res.data.data.services
const servicelist = res.data.services
this.array2Object(servicelist)
})
},
......@@ -327,9 +327,9 @@ export default {
this.tagName = ''
this.options = []
this.tagOptions = []
fetchUserrepository({ 'namespace': 'qa-' + label }).then(res => {
this.options = res.data.data.repoInfo
this.server = res.data.data.server
fetchUserrepository({ 'namespace': 'qa-' + type }).then(res => {
this.options = res.data.repoInfo
this.server = res.data.server
})
this.dialogVisible = true
},
......@@ -340,8 +340,8 @@ export default {
}
})
fetchTaglist({ reponame: this.reponame }).then(res => {
if (res.data.data.tagCount) {
this.tagOptions = res.data.data.tagInfo
if (res.data.tagCount) {
this.tagOptions = res.data.tagInfo
}
})
},
......@@ -359,7 +359,7 @@ export default {
}).then(res => {
if (res.data.code === '0000') {
this.$message({
message: res.data.data,
message: res.data,
type: 'success',
duration: 1000,
onClose() {
......@@ -377,14 +377,14 @@ export default {
},
handleDetail(data) {
ingressHost({ namespace: data.namespace, serviceName: data.serviceName }).then(res => {
const url = res.data.data.body.spec.rules[0].host
const url = res.data.body.spec.rules[0].host
window.open(`http://${url}/`, '_blank')
})
},
getServiceDetails(value) {
fetchServiceDetails({ serviceName: value.serviceName, namespace: value.namespace }).then(res => {
this.centerDialogVisible = true
this.value = res.data.data.service
this.value = res.data.service
})
},
submitHost(formName) {
......@@ -417,9 +417,9 @@ export default {
this.rename = value.serviceName
this.tagOptions = []
fetchTaglist({ reponame: 'qa-' + value.userLabels.type + '/' + this.rename }).then(res => {
if (res.data.data.tagCount) {
this.tagOptions = res.data.data.tagInfo
this.server = res.data.data.server
if (res.data.tagCount) {
this.tagOptions = res.data.tagInfo
this.server = res.data.server
}
})
},
......@@ -482,7 +482,7 @@ export default {
this.form.host = ''
this.ingressName = data.serviceName
ingressHost({ namespace: data.namespace, serviceName: data.serviceName }).then(res => {
this.form.host = res.data.data.body.spec.rules[0].host
this.form.host = res.data.body.spec.rules[0].host
})
},
deleteService(data) {
......@@ -514,7 +514,7 @@ export default {
},
linkShell(scopeData) {
fetchServiceInstance({ serviceName: scopeData.serviceName, namespace: scopeData.namespace }).then(res => {
const podName = res.data.data.instances[0].name
const podName = res.data.instances[0].name
window.open(`http://172.30.220.24:9600/static/terminal.html?namespace=${scopeData.namespace}&pod=${podName}`, '_blank')
})
},
......@@ -526,7 +526,7 @@ export default {
path = `/home/quant_group/logs/${scopeData.serviceName}.log`
}
fetchServiceInstance({ serviceName: scopeData.serviceName, namespace: scopeData.namespace }).then(res => {
const podName = res.data.data.instances[0].name
const podName = res.data.instances[0].name
window.open(`http://172.30.220.24:9600/static/logs.html?namespace=${scopeData.namespace}&pod=${podName}&paths=${path}`, '_blank')
})
},
......
......@@ -64,7 +64,7 @@ export default {
getAllEnvTemplate() {
getAllEnvTemplate().then(res => {
// todo: http服务统一返回res.data
this.templates = res.data.data
this.templates = res.data
})
},
......
......@@ -62,7 +62,7 @@ export default {
fetchK8sList() {
fetchKubernetesList().then(res => {
const exculde = ['default', 'kube-system']
this.tableData = res.data.data.namespaces.filter((item) => {
this.tableData = res.data.namespaces.filter((item) => {
if (!exculde.includes(item.name)) {
return item
}
......
<template>
<div class="app-container documentation-container">
<a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/">{{ $t('documentation.documentation') }}</a>
<a class="document-btn" target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">{{ $t('documentation.github') }}</a>
<a class="document-btn" target="_blank" href="https://panjiachen.gitee.io/vue-element-admin-site/zh/">国内文档</a>
<dropdown-menu :items="articleList" style="float:left;margin-left:50px;" title="系列文章"/>
</div>
</template>
<script>
import DropdownMenu from '@/components/Share/dropdownMenu'
export default {
name: 'Documentation',
components: { DropdownMenu },
data() {
return {
articleList: [
{ title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },
{ title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },
{ title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },
{ title: 'vue-admin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
{ title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
{ title: 'webpack4(上)', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
{ title: 'webpack4(下)', href: 'https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc' }
]
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.documentation-container {
margin: 50px;
.document-btn {
float: left;
margin-left: 50px;
display: block;
cursor: pointer;
background: black;
color: white;
height: 60px;
width: 200px;
line-height: 60px;
font-size: 20px;
text-align: center;
}
}
</style>
<template>
<div>
<!--error code-->
{{ a.a }}
<!--error code-->
</div>
</template>
<script>
export default {
name: 'ErrorTestA'
}
</script>
<template>
<div/>
</template>
<script>
export default {
created() {
this.b = b // eslint-disable-line
}
}
</script>
<template>
<div class="errPage-container">
<errorA/>
<errorB/>
<!-- $t is vue-i18n global function to translate lang -->
<h3>{{ $t('errorLog.tips') }}</h3>
<code>
{{ $t('errorLog.description') }}
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/error.html">
{{ $t('errorLog.documentation') }}
</a>
</code>
<a href="#">
<img src="https://wpimg.wallstcn.com/360e4842-4db5-42d0-b078-f9a84a825546.gif">
</a>
</div>
</template>
<script>
import errorA from './errorTestA'
import errorB from './errorTestB'
export default {
name: 'ErrorLog',
components: { errorA, errorB }
}
</script>
<style scoped>
.errPage-container {
padding: 30px;
}
</style>
<template>
<div class="createPost-container">
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
<sticky :class-name="'sub-navbar '+postForm.status">
<CommentDropdown v-model="postForm.comment_disabled" />
<PlatformDropdown v-model="postForm.platforms" />
<SourceUrlDropdown v-model="postForm.source_uri" />
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">发布
</el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">草稿</el-button>
</sticky>
<div class="createPost-main-container">
<el-row>
<Warning />
<el-col :span="24">
<el-form-item style="margin-bottom: 40px;" prop="title">
<MDinput v-model="postForm.title" :maxlength="100" name="name" required>
标题
</MDinput>
</el-form-item>
<div class="postInfo-container">
<el-row>
<el-col :span="8">
<el-form-item label-width="45px" label="作者:" class="postInfo-container-item">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable remote placeholder="搜索用户">
<el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label-width="80px" label="发布时间:" class="postInfo-container-item">
<el-date-picker v-model="postForm.display_time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间"/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="60px" label="重要性:" class="postInfo-container-item">
<el-rate
v-model="postForm.importance"
:max="3"
:colors="['#99A9BF', '#F7BA2A', '#FF9900']"
:low-threshold="1"
:high-threshold="3"
style="margin-top:8px;"/>
</el-form-item>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-form-item style="margin-bottom: 40px;" label-width="45px" label="摘要:">
<el-input :rows="1" v-model="postForm.content_short" type="textarea" class="article-textarea" autosize placeholder="请输入内容"/>
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}</span>
</el-form-item>
<el-form-item prop="content" style="margin-bottom: 30px;">
<Tinymce ref="editor" :height="400" v-model="postForm.content" />
</el-form-item>
<el-form-item prop="image_uri" style="margin-bottom: 30px;">
<Upload v-model="postForm.image_uri" />
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
import Upload from '@/components/Upload/singleImage3'
import MDinput from '@/components/MDinput'
import Sticky from '@/components/Sticky' // 粘性header组件
import { validateURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article'
import { userSearch } from '@/api/remoteSearch'
import Warning from './Warning'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
const defaultForm = {
status: 'draft',
title: '', // 文章题目
content: '', // 文章内容
content_short: '', // 文章摘要
source_uri: '', // 文章外链
image_uri: '', // 文章图片
display_time: undefined, // 前台展示时间
id: undefined,
platforms: ['a-platform'],
comment_disabled: false,
importance: 0
}
export default {
name: 'ArticleDetail',
components: { Tinymce, MDinput, Upload, Sticky, Warning, CommentDropdown, PlatformDropdown, SourceUrlDropdown },
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
this.$message({
message: rule.field + '为必传项',
type: 'error'
})
callback(new Error(rule.field + '为必传项'))
} else {
callback()
}
}
const validateSourceUri = (rule, value, callback) => {
if (value) {
if (validateURL(value)) {
callback()
} else {
this.$message({
message: '外链url填写不正确',
type: 'error'
})
callback(new Error('外链url填写不正确'))
}
} else {
callback()
}
}
return {
postForm: Object.assign({}, defaultForm),
loading: false,
userListOptions: [],
rules: {
image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }],
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
},
tempRoute: {}
}
},
computed: {
contentShortLength() {
return this.postForm.content_short.length
},
lang() {
return this.$store.getters.language
}
},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
} else {
this.postForm = Object.assign({}, defaultForm)
}
// Why need to make a copy of this.$route here?
// Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
// https://github.com/PanJiaChen/vue-element-admin/issues/1221
this.tempRoute = Object.assign({}, this.$route)
},
methods: {
fetchData(id) {
fetchArticle(id).then(response => {
this.postForm = response.data
// Just for test
this.postForm.title += ` Article Id:${this.postForm.id}`
this.postForm.content_short += ` Article Id:${this.postForm.id}`
// Set tagsview title
this.setTagsViewTitle()
}).catch(err => {
console.log(err)
})
},
setTagsViewTitle() {
const title = this.lang === 'zh' ? '编辑文章' : 'Edit Article'
const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
this.$store.dispatch('updateVisitedView', route)
},
submitForm() {
this.postForm.display_time = parseInt(this.display_time / 1000)
console.log(this.postForm)
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
this.userListOptions = response.data.items.map(v => v.name)
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "~@/styles/mixin.scss";
.createPost-container {
position: relative;
.createPost-main-container {
padding: 40px 45px 20px 50px;
.postInfo-container {
position: relative;
@include clearfix;
margin-bottom: 10px;
.postInfo-container-item {
float: left;
}
}
}
.word-counter {
width: 40px;
position: absolute;
right: -10px;
top: 0px;
}
}
</style>
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>{{ !comment_disabled?'评论已打开':'评论已关闭' }}
<i class="el-icon-caret-bottom el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding">
<el-dropdown-item>
<el-radio-group v-model="comment_disabled" style="padding: 10px;">
<el-radio :label="true">关闭评论</el-radio>
<el-radio :label="false">打开评论</el-radio>
</el-radio-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: false
}
},
computed: {
comment_disabled: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>
<template>
<el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
<el-button plain>
平台({{ platforms.length }})
<i class="el-icon-caret-bottom el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown" class="no-border">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :label="item.key" :key="item.key">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
required: true,
default: () => [],
type: Array
}
},
data() {
return {
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
]
}
},
computed: {
platforms: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
外链
<i class="el-icon-caret-bottom el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input v-model="source_uri" placeholder="请输入内容">
<template slot="prepend">填写url</template>
</el-input>
</el-form-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
computed: {
source_uri: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>
export { default as CommentDropdown } from './Comment'
export { default as PlatformDropdown } from './Platform'
export { default as SourceUrlDropdown } from './SourceUrl'
<template>
<p class="warn-content">
创建和编辑页面是不能被keep-alive 缓存的,因为keep-alive 的include 目前不支持根据路由来缓存,所以目前都是基于component name 来缓存的,如果你想要实现缓存的效果,可以使用localstorage 等浏览器缓存方案。或者不要使用keep-alive
的include,直接缓存所有页面。详情见
<a
href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank">文档</a>
</p>
</template>
<template>
<article-detail :is-edit="false"/>
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'CreateForm',
components: { ArticleDetail }
}
</script>
<template>
<article-detail :is-edit="true"/>
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'EditForm',
components: { ArticleDetail }
}
</script>
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="80">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="Author">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="meta-item__icon"/>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<router-link :to="'/example/edit/'+scope.row.id" class="link-type">
<span>{{ scope.row.title }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column align="center" label="Actions" width="120">
<template slot-scope="scope">
<router-link :to="'/example/edit/'+scope.row.id">
<el-button type="primary" size="small" icon="el-icon-edit">Edit</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
export default {
name: 'ArticleList',
components: { Pagination },
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 20
}
}
},
created() {
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
})
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
}
}
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
</style>
<template>
<div style="display:inline-block;">
<label class="radio-label">Cell Auto-Width: </label>
<el-radio-group v-model="autoWidth">
<el-radio :label="true" border>True</el-radio>
<el-radio :label="false" border>False</el-radio>
</el-radio-group>
</div>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: true
}
},
computed: {
autoWidth: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>
<template>
<div style="display:inline-block;">
<label class="radio-label">Book Type: </label>
<el-select v-model="bookType" style="width:120px;" >
<el-option
v-for="item in options"
:key="item"
:label="item"
:value="item"/>
</el-select>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: 'xlsx'
}
},
data() {
return {
options: ['xlsx', 'csv', 'txt']
}
},
computed: {
bookType: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>
<template>
<div style="display:inline-block;">
<!-- $t is vue-i18n global function to translate lang -->
<label class="radio-label" style="padding-left:0;">Filename: </label>
<el-input :placeholder="$t('excel.placeholder')" v-model="filename" style="width:340px;" prefix-icon="el-icon-document"/>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
computed: {
filename: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>
<template>
<!-- $t is vue-i18n global function to translate lang -->
<div class="app-container">
<div>
<FilenameOption v-model="filename" />
<AutoWidthOption v-model="autoWidth" />
<BookTypeOption v-model="bookType" />
<el-button :loading="downloadLoading" style="margin:0 0 20px 20px;" type="primary" icon="document" @click="handleDownload">{{ $t('excel.export') }} Excel</el-button>
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
<el-tag type="info">Documentation</el-tag>
</a>
</div>
<el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table-column align="center" label="Id" width="95">
<template slot-scope="scope">
{{ scope.$index }}
</template>
</el-table-column>
<el-table-column label="Title">
<template slot-scope="scope">
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{ scope.row.author }}</el-tag>
</template>
</el-table-column>
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{ scope.row.pageviews }}
</template>
</el-table-column>
<el-table-column align="center" label="Date" width="220">
<template slot-scope="scope">
<i class="el-icon-time"/>
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import { parseTime } from '@/utils'
// options components
import FilenameOption from './components/FilenameOption'
import AutoWidthOption from './components/AutoWidthOption'
import BookTypeOption from './components/BookTypeOption'
export default {
name: 'ExportExcel',
components: { FilenameOption, AutoWidthOption, BookTypeOption },
data() {
return {
list: null,
listLoading: true,
downloadLoading: false,
filename: '',
autoWidth: true,
bookType: 'xlsx'
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList().then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.list
const data = this.formatJson(filterVal, list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: this.filename,
autoWidth: this.autoWidth,
bookType: this.bookType
})
this.downloadLoading = false
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
}
}
}
</script>
<style>
.radio-label {
font-size: 14px;
color: #606266;
line-height: 40px;
padding: 0 12px 0 30px;
}
</style>
<template>
<div class="app-container">
<!-- $t is vue-i18n global function to translate lang -->
<el-input :placeholder="$t('excel.placeholder')" v-model="filename" style="width:340px;" prefix-icon="el-icon-document"/>
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">{{ $t('excel.selectedExport') }}</el-button>
<a href="https://panjiachen.github.io/vue-element-admin-site/feature/component/excel.html" target="_blank" style="margin-left:15px;">
<el-tag type="info">Documentation</el-tag>
</a>
<el-table
v-loading="listLoading"
ref="multipleTable"
:data="list"
element-loading-text="拼命加载中"
border
fit
highlight-current-row
@selection-change="handleSelectionChange">
<el-table-column type="selection" align="center"/>
<el-table-column align="center" label="Id" width="95">
<template slot-scope="scope">
{{ scope.$index }}
</template>
</el-table-column>
<el-table-column label="Title">
<template slot-scope="scope">
{{ scope.row.title }}
</template>
</el-table-column>
<el-table-column label="Author" width="110" align="center">
<template slot-scope="scope">
<el-tag>{{ scope.row.author }}</el-tag>
</template>
</el-table-column>
<el-table-column label="Readings" width="115" align="center">
<template slot-scope="scope">
{{ scope.row.pageviews }}
</template>
</el-table-column>
<el-table-column align="center" label="PDate" width="220">
<template slot-scope="scope">
<i class="el-icon-time"/>
<span>{{ scope.row.display_time }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'SelectExcel',
data() {
return {
list: null,
listLoading: true,
multipleSelection: [],
downloadLoading: false,
filename: ''
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleSelectionChange(val) {
this.multipleSelection = val
},
handleDownload() {
if (this.multipleSelection.length) {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['Id', 'Title', 'Author', 'Readings', 'Date']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.multipleSelection
const data = this.formatJson(filterVal, list)
excel.export_json_to_excel({
header: tHeader,
data,
filename: this.filename
})
this.$refs.multipleTable.clearSelection()
this.downloadLoading = false
})
} else {
this.$message({
message: 'Please select at least one item',
type: 'warning'
})
}
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => v[j]))
}
}
}
</script>
<template>
<div class="app-container">
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload"/>
<el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
<el-table-column v-for="item of tableHeader" :prop="item" :label="item" :key="item"/>
</el-table>
</div>
</template>
<script>
import UploadExcelComponent from '@/components/UploadExcel/index.vue'
export default {
name: 'UploadExcel',
components: { UploadExcelComponent },
data() {
return {
tableData: [],
tableHeader: []
}
},
methods: {
beforeUpload(file) {
const isLt1M = file.size / 1024 / 1024 < 1
if (isLt1M) {
return true
}
this.$message({
message: 'Please do not upload files larger than 1m in size.',
type: 'warning'
})
return false
},
handleSuccess({ results, header }) {
this.tableData = results
this.tableHeader = header
}
}
}
</script>
const steps = [
{
element: '.hamburger-container',
popover: {
title: 'Hamburger',
description: 'Open && Close sidebar',
position: 'bottom'
}
},
{
element: '.breadcrumb-container',
popover: {
title: 'Breadcrumb',
description: 'Indicate the current page location',
position: 'bottom'
}
},
{
element: '.screenfull',
popover: {
title: 'Screenfull',
description: 'Bring the page into fullscreen',
position: 'left'
}
},
{
element: '.international-icon',
popover: {
title: 'Switch language',
description: 'Switch the system language',
position: 'left'
}
},
{
element: '.theme-switch',
popover: {
title: 'Theme Switch',
description: 'Custom switch system theme',
position: 'left'
}
},
{
element: '.tags-view-container',
popover: {
title: 'Tags view',
description: 'The history of the page you visited',
position: 'bottom'
},
padding: 0
}
]
export default steps
<template>
<div class="app-container">
<p class="warn-content">
{{ $t('guide.description') }}
<a href="https://github.com/kamranahmedse/driver.js" target="_blank">driver.js.
</a>
</p>
<el-button icon="el-icon-question" type="primary" @click.prevent.stop="guide">{{ $t('guide.button') }}</el-button>
</div>
</template>
<script>
import Driver from 'driver.js' // import driver.js
import 'driver.js/dist/driver.min.css' // import driver.js css
import steps from './defineSteps'
export default {
name: 'Guide',
data() {
return {
driver: null
}
},
mounted() {
this.driver = new Driver()
},
methods: {
guide() {
this.driver.defineSteps(steps)
this.driver.start()
}
}
}
</script>
<template>
<div>
<el-card class="box-card" style="margin-top:40px;">
<div slot="header" class="clearfix">
<svg-icon icon-class="international" />
<span style="margin-left:10px;">{{ $t('i18nView.title') }}</span>
</div>
<div>
<el-radio-group v-model="lang" size="small">
<el-radio label="zh" border>简体中文</el-radio>
<el-radio label="en" border>English</el-radio>
<el-radio label="es" border>Español</el-radio>
</el-radio-group>
<el-tag style="margin-top:15px;display:block;" type="info">{{ $t('i18nView.note') }}</el-tag>
</div>
</el-card>
<el-row :gutter="20" style="margin:100px 15px 50px;">
<el-col :span="12" :xs="24">
<div class="block">
<el-date-picker v-model="date" :placeholder="$t('i18nView.datePlaceholder')" type="date"/>
</div>
<div class="block">
<el-select v-model="value" :placeholder="$t('i18nView.selectPlaceholder')">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
</div>
<div class="block">
<el-button class="item-btn" size="small">{{ $t('i18nView.default') }}</el-button>
<el-button class="item-btn" size="small" type="primary">{{ $t('i18nView.primary') }}</el-button>
<el-button class="item-btn" size="small" type="success">{{ $t('i18nView.success') }}</el-button>
<el-button class="item-btn" size="small" type="info">{{ $t('i18nView.info') }}</el-button>
<el-button class="item-btn" size="small" type="warning">{{ $t('i18nView.warning') }}</el-button>
<el-button class="item-btn" size="small" type="danger">{{ $t('i18nView.danger') }}</el-button>
</div>
</el-col>
<el-col :span="12" :xs="24">
<el-table :data="tableData" fit highlight-current-row border style="width: 100%">
<el-table-column :label="$t('i18nView.tableName')" prop="name" width="100" align="center"/>
<el-table-column :label="$t('i18nView.tableDate')" prop="date" width="120" align="center"/>
<el-table-column :label="$t('i18nView.tableAddress')" prop="address"/>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
import local from './local'
const viewName = 'i18nView'
export default {
name: 'I18n',
data() {
return {
date: '',
tableData: [{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
}],
options: [],
value: ''
}
},
computed: {
lang: {
get() {
return this.$store.state.app.language
},
set(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
}
}
},
watch: {
lang() {
this.setOptions()
}
},
created() {
if (!this.$i18n.getLocaleMessage('en')[viewName]) {
this.$i18n.mergeLocaleMessage('en', local.en)
this.$i18n.mergeLocaleMessage('zh', local.zh)
this.$i18n.mergeLocaleMessage('es', local.es)
}
this.setOptions() // set default select options
},
methods: {
setOptions() {
this.options = [
{
value: '1',
label: this.$t('i18nView.one')
},
{
value: '2',
label: this.$t('i18nView.two')
},
{
value: '3',
label: this.$t('i18nView.three')
}
]
}
}
}
</script>
<style scoped>
.box-card {
width: 600px;
max-width: 100%;
margin: 20px auto;
}
.item-btn{
margin-bottom: 15px;
margin-left: 0px;
}
.block {
padding: 25px;
}
</style>
export default {
zh: {
i18nView: {
title: '切换语言',
note: '本项目国际化基于 vue-i18n',
datePlaceholder: '请选择日期',
selectPlaceholder: '请选择',
tableDate: '日期',
tableName: '姓名',
tableAddress: '地址',
default: '默认按钮',
primary: '主要按钮',
success: '成功按钮',
info: '信息按钮',
warning: '警告按钮',
danger: '危险按钮',
one: '',
two: '',
three: ''
}
},
en: {
i18nView: {
title: 'Switch Language',
note: 'The internationalization of this project is based on vue-i18n',
datePlaceholder: 'Pick a day',
selectPlaceholder: 'Select',
tableDate: 'tableDate',
tableName: 'tableName',
tableAddress: 'tableAddress',
default: 'default:',
primary: 'primary',
success: 'success',
info: 'info',
warning: 'warning',
danger: 'danger',
one: 'One',
two: 'Two',
three: 'Three'
}
},
es: {
i18nView: {
title: 'Switch Language',
note: 'The internationalization of this project is based on vue-i18n',
datePlaceholder: 'Pick a day',
selectPlaceholder: 'Select',
tableDate: 'tableDate',
tableName: 'tableName',
tableAddress: 'tableAddress',
default: 'default:',
primary: 'primary',
success: 'success',
info: 'info',
warning: 'warning',
danger: 'danger',
one: 'One',
two: 'Two',
three: 'Three'
}
}
}
......@@ -89,7 +89,7 @@ export default {
this.$store.dispatch('toggleSideBar')
},
logout() {
this.$store.dispatch('LogOut').then(() => {
this.$store.dispatch('FedLogOut').then(() => {
location.reload()// In order to re-instantiate the vue-router object to avoid bugs
})
}
......
<template >
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1">
<router-view />
</el-alert>
</div>
</template>
<template >
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1-1" type="success">
<router-view />
</el-alert>
</div>
</template>
<template>
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1-2" type="success">
<router-view />
</el-alert>
</div>
</template>
<template functional>
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1-2-1" type="warning" />
</div>
</template>
<template functional>
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1-2-2" type="warning" />
</div>
</template>
<template functional>
<div style="padding:30px;">
<el-alert :closable="false" title="menu 1-3" type="success" />
</div>
</template>
<template>
<div style="padding:30px;">
<el-alert :closable="false" title="menu 2" />
</div>
</template>
This diff is collapsed.
<template>
<div v-loading.fullscreen.lock="fullscreenLoading" class="main-article" element-loading-text="Efforts to generate PDF">
<div class="article__heading">
<div class="article__heading__title"> {{ article.title }}</div>
</div>
<div style="color: #ccc;">
This article is from Evan You on <a target="_blank" href="https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf">medium</a>
</div>
<div ref="content" class="node-article-content" v-html="article.content"/>
</div>
</template>
<script>
export default {
data() {
return {
article: '',
fullscreenLoading: true
}
},
mounted() {
this.fetchData()
},
methods: {
fetchData() {
import('./content.js').then(data => {
const { title } = data.default
document.title = title
this.article = data.default
setTimeout(() => {
this.fullscreenLoading = false
this.$nextTick(() => {
window.print()
})
}, 3000)
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
@mixin clearfix {
&:before {
display: table;
content: '';
clear: both;
}
&:after {
display: table;
content: '';
clear: both;
}
}
.main-article {
padding: 20px;
margin: 0 auto;
display: block;
width: 740px;
background: #fff;
}
.article__heading {
position: relative;
padding: 0 0 20px;
overflow: hidden;
}
.article__heading__title {
display: block;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
word-wrap: break-word;
overflow-wrap: break-word;
font-size: 32px;
line-height: 48px;
font-weight: 600;
color: #333;
overflow: hidden;
}
.node-article-content {
margin: 20px 0 0;
@include clearfix;
font-size: 16px;
color: #333;
letter-spacing: 0.5px;
line-height: 28px;
margin-bottom: 30px;
font-family: medium-content-serif-font, Georgia, Cambria, "Times New Roman", Times, serif;
&> :last-child {
margin-bottom: 0;
}
b,
strong {
font-weight: inherit;
font-weight: bolder;
}
img {
max-width: 100%;
display: block;
margin: 0 auto;
}
p {
font-weight: 400;
font-style: normal;
font-size: 21px;
line-height: 1.58;
letter-spacing: -.003em;
}
ul {
margin-bottom: 30px;
}
li {
--x-height-multiplier: 0.375;
--baseline-multiplier: 0.17;
letter-spacing: .01rem;
font-weight: 400;
font-style: normal;
font-size: 21px;
line-height: 1.58;
letter-spacing: -.003em;
margin-left: 30px;
margin-bottom: 14px;
}
a {
text-decoration: none;
background-repeat: repeat-x;
background-image: linear-gradient(to right, rgba(0, 0, 0, .84) 100%, rgba(0, 0, 0, 0) 0);
background-size: 1px 1px;
background-position: 0 calc(1em + 1px);
padding: 0 6px;
}
code {
background: rgba(0, 0, 0, .05);
padding: 3px 4px;
margin: 0 2px;
font-size: 16px;
display: inline-block;
}
img {
border: 0;
}
/* 解决 IE6-7 图片缩放锯齿问题 */
img {
-ms-interpolation-mode: bicubic;
}
blockquote {
--x-height-multiplier: 0.375;
--baseline-multiplier: 0.17;
font-family: medium-content-serif-font, Georgia, Cambria, "Times New Roman", Times, serif;
letter-spacing: .01rem;
font-weight: 400;
font-style: italic;
font-size: 21px;
line-height: 1.58;
letter-spacing: -.003em;
border-left: 3px solid rgba(0, 0, 0, .84);
padding-left: 20px;
margin-left: -23px;
padding-bottom: 2px;
}
a {
text-decoration: none;
}
h2,
h3,
h4 {
font-size: 34px;
line-height: 1.15;
letter-spacing: -.015em;
margin: 53px 0 0;
}
h4 {
font-size: 26px;
}
}
</style>
<template>
<div class="app-container">
<code style="margin-top:15px;">{{ $t('pdf.tips') }}</code>
<router-link target="_blank" to="/pdf/download">
<el-button type="primary">Click to download PDF</el-button>
</router-link>
</div>
</template>
<template>
<div>
<div style="margin-bottom:15px;">{{ $t('permission.roles') }}{{ roles }}</div>
{{ $t('permission.switchRoles') }}
<el-radio-group v-model="switchRoles">
<el-radio-button label="editor"/>
<el-radio-button label="admin"/>
</el-radio-group>
</div>
</template>
<script>
export default {
computed: {
roles() {
return this.$store.getters.roles
},
switchRoles: {
get() {
return this.roles[0]
},
set(val) {
this.$store.dispatch('ChangeRoles', val).then(() => {
this.$emit('change')
})
}
}
}
}
</script>
<template>
<div class="app-container">
<switch-roles @change="handleRolesChange" />
<div :key="key" style="margin-top:30px;">
<div>
<span v-permission="['admin']" class="permission-alert">
Only
<el-tag class="permission-tag" size="small">admin</el-tag> can see this
</span>
<el-tag v-permission="['admin']" class="permission-sourceCode" type="info">v-permission="['admin']"</el-tag>
</div>
<div>
<span v-permission="['editor']" class="permission-alert">
Only
<el-tag class="permission-tag" size="small">editor</el-tag> can see this
</span>
<el-tag v-permission="['editor']" class="permission-sourceCode" type="info">v-permission="['editor']"</el-tag>
</div>
<div>
<span v-permission="['admin','editor']" class="permission-alert">
Both
<el-tag class="permission-tag" size="small">admin</el-tag> and
<el-tag class="permission-tag" size="small">editor</el-tag> can see this
</span>
<el-tag v-permission="['admin','editor']" class="permission-sourceCode" type="info">v-permission="['admin','editor']"</el-tag>
</div>
</div>
<div :key="'checkPermission'+key" style="margin-top:60px;">
<code>
{{ $t('permission.tips') }}
<br> e.g.
</code>
<el-tabs type="border-card" style="width:550px;">
<el-tab-pane v-if="checkPermission(['admin'])" label="Admin">
Admin can see this
<el-tag class="permission-sourceCode" type="info">v-if="checkPermission(['admin'])"</el-tag>
</el-tab-pane>
<el-tab-pane v-if="checkPermission(['editor'])" label="Editor">
Editor can see this
<el-tag class="permission-sourceCode" type="info">v-if="checkPermission(['editor'])"</el-tag>
</el-tab-pane>
<el-tab-pane v-if="checkPermission(['admin','editor'])" label="Admin-OR-Editor">
Both admin or editor can see this
<el-tag class="permission-sourceCode" type="info">v-if="checkPermission(['admin','editor'])"</el-tag>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import permission from '@/directive/permission/index.js' // 权限判断指令
import checkPermission from '@/utils/permission' // 权限判断函数
import SwitchRoles from './components/SwitchRoles'
export default{
name: 'DirectivePermission',
components: { SwitchRoles },
directives: { permission },
data() {
return {
key: 1 // 为了能每次切换权限的时候重新初始化指令
}
},
methods: {
checkPermission,
handleRolesChange() {
this.key++
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-container {
/deep/ .permission-alert {
width: 320px;
margin-top: 15px;
background-color: #f0f9eb;
color: #67c23a;
padding: 8px 16px;
border-radius: 4px;
display: inline-block;
}
/deep/ .permission-sourceCode {
margin-left: 15px;
}
/deep/ .permission-tag {
background-color: #ecf5ff;
}
}
</style>
<template>
<div class="app-container">
<switch-roles @change="handleRolesChange" />
</div>
</template>
<script>
import SwitchRoles from './components/SwitchRoles'
export default{
name: 'PagePermission',
components: { SwitchRoles },
methods: {
handleRolesChange() {
this.$router.push({ path: '/permission/index?' + +new Date() })
}
}
}
</script>
......@@ -216,7 +216,7 @@ export default {
}
}
getProjects(this.listQuery).then(res => {
this.proconfigs = res.data.data
this.proconfigs = res.data
this.listLoading = false
})
},
......
<template>
<el-upload :data="dataObj" :multiple="true" :before-upload="beforeUpload" action="https://upload.qbox.me" drag>
<i class="el-icon-upload"/>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
</template>
<script>
import { getToken } from '@/api/qiniu'
// 获取七牛token 后端通过Access Key,Secret Key,bucket等生成token
// 七牛官方sdk https://developer.qiniu.com/sdk#official-sdk
export default{
data() {
return {
dataObj: { token: '', key: '' },
image_uri: [],
fileList: []
}
},
methods: {
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
resolve(true)
}).catch(err => {
console.log(err)
reject(false)
})
})
}
}
}
</script>
<script>
export default {
beforeCreate() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({ path: '/' + path, query })
},
render: function(h) {
return h() // avoid warning message
}
}
</script>
......@@ -117,14 +117,14 @@ export default {
}
}
getLog(this.listQuery).then(res => {
this.logs = res.data.data
this.logs = res.data
this.listLoading = false
})
},
getLogCount() {
getLogCount(this.listQuery).then(res => {
this.count = res.data.data
this.count = res.data
})
},
......
<template>
<div class="icons-container">
<p class="warn-content">
<a href="https://panjiachen.github.io/vue-element-admin-site/guide/advanced/icon.html" target="_blank">Add and use
</a>
</p>
<div class="icons-wrapper">
<div v-for="item of iconsMap" :key="item" @click="handleClipboard(generateIconCode(item),$event)">
<el-tooltip placement="top">
<div slot="content">
{{ generateIconCode(item) }}
</div>
<div class="icon-item">
<svg-icon :icon-class="item" class-name="disabled" />
<span>{{ item }}</span>
</div>
</el-tooltip>
</div>
</div>
</div>
</template>
<script>
import icons from './requireIcons'
import clipboard from '@/utils/clipboard'
export default {
name: 'Icons',
data() {
return {
iconsMap: icons
}
},
methods: {
generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`
},
handleClipboard(text, event) {
clipboard(text, event)
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.icons-container {
margin: 10px 20px 0;
overflow: hidden;
.icons-wrapper {
margin: 0 auto;
}
.icon-item {
margin: 20px;
height: 110px;
text-align: center;
width: 110px;
float: left;
font-size: 30px;
color: #24292e;
cursor: pointer;
}
span {
display: block;
font-size: 24px;
margin-top: 10px;
}
.disabled{
pointer-events: none;
}
}
</style>
const req = require.context('../../icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()
const re = /\.\/(.*)\.svg/
const icons = requireAll(req).map(i => {
return i.match(re)[1]
})
export default icons
<template>
<el-table :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column
v-loading="loading"
align="center"
label="ID"
width="65"
element-loading-text="请给我点时间!">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<span>{{ scope.row.title }}</span>
<el-tag>{{ scope.row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column width="110px" align="center" label="Author">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column width="120px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star"/>
</template>
</el-table-column>
<el-table-column align="center" label="Readings" width="95">
<template slot-scope="scope">
<span>{{ scope.row.pageviews }}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
props: {
type: {
type: String,
default: 'CN'
}
},
data() {
return {
list: null,
listQuery: {
page: 1,
limit: 5,
type: this.type,
sort: '+id'
},
loading: false
}
},
created() {
this.getList()
},
methods: {
getList() {
this.loading = true
this.$emit('create') // for test
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.loading = false
})
}
}
}
</script>
<template>
<div class="tab-container">
<el-tag>mounted times :{{ createdTimes }}</el-tag>
<el-alert :closable="false" style="width:200px;display:inline-block;vertical-align: middle;margin-left:30px;" title="Tab with keep-alive" type="success"/>
<el-tabs v-model="activeName" style="margin-top:15px;" type="border-card">
<el-tab-pane v-for="item in tabMapOptions" :label="item.label" :key="item.key" :name="item.key">
<keep-alive>
<tab-pane v-if="activeName==item.key" :type="item.key" @create="showCreatedTimes"/>
</keep-alive>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import tabPane from './components/tabPane'
export default {
name: 'Tab',
components: { tabPane },
data() {
return {
tabMapOptions: [
{ label: 'China', key: 'CN' },
{ label: 'USA', key: 'US' },
{ label: 'Japan', key: 'JP' },
{ label: 'Eurozone', key: 'EU' }
],
activeName: 'CN',
createdTimes: 0
}
},
methods: {
showCreatedTimes() {
this.createdTimes = this.createdTimes + 1
}
}
}
</script>
<style scoped>
.tab-container{
margin: 30px;
}
</style>
This diff is collapsed.
<template>
<div class="app-container">
<!-- Note that row-key is necessary to get a correct row order. -->
<el-table v-loading="listLoading" :data="list" row-key="id" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="65">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<span>{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column width="110px" align="center" label="Author">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="icon-star"/>
</template>
</el-table-column>
<el-table-column align="center" label="Readings" width="95">
<template slot-scope="scope">
<span>{{ scope.row.pageviews }}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="Drag" width="80">
<template slot-scope="{}">
<svg-icon class="drag-handler" icon-class="drag"/>
</template>
</el-table-column>
</el-table>
<!-- $t is vue-i18n global function to translate lang (lang in @/lang) -->
<div class="show-d">{{ $t('table.dragTips1') }} : &nbsp; {{ oldList }}</div>
<div class="show-d">{{ $t('table.dragTips2') }} : {{ newList }}</div>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import Sortable from 'sortablejs'
export default {
name: 'DragTable',
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
},
sortable: null,
oldList: [],
newList: []
}
},
created() {
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
this.oldList = this.list.map(v => v.id)
this.newList = this.oldList.slice()
this.$nextTick(() => {
this.setSort()
})
})
},
setSort() {
const el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
setData: function(dataTransfer) {
dataTransfer.setData('Text', '')
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
},
onEnd: evt => {
const targetRow = this.list.splice(evt.oldIndex, 1)[0]
this.list.splice(evt.newIndex, 0, targetRow)
// for show the changes, you can delete in you code
const tempIndex = this.newList.splice(evt.oldIndex, 1)[0]
this.newList.splice(evt.newIndex, 0, tempIndex)
}
})
}
}
}
</script>
<style>
.sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
</style>
<style scoped>
.icon-star{
margin-right:2px;
}
.drag-handler{
width: 20px;
height: 20px;
cursor: pointer;
}
.show-d{
margin-top: 15px;
}
</style>
<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" border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180"/>
<el-table-column v-for="fruit in formThead" :key="fruit" :label="fruit">
<template slot-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 // 默认表头 Default header
}
},
watch: {
checkboxVal(valArr) {
this.formThead = this.formTheadOptions.filter(i => valArr.indexOf(i) >= 0)
this.key = this.key + 1// 为了保证table 每次都会重渲 In order to ensure the table will be re-rendered each time
}
}
}
</script>
<template>
<div class="app-container">
<div style="margin:0 0 5px 20px">{{ $t('table.dynamicTips1') }}</div>
<fixed-thead/>
<div style="margin:30px 0 5px 20px">{{ $t('table.dynamicTips2') }}</div>
<unfixed-thead/>
</div>
</template>
<script>
import fixedThead from './fixedThead'
import unfixedThead from './unfixedThead'
export default {
name: 'DynamicTable',
components: { fixedThead, unfixedThead }
}
</script>
<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" border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180"/>
<el-table-column v-for="fruit in formThead" :key="fruit" :label="fruit">
<template slot-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>
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="ID" width="80">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Date">
<template slot-scope="scope">
<span>{{ scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}') }}</span>
</template>
</el-table-column>
<el-table-column width="120px" align="center" label="Author">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column width="100px" label="Importance">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" :key="n" icon-class="star" class="meta-item__icon"/>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="Title">
<template slot-scope="scope">
<template v-if="scope.row.edit">
<el-input v-model="scope.row.title" class="edit-input" size="small"/>
<el-button class="cancel-btn" size="small" icon="el-icon-refresh" type="warning" @click="cancelEdit(scope.row)">cancel</el-button>
</template>
<span v-else>{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="Actions" width="120">
<template slot-scope="scope">
<el-button v-if="scope.row.edit" type="success" size="small" icon="el-icon-circle-check-outline" @click="confirmEdit(scope.row)">Ok</el-button>
<el-button v-else type="primary" size="small" icon="el-icon-edit" @click="scope.row.edit=!scope.row.edit">Edit</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'InlineEditTable',
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
}
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
const items = response.data.items
this.list = items.map(v => {
this.$set(v, 'edit', false) // https://vuejs.org/v2/guide/reactivity.html
v.originalTitle = v.title // will be used when user click the cancel botton
return v
})
this.listLoading = false
})
},
cancelEdit(row) {
row.title = row.originalTitle
row.edit = false
this.$message({
message: 'The title has been restored to the original value',
type: 'warning'
})
},
confirmEdit(row) {
row.edit = false
row.originalTitle = row.title
this.$message({
message: 'The title has been edited',
type: 'success'
})
}
}
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
</style>
/**
* @Author: jianglei
* @Date: 2017-10-12 12:06:49
*/
'use strict'
import Vue from 'vue'
export default function treeToArray(data, expandAll, parent, level, item) {
const marLTemp = []
let tmp = []
Array.from(data).forEach(function(record) {
if (record._expanded === undefined) {
Vue.set(record, '_expanded', expandAll)
}
let _level = 1
if (level !== undefined && level !== null) {
_level = level + 1
}
Vue.set(record, '_level', _level)
// 如果有父元素
if (parent) {
Vue.set(record, 'parent', parent)
// 如果父元素有偏移量,需要计算在this的偏移量中
// 偏移量还与前面同级元素有关,需要加上前面所有元素的长度和
if (!marLTemp[_level]) {
marLTemp[_level] = 0
}
Vue.set(record, '_marginLeft', marLTemp[_level] + parent._marginLeft)
Vue.set(record, '_width', record[item] / parent[item] * parent._width)
// 在本次计算过偏移量后加上自己长度,以供下一个元素使用
marLTemp[_level] += record._width
} else {
// 如果为根
// 初始化偏移量存储map
marLTemp[record.id] = []
// map中是一个数组,存储的是每级的长度和
// 初始情况下为0
marLTemp[record.id][_level] = 0
Vue.set(record, '_marginLeft', 0)
Vue.set(record, '_width', 1)
}
tmp.push(record)
if (record.children && record.children.length > 0) {
const children = treeToArray(record.children, expandAll, record, _level, item)
tmp = tmp.concat(children)
}
})
return tmp
}
<template>
<div class="app-container">
<el-tag style="margin-bottom:20px;">
<a href="https://github.com/PanJiaChen/vue-element-admin/tree/master/src/components/TreeTable" target="_blank">Documentation</a>
</el-tag>
<tree-table :data="data" :eval-func="func" :eval-args="args" :expand-all="expandAll" border>
<el-table-column label="事件">
<template slot-scope="scope">
<span style="color:sandybrown">{{ scope.row.event }}</span>
<el-tag>{{ scope.row.timeLine+'ms' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="时间线">
<template slot-scope="scope">
<el-tooltip :content="scope.row.timeLine+'ms'" effect="dark" placement="left">
<div class="processContainer">
<div
:style="{ width:scope.row._width * 500+'px',
background:scope.row._width>0.5?'rgba(233,0,0,.5)':'rgba(0,0,233,0.5)',
marginLeft:scope.row._marginLeft * 500+'px' }"
class="process">
<span style="display:inline-block"/>
</div>
</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button type="text" @click="message(scope.row)">点击</el-button>
</template>
</el-table-column>
</tree-table>
</div>
</template>
<script>
/**
Auth: Lei.j1ang
Created: 2018/1/19-14:54
*/
import treeTable from '@/components/TreeTable'
import treeToArray from './customEval'
export default {
name: 'CustomTreeTableDemo',
components: { treeTable },
data() {
return {
func: treeToArray,
expandAll: false,
data:
{
id: 1,
event: '事件1',
timeLine: 100,
comment: '',
children: [
{
id: 2,
event: '事件2',
timeLine: 10,
comment: ''
},
{
id: 3,
event: '事件3',
timeLine: 90,
comment: '',
children: [
{
id: 4,
event: '事件4',
timeLine: 5,
comment: ''
},
{
id: 5,
event: '事件5',
timeLine: 10,
comment: ''
},
{
id: 6,
event: '事件6',
timeLine: 75,
comment: '',
children: [
{
id: 7,
event: '事件7',
timeLine: 50,
comment: '',
children: [
{
id: 71,
event: '事件71',
timeLine: 25,
comment: 'xx'
},
{
id: 72,
event: '事件72',
timeLine: 5,
comment: 'xx'
},
{
id: 73,
event: '事件73',
timeLine: 20,
comment: 'xx'
}
]
},
{
id: 8,
event: '事件8',
timeLine: 25,
comment: ''
}
]
}
]
}
]
},
args: [null, null, 'timeLine']
}
},
methods: {
message(row) {
this.$message.info(row.event)
}
}
}
</script>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment