Merge pull request #1 from PanJiaChen/master

merge update
This commit is contained in:
小新 2019-03-22 10:25:35 +08:00 committed by GitHub
commit 2b98e62327
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
125 changed files with 1189 additions and 360 deletions

View File

@ -21,6 +21,8 @@ module.exports = {
"allowFirstLine": false "allowFirstLine": false
} }
}], }],
"vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline":"off",
"vue/name-property-casing": ["error", "PascalCase"], "vue/name-property-casing": ["error", "PascalCase"],
'accessor-pairs': 2, 'accessor-pairs': 2,
'arrow-spacing': [2, { 'arrow-spacing': [2, {

View File

@ -80,10 +80,10 @@
"copy-webpack-plugin": "4.5.2", "copy-webpack-plugin": "4.5.2",
"cross-env": "5.2.0", "cross-env": "5.2.0",
"css-loader": "1.0.0", "css-loader": "1.0.0",
"eslint": "4.19.1", "eslint": "5.15.2",
"eslint-friendly-formatter": "4.0.1", "eslint-friendly-formatter": "4.0.1",
"eslint-loader": "2.0.0", "eslint-loader": "2.1.2",
"eslint-plugin-vue": "4.7.1", "eslint-plugin-vue": "5.2.2",
"file-loader": "1.1.11", "file-loader": "1.1.11",
"friendly-errors-webpack-plugin": "1.7.0", "friendly-errors-webpack-plugin": "1.7.0",
"hash-sum": "1.0.2", "hash-sum": "1.0.2",

38
src/api/role.js Normal file
View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
export function getRoutes() {
return request({
url: '/routes',
method: 'get'
})
}
export function getRoles() {
return request({
url: '/roles',
method: 'get'
})
}
export function deleteRole(id) {
return request({
url: `/roles/${id}`,
method: 'delete'
})
}
export function addRole(data) {
return request({
url: '/roles',
method: 'post',
data
})
}
export function updateRole(key, data) {
return request({
url: `/roles/${key}`,
method: 'put',
data
})
}

View File

@ -2,8 +2,9 @@
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path"> <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">
generateTitle(item.meta.title) }}</span> {{ generateTitle(item.meta.title) }}
</span>
<a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a> <a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class="className" :id="id" :style="{height:height,width:width}"/> <div :id="id" :class="className" :style="{height:height,width:width}" />
</template> </template>
<script> <script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class="className" :id="id" :style="{height:height,width:width}"/> <div :id="id" :class="className" :style="{height:height,width:width}" />
</template> </template>
<script> <script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div :class="className" :id="id" :style="{height:height,width:width}"/> <div :id="id" :class="className" :style="{height:height,width:width}" />
</template> </template>
<script> <script>

View File

@ -1,5 +1,5 @@
<template> <template>
<div :ref="id" :action="url" :id="id" class="dropzone"> <div :id="id" :ref="id" :action="url" class="dropzone">
<input type="file" name="file"> <input type="file" name="file">
</div> </div>
</template> </template>

View File

@ -5,17 +5,20 @@
height="80" height="80"
viewBox="0 0 250 250" viewBox="0 0 250 250"
style="fill:#40c9c6; color:#fff;" style="fill:#40c9c6; color:#fff;"
aria-hidden="true"> aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" /> <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path <path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" fill="currentColor"
style="transform-origin: 130px 106px;" style="transform-origin: 130px 106px;"
class="octo-arm"/> class="octo-arm"
/>
<path <path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" fill="currentColor"
class="octo-body"/> class="octo-body"
/>
</svg> </svg>
</a> </a>
</template> </template>

View File

@ -1,6 +1,6 @@
<template> <template>
<div :class="{'show':show}" class="header-search"> <div :class="{'show':show}" class="header-search">
<svg-icon class-name="search-icon" icon-class="search" @click="click" /> <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
<el-select <el-select
ref="headerSearchSelect" ref="headerSearchSelect"
v-model="search" v-model="search"
@ -10,7 +10,8 @@
remote remote
placeholder="Search" placeholder="Search"
class="header-search-select" class="header-search-select"
@change="change"> @change="change"
>
<el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" /> <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
</el-select> </el-select>
</div> </div>
@ -33,8 +34,8 @@ export default {
} }
}, },
computed: { computed: {
routers() { routes() {
return this.$store.getters.permission_routers return this.$store.getters.permission_routes
}, },
lang() { lang() {
return this.$store.getters.language return this.$store.getters.language
@ -42,10 +43,10 @@ export default {
}, },
watch: { watch: {
lang() { lang() {
this.searchPool = this.generateRouters(this.routers) this.searchPool = this.generateRoutes(this.routes)
}, },
routers() { routes() {
this.searchPool = this.generateRouters(this.routers) this.searchPool = this.generateRoutes(this.routes)
}, },
searchPool(list) { searchPool(list) {
this.initFuse(list) this.initFuse(list)
@ -59,7 +60,7 @@ export default {
} }
}, },
mounted() { mounted() {
this.searchPool = this.generateRouters(this.routers) this.searchPool = this.generateRoutes(this.routes)
}, },
methods: { methods: {
click() { click() {
@ -100,10 +101,10 @@ export default {
}, },
// Filter out the routes that can be displayed in the sidebar // Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title // And generate the internationalized title
generateRouters(routers, basePath = '/', prefixTitle = []) { generateRoutes(routes, basePath = '/', prefixTitle = []) {
let res = [] let res = []
for (const router of routers) { for (const router of routes) {
// skip hidden router // skip hidden router
if (router.hidden) { continue } if (router.hidden) { continue }
@ -125,11 +126,11 @@ export default {
} }
} }
// recursive child routers // recursive child routes
if (router.children) { if (router.children) {
const tempRouters = this.generateRouters(router.children, data.path, data.title) const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
if (tempRouters.length >= 1) { if (tempRoutes.length >= 1) {
res = [...res, ...tempRouters] res = [...res, ...tempRoutes]
} }
} }
} }

View File

@ -48,7 +48,8 @@
@mousedown="imgStartMove" @mousedown="imgStartMove"
@mousemove="imgMove" @mousemove="imgMove"
@mouseup="createImg" @mouseup="createImg"
@mouseout="createImg"> @mouseout="createImg"
>
<div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-1" /> <div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-1" />
<div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-2" /> <div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-2" />
</div> </div>

View File

@ -6,7 +6,8 @@
<draggable <draggable
:list="list" :list="list"
:options="options" :options="options"
class="board-column-content"> class="board-column-content"
>
<div v-for="element in list" :key="element.id" class="board-item"> <div v-for="element in list" :key="element.id" class="board-item">
{{ element.name }} {{ element.id }} {{ element.name }} {{ element.id }}
</div> </div>

View File

@ -4,9 +4,15 @@
<svg-icon class-name="international-icon" icon-class="language" /> <svg-icon class-name="international-icon" icon-class="language" />
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item> <el-dropdown-item :disabled="language==='zh'" command="zh">
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item> 中文
<el-dropdown-item :disabled="language==='es'" command="es">Español</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">
English
</el-dropdown-item>
<el-dropdown-item :disabled="language==='es'" command="es">
Español
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</template> </template>

View File

@ -4,9 +4,9 @@
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon" /> <i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon" />
<input <input
v-if="type === 'email'" v-if="type === 'email'"
v-model="currentValue"
:name="name" :name="name"
:placeholder="fillPlaceHolder" :placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly" :readonly="readonly"
:disabled="disabled" :disabled="disabled"
:autoComplete="autoComplete" :autoComplete="autoComplete"
@ -15,12 +15,13 @@
class="material-input" class="material-input"
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput"> @input="handleModelInput"
>
<input <input
v-if="type === 'url'" v-if="type === 'url'"
v-model="currentValue"
:name="name" :name="name"
:placeholder="fillPlaceHolder" :placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly" :readonly="readonly"
:disabled="disabled" :disabled="disabled"
:autoComplete="autoComplete" :autoComplete="autoComplete"
@ -29,12 +30,13 @@
class="material-input" class="material-input"
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput"> @input="handleModelInput"
>
<input <input
v-if="type === 'number'" v-if="type === 'number'"
v-model="currentValue"
:name="name" :name="name"
:placeholder="fillPlaceHolder" :placeholder="fillPlaceHolder"
v-model="currentValue"
:step="step" :step="step"
:readonly="readonly" :readonly="readonly"
:disabled="disabled" :disabled="disabled"
@ -48,12 +50,13 @@
class="material-input" class="material-input"
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput"> @input="handleModelInput"
>
<input <input
v-if="type === 'password'" v-if="type === 'password'"
v-model="currentValue"
:name="name" :name="name"
:placeholder="fillPlaceHolder" :placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly" :readonly="readonly"
:disabled="disabled" :disabled="disabled"
:autoComplete="autoComplete" :autoComplete="autoComplete"
@ -64,12 +67,13 @@
class="material-input" class="material-input"
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput"> @input="handleModelInput"
>
<input <input
v-if="type === 'tel'" v-if="type === 'tel'"
v-model="currentValue"
:name="name" :name="name"
:placeholder="fillPlaceHolder" :placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly" :readonly="readonly"
:disabled="disabled" :disabled="disabled"
:autoComplete="autoComplete" :autoComplete="autoComplete"
@ -78,12 +82,13 @@
class="material-input" class="material-input"
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput"> @input="handleModelInput"
>
<input <input
v-if="type === 'text'" v-if="type === 'text'"
v-model="currentValue"
:name="name" :name="name"
:placeholder="fillPlaceHolder" :placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly" :readonly="readonly"
:disabled="disabled" :disabled="disabled"
:autoComplete="autoComplete" :autoComplete="autoComplete"
@ -94,7 +99,8 @@
class="material-input" class="material-input"
@focus="handleMdFocus" @focus="handleMdFocus"
@blur="handleMdBlur" @blur="handleMdBlur"
@input="handleModelInput"> @input="handleModelInput"
>
<span class="material-input-bar" /> <span class="material-input-bar" />
<label class="material-label"> <label class="material-label">
<slot /> <slot />

View File

@ -9,7 +9,8 @@
:total="total" :total="total"
v-bind="$attrs" v-bind="$attrs"
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange"/> @current-change="handleCurrentChange"
/>
</div> </div>
</template> </template>

View File

@ -4,8 +4,9 @@
<svg-icon class-name="size-icon" icon-class="size" /> <svg-icon class-name="size-icon" icon-class="size" />
</div> </div>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">{{ <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
item.label }}</el-dropdown-item> {{ item.label }}
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</template> </template>

View File

@ -1,6 +1,9 @@
<template> <template>
<div :style="{height:height+'px',zIndex:zIndex}"> <div :style="{height:height+'px',zIndex:zIndex}">
<div :class="className" :style="{top:stickyTop+'px',zIndex:zIndex,position:position,width:width,height:height+'px'}"> <div
:class="className"
:style="{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}"
>
<slot> <slot>
<div>sticky</div> <div>sticky</div>
</slot> </slot>

View File

@ -1,8 +1,10 @@
<template> <template>
<el-color-picker <el-color-picker
v-model="theme" v-model="theme"
:predefine="['#409EFF', '#11a983', '#13c2c2', '#6959CD', '#f5222d', '#eb2f96', '#DB7093', '#e6a23c', '#8B8989', '#212121']"
class="theme-picker" class="theme-picker"
popper-class="theme-picker-dropdown"/> popper-class="theme-picker-dropdown"
/>
</template> </template>
<script> <script>

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="upload-container"> <div class="upload-container">
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">上传图片 <el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
上传图片
</el-button> </el-button>
<el-dialog :visible.sync="dialogVisible"> <el-dialog :visible.sync="dialogVisible">
<el-upload <el-upload
@ -12,7 +13,8 @@
:before-upload="beforeUpload" :before-upload="beforeUpload"
class="editor-slide-upload" class="editor-slide-upload"
action="https://httpbin.org/post" action="https://httpbin.org/post"
list-type="picture-card"> list-type="picture-card"
>
<el-button size="small" type="primary">点击上传</el-button> <el-button size="small" type="primary">点击上传</el-button>
</el-upload> </el-upload>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>

View File

@ -4,11 +4,12 @@
<slot name="pre-column" /> <slot name="pre-column" />
<el-table-column <el-table-column
v-for="item in columns" v-for="item in columns"
:label="item.label"
:key="item.key" :key="item.key"
:label="item.label"
:width="item.width" :width="item.width"
:align="item.align||'center'" :align="item.align||'center'"
:header-align="item.headerAlign"> :header-align="item.headerAlign"
>
<template slot-scope="scope"> <template slot-scope="scope">
<slot :scope="scope" :name="item.key"> <slot :scope="scope" :name="item.key">
<template v-if="item.expand"> <template v-if="item.expand">
@ -21,15 +22,17 @@
<template v-if="item.checkbox"> <template v-if="item.checkbox">
<el-checkbox <el-checkbox
v-if="scope.row[defaultChildren]&&scope.row[defaultChildren].length>0" v-if="scope.row[defaultChildren]&&scope.row[defaultChildren].length>0"
v-model="scope.row._select"
:style="{'padding-left':+scope.row._level*indent + 'px'} " :style="{'padding-left':+scope.row._level*indent + 'px'} "
:indeterminate="scope.row._select" :indeterminate="scope.row._select"
v-model="scope.row._select" @change="handleCheckAllChange(scope.row)"
@change="handleCheckAllChange(scope.row)" /> />
<el-checkbox <el-checkbox
v-else v-else
:style="{'padding-left':+scope.row._level*indent + 'px'} "
v-model="scope.row._select" v-model="scope.row._select"
@change="handleCheckAllChange(scope.row)" /> :style="{'padding-left':+scope.row._level*indent + 'px'} "
@change="handleCheckAllChange(scope.row)"
/>
</template> </template>
{{ scope.row[item.key] }} {{ scope.row[item.key] }}
</slot> </slot>

View File

@ -0,0 +1,220 @@
- [Enlgish](#Brief)
# 中文
## 写在前面
此组件仅提供一个创建 `TreeTable` 的解决思路。它基于`element-ui`的 table 组件实现,通过`el-table`的`row-style`方法,在里面判断元素是否需要隐藏或者显示,从而实现`TreeTable`的展开与收起。
并且本组件充分利用 `vue` 插槽的特性来方便用户自定义。
`evel.js` 里面,`addAttrs` 方法会给数据添加几个属性,`treeTotable` 会对数组扁平化。这些操作都不会破坏源数据,只是会新增属性。
## Props 说明
| Attribute | Description | Type | Default |
| :--------------: | :--------------------------------- | :-----: | :------: |
| data | 原始展示数据 | Array | [] |
| columns | 列属性 | Array | [] |
| defaultExpandAll | 默认是否全部展开 | Boolean | false |
| defaultChildren | 指定子树为节点对象的某个属性值 | String | children | |
| indent | 相邻级节点间的水平缩进,单位为像素 | Number | 50 |
> 任何 `el-table` 的属性都支持,例如`border`、`fit`、`size`或者`@select`、`@cell-click`等方法。详情属性见`el-table`文档。
---
### 代码示例
```html
<tree-table :data="data" :columns="columns" border>
```
#### data(**必填**)
```js
const data = [
{
name:'1'
children: [
{
name: '1-1'
},
{
name: '1-2'
}
]
},
{
name: `2`
}
]
```
#### columns(**必填**)
- label: 显示在表头的文字
- key: 对应 data 的 key。treeTable 将显示相应的 value
- expand: `true` or `false`。若为 true则在该列显示展开收起图标
- checkbox: `true` or `false`。若为 true则在该列显示`checkbox`
- width: 每列的宽度,为一个数字(可选)。例如`200`
- align: 对齐方式 `left/center/right`
- header-align: 表头对齐方式 `left/center/right`
```javascript
const columns = [
{
label: 'Checkbox',
checkbox: true
},
{
label: '',
key: 'id',
expand: true
},
{
label: 'Event',
key: 'event',
width: 200,
align: 'left'
},
{
label: 'Scope',
key: 'scope'
}
]
```
> 树表组件将会根据 columns 的 key 属性生成具名插槽,如果你需要对列数据进行自定义,通过插槽即可实现
```html
<template slot="your key" slot-scope="{scope}">
<el-tag>level: {{ scope.row._level }}</el-tag>
<el-tag>expand: {{ scope.row._expand }}</el-tag>
<el-tag>select: {{ scope.row._select }}</el-tag>
</template>
```
## Events
目前提供了几个方法,不过只是`beta`版本,之后很可能会修改。
```js
this.$refs.TreeTable.addChild(row, data) //添加子元素
this.$refs.TreeTable.addBrother(row, data) //添加兄弟元素
this.$refs.TreeTable.delete(row) //删除该元素
```
## 其他
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的 api 自行修改 index.vue
# English
## Brief
This component only provides a solution for creating `TreeTable`. It is based on the `element-ui` table component. It uses the `row-style` method of `el-table` to determine whether the element needs to be hidden or displayed.
And this component makes full use of the features of the `vue` slot to make it user-friendly.
In `evel.js`, the `addAttrs` method adds several properties to the data, and `treeTotable` flattens the array. None of these operations will destroy the source data, just add properties.
## Props
| Attribute | Description | Type | Default |
| :--------------: | :----------------------------------------------------------- | :-----: | :------: |
| data | original display data | Array | [] |
| columns | column attribute | Array | [] |
| defaultExpandAll | whether to expand all nodes by default | Boolean | false |
| defaultChildren | specify which node object is used as the node's subtree | String | children | |
| indent | horizontal indentation of nodes in adjacent levels in pixels | Number | 50 |
> Any of the `el-table` properties are supported, such as `border`, `fit`, `size` or `@select`, `@cell-click`. See the ʻel-table` documentation for details.
---
### Example
```html
<tree-table :data="data" :columns="columns" border>
```
#### data(**Required**)
```js
const data = [
{
name:'1'
children: [
{
name: '1-1'
},
{
name: '1-2'
}
]
},
{
name: `2`
}
]
```
#### columns(**Required**)
- label: text displayed in the header
- key: data.key will show in column
- expand: `true` or `false`
- checkbox: `true` or `false`
- width: column width 。such as `200`
- align: alignment `left/center/right`
- header-align: alignment of the table header `left/center/right`
```javascript
const columns = [
{
label: 'Checkbox',
checkbox: true
},
{
label: '',
key: 'id',
expand: true
},
{
label: 'Event',
key: 'event',
width: 200,
align: 'left'
},
{
label: 'Scope',
key: 'scope'
}
]
```
> The tree table component will generate a named slot based on the key property of columns. If you need to customize the column data, you can do it through the slot.
```html
<template slot="your key" slot-scope="{scope}">
<el-tag>level: {{ scope.row._level }}</el-tag>
<el-tag>expand: {{ scope.row._expand }}</el-tag>
<el-tag>select: {{ scope.row._select }}</el-tag>
</template>
```
## Events
Several methods are currently available, but only the `beta` version, which is likely to be modified later.
```js
this.$refs.TreeTable.addChild(row, data) //Add child elements
this.$refs.TreeTable.addBrother(row, data) //Add a sibling element
this.$refs.TreeTable.delete(row) //Delete the element
```
## Other
If you have other requirements, please refer to the [el-table](http://element-cn.eleme.io/#/en-US/component/table) api to modify the index.vue

View File

@ -7,7 +7,8 @@
:on-success="handleImageSuccess" :on-success="handleImageSuccess"
class="image-uploader" class="image-uploader"
drag drag
action="https://httpbin.org/post"> action="https://httpbin.org/post"
>
<i class="el-icon-upload" /> <i class="el-icon-upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload> </el-upload>

View File

@ -7,7 +7,8 @@
:on-success="handleImageSuccess" :on-success="handleImageSuccess"
class="image-uploader" class="image-uploader"
drag drag
action="https://httpbin.org/post"> action="https://httpbin.org/post"
>
<i class="el-icon-upload" /> <i class="el-icon-upload" />
<div class="el-upload__text">Drag或<em>点击上传</em></div> <div class="el-upload__text">Drag或<em>点击上传</em></div>
</el-upload> </el-upload>

View File

@ -7,7 +7,8 @@
:on-success="handleImageSuccess" :on-success="handleImageSuccess"
class="image-uploader" class="image-uploader"
drag drag
action="https://httpbin.org/post"> action="https://httpbin.org/post"
>
<i class="el-icon-upload" /> <i class="el-icon-upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload> </el-upload>

View File

@ -6,6 +6,7 @@ export default {
guide: 'Guide', guide: 'Guide',
permission: 'Permission', permission: 'Permission',
pagePermission: 'Page Permission', pagePermission: 'Page Permission',
rolePermission: 'Role Permission',
directivePermission: 'Directive Permission', directivePermission: 'Directive Permission',
icons: 'Icons', icons: 'Icons',
components: 'Components', components: 'Components',
@ -56,6 +57,7 @@ export default {
excel: 'Excel', excel: 'Excel',
exportExcel: 'Export Excel', exportExcel: 'Export Excel',
selectExcel: 'Export Selected', selectExcel: 'Export Selected',
mergeHeader: 'Merge Header',
uploadExcel: 'Upload Excel', uploadExcel: 'Upload Excel',
zip: 'Zip', zip: 'Zip',
pdf: 'PDF', pdf: 'PDF',
@ -86,9 +88,14 @@ export default {
github: 'Github Repository' github: 'Github Repository'
}, },
permission: { permission: {
addRole: 'New Role',
editPermission: 'Edit Permission',
roles: 'Your roles', roles: 'Your roles',
switchRoles: 'Switch roles', switchRoles: 'Switch roles',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.' tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
delete: 'Delete',
confirm: 'Confirm',
cancel: 'Cancel'
}, },
guide: { guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',

View File

@ -5,6 +5,7 @@ export default {
documentation: 'Documentación', documentation: 'Documentación',
guide: 'Guía', guide: 'Guía',
permission: 'Permisos', permission: 'Permisos',
rolePermission: 'Permisos de rol',
pagePermission: 'Permisos de la página', pagePermission: 'Permisos de la página',
directivePermission: 'Permisos de la directiva', directivePermission: 'Permisos de la directiva',
icons: 'Iconos', icons: 'Iconos',
@ -56,6 +57,7 @@ export default {
excel: 'Excel', excel: 'Excel',
exportExcel: 'Exportar a Excel', exportExcel: 'Exportar a Excel',
selectExcel: 'Export seleccionado', selectExcel: 'Export seleccionado',
mergeHeader: 'Merge Header',
uploadExcel: 'Subir Excel', uploadExcel: 'Subir Excel',
zip: 'Zip', zip: 'Zip',
pdf: 'PDF', pdf: 'PDF',
@ -86,9 +88,14 @@ export default {
github: 'Repositorio Github' github: 'Repositorio Github'
}, },
permission: { permission: {
addRole: 'Nuevo rol',
editPermission: 'Permiso de edición',
roles: 'Tus permisos', roles: 'Tus permisos',
switchRoles: 'Cambiar permisos', switchRoles: 'Cambiar permisos',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.' tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
delete: 'Borrar',
confirm: 'Confirmar',
cancel: 'Cancelar'
}, },
guide: { guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',

View File

@ -5,6 +5,7 @@ export default {
documentation: '文档', documentation: '文档',
guide: '引导页', guide: '引导页',
permission: '权限测试页', permission: '权限测试页',
rolePermission: '角色权限',
pagePermission: '页面权限', pagePermission: '页面权限',
directivePermission: '指令权限', directivePermission: '指令权限',
icons: '图标', icons: '图标',
@ -54,9 +55,10 @@ export default {
page404: '404', page404: '404',
errorLog: '错误日志', errorLog: '错误日志',
excel: 'Excel', excel: 'Excel',
exportExcel: 'Export Excel', exportExcel: '导出 Excel',
selectExcel: 'Export Selected', selectExcel: '导出 已选择项',
uploadExcel: 'Upload Excel', mergeHeader: '导出 多级表头',
uploadExcel: '上传 Excel',
zip: 'Zip', zip: 'Zip',
pdf: 'PDF', pdf: 'PDF',
exportZip: 'Export Zip', exportZip: 'Export Zip',
@ -86,9 +88,14 @@ export default {
github: 'Github 地址' github: 'Github 地址'
}, },
permission: { permission: {
addRole: '新增角色',
editPermission: '编辑权限',
roles: '你的权限', roles: '你的权限',
switchRoles: '切换权限', switchRoles: '切换权限',
tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。' tips: '在某些情况下,不适合使用 v-permission。例如Element-UI 的 Tab 组件或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。',
delete: '删除',
confirm: '确定',
cancel: '取消'
}, },
guide: { guide: {
description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于', description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',

View File

@ -3,6 +3,7 @@ import loginAPI from './login'
import articleAPI from './article' import articleAPI from './article'
import remoteSearchAPI from './remoteSearch' import remoteSearchAPI from './remoteSearch'
import transactionAPI from './transaction' import transactionAPI from './transaction'
import roleAPI from './role'
// 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题 // 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题
// https://github.com/nuysoft/Mock/issues/300 // https://github.com/nuysoft/Mock/issues/300
@ -23,6 +24,13 @@ Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
Mock.mock(/\/login\/logout/, 'post', loginAPI.logout) Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo) Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
// 角色相关
Mock.mock(/\/routes/, 'get', roleAPI.getRoutes)
Mock.mock(/\/roles/, 'get', roleAPI.getRoles)
Mock.mock(/\/roles$/, 'post', roleAPI.addRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'delete', roleAPI.deleteRole)
// 文章相关 // 文章相关
Mock.mock(/\/article\/list/, 'get', articleAPI.getList) Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle) Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)

61
src/mock/role.js Normal file
View File

@ -0,0 +1,61 @@
import Mock from 'mockjs'
import { deepClone } from '@/utils'
import { filterAsyncRoutes } from '@/store/modules/permission'
import { asyncRoutes, constantRoutes } from '@/router'
const routes = deepClone([...constantRoutes, ...asyncRoutes])
const roles = [
{
key: 'admin',
name: 'admin',
description: 'Super Administrator. Have access to view all pages.',
routes: routes
},
{
key: 'editor',
name: 'editor',
description: 'Normal Editor. Can see all pages except permission page',
routes: filterAsyncRoutes(routes, ['editor'])
},
{
key: 'visitor',
name: 'visitor',
description: 'Just a visitor. Can only see the home page and the document page',
routes: [{
path: '',
redirect: 'dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard' }
}
]
}]
}
]
export default {
getRoutes() {
return routes
},
getRoles() {
return roles
},
addRole() {
return Mock.mock('@integer(300, 5000)')
},
updateRole() {
const res = {
data: 'success'
}
return res
},
deleteRole() {
const res = {
data: 'success'
}
return res
}
}

View File

@ -3,13 +3,13 @@ import store from './store'
import { Message } from 'element-ui' import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie import { getToken } from '@/utils/auth' // get token from cookie
NProgress.configure({ showSpinner: false }) // NProgress Configuration NProgress.configure({ showSpinner: false }) // NProgress Configuration
// permission judge function // permission judge function
function hasPermission(roles, permissionRoles) { function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly if (roles.includes('admin')) return true // admin permission passed directly
if (!permissionRoles) return true if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0) return roles.some(role => permissionRoles.indexOf(role) >= 0)
} }
@ -18,20 +18,28 @@ const whiteList = ['/login', '/auth-redirect']// no redirect whitelist
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar NProgress.start() // start progress bar
if (getToken()) { // determine if there has token if (getToken()) {
// determine if there has token
/* has token*/ /* has token*/
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' }) next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else { } else {
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息 if (store.getters.roles.length === 0) {
store.dispatch('GetUserInfo').then(res => { // 拉取user_info // 判断当前用户是否已拉取完user_info信息
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop'] store
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表 .dispatch('GetUserInfo')
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表 .then(res => {
// 拉取user_info
const roles = res.data.roles // note: roles must be a object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}]
store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
// 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
}) })
}).catch((err) => { })
.catch(err => {
store.dispatch('FedLogOut').then(() => { store.dispatch('FedLogOut').then(() => {
Message.error(err) Message.error(err)
next({ path: '/' }) next({ path: '/' })
@ -49,7 +57,8 @@ router.beforeEach((to, from, next) => {
} }
} else { } else {
/* has no token*/ /* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入 if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next() next()
} else { } else {
next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页 next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页

View File

@ -33,7 +33,7 @@ import nestedRouter from './modules/nested'
affix: true if true, the tag will affix in the tags-view affix: true if true, the tag will affix in the tags-view
} }
**/ **/
export const constantRouterMap = [ export const constantRoutes = [
{ {
path: '/redirect', path: '/redirect',
component: Layout, component: Layout,
@ -81,7 +81,6 @@ export const constantRouterMap = [
{ {
path: '/documentation', path: '/documentation',
component: Layout, component: Layout,
redirect: '/documentation/index',
children: [ children: [
{ {
path: 'index', path: 'index',
@ -109,10 +108,10 @@ export const constantRouterMap = [
export default new Router({ export default new Router({
// mode: 'history', // require service support // mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }), scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap routes: constantRoutes
}) })
export const asyncRouterMap = [ export const asyncRoutes = [
{ {
path: '/permission', path: '/permission',
component: Layout, component: Layout,
@ -141,6 +140,15 @@ export const asyncRouterMap = [
title: 'directivePermission' title: 'directivePermission'
// if do not set roles, means: this page does not require permission // if do not set roles, means: this page does not require permission
} }
},
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: 'rolePermission',
roles: ['admin']
}
} }
] ]
}, },
@ -271,6 +279,12 @@ export const asyncRouterMap = [
name: 'SelectExcel', name: 'SelectExcel',
meta: { title: 'selectExcel' } meta: { title: 'selectExcel' }
}, },
{
path: 'export-merge-header',
component: () => import('@/views/excel/mergeHeader'),
name: 'MergeHeader',
meta: { title: 'mergeHeader' }
},
{ {
path: 'upload-excel', path: 'upload-excel',
component: () => import('@/views/excel/uploadExcel'), component: () => import('@/views/excel/uploadExcel'),

View File

@ -12,8 +12,8 @@ const getters = {
status: state => state.user.status, status: state => state.user.status,
roles: state => state.user.roles, roles: state => state.user.roles,
setting: state => state.user.setting, setting: state => state.user.setting,
permission_routers: state => state.permission.routers, permission_routes: state => state.permission.routes,
addRouters: state => state.permission.addRouters, addRoutes: state => state.permission.addRoutes,
errorLogs: state => state.errorLog.logs errorLogs: state => state.errorLog.logs
} }
export default getters export default getters

View File

@ -1,4 +1,4 @@
import { asyncRouterMap, constantRouterMap } from '@/router' import { asyncRoutes, constantRoutes } from '@/router'
/** /**
* 通过meta.role判断是否与当前用户权限匹配 * 通过meta.role判断是否与当前用户权限匹配
@ -15,17 +15,17 @@ function hasPermission(roles, route) {
/** /**
* 递归过滤异步路由表返回符合用户角色权限的路由表 * 递归过滤异步路由表返回符合用户角色权限的路由表
* @param routes asyncRouterMap * @param routes asyncRoutes
* @param roles * @param roles
*/ */
function filterAsyncRouter(routes, roles) { export function filterAsyncRoutes(routes, roles) {
const res = [] const res = []
routes.forEach(route => { routes.forEach(route => {
const tmp = { ...route } const tmp = { ...route }
if (hasPermission(roles, tmp)) { if (hasPermission(roles, tmp)) {
if (tmp.children) { if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, roles) tmp.children = filterAsyncRoutes(tmp.children, roles)
} }
res.push(tmp) res.push(tmp)
} }
@ -36,27 +36,27 @@ function filterAsyncRouter(routes, roles) {
const permission = { const permission = {
state: { state: {
routers: [], routes: [],
addRouters: [] addRoutes: []
}, },
mutations: { mutations: {
SET_ROUTERS: (state, routers) => { SET_ROUTES: (state, routes) => {
state.addRouters = routers state.addRoutes = routes
state.routers = constantRouterMap.concat(routers) state.routes = constantRoutes.concat(routes)
} }
}, },
actions: { actions: {
GenerateRoutes({ commit }, data) { GenerateRoutes({ commit }, data) {
return new Promise(resolve => { return new Promise(resolve => {
const { roles } = data const { roles } = data
let accessedRouters let accessedRoutes
if (roles.includes('admin')) { if (roles.includes('admin')) {
accessedRouters = asyncRouterMap accessedRoutes = asyncRoutes
} else { } else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles) accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
} }
commit('SET_ROUTERS', accessedRouters) commit('SET_ROUTES', accessedRoutes)
resolve() resolve(accessedRoutes)
}) })
} }
} }

View File

@ -274,7 +274,7 @@ export function debounce(func, wait, immediate) {
*/ */
export function deepClone(source) { export function deepClone(source) {
if (!source && typeof source !== 'object') { if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'shallowClone') throw new Error('error arguments', 'deepClone')
} }
const targetObj = source.constructor === Array ? [] : {} const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => { Object.keys(source).forEach(keys => {

View File

@ -145,9 +145,11 @@ export function export_table_to_excel(id) {
} }
export function export_json_to_excel({ export function export_json_to_excel({
multiHeader = [],
header, header,
data, data,
filename, filename,
merges = [],
autoWidth = true, autoWidth = true,
bookType= 'xlsx' bookType= 'xlsx'
} = {}) { } = {}) {
@ -155,10 +157,22 @@ export function export_json_to_excel({
filename = filename || 'excel-list' filename = filename || 'excel-list'
data = [...data] data = [...data]
data.unshift(header); data.unshift(header);
for (let i = multiHeader.length-1; i > -1; i--) {
data.unshift(multiHeader[i])
}
var ws_name = "SheetJS"; var ws_name = "SheetJS";
var wb = new Workbook(), var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data); ws = sheet_from_array_of_arrays(data);
if (merges.length > 0) {
if (!ws['!merges']) ws['!merges'] = [];
merges.forEach(item => {
ws['!merges'].push(XLSX.utils.decode_range(item))
})
}
if (autoWidth) { if (autoWidth) {
/*设置worksheet每列的最大宽度*/ /*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => { const colWidth = data.map(row => row.map(val => {

View File

@ -7,18 +7,20 @@
<pan-thumb :image="image" /> <pan-thumb :image="image" />
<el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;" @click="imagecropperShow=true">Change Avatar <el-button type="primary" icon="upload" style="position: absolute;bottom: 15px;margin-left: 40px;" @click="imagecropperShow=true">
Change Avatar
</el-button> </el-button>
<image-cropper <image-cropper
v-show="imagecropperShow" v-show="imagecropperShow"
:key="imagecropperKey"
:width="300" :width="300"
:height="300" :height="300"
:key="imagecropperKey"
url="https://httpbin.org/post" url="https://httpbin.org/post"
lang-type="en" lang-type="en"
@close="close" @close="close"
@crop-upload-success="cropSuccess"/> @crop-upload-success="cropSuccess"
/>
</div> </div>
</template> </template>

View File

@ -13,7 +13,8 @@
:prefix="_prefix" :prefix="_prefix"
:suffix="_suffix" :suffix="_suffix"
:autoplay="false" :autoplay="false"
class="example"/> class="example"
/>
<div style="margin-left: 25%;margin-top: 40px;"> <div style="margin-left: 25%;margin-top: 40px;">
<label class="label" for="startValInput">startVal: <label class="label" for="startValInput">startVal:
<input v-model.number="setStartVal" type="number" name="startValInput"> <input v-model.number="setStartVal" type="number" name="startValInput">
@ -40,9 +41,11 @@
<input v-model="setSuffix" name="suffixInput"> <input v-model="setSuffix" name="suffixInput">
</label> </label>
</div> </div>
<code>&lt;count-to :start-val=&#x27;{{ _startVal }}&#x27; :end-val=&#x27;{{ _endVal }}&#x27; :duration=&#x27;{{ _duration }}&#x27; <code>
&lt;count-to :start-val=&#x27;{{ _startVal }}&#x27; :end-val=&#x27;{{ _endVal }}&#x27; :duration=&#x27;{{ _duration }}&#x27;
:decimals=&#x27;{{ _decimals }}&#x27; :separator=&#x27;{{ _separator }}&#x27; :prefix=&#x27;{{ _prefix }}&#x27; :suffix=&#x27;{{ _suffix }}&#x27; :decimals=&#x27;{{ _decimals }}&#x27; :separator=&#x27;{{ _separator }}&#x27; :prefix=&#x27;{{ _prefix }}&#x27; :suffix=&#x27;{{ _suffix }}&#x27;
:autoplay=false&gt;</code> :autoplay=false&gt;
</code>
</div> </div>
</template> </template>

View File

@ -2,7 +2,7 @@
<div class="components-container"> <div class="components-container">
<el-drag-select v-model="value" style="width:500px;" multiple placeholder="请选择"> <el-drag-select v-model="value" style="width:500px;" multiple placeholder="请选择">
<el-option v-for="item in options" :label="item.label" :value="item.value" :key="item.value" /> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-drag-select> </el-drag-select>
<div style="margin-top:30px;"> <div style="margin-top:30px;">

View File

@ -33,6 +33,7 @@
</div> </div>
<el-button style="margin-top:80px;" type="primary" icon="el-icon-document" @click="getHtml">Get HTML</el-button> <el-button style="margin-top:80px;" type="primary" icon="el-icon-document" @click="getHtml">Get HTML</el-button>
<!-- eslint-disable-next-line -->
<div v-html="html" /> <div v-html="html" />
</div> </div>

View File

@ -92,7 +92,6 @@
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>

View File

@ -7,7 +7,7 @@
</el-button> </el-button>
<el-dropdown-menu slot="dropdown" class="no-border"> <el-dropdown-menu slot="dropdown" class="no-border">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;"> <el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :label="item.key" :key="item.key"> <el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
{{ item.name }} {{ item.name }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>
@ -29,7 +29,8 @@
<el-date-picker v-model="time" :picker-options="pickerOptions" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Release time" /> <el-date-picker v-model="time" :picker-options="pickerOptions" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Release time" />
</div> </div>
<el-button style="margin-left: 10px;" type="success">publish <el-button style="margin-left: 10px;" type="success">
publish
</el-button> </el-button>
</sticky> </sticky>
@ -48,7 +49,9 @@
<div>placeholder</div> <div>placeholder</div>
<div>placeholder</div> <div>placeholder</div>
<div>placeholder</div> <div>placeholder</div>
<div>placeholder</div> <sticky :sticky-top="200">
<el-button type="primary"> placeholder</el-button>
</sticky>
<div>placeholder</div> <div>placeholder</div>
<div>placeholder</div> <div>placeholder</div>
<div>placeholder</div> <div>placeholder</div>

View File

@ -5,8 +5,9 @@
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/component/rich-editor.html"> {{ $t('components.documentation') }}</a> <a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/component/rich-editor.html"> {{ $t('components.documentation') }}</a>
</code> </code>
<div> <div>
<tinymce :height="300" v-model="content"/> <tinymce v-model="content" :height="300" />
</div> </div>
<!-- eslint-disable-next-line -->
<div class="editor-content" v-html="content" /> <div class="editor-content" v-html="content" />
</div> </div>
</template> </template>

View File

@ -5,18 +5,20 @@
:checked="todo.done" :checked="todo.done"
class="toggle" class="toggle"
type="checkbox" type="checkbox"
@change="toggleTodo( todo)"> @change="toggleTodo( todo)"
>
<label @dblclick="editing = true" v-text="todo.text" /> <label @dblclick="editing = true" v-text="todo.text" />
<button class="destroy" @click="deleteTodo( todo )" /> <button class="destroy" @click="deleteTodo( todo )" />
</div> </div>
<input <input
v-focus="editing"
v-show="editing" v-show="editing"
v-focus="editing"
:value="todo.text" :value="todo.text"
class="edit" class="edit"
@keyup.enter="doneEdit" @keyup.enter="doneEdit"
@keyup.esc="cancelEdit" @keyup.esc="cancelEdit"
@blur="doneEdit"> @blur="doneEdit"
>
</li> </li>
</template> </template>

View File

@ -15,7 +15,8 @@
:todo="todo" :todo="todo"
@toggleTodo="toggleTodo" @toggleTodo="toggleTodo"
@editTodo="editTodo" @editTodo="editTodo"
@deleteTodo="deleteTodo"/> @deleteTodo="deleteTodo"
/>
</ul> </ul>
</section> </section>
<!-- footer --> <!-- footer -->

View File

@ -12,7 +12,9 @@
</el-table-column> </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"> <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> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

View File

@ -1,7 +1,8 @@
<template> <template>
<div class="dashboard-editor-container"> <div class="dashboard-editor-container">
<div class=" clearfix"> <div class=" clearfix">
<pan-thumb :image="avatar" style="float: left"> Your roles: <pan-thumb :image="avatar" style="float: left">
Your roles:
<span v-for="item in roles" :key="item" class="pan-info-roles">{{ item }}</span> <span v-for="item in roles" :key="item" class="pan-info-roles">{{ item }}</span>
</pan-thumb> </pan-thumb>
<github-corner style="position: absolute; top: 0px; border: 0; right: 0;" /> <github-corner style="position: absolute; top: 0px; border: 0; right: 0;" />

View File

@ -9,7 +9,8 @@
</div> </div>
<div class="bullshit"> <div class="bullshit">
<div class="bullshit__oops">OOPS!</div> <div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">版权所有 <div class="bullshit__info">
版权所有
<a class="link-type" href="https://wallstreetcn.com" target="_blank">华尔街见闻</a> <a class="link-type" href="https://wallstreetcn.com" target="_blank">华尔街见闻</a>
</div> </div>
<div class="bullshit__headline">{{ message }}</div> <div class="bullshit__headline">{{ message }}</div>

View File

@ -6,7 +6,8 @@
<CommentDropdown v-model="postForm.comment_disabled" /> <CommentDropdown v-model="postForm.comment_disabled" />
<PlatformDropdown v-model="postForm.platforms" /> <PlatformDropdown v-model="postForm.platforms" />
<SourceUrlDropdown v-model="postForm.source_uri" /> <SourceUrlDropdown v-model="postForm.source_uri" />
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">发布 <el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
发布
</el-button> </el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">草稿</el-button> <el-button v-loading="loading" type="warning" @click="draftForm">草稿</el-button>
</sticky> </sticky>
@ -47,7 +48,8 @@
:colors="['#99A9BF', '#F7BA2A', '#FF9900']" :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
:low-threshold="1" :low-threshold="1"
:high-threshold="3" :high-threshold="3"
style="margin-top:8px;"/> style="margin-top:8px;"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -56,12 +58,12 @@
</el-row> </el-row>
<el-form-item style="margin-bottom: 40px;" label-width="45px" label="摘要:"> <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="请输入内容"/> <el-input v-model="postForm.content_short" :rows="1" type="textarea" class="article-textarea" autosize placeholder="请输入内容" />
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}</span> <span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}</span>
</el-form-item> </el-form-item>
<el-form-item prop="content" style="margin-bottom: 30px;"> <el-form-item prop="content" style="margin-bottom: 30px;">
<Tinymce ref="editor" :height="400" v-model="postForm.content" /> <Tinymce ref="editor" v-model="postForm.content" :height="400" />
</el-form-item> </el-form-item>
<el-form-item prop="image_uri" style="margin-bottom: 30px;"> <el-form-item prop="image_uri" style="margin-bottom: 30px;">

View File

@ -1,13 +1,18 @@
<template> <template>
<el-dropdown :show-timeout="100" trigger="click"> <el-dropdown :show-timeout="100" trigger="click">
<el-button plain>{{ !comment_disabled?'评论已打开':'评论已关闭' }} <el-button plain>
{{ !comment_disabled?'评论已打开':'评论已关闭' }}
<i class="el-icon-caret-bottom el-icon--right" /> <i class="el-icon-caret-bottom el-icon--right" />
</el-button> </el-button>
<el-dropdown-menu slot="dropdown" class="no-padding"> <el-dropdown-menu slot="dropdown" class="no-padding">
<el-dropdown-item> <el-dropdown-item>
<el-radio-group v-model="comment_disabled" style="padding: 10px;"> <el-radio-group v-model="comment_disabled" style="padding: 10px;">
<el-radio :label="true">关闭评论</el-radio> <el-radio :label="true">
<el-radio :label="false">打开评论</el-radio> 关闭评论
</el-radio>
<el-radio :label="false">
打开评论
</el-radio>
</el-radio-group> </el-radio-group>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>

View File

@ -6,7 +6,7 @@
</el-button> </el-button>
<el-dropdown-menu slot="dropdown" class="no-border"> <el-dropdown-menu slot="dropdown" class="no-border">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;"> <el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :label="item.key" :key="item.key"> <el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
{{ item.name }} {{ item.name }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>

View File

@ -7,7 +7,9 @@
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px"> <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-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input v-model="source_uri" placeholder="请输入内容"> <el-input v-model="source_uri" placeholder="请输入内容">
<template slot="prepend">填写url</template> <template slot="prepend">
填写url
</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
</el-dropdown-menu> </el-dropdown-menu>

View File

@ -4,7 +4,8 @@
的include直接缓存所有页面详情见 的include直接缓存所有页面详情见
<a <a
href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html" href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank">文档</a> target="_blank"
>文档</a>
</p> </p>
</template> </template>

View File

@ -6,7 +6,8 @@
v-for="item in options" v-for="item in options"
:key="item" :key="item"
:label="item" :label="item"
:value="item"/> :value="item"
/>
</el-select> </el-select>
</div> </div>
</template> </template>

View File

@ -2,7 +2,7 @@
<div style="display:inline-block;"> <div style="display:inline-block;">
<!-- $t is vue-i18n global function to translate lang --> <!-- $t is vue-i18n global function to translate lang -->
<label class="radio-label" style="padding-left:0;">Filename: </label> <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"/> <el-input v-model="filename" :placeholder="$t('excel.placeholder')" style="width:340px;" prefix-icon="el-icon-document" />
</div> </div>
</template> </template>

View File

@ -46,12 +46,10 @@
<script> <script>
import { fetchList } from '@/api/article' import { fetchList } from '@/api/article'
import { parseTime } from '@/utils' import { parseTime } from '@/utils'
// options components // options components
import FilenameOption from './components/FilenameOption' import FilenameOption from './components/FilenameOption'
import AutoWidthOption from './components/AutoWidthOption' import AutoWidthOption from './components/AutoWidthOption'
import BookTypeOption from './components/BookTypeOption' import BookTypeOption from './components/BookTypeOption'
export default { export default {
name: 'ExportExcel', name: 'ExportExcel',
components: { FilenameOption, AutoWidthOption, BookTypeOption }, components: { FilenameOption, AutoWidthOption, BookTypeOption },
@ -114,4 +112,3 @@ export default {
padding: 0 12px 0 30px; padding: 0 12px 0 30px;
} }
</style> </style>

View File

@ -0,0 +1,101 @@
<template>
<div class="app-container">
<el-button :loading="downloadLoading" style="margin-bottom:20px" type="primary" icon="document" @click="handleDownload">Export</el-button>
<el-table
ref="multipleTable"
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
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="Main Information" align="center">
<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>
<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'
export default {
name: 'MergeHeader',
data() {
return {
list: null,
listLoading: true,
downloadLoading: false
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const multiHeader = [['Id', 'Main Information', '', '', 'Date']]
const header = ['', 'Title', 'Author', 'Readings', '']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.list
const data = this.formatJson(filterVal, list)
const merges = ['A1:A2', 'B1:D1', 'E1:E2']
excel.export_json_to_excel({
multiHeader,
header,
merges,
data
})
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>

View File

@ -1,20 +1,21 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- $t is vue-i18n global function to translate lang --> <!-- $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-input v-model="filename" :placeholder="$t('excel.placeholder')" 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> <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;"> <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> <el-tag type="info">Documentation</el-tag>
</a> </a>
<el-table <el-table
v-loading="listLoading"
ref="multipleTable" ref="multipleTable"
v-loading="listLoading"
:data="list" :data="list"
element-loading-text="拼命加载中" element-loading-text="拼命加载中"
border border
fit fit
highlight-current-row highlight-current-row
@selection-change="handleSelectionChange"> @selection-change="handleSelectionChange"
>
<el-table-column type="selection" align="center" /> <el-table-column type="selection" align="center" />
<el-table-column align="center" label="Id" width="95"> <el-table-column align="center" label="Id" width="95">
<template slot-scope="scope"> <template slot-scope="scope">

View File

@ -2,7 +2,7 @@
<div class="app-container"> <div class="app-container">
<upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" /> <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 :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-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
</el-table> </el-table>
</div> </div>
</template> </template>

View File

@ -26,7 +26,8 @@
v-for="item in options" v-for="item in options"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"/> :value="item.value"
/>
</el-select> </el-select>
</div> </div>
<div class="block"> <div class="block">

View File

@ -15,11 +15,12 @@
</template> </template>
<sidebar-item <sidebar-item
v-for="child in item.children" v-for="child in item.children"
:key="child.path"
:is-nest="true" :is-nest="true"
:item="child" :item="child"
:key="child.path"
:base-path="resolvePath(child.path)" :base-path="resolvePath(child.path)"
class="nest-menu" /> class="nest-menu"
/>
</el-submenu> </el-submenu>
</div> </div>

View File

@ -9,7 +9,7 @@
:collapse-transition="false" :collapse-transition="false"
mode="vertical" mode="vertical"
> >
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/> <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</template> </template>
@ -23,7 +23,7 @@ export default {
components: { SidebarItem }, components: { SidebarItem },
computed: { computed: {
...mapGetters([ ...mapGetters([
'permission_routers', 'permission_routes',
'sidebar' 'sidebar'
]), ]),
variables() { variables() {

View File

@ -4,21 +4,23 @@
<router-link <router-link
v-for="tag in visitedViews" v-for="tag in visitedViews"
ref="tag" ref="tag"
:key="tag.path"
:class="isActive(tag)?'active':''" :class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
:key="tag.path"
tag="span" tag="span"
class="tags-view-item" class="tags-view-item"
@click.middle.native="closeSelectedTag(tag)" @click.middle.native="closeSelectedTag(tag)"
@contextmenu.prevent.native="openMenu(tag,$event)"> @contextmenu.prevent.native="openMenu(tag,$event)"
>
{{ generateTitle(tag.title) }} {{ generateTitle(tag.title) }}
<span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> <span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
</router-link> </router-link>
</scroll-pane> </scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">{{ $t('tagsView.refresh') }}</li> <li @click="refreshSelectedTag(selectedTag)">{{ $t('tagsView.refresh') }}</li>
<li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">{{ <li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">
$t('tagsView.close') }}</li> {{ $t('tagsView.close') }}
</li>
<li @click="closeOthersTags">{{ $t('tagsView.closeOthers') }}</li> <li @click="closeOthersTags">{{ $t('tagsView.closeOthers') }}</li>
<li @click="closeAllTags(selectedTag)">{{ $t('tagsView.closeAll') }}</li> <li @click="closeAllTags(selectedTag)">{{ $t('tagsView.closeAll') }}</li>
</ul> </ul>
@ -45,8 +47,8 @@ export default {
visitedViews() { visitedViews() {
return this.$store.state.tagsView.visitedViews return this.$store.state.tagsView.visitedViews
}, },
routers() { routes() {
return this.$store.state.permission.routers return this.$store.state.permission.routes
} }
}, },
watch: { watch: {
@ -93,7 +95,7 @@ export default {
return tags return tags
}, },
initTags() { initTags() {
const affixTags = this.affixTags = this.filterAffixTags(this.routers) const affixTags = this.affixTags = this.filterAffixTags(this.routes)
for (const tag of affixTags) { for (const tag of affixTags) {
// Must have tag name // Must have tag name
if (tag.name) { if (tag.name) {

View File

@ -6,6 +6,7 @@
<div style="color: #ccc;"> <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> 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>
<!-- eslint-disable-next-line -->
<div ref="content" class="node-article-content" v-html="article.content" /> <div ref="content" class="node-article-content" v-html="article.content" />
</div> </div>
</template> </template>

View File

@ -0,0 +1,268 @@
<template>
<div class="app-container">
<el-button type="primary" @click="handleAddRole">{{ $t('permission.addRole') }}</el-button>
<el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
<el-table-column align="center" label="Role Key" width="220">
<template slot-scope="scope">{{ scope.row.key }}</template>
</el-table-column>
<el-table-column align="center" label="Role Name" width="220">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column align="header-center" label="Description">
<template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column>
<el-table-column align="center" label="Operations">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleEdit(scope)">{{ $t('permission.editPermission') }}</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope)">{{ $t('permission.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
<el-form :model="role" label-width="80px" label-position="left">
<el-form-item label="Name">
<el-input v-model="role.name" placeholder="Role Name" />
</el-form-item>
<el-form-item label="Desc">
<el-input
v-model="role.description"
:autosize="{ minRows: 2, maxRows: 4}"
type="textarea"
placeholder="Role Description"
/>
</el-form-item>
<el-form-item label="Menus">
<el-tree ref="tree" :check-strictly="checkStrictly" :data="routesData" :props="defaultProps" show-checkbox node-key="path" class="permission-tree" />
</el-form-item>
</el-form>
<div style="text-align:right;">
<el-button type="danger" @click="dialogVisible=false">{{ $t('permission.cancel') }}</el-button>
<el-button type="primary" @click="confirmRole">{{ $t('permission.confirm') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import path from 'path'
import { deepClone } from '@/utils'
import { getRoutes, getRoles, addRole, deleteRole, updateRole } from '@/api/role'
import i18n from '@/lang'
const defaultRole = {
key: '',
name: '',
description: '',
routes: []
}
export default {
data() {
return {
role: Object.assign({}, defaultRole),
routes: [],
rolesList: [],
dialogVisible: false,
dialogType: 'new',
checkStrictly: false,
defaultProps: {
children: 'children',
label: 'title'
}
}
},
computed: {
routesData() {
return this.routes
}
},
created() {
// Mock: get all routes and roles list from server
this.getRoutes()
this.getRoles()
},
methods: {
async getRoutes() {
const res = await getRoutes()
this.serviceRoutes = res.data
const routes = this.generateRoutes(res.data)
this.routes = this.i18n(routes)
},
async getRoles() {
const res = await getRoles()
this.rolesList = res.data
},
i18n(routes) {
const app = routes.map(route => {
route.title = i18n.t(`route.${route.title}`)
if (route.children) {
route.children = this.i18n(route.children)
}
return route
})
return app
},
// Reshape the routes structure so that it looks the same as the sidebar
generateRoutes(routes, basePath = '/') {
const res = []
for (let route of routes) {
// skip some route
if (route.hidden) { continue }
const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)
if (route.children && onlyOneShowingChild && !route.alwaysShow) {
route = onlyOneShowingChild
}
const data = {
path: path.resolve(basePath, route.path),
title: route.meta && route.meta.title
}
// recursive child routes
if (route.children) {
data.children = this.generateRoutes(route.children, data.path)
}
res.push(data)
}
return res
},
generateArr(routes) {
let data = []
routes.forEach(route => {
data.push(route)
if (route.children) {
const temp = this.generateArr(route.children)
if (temp.length > 0) {
data = [...data, ...temp]
}
}
})
return data
},
handleAddRole() {
this.role = Object.assign({}, defaultRole)
if (this.$refs.tree) {
this.$refs.tree.setCheckedNodes([])
}
this.dialogType = 'new'
this.dialogVisible = true
},
handleEdit(scope) {
this.dialogType = 'edit'
this.dialogVisible = true
this.checkStrictly = true
this.role = deepClone(scope.row)
this.$nextTick(() => {
const routes = this.generateRoutes(this.role.routes)
this.$refs.tree.setCheckedNodes(this.generateArr(routes))
// set checked state of a node not affects its father and child nodes
this.checkStrictly = false
})
},
handleDelete({ $index, row }) {
this.$confirm('Confirm to remove the role?', 'Warning', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(async() => {
await deleteRole(row.id)
this.rolesList.splice($index, 1)
this.$message({
type: 'success',
message: 'Delete succed!'
})
})
.catch(err => { console.error(err) })
},
generateTree(routes, basePath = '/', checkedKeys) {
const res = []
for (const route of routes) {
const routePath = path.resolve(basePath, route.path)
// recursive child routes
if (route.children) {
route.children = this.generateTree(route.children, routePath, checkedKeys)
}
if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
res.push(route)
}
}
return res
},
async confirmRole() {
const isEdit = this.dialogType === 'edit'
const checkedKeys = this.$refs.tree.getCheckedKeys()
this.role.routes = this.generateTree(deepClone(this.serviceRoutes), '/', checkedKeys)
if (isEdit) {
await updateRole(this.role.key, this.role)
for (let index = 0; index < this.rolesList.length; index++) {
if (this.rolesList[index].key === this.role.key) {
this.rolesList.splice(index, 1, Object.assign({}, this.role))
break
}
}
} else {
const { data } = await addRole(this.role)
this.role.key = data
this.rolesList.push(this.role)
}
const { description, key, name } = this.role
this.dialogVisible = false
this.$notify({
title: 'Success',
dangerouslyUseHTMLString: true,
message: `
<div>Role Key: ${key}</div>
<div>Role Nmae: ${name}</div>
<div>Description: ${description}</div>
`,
type: 'success'
})
},
// reference: src/view/layout/components/Sidebar/SidebarItem.vue
onlyOneShowingChild(children = [], parent) {
let onlyOneChild = null
const showingChildren = children.filter(item => !item.hidden)
// When there is only one child route, the child route is displayed by default
if (showingChildren.length === 1) {
onlyOneChild = showingChildren[0]
onlyOneChild.path = path.resolve(parent.path, onlyOneChild.path)
return onlyOneChild
}
// Show parent if there are no child route to display
if (showingChildren.length === 0) {
onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return onlyOneChild
}
return false
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
.roles-table {
margin-top: 30px;
}
.permission-tree {
margin-bottom: 30px;
}
}
</style>

View File

@ -6,7 +6,8 @@
align="center" align="center"
label="ID" label="ID"
width="65" width="65"
element-loading-text="请给我点时间!"> element-loading-text="请给我点时间!"
>
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.id }}</span> <span>{{ scope.row.id }}</span>
</template> </template>
@ -45,7 +46,9 @@
<el-table-column class-name="status-col" label="Status" width="110"> <el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="scope"> <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> </template>
</el-table-column> </el-table-column>

View File

@ -3,7 +3,7 @@
<el-tag>mounted times {{ createdTimes }}</el-tag> <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-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-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"> <el-tab-pane v-for="item in tabMapOptions" :key="item.key" :label="item.label" :name="item.key">
<keep-alive> <keep-alive>
<tab-pane v-if="activeName==item.key" :type="item.key" @create="showCreatedTimes" /> <tab-pane v-if="activeName==item.key" :type="item.key" @create="showCreatedTimes" />
</keep-alive> </keep-alive>

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div class="filter-container"> <div class="filter-container">
<el-input :placeholder="$t('table.title')" v-model="listQuery.title" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter"/> <el-input v-model="listQuery.title" :placeholder="$t('table.title')" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.importance" :placeholder="$t('table.importance')" clearable style="width: 90px" class="filter-item"> <el-select v-model="listQuery.importance" :placeholder="$t('table.importance')" clearable style="width: 90px" class="filter-item">
<el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item" /> <el-option v-for="item in importanceOptions" :key="item" :label="item" :value="item" />
</el-select> </el-select>
@ -18,14 +18,15 @@
</div> </div>
<el-table <el-table
v-loading="listLoading"
:key="tableKey" :key="tableKey"
v-loading="listLoading"
:data="list" :data="list"
border border
fit fit
highlight-current-row highlight-current-row
style="width: 100%;" style="width: 100%;"
@sort-change="sortChange"> @sort-change="sortChange"
>
<el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="65"> <el-table-column :label="$t('table.id')" prop="id" sortable="custom" align="center" width="65">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.row.id }}</span> <span>{{ scope.row.id }}</span>
@ -71,11 +72,14 @@
<el-table-column :label="$t('table.actions')" align="center" width="230" class-name="small-padding fixed-width"> <el-table-column :label="$t('table.actions')" align="center" width="230" class-name="small-padding fixed-width">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">{{ $t('table.edit') }}</el-button> <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">{{ $t('table.edit') }}</el-button>
<el-button v-if="scope.row.status!='published'" size="mini" type="success" @click="handleModifyStatus(scope.row,'published')">{{ $t('table.publish') }} <el-button v-if="scope.row.status!='published'" size="mini" type="success" @click="handleModifyStatus(scope.row,'published')">
{{ $t('table.publish') }}
</el-button> </el-button>
<el-button v-if="scope.row.status!='draft'" size="mini" @click="handleModifyStatus(scope.row,'draft')">{{ $t('table.draft') }} <el-button v-if="scope.row.status!='draft'" size="mini" @click="handleModifyStatus(scope.row,'draft')">
{{ $t('table.draft') }}
</el-button> </el-button>
<el-button v-if="scope.row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">{{ $t('table.delete') }} <el-button v-if="scope.row.status!='deleted'" size="mini" type="danger" @click="handleModifyStatus(scope.row,'deleted')">
{{ $t('table.delete') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -105,7 +109,7 @@
<el-rate v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max="3" style="margin-top:8px;" /> <el-rate v-model="temp.importance" :colors="['#99A9BF', '#F7BA2A', '#FF9900']" :max="3" style="margin-top:8px;" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('table.remark')"> <el-form-item :label="$t('table.remark')">
<el-input :autosize="{ minRows: 2, maxRows: 4}" v-model="temp.remark" type="textarea" placeholder="Please input"/> <el-input v-model="temp.remark" :autosize="{ minRows: 2, maxRows: 4}" type="textarea" placeholder="Please input" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- Note that row-key is necessary to get a correct row order. --> <!-- Note that row-key is necessary to get a correct row order. -->
<el-table v-loading="listLoading" ref="dragTable" :data="list" row-key="id" border fit highlight-current-row style="width: 100%"> <el-table ref="dragTable" 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"> <el-table-column align="center" label="ID" width="65">
<template slot-scope="scope"> <template slot-scope="scope">

View File

@ -9,7 +9,7 @@
</el-checkbox-group> </el-checkbox-group>
</div> </div>
<el-table :data="tableData" :key="key" border fit highlight-current-row style="width: 100%"> <el-table :key="key" :data="tableData" border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180" /> <el-table-column prop="name" label="fruitName" width="180" />
<el-table-column v-for="fruit in formThead" :key="fruit" :label="fruit"> <el-table-column v-for="fruit in formThead" :key="fruit" :label="fruit">
<template slot-scope="scope"> <template slot-scope="scope">

View File

@ -33,7 +33,7 @@
</div> </div>
<div class="block"> <div class="block">
<el-tag v-for="tag in tags" :type="tag.type" :key="tag.type" class="tag-item"> <el-tag v-for="tag in tags" :key="tag.type" :type="tag.type" class="tag-item">
{{ tag.name }} {{ tag.name }}
</el-tag> </el-tag>
</div> </div>

View File

@ -38,7 +38,8 @@
:style="{ width:(scope.row.timeLine||0) * 3+'px', :style="{ width:(scope.row.timeLine||0) * 3+'px',
background:scope.row.timeLine>50?'rgba(233,0,0,.5)':'rgba(0,0,233,0.5)', background:scope.row.timeLine>50?'rgba(233,0,0,.5)':'rgba(0,0,233,0.5)',
marginLeft:scope.row._level * 50+'px' }" marginLeft:scope.row._level * 50+'px' }"
class="process"> class="process"
>
<span style="display:inline-block" /> <span style="display:inline-block" />
</div> </div>
</div> </div>

View File

@ -13,7 +13,8 @@
v-model="defaultExpandAll" v-model="defaultExpandAll"
active-color="#13ce66" active-color="#13ce66"
inactive-color="#ff4949" inactive-color="#ff4949"
@change="reset"/> @change="reset"
/>
</div> </div>
<div class="option-item"> <div class="option-item">

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- $t is vue-i18n global function to translate lang --> <!-- $t is vue-i18n global function to translate lang -->
<el-input :placeholder="$t('zip.placeholder')" v-model="filename" style="width:300px;" prefix-icon="el-icon-document"/> <el-input v-model="filename" :placeholder="$t('zip.placeholder')" style="width:300px;" prefix-icon="el-icon-document" />
<el-button :loading="downloadLoading" style="margin-bottom:20px;" type="primary" icon="document" @click="handleDownload">{{ $t('zip.export') }} zip</el-button> <el-button :loading="downloadLoading" style="margin-bottom:20px;" type="primary" icon="document" @click="handleDownload">{{ $t('zip.export') }} zip</el-button>
<el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row> <el-table v-loading="listLoading" :data="list" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table-column align="center" label="ID" width="95"> <el-table-column align="center" label="ID" width="95">