Compare commits

...

21 Commits

Author SHA1 Message Date
Pan
44b180a7b3 [release] 3.1.1 2017-12-06 14:19:06 +08:00
Pan
acebaeae0c perf(i18n): add generateTitle to utils 2017-12-06 14:18:28 +08:00
Pan
d09923ff4f fix[sidebar]:fixed when sidebar item only has one children but nested #327 2017-12-05 18:42:15 +08:00
Pan
fc1a13d10b [release] 3.1.0 2017-12-05 11:41:57 +08:00
Pan
705b9ccefd add(tags-view): moveToCurrentTag 2017-12-05 11:15:39 +08:00
Pan
c84964d77c perf(sidebar): rm unique-opened 2017-12-04 17:33:58 +08:00
Pan
8f9de5c641 perf:refine dashboard responsive 2017-12-04 14:28:26 +08:00
Pan
96a7035b1d doc:add new article 2017-12-01 17:48:22 +08:00
Pan
7f2bd58035 doc:add new article 2017-12-01 17:47:08 +08:00
Pan
b4b9d166c4 perf:el-agination add background 2017-11-30 18:18:36 +08:00
Pan
da24d61008 dump: update element-ui version 2017-11-30 18:17:54 +08:00
Pan
8ce571b61d perf:rm mock api timeout 2017-11-30 18:04:00 +08:00
Pan
38192b828d perf:refine complexTable demo 2017-11-30 18:02:31 +08:00
Pan
72f653dcc7 perf:refine article demo 2017-11-30 16:55:46 +08:00
donng
4dfa878fc6 index首页代码修改 2017-11-30 15:46:41 +08:00
Pan
cef1f11931 fix:fixed sticky component bug in keep-alive 2017-11-29 15:57:47 +08:00
Pan
443c7aed3c style:fixed class conflict 2017-11-29 14:17:37 +08:00
Pan
0f5304112c fix:fixed theme-picker bug 2017-11-29 14:11:25 +08:00
Pan
dd3e22d5a1 perf:change i18n-demo to resizeable 2017-11-29 11:33:11 +08:00
Pan
d99cb068aa fix:fixed tags-view bug 2017-11-29 11:22:25 +08:00
Pan
394f366aac doc:update readme 2017-11-29 11:21:48 +08:00
27 changed files with 310 additions and 241 deletions

View File

@@ -5,15 +5,15 @@
# vue-element-admin
[![vue](https://img.shields.io/badge/vue-2.5.9-brightgreen.svg)](https://github.com/vuejs/vue)
[![element-ui](https://img.shields.io/badge/element--ui-2.0.5-brightgreen.svg)](https://github.com/ElemeFE/element)
[![element-ui](https://img.shields.io/badge/element--ui-2.0.7-brightgreen.svg)](https://github.com/ElemeFE/element)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg)]()
A magical vue admin.
**A magical vue admin.**
- [线上地址](http://panjiachen.github.io/vue-element-admin)
- [文档地址](https://panjiachen.github.io/vue-element-admin-site/#/)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/)
- [English Document](https://github.com/PanJiaChen/vue-element-admin/blob/master/README-en.md)
@@ -38,14 +38,15 @@ A magical vue admin.
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手带你封装一个vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
相应需求开了一个qq群 `591724180` 方便大家交流
或者可以加入该 **[圈子](https://jianshiapp.com/circles/1209)** 讨论问题
**如有问题请先看上述文章和Wiki,若不能满足,欢迎 issue 和 pr**
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
**本项目并不是一个脚手架,更倾向于是一个集成解决方案**
@@ -96,7 +97,8 @@ A magical vue admin.
# 安装依赖
npm install
   //or # 建议不要用cnpm  安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
   
//or # 建议不要用cnpm  安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 本地开发 开启服务
@@ -113,6 +115,8 @@ A magical vue admin.
npm run build:prod
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/#/)
## 目录结构
```shell
├── build // 构建相关  
@@ -149,9 +153,6 @@ A magical vue admin.
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## 状态管理
后台只有user和app配置相关状态使用vuex存在全局其它数据都由每个业务页面自己管理。
## [查看更多demo](http://panjiachen.github.io/vue-element-admin)
![](https://wpimg.wallstcn.com/1bc334a6-32a8-4f29-a037-ac3f5ce32588.png)

View File

@@ -1,6 +1,6 @@
{
"name": "vue-element-admin",
"version": "3.0.0",
"version": "3.1.1",
"description": "A Vue.js admin",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
@@ -18,7 +18,7 @@
"codemirror": "5.31.0",
"dropzone": "5.2.0",
"echarts": "3.8.5",
"element-ui": "2.0.5",
"element-ui": "2.0.7",
"file-saver": "1.3.3",
"font-awesome": "4.7.0",
"js-cookie": "2.2.0",

View File

@@ -23,3 +23,18 @@ export function fetchPv(pv) {
})
}
export function createArticle(data) {
return request({
url: '/article/create',
method: 'post',
data
})
}
export function updateArticle(data) {
return request({
url: '/article/update',
method: 'post',
data
})
}

View File

@@ -10,6 +10,8 @@
</template>
<script>
import { generateTitle } from '@/utils/i18n'
export default {
created() {
this.getBreadcrumb()
@@ -25,6 +27,7 @@ export default {
}
},
methods: {
generateTitle,
getBreadcrumb() {
let matched = this.$route.matched.filter(item => item.name)
const first = matched[0]
@@ -32,9 +35,6 @@ export default {
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
}
this.levelList = matched
},
generateTitle(title) {
return this.$t('route.' + title)
}
}
}

View File

@@ -7,6 +7,8 @@
</template>
<script>
const padding = 15 // tag's padding
export default {
name: 'scrollPane',
data() {
@@ -21,19 +23,37 @@ export default {
const $containerWidth = $container.offsetWidth
const $wrapper = this.$refs.scrollWrapper
const $wrapperWidth = $wrapper.offsetWidth
if (e.wheelDelta > 0) {
this.left = Math.min(0, this.left + e.wheelDelta)
} else {
if ($containerWidth - 100 < $wrapperWidth) {
if (this.left < -($wrapperWidth - $containerWidth + 100)) {
if ($containerWidth - padding < $wrapperWidth) {
if (this.left < -($wrapperWidth - $containerWidth + padding)) {
this.left = this.left
} else {
this.left = Math.max(this.left + e.wheelDelta, $containerWidth - $wrapperWidth - 100)
this.left = Math.max(this.left + e.wheelDelta, $containerWidth - $wrapperWidth - padding)
}
} else {
this.left = 0
}
}
},
moveToTarget($target) {
const $container = this.$refs.scrollContainer
const $containerWidth = $container.offsetWidth
const $targetLeft = $target.offsetLeft
const $targetWidth = $target.offsetWidth
if ($targetLeft < -this.left) {
// tag in the left
this.left = -$targetLeft + padding
} else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {
// tag in the current view
// eslint-disable-line
} else {
// tag in the right
this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)
}
}
}
}
@@ -43,6 +63,7 @@ export default {
.scroll-container {
white-space: nowrap;
position: relative;
overflow: hidden;
.scroll-wrapper {
position: absolute;
}

View File

@@ -35,7 +35,7 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" >
$n: 5; //和items.length 相同
$n: 6; //和items.length 相同
$t: .1s;
.share-dropdown-menu {
width: 250px;

View File

@@ -33,9 +33,18 @@ export default {
height: undefined,
child: null,
stickyHeight: 0
}
},
mounted() {
this.height = this.$el.getBoundingClientRect().height
window.addEventListener('scroll', this.handleScroll)
},
activated() {
this.handleScroll()
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
sticky() {
if (this.active) {
@@ -62,13 +71,6 @@ export default {
}
this.reset()
}
},
mounted() {
this.height = this.$el.getBoundingClientRect().height
window.addEventListener('scroll', this.handleScroll)
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
}
}
</script>

View File

@@ -7,6 +7,7 @@
<script>
import { getVersion } from '@/utils/index.js'
const version = getVersion('element-ui') // element-ui version from package.json
const ORIGINAL_THEME = '#409EFF' // default color

View File

@@ -60,5 +60,11 @@ export default {
status: 'published',
tags: [],
title: 'vue-element-admin'
}),
createArticle: () => ({
data: 'success'
}),
updateArticle: () => ({
data: 'success'
})
}

View File

@@ -4,9 +4,9 @@ import articleAPI from './article'
import remoteSearchAPI from './remoteSearch'
import transactionAPI from './transaction'
Mock.setup({
timeout: '350-600'
})
// Mock.setup({
// timeout: '350-600'
// })
// 登录相关
Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
@@ -17,6 +17,8 @@ Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)
Mock.mock(/\/article\/pv/, 'get', articleAPI.getPv)
Mock.mock(/\/article\/create/, 'post', articleAPI.createArticle)
Mock.mock(/\/article\/update/, 'post', articleAPI.updateArticle)
// 搜索相关
Mock.mock(/\/search\/user/, 'get', remoteSearchAPI.searchUser)

View File

@@ -18,8 +18,8 @@
.small-padding {
.cell {
padding-left: 8px;
padding-right: 8px;
padding-left: 5px;
padding-right: 5px;
}
}

3
src/utils/i18n.js Normal file
View File

@@ -0,0 +1,3 @@
export function generateTitle(title) {
return this.$t('route.' + title) // $t :this method from vue-i18n ,inject in @/lang/index.js
}

View File

@@ -266,9 +266,8 @@ export function deepClone(source) {
return targetObj
}
// get dependencies verison from package.json by webpack.DefinePlugin
// get dependencies verison from package.json
export function getVersion(name) {
import('../../package').then(p => {
return p.dependencies[name]
})
const p = require('../../package')
return p.dependencies[name]
}

View File

@@ -86,7 +86,7 @@
<div slot="header" class="clearfix">
<span>Share</span>
</div>
<div class="component-item" style="height:360px;">
<div class="component-item" style="height:420px;">
<dropdown-menu style="margin:0 auto;" title='系列文章' :items='articleList'></dropdown-menu>
</div>
</el-card>
@@ -134,7 +134,8 @@ export default {
{ title: '登录权限篇', href: 'https://segmentfault.com/a/1190000009506097' },
{ title: '实战篇', href: 'https://segmentfault.com/a/1190000009762198' },
{ title: 'vueAdmin-template 篇', href: 'https://segmentfault.com/a/1190000010043013' },
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' }
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
{ title: '优雅的使用 icon', href: 'https://segmentfault.com/a/https://segmentfault.com/a/1190000012213278' }
]
}
}

View File

@@ -1,5 +1,5 @@
<template>
<el-card class="box-card" style="margin-left:8px;">
<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>
@@ -62,50 +62,57 @@ export default {
</script>
<style rel="stylesheet/scss" lang="scss" >
.box-card {
.box-card-component{
.el-card__header {
padding: 0px!important;
}
}
</style>
<style rel="stylesheet/scss" lang="scss" scoped>
.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%);
.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;
}
}
}
.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;
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<el-row class="panel-group" :gutter="40">
<el-col :span="6">
<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" />
@@ -11,7 +11,7 @@
</div>
</div>
</el-col>
<el-col :span="6">
<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" />
@@ -22,7 +22,7 @@
</div>
</div>
</el-col>
<el-col :span="6">
<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" />
@@ -33,7 +33,7 @@
</div>
</div>
</el-col>
<el-col :span="6">
<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-shoppingCard">
<svg-icon icon-class="shoppingCard" class-name="card-panel-icon" />
@@ -64,7 +64,10 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped>
.panel-group {
margin-top: 20px;
margin-top: 18px;
.card-panel-col{
margin-bottom: 32px;
}
.card-panel {
height: 108px;
cursor: pointer;
@@ -132,5 +135,4 @@ export default {
}
}
}
</style>

View File

@@ -246,16 +246,12 @@
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: -20px;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
margin: 3px;
font-size: 12px;
padding: 3px 7px;
text-decoration: none;

View File

@@ -1,6 +1,6 @@
<template>
<el-table :data="list" style="width: 100%;padding-top: 15px;" >
<el-table-column label="Order_No">
<el-table :data="list" style="width: 100%;padding-top: 15px;">
<el-table-column label="Order_No" show-overflow-tooltip>
<template slot-scope="scope">
{{scope.row.order_no}}
</template>
@@ -10,16 +10,11 @@
¥{{scope.row.price | toThousandslsFilter}}
</template>
</el-table-column>
<el-table-column label="Status" width="100" align="center">
<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>
<el-tag :type="scope.row.status | statusFilter"> {{scope.row.status}}</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="Username" width="135" align="center">
<template slot-scope="scope">
{{scope.row.username}}
</template>
</el-table-column> -->
</el-table>
</template>

View File

@@ -4,36 +4,36 @@
<panel-group @handleSetLineChartData="handleSetLineChartData"></panel-group>
<el-row style="margin-top:30px;background:#fff;padding:15px 15px 0;">
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<line-chart :chart-data="lineChartData"></line-chart>
</el-row>
<el-row style="margin-top:30px;" :gutter="30">
<el-col :span="8">
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<raddar-chart></raddar-chart>
</div>
</el-col>
<el-col :span="8">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<pie-chart></pie-chart>
</div>
</el-col>
<el-col :span="8">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<bar-chart></bar-chart>
</div>
</el-col>
</el-row>
<el-row style="margin-top:30px;">
<el-col :span="12" style="padding-right:8px;">
<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></transaction-table>
</el-col>
<el-col :span="6">
<todo-list style="margin:0 8px;"></todo-list>
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}">
<todo-list></todo-list>
</el-col>
<el-col :span="6">
<el-col :xs="{span: 12}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 5}">
<box-card></box-card>
</el-col>
</el-row>
@@ -99,11 +99,12 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 30px;
padding: 32px;
background-color: rgb(240, 242, 245);
.chart-wrapper {
background: #fff;
padding: 15px 15px 0;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
</style>

View File

@@ -6,8 +6,8 @@
<script>
import { mapGetters } from 'vuex'
import adminDashboard from './admin/index'
import editorDashboard from './editor/index'
import adminDashboard from './admin'
import editorDashboard from './editor'
export default {
name: 'dashboard',
@@ -23,10 +23,9 @@ export default {
])
},
created() {
if (this.roles.indexOf('admin') >= 0) {
return
if (!this.roles.includes('admin')) {
this.currentRole = 'editorDashboard'
}
this.currentRole = 'editorDashboard'
}
}
</script>

View File

@@ -19,7 +19,8 @@ export default {
{ title: '登录权限篇', href: 'https://segmentfault.com/a/1190000009506097' },
{ title: '实战篇', href: 'https://segmentfault.com/a/1190000009762198' },
{ title: 'vueAdmin-template 篇', href: 'https://segmentfault.com/a/1190000010043013' },
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' }
{ title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
{ title: '优雅的使用 icon', href: 'https://segmentfault.com/a/https://segmentfault.com/a/1190000012213278' }
]
}
}

View File

@@ -3,127 +3,111 @@
<div class="filter-container">
<el-input @keyup.enter.native="handleFilter" style="width: 200px;" class="filter-item" placeholder="标题" v-model="listQuery.title">
</el-input>
<el-select clearable style="width: 90px" class="filter-item" v-model="listQuery.importance" placeholder="重要性">
<el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item">
</el-option>
</el-select>
<el-select clearable class="filter-item" style="width: 130px" v-model="listQuery.type" placeholder="类型">
<el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name+'('+item.key+')'" :value="item.key">
</el-option>
</el-select>
<el-select @change='handleFilter' style="width: 120px" class="filter-item" v-model="listQuery.sort" placeholder="排序">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key">
</el-option>
</el-select>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<el-button class="filter-item" style="margin-left: 10px;" @click="handleCreate" type="primary" icon="el-icon-edit">添加</el-button>
<el-button class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
<el-button class="filter-item" type="primary" v-waves icon="el-icon-download" @click="handleDownload">导出</el-button>
<el-checkbox class="filter-item" style='margin-left:15px;' @change='tableKey=tableKey+1' v-model="showAuditor">显示审核人</el-checkbox>
</div>
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row style="width: 100%">
<el-table :key='tableKey' :data="list" v-loading="listLoading" element-loading-text="给我一点时间" border fit highlight-current-row
style="width: 100%">
<el-table-column align="center" label="序号" width="65">
<template slot-scope="scope">
<span>{{scope.row.id}}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="时间">
<template slot-scope="scope">
<span>{{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}</span>
</template>
</el-table-column>
<el-table-column min-width="150px" label="标题">
<template slot-scope="scope">
<span class="link-type" @click="handleUpdate(scope.row)">{{scope.row.title}}</span>
<el-tag>{{scope.row.type | typeFilter}}</el-tag>
</template>
</el-table-column>
<el-table-column width="110px" align="center" label="作者">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="110px" v-if='showAuditor' align="center" label="审核人">
<template slot-scope="scope">
<span style='color:red;'>{{scope.row.auditor}}</span>
</template>
</el-table-column>
<el-table-column width="80px" label="重要性">
<template slot-scope="scope">
<svg-icon v-for="n in +scope.row.importance" icon-class="star" class="meta-item__icon" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column align="center" label="阅读数" width="95">
<template slot-scope="scope">
<span class="link-type" @click='handleFetchPv(scope.row.pageviews)'>{{scope.row.pageviews}}</span>
<span v-if="scope.row.pageviews" class="link-type" @click='handleFetchPv(scope.row.pageviews)'>{{scope.row.pageviews}}</span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="100">
<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="操作" width="150">
<el-table-column align="center" label="操作" width="220" class-name="small-padding">
<template slot-scope="scope">
<el-button v-if="scope.row.status!='published'" size="small" type="success" @click="handleModifyStatus(scope.row,'published')">发布
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
<el-button v-if="scope.row.status!='published'" size="mini" type="success" @click="handleModifyStatus(scope.row,'published')">发布
</el-button>
<el-button v-if="scope.row.status!='draft'" size="small" @click="handleModifyStatus(scope.row,'draft')">草稿
<el-button v-if="scope.row.status!='draft'" size="mini" @click="handleModifyStatus(scope.row,'draft')">草稿
</el-button>
<el-button v-if="scope.row.status!='deleted'" size="small" type="danger" @click="handleModifyStatus(scope.row,'deleted')">删除
<el-button v-if="scope.row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">删除
</el-button>
</template>
</el-table-column>
</el-table>
<div v-show="!listLoading" class="pagination-container">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="listQuery.page"
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="listQuery.page"
:page-sizes="[10,20,30, 50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form class="small-space" :model="temp" label-position="left" label-width="70px" style='width: 400px; margin-left:50px;'>
<el-form-item label="类型">
<el-form :rules="rules" ref="dataForm" :model="temp" label-position="left" label-width="70px" style='width: 400px; margin-left:50px;'>
<el-form-item label="类型" prop="type">
<el-select class="filter-item" v-model="temp.type" placeholder="请选择">
<el-option v-for="item in calendarTypeOptions" :key="item.key" :label="item.display_name" :value="item.key">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="时间" prop="timestamp">
<el-date-picker v-model="temp.timestamp" type="datetime" placeholder="选择日期时间">
</el-date-picker>
</el-form-item>
<el-form-item label="标题" prop="title">
<el-input v-model="temp.title"></el-input>
</el-form-item>
<el-form-item label="状态">
<el-select class="filter-item" v-model="temp.status" placeholder="请选择">
<el-option v-for="item in statusOptions" :key="item" :label="item" :value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="时间">
<el-date-picker v-model="temp.timestamp" type="datetime" placeholder="选择日期时间">
</el-date-picker>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="temp.title"></el-input>
</el-form-item>
<el-form-item label="重要性">
<el-rate style="margin-top:8px;" v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max='3'></el-rate>
</el-form-item>
<el-form-item label="点评">
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4}" placeholder="请输入内容" v-model="temp.remark">
</el-input>
@@ -131,8 +115,8 @@
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="create"> </el-button>
<el-button v-else type="primary" @click="update"> </el-button>
<el-button v-if="dialogStatus=='create'" type="primary" @click="createData"> </el-button>
<el-button v-else type="primary" @click="updateData"> </el-button>
</div>
</el-dialog>
@@ -150,8 +134,8 @@
</template>
<script>
import { fetchList, fetchPv } from '@/api/article'
import waves from '@/directive/waves/index.js' // 水波纹指令
import { fetchList, fetchPv, createArticle, updateArticle } from '@/api/article'
import waves from '@/directive/waves' // 水波纹指令
import { parseTime } from '@/utils'
const calendarTypeOptions = [
@@ -161,7 +145,7 @@ const calendarTypeOptions = [
{ key: 'EU', display_name: '欧元区' }
]
// arr to obj
// arr to obj ,such as { CN : "中国", US : "美国" }
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name
return acc
@@ -174,6 +158,7 @@ export default {
},
data() {
return {
tableKey: 0,
list: null,
total: null,
listLoading: true,
@@ -185,19 +170,20 @@ export default {
type: undefined,
sort: '+id'
},
temp: {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
type: '',
status: 'published'
},
importanceOptions: [1, 2, 3],
calendarTypeOptions,
sortOptions: [{ label: '按ID升序列', key: '+id' }, { label: '按ID降序', key: '-id' }],
statusOptions: ['published', 'draft', 'deleted'],
showAuditor: false,
temp: {
id: undefined,
importance: 1,
remark: '',
timestamp: new Date(),
title: '',
type: '',
status: 'published'
},
dialogFormVisible: false,
dialogStatus: '',
textMap: {
@@ -206,8 +192,11 @@ export default {
},
dialogPvVisible: false,
pvData: [],
showAuditor: false,
tableKey: 0
rules: {
type: [{ required: true, message: 'type is required', trigger: 'change' }],
timestamp: [{ type: 'date', required: true, message: 'timestamp is required', trigger: 'change' }],
title: [{ required: true, message: 'title is required', trigger: 'blur' }]
}
}
},
filters: {
@@ -247,15 +236,6 @@ export default {
this.listQuery.page = val
this.getList()
},
timeFilter(time) {
if (!time[0]) {
this.listQuery.start = undefined
this.listQuery.end = undefined
return
}
this.listQuery.start = parseInt(+time[0] / 1000)
this.listQuery.end = parseInt((+time[1] + 3600 * 1000 * 24) / 1000)
},
handleModifyStatus(row, status) {
this.$message({
message: '操作成功',
@@ -263,15 +243,75 @@ export default {
})
row.status = status
},
resetTemp() {
this.temp = {
id: undefined,
importance: 1,
remark: '',
timestamp: new Date(),
title: '',
status: 'published',
type: ''
}
},
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.temp.id = parseInt(Math.random() * 100) + 1024 // mock a id
this.temp.author = '原创作者'
createArticle(this.temp).then(() => {
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row)
this.temp = Object.assign({}, row) // copy obj
this.temp.timestamp = new Date(this.temp.timestamp)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const tempData = Object.assign({}, this.temp)
tempData.timestamp = +new Date(tempData.timestamp) // change Thu Nov 30 2017 16:41:05 GMT+0800 (CST) to 1512031311464
updateArticle(tempData).then(() => {
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.temp)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
})
}
})
},
handleDelete(row) {
this.$notify({
@@ -283,47 +323,6 @@ export default {
const index = this.list.indexOf(row)
this.list.splice(index, 1)
},
create() {
this.temp.id = parseInt(Math.random() * 100) + 1024
this.temp.timestamp = +new Date()
this.temp.author = '原创作者'
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
},
update() {
this.temp.timestamp = +this.temp.timestamp
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.temp)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
},
resetTemp() {
this.temp = {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
status: 'published',
type: ''
}
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData

View File

@@ -133,6 +133,7 @@ import { fetchArticle } from '@/api/article'
import { userSearch } from '@/api/remoteSearch'
const defaultForm = {
status: 'draft',
title: '', // 文章题目
content: '', // 文章内容
content_short: '', // 文章摘要

View File

@@ -1,6 +1,6 @@
<template>
<div class="app-container">
<el-card class="box-card">
<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>
@@ -13,29 +13,30 @@
<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 50px 50px;">
<el-row :gutter="20" style="margin:100px 15px 50px;">
<el-col :span="12">
<div class="block">
<el-date-picker v-model="date" type="date" :placeholder="$t('i18nView.datePlaceholder')"></el-date-picker>
</div>
<div class="block">
<el-pagination :current-page="currentPage" :page-sizes="[100, 200, 300, 400]" :page-size="100" layout="total, sizes, prev, pager, next, jumper"
<el-pagination background :current-page="currentPage" :page-sizes="[100, 200, 300, 400]" :page-size="100" layout="total, sizes, prev, pager, next"
:total="400">
</el-pagination>
</div>
<div class="block">
<el-button size="small">{{$t('i18nView.default')}}</el-button>
<el-button size="small" type="primary">{{$t('i18nView.primary')}}</el-button>
<el-button size="small" type="success">{{$t('i18nView.success')}}</el-button>
<el-button size="small" type="info">{{$t('i18nView.info')}}</el-button>
<el-button size="small" type="warning">{{$t('i18nView.warning')}}</el-button>
<el-button size="small" type="danger">{{$t('i18nView.danger')}}</el-button>
<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">
<el-table :data="tableData" fit highlight-current-row border style="width: 100%">
<el-table-column prop="date" :label="$t('i18nView.tableDate')" width="180"></el-table-column>
<el-table-column prop="name" :label="$t('i18nView.tableName')" width="180"></el-table-column>
<el-table-column prop="name" :label="$t('i18nView.tableName')" width="100" align="center"></el-table-column>
<el-table-column prop="date" :label="$t('i18nView.tableDate')" width="120" align="center"></el-table-column>
<el-table-column prop="address" :label="$t('i18nView.tableAddress')"></el-table-column>
</el-table>
</el-col>
@@ -100,7 +101,10 @@ export default {
width: 600px;
margin: 20px auto;
}
.item-btn{
margin-bottom: 15px;
margin-left: 0px;
}
.block {
padding: 25px;
}

View File

@@ -1,21 +1,21 @@
<template>
<div class="menu-wrapper">
<template v-for="item in routes">
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<router-link v-if="!item.hidden&&item.children&&item.children.length===1" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name">
<router-link v-if="item.children.length===1 && !item.children[0].children" :to="item.path+'/'+item.children[0].path" :key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" class='submenu-title-noDropdown'>
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if="item.children[0].meta&&item.children[0].meta.title">{{generateTitle(item.children[0].meta.title)}}</span>
</el-menu-item>
</router-link>
<el-submenu v-if="!item.hidden&&item.children&&item.children.length>1" :index="item.name||item.path" :key="item.name">
<el-submenu v-else :index="item.name||item.path" :key="item.name">
<template slot="title">
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<span v-if="item.meta&&item.meta.title">{{generateTitle(item.meta.title)}}</span>
</template>
<template v-if="!child.hidden" v-for="child in item.children">
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
@@ -32,6 +32,8 @@
</template>
<script>
import { generateTitle } from '@/utils/i18n'
export default {
name: 'SidebarItem',
props: {
@@ -40,9 +42,7 @@ export default {
}
},
methods: {
generateTitle(title) {
return this.$t('route.' + title)
}
generateTitle
}
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<scroll-bar>
<el-menu mode="vertical" unique-opened :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#fff" active-text-color="#409EFF">
<el-menu mode="vertical" :default-active="$route.path" :collapse="isCollapse" background-color="#304156" text-color="#fff" active-text-color="#409EFF">
<sidebar-item :routes="permission_routers"></sidebar-item>
</el-menu>
</scroll-bar>

View File

@@ -1,7 +1,7 @@
<template>
<scroll-pane class='tags-view-container'>
<router-link class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path":key="tag.path">
{{$t('route.'+tag.title)}}
<scroll-pane class='tags-view-container' ref='scrollPane'>
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path":key="tag.path">
{{generateTitle(tag.title)}}
<span class='el-icon-close' @click='closeViewTags(tag,$event)'></span>
</router-link>
</scroll-pane>
@@ -9,6 +9,7 @@
<script>
import ScrollPane from '@/components/ScrollPane'
import { generateTitle } from '@/utils/i18n'
export default {
components: { ScrollPane },
@@ -21,6 +22,7 @@ export default {
this.addViewTags()
},
methods: {
generateTitle,
closeViewTags(view, $event) {
this.$store.dispatch('delVisitedViews', view).then((views) => {
if (this.isActive(view)) {
@@ -49,12 +51,23 @@ export default {
},
isActive(route) {
return route.path === this.$route.path || route.name === this.$route.name
},
moveToCurrentTag() {
const tags = this.$refs.tag
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag.$el)
break
}
}
})
}
},
watch: {
$route() {
this.addViewTags()
this.moveToCurrentTag()
}
}
}