Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pleroma/admin-fe
  • linafilippova/admin-fe
  • Exilat_a_Tolosa/admin-fe
  • mkljczk/admin-fe
  • maxf/admin-fe
  • kphrx/admin-fe
  • vaartis/admin-fe
  • ELR/admin-fe
  • eugenijm/admin-fe
  • jp/admin-fe
  • mkfain/admin-fe
  • lorenzoancora/admin-fe
  • alexgleason/admin-fe
  • seanking/admin-fe
  • ilja/admin-fe
15 results
Show changes
Showing
with 466 additions and 1595 deletions
<template>
<el-table :data="list"border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="序号" width="65" v-loading="loading"
element-loading-text="请给我点时间!">
<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="300px" label="标题">
<template slot-scope="scope">
<span>{{scope.row.title}}</span>
<el-tag>{{scope.row.type}}</el-tag>
</template>
</el-table-column>
<el-table-column width="110px" align="center" label="作者">
<template slot-scope="scope">
<span>{{scope.row.author}}</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" :key="n"></svg-icon>
</template>
</el-table-column>
<el-table-column align="center" label="阅读数" width="95">
<template slot-scope="scope">
<span>{{scope.row.pageviews}}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
props: {
type: {
type: String,
default: 'CN'
}
},
data() {
return {
list: null,
listQuery: {
page: 1,
limit: 5,
type: this.type,
sort: '+id'
},
loading: false
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
created() {
this.getList()
},
methods: {
getList() {
this.loading = true
this.$emit('create') // for test
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.loading = false
})
}
}
}
</script>
<template>
<div class="tab-container">
<el-tag>mounted times :{{createdTimes}}</el-tag>
<el-tabs style='margin-top:15px;' v-model="activeName" type="border-card">
<el-tab-pane v-for="item in tabMapOptions" :label="item.label" :key='item.key' :name="item.key">
<keep-alive>
<tab-pane v-if='activeName==item.key' :type='item.key' @create='showCreatedTimes'></tab-pane>
</keep-alive>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import tabPane from './components/tabPane'
export default {
name: 'tab',
components: { tabPane },
data() {
return {
tabMapOptions: [
{ label: '中国', key: 'CN' },
{ label: '美国', key: 'US' },
{ label: '日本', key: 'JP' },
{ label: '欧元区', key: 'EU' }
],
activeName: 'CN',
createdTimes: 0
}
},
methods: {
showCreatedTimes() {
this.createdTimes = this.createdTimes + 1
}
}
}
</script>
<style scoped>
.tab-container{
margin: 30px;
}
</style>
<template>
<div class="app-container calendar-list-container">
<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-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-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>
</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">
<template slot-scope="scope">
<el-button v-if="scope.row.status!='published'" size="small" 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>
<el-button v-if="scope.row.status!='deleted'" size="small" 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"
: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-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="状态">
<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>
</el-form-item>
</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>
</div>
</el-dialog>
<el-dialog title="阅读数统计" :visible.sync="dialogPvVisible">
<el-table :data="pvData" border fit highlight-current-row style="width: 100%">
<el-table-column prop="key" label="渠道"> </el-table-column>
<el-table-column prop="pv" label="pv"> </el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogPvVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { fetchList, fetchPv } from '@/api/article'
import waves from '@/directive/waves/index.js' // 水波纹指令
import { parseTime } from '@/utils'
const calendarTypeOptions = [
{ key: 'CN', display_name: '中国' },
{ key: 'US', display_name: '美国' },
{ key: 'JP', display_name: '日本' },
{ key: 'EU', display_name: '欧元区' }
]
// arr to obj
const calendarTypeKeyValue = calendarTypeOptions.reduce((acc, cur) => {
acc[cur.key] = cur.display_name
return acc
}, {})
export default {
name: 'complexTable',
directives: {
waves
},
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 20,
importance: undefined,
title: undefined,
type: undefined,
sort: '+id'
},
temp: {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
type: '',
status: 'published'
},
importanceOptions: [1, 2, 3],
calendarTypeOptions,
sortOptions: [{ label: '按ID升序列', key: '+id' }, { label: '按ID降序', key: '-id' }],
statusOptions: ['published', 'draft', 'deleted'],
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: '编辑',
create: '创建'
},
dialogPvVisible: false,
pvData: [],
showAuditor: false,
tableKey: 0
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
},
typeFilter(type) {
return calendarTypeKeyValue[type]
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
timeFilter(time) {
if (!time[0]) {
this.listQuery.start = undefined
this.listQuery.end = undefined
return
}
this.listQuery.start = parseInt(+time[0] / 1000)
this.listQuery.end = parseInt((+time[1] + 3600 * 1000 * 24) / 1000)
},
handleModifyStatus(row, status) {
this.$message({
message: '操作成功',
type: 'success'
})
row.status = status
},
handleCreate() {
this.resetTemp()
this.dialogStatus = 'create'
this.dialogFormVisible = true
},
handleUpdate(row) {
this.temp = Object.assign({}, row)
this.dialogStatus = 'update'
this.dialogFormVisible = true
},
handleDelete(row) {
this.$notify({
title: '成功',
message: '删除成功',
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
},
create() {
this.temp.id = parseInt(Math.random() * 100) + 1024
this.temp.timestamp = +new Date()
this.temp.author = '原创作者'
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000
})
},
update() {
this.temp.timestamp = +this.temp.timestamp
for (const v of this.list) {
if (v.id === this.temp.id) {
const index = this.list.indexOf(v)
this.list.splice(index, 1, this.temp)
break
}
}
this.dialogFormVisible = false
this.$notify({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000
})
},
resetTemp() {
this.temp = {
id: undefined,
importance: 0,
remark: '',
timestamp: 0,
title: '',
status: 'published',
type: ''
}
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData
this.dialogPvVisible = true
})
},
handleDownload() {
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel')
const tHeader = ['时间', '地区', '类型', '标题', '重要性']
const filterVal = ['timestamp', 'province', 'type', 'title', 'importance']
const data = this.formatJson(filterVal, this.list)
export_json_to_excel(tHeader, data, 'table数据')
})
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => {
if (j === 'timestamp') {
return parseTime(v[j])
} else {
return v[j]
}
}))
}
}
}
</script>
<template>
<div class="app-container calendar-list-container">
<el-table :data="list" v-loading.body="listLoading" 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="300px" label="标题">
<template slot-scope="scope">
<span>{{scope.row.title}}</span>
</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="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>{{scope.row.pageviews}}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="状态" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="拖拽" width="95">
<template slot-scope="scope">
<svg-icon class='drag-handler' icon-class="drag"></svg-icon>
</template>
</el-table-column>
</el-table>
<div class='show-d'>默认顺序 &nbsp; {{ olderList}}</div>
<div class='show-d'>拖拽后顺序{{newList}}</div>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
import Sortable from 'sortablejs'
export default {
name: 'dragTable',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
},
sortable: null,
olderList: [],
newList: []
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
this.olderList = this.list.map(v => v.id)
this.newList = this.olderList.slice()
this.$nextTick(() => {
this.setSort()
})
})
},
setSort() {
const el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
this.sortable = Sortable.create(el, {
onEnd: evt => {
const tempIndex = this.newList.splice(evt.oldIndex, 1)[0]
this.newList.splice(evt.newIndex, 0, tempIndex)
}
})
}
}
}
</script>
<style scoped>
.drag-handler{
width: 30px;
height: 30px;
cursor: pointer;
}
.show-d{
margin-top: 15px;
}
</style>
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="checkboxVal">
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" :key='key' border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180"></el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<template slot-scope="scope">
{{scope.row[fruit]}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
const defaultFormThead = ['apple', 'banana'] // 默认选中项
export default {
data() {
return {
tableData: [
{
name: 'fruit-1',
apple: 'apple-10',
banana: 'banana-10',
orange: 'orange-10'
},
{
name: 'fruit-2',
apple: 'apple-20',
banana: 'banana-20',
orange: 'orange-20'
}
],
key: 1, // table key
formTheadOptions: ['apple', 'banana', 'orange'], // 可选择表头
checkboxVal: defaultFormThead, // checkboxVal
formThead: defaultFormThead // 默认表头
}
},
watch: {
checkboxVal(valArr) {
this.formThead = this.formTheadOptions.filter(i => valArr.indexOf(i) >= 0)
this.key = this.key + 1// 为了保证table 每次都会重渲 (牺牲性能保证效果,当然也可以不用)
}
}
}
</script>
<template>
<div class="app-container">
<div style='margin:0 0 5px 20px'>固定表头 按照表头顺序排序</div>
<fixed-thead></fixed-thead>
<div style='margin:30px 0 5px 20px'>不固定表头 按照点击顺序排序</div>
<unfixed-thead></unfixed-thead>
</div>
</template>
<script>
import fixedThead from './fixedThead'
import unfixedThead from './unfixedThead'
export default {
name: 'dynamicTable',
components: { fixedThead, unfixedThead }
}
</script>
<template>
<div class="app-container">
<div class="filter-container">
<el-checkbox-group v-model="formThead">
<el-checkbox label="apple">apple</el-checkbox>
<el-checkbox label="banana">banana</el-checkbox>
<el-checkbox label="orange">orange</el-checkbox>
</el-checkbox-group>
</div>
<el-table :data="tableData" border fit highlight-current-row style="width: 100%">
<el-table-column prop="name" label="fruitName" width="180">
</el-table-column>
<el-table-column :key='fruit' v-for='(fruit,index) in formThead' :label="fruit">
<template slot-scope="scope">
{{scope.row[fruit]}}
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [
{
name: 'fruit-1',
apple: 'apple-10',
banana: 'banana-10',
orange: 'orange-10'
},
{
name: 'fruit-2',
apple: 'apple-20',
banana: 'banana-20',
orange: 'orange-20'
}
],
formThead: ['apple', 'banana']
}
}
}
</script>
<template>
<transition name="fade" mode="out-in">
<keep-alive :include='cachedViews'>
<router-view></router-view>
</keep-alive>
</transition>
</template>
<script>
export default {
name: 'TableMain',
computed: {
cachedViews() {
return this.$store.state.app.cachedViews
}
}
}
</script>
<template>
<div class="app-container calendar-list-container">
<el-table :data="list" v-loading.body="listLoading" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" label="序号" width="80">
<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 width="120px" align="center" label="作者">
<template slot-scope="scope">
<span>{{scope.row.author}}</span>
</template>
</el-table-column>
<el-table-column width="100px" 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 class-name="status-col" label="状态" width="110">
<template slot-scope="scope">
<el-tag :type="scope.row.status | statusFilter">{{scope.row.status}}</el-tag>
</template>
</el-table-column>
<el-table-column min-width="300px" label="标题">
<template slot-scope="scope">
<el-input v-show="scope.row.edit" size="small" v-model="scope.row.title"></el-input>
<span v-show="!scope.row.edit">{{ scope.row.title }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="编辑" width="120">
<template slot-scope="scope">
<el-button :type="scope.row.edit?'success':'primary'" @click='scope.row.edit=!scope.row.edit' size="small" icon="edit">{{scope.row.edit?'完成':'编辑'}}</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'inlineEditTable',
data() {
return {
list: null,
listLoading: true,
listQuery: {
page: 1,
limit: 10
}
}
},
filters: {
statusFilter(status) {
const statusMap = {
published: 'success',
draft: 'info',
deleted: 'danger'
}
return statusMap[status]
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
const items = response.data.items
this.list = items.map(v => {
this.$set(v, 'edit', false)
return v
})
this.listLoading = false
})
}
}
}
</script>
<template>
<div class="app-container">
<el-input style='width:240px;' placeholder="请输入文件名(默认excel-list)" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px;' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">导出excel</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row>
<el-table-column align="center" label='ID' width="95">
<template slot-scope="scope">
{{scope.$index}}
</template>
</el-table-column>
<el-table-column label="文章标题">
<template slot-scope="scope">
{{scope.row.title}}
</template>
</el-table-column>
<el-table-column label="作者" width="95" align="center">
<template slot-scope="scope">
<el-tag>{{scope.row.author}}</el-tag>
</template>
</el-table-column>
<el-table-column label="阅读数" width="115" align="center">
<template slot-scope="scope">
{{scope.row.pageviews}}
</template>
</el-table-column>
<el-table-column align="center" prop="created_at" label="发布时间" width="220">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<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: 'exportExcel',
data() {
return {
list: null,
listLoading: true,
downloadLoading: false,
filename: ''
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList().then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleDownload() {
this.downloadLoading = true
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel')
const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.list
const data = this.formatJson(filterVal, list)
export_json_to_excel(tHeader, data, this.filename)
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>
<template>
<div class="app-container">
<el-input style='width:240px;' placeholder="请输入文件名(默认excel-list)" prefix-icon="el-icon-document" v-model="filename"></el-input>
<el-button style='margin-bottom:20px' type="primary" icon="document" @click="handleDownload" :loading="downloadLoading">导出已选择项</el-button>
<el-table :data="list" v-loading.body="listLoading" element-loading-text="拼命加载中" border fit highlight-current-row @selection-change="handleSelectionChange"
ref="multipleTable">
<el-table-column type="selection" align="center"></el-table-column>
<el-table-column align="center" label='ID' width="95">
<template slot-scope="scope">
{{scope.$index}}
</template>
</el-table-column>
<el-table-column label="文章标题">
<template slot-scope="scope">
{{scope.row.title}}
</template>
</el-table-column>
<el-table-column label="作者" width="95" align="center">
<template slot-scope="scope">
<el-tag>{{scope.row.author}}</el-tag>
</template>
</el-table-column>
<el-table-column label="阅读数" width="115" align="center">
<template slot-scope="scope">
{{scope.row.pageviews}}
</template>
</el-table-column>
<el-table-column align="center" prop="created_at" label="发布时间" width="220">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span>{{scope.row.display_time}}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'selectExcel',
data() {
return {
list: null,
listLoading: true,
multipleSelection: [],
downloadLoading: false,
filename: ''
}
},
created() {
this.fetchData()
},
methods: {
fetchData() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.listLoading = false
})
},
handleSelectionChange(val) {
this.multipleSelection = val
},
handleDownload() {
if (this.multipleSelection.length) {
this.downloadLoading = true
require.ensure([], () => {
const { export_json_to_excel } = require('vendor/Export2Excel')
const tHeader = ['序号', '文章标题', '作者', '阅读数', '发布时间']
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
const list = this.multipleSelection
const data = this.formatJson(filterVal, list)
export_json_to_excel(tHeader, data, this.filename)
this.$refs.multipleTable.clearSelection()
this.downloadLoading = false
})
} else {
this.$message({
message: '请至少选择一条记录',
type: 'warning'
})
}
},
formatJson(filterVal, jsonData) {
return jsonData.map(v => filterVal.map(j => v[j]))
}
}
}
</script>
<template>
<div class="app-container">
<upload-excel-component @on-selected-file='selected'></upload-excel-component>
<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>
</el-table>
</div>
</template>
<script>
import UploadExcelComponent from 'components/UploadExcel/index.vue'
export default {
name: 'uploadExcel',
components: { UploadExcelComponent },
data() {
return {
tableData: [],
tableHeader: []
}
},
methods: {
selected(data) {
this.tableData = data.results
this.tableHeader = data.header
}
}
}
</script>
<template>
<div class="createPost-container">
<el-form class="form-container" :model="postForm" :rules="rules" ref="postForm">
<sticky :className="'sub-navbar '+postForm.status">
<template v-if="fetchSuccess">
<router-link style="margin-right:15px;" v-show='isEdit' :to="{ path:'create-form'}">
<el-button type="info">创建form</el-button>
</router-link>
<el-dropdown trigger="click">
<el-button>{{!postForm.comment_disabled?'评论已打开':'评论已关闭'}}
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding" slot="dropdown">
<el-dropdown-item>
<el-radio-group style="padding: 10px;" v-model="postForm.comment_disabled">
<el-radio :label="true">关闭评论</el-radio>
<el-radio :label="false">打开评论</el-radio>
</el-radio-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown trigger="click">
<el-button>平台
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-border" slot="dropdown">
<el-checkbox-group v-model="postForm.platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :label="item.key" :key="item.key">
{{item.name}}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown trigger="click">
<el-button>
外链
<i class="el-icon-caret-bottom el-icon--right"></i>
</el-button>
<el-dropdown-menu class="no-padding no-border" style="width:300px" slot="dropdown">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input placeholder="请输入内容" v-model="postForm.source_uri">
<template slot="prepend">填写url</template>
</el-input>
</el-form-item>
</el-dropdown-menu>
</el-dropdown>
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm()">发布
</el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">草稿</el-button>
</template>
<template v-else>
<el-tag>发送异常错误,刷新页面,或者联系程序员</el-tag>
</template>
</sticky>
<div class="createPost-main-container">
<el-row>
<el-col :span="21">
<el-form-item style="margin-bottom: 40px;" prop="title">
<MDinput name="name" v-model="postForm.title" required :maxlength="100">
标题
</MDinput>
<span v-show="postForm.title.length>=26" class='title-prompt'>app可能会显示不全</span>
</el-form-item>
<div class="postInfo-container">
<el-row>
<el-col :span="8">
<el-form-item label-width="45px" label="作者:" class="postInfo-container-item">
<multiselect v-model="postForm.author" :options="userLIstOptions" @search-change="getRemoteUserList" placeholder="搜索用户" selectLabel="选择"
deselectLabel="删除" track-by="key" :internalSearch="false" label="key">
<span slot='noResult'>无结果</span>
</multiselect>
</el-form-item>
</el-col>
<el-col :span="8">
<el-tooltip class="item" effect="dark" content="将替换作者" placement="top">
<el-form-item label-width="50px" label="来源:" class="postInfo-container-item">
<el-input placeholder="将替换作者" style='min-width:150px;' v-model="postForm.source_name">
</el-input>
</el-form-item>
</el-tooltip>
</el-col>
<el-col :span="8">
<el-form-item label-width="80px" label="发布时间:" class="postInfo-container-item">
<el-date-picker v-model="postForm.display_time" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-form-item style="margin-bottom: 40px;" label-width="45px" label="摘要:">
<el-input type="textarea" class="article-textarea" :rows="1" autosize placeholder="请输入内容" v-model="postForm.content_short">
</el-input>
<span class="word-counter" v-show="contentShortLength">{{contentShortLength}}字</span>
</el-form-item>
<div class="editor-container">
<tinymce :height=400 ref="editor" v-model="postForm.content"></tinymce>
</div>
<div style="margin-bottom: 20px;">
<Upload v-model="postForm.image_uri"></Upload>
</div>
</div>
</el-form>
</div>
</template>
<script>
import Tinymce from '@/components/Tinymce'
import Upload from '@/components/Upload/singleImage3'
import MDinput from '@/components/MDinput'
import Multiselect from 'vue-multiselect'// 使用的一个多选框组件,element-ui的select不能满足所有需求
import 'vue-multiselect/dist/vue-multiselect.min.css'// 多选框组件css
import Sticky from '@/components/Sticky' // 粘性header组件
import { validateURL } from '@/utils/validate'
import { fetchArticle } from '@/api/article'
import { userSearch } from '@/api/remoteSearch'
const defaultForm = {
title: '', // 文章题目
content: '', // 文章内容
content_short: '', // 文章摘要
source_uri: '', // 文章外链
image_uri: '', // 文章图片
source_name: '', // 文章外部作者
display_time: undefined, // 前台展示时间
id: undefined,
platforms: ['a-platform'],
comment_disabled: false
}
export default {
name: 'articleDetail',
components: { Tinymce, MDinput, Upload, Multiselect, Sticky },
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
this.$message({
message: rule.field + '为必传项',
type: 'error'
})
callback(null)
} else {
callback()
}
}
const validateSourceUri = (rule, value, callback) => {
if (value) {
if (validateURL(value)) {
callback()
} else {
this.$message({
message: '外链url填写不正确',
type: 'error'
})
callback(null)
}
} else {
callback()
}
}
return {
postForm: Object.assign({}, defaultForm),
fetchSuccess: true,
loading: false,
userLIstOptions: [],
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
],
rules: {
image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }],
source_uri: [{ validator: validateSourceUri, trigger: 'blur' }]
}
}
},
computed: {
contentShortLength() {
return this.postForm.content_short.length
}
},
created() {
if (this.isEdit) {
this.fetchData()
} else {
this.postForm = Object.assign({}, defaultForm)
}
},
methods: {
fetchData() {
fetchArticle().then(response => {
this.postForm = response.data
}).catch(err => {
this.fetchSuccess = false
console.log(err)
})
},
submitForm() {
this.postForm.display_time = parseInt(this.display_time / 1000)
console.log(this.postForm)
this.$refs.postForm.validate(valid => {
if (valid) {
this.loading = true
this.$notify({
title: '成功',
message: '发布文章成功',
type: 'success',
duration: 2000
})
this.postForm.status = 'published'
this.loading = false
} else {
console.log('error submit!!')
return false
}
})
},
draftForm() {
if (this.postForm.content.length === 0 || this.postForm.title.length === 0) {
this.$message({
message: '请填写必要的标题和内容',
type: 'warning'
})
return
}
this.$message({
message: '保存成功',
type: 'success',
showClose: true,
duration: 1000
})
this.postForm.status = 'draft'
},
getRemoteUserList(query) {
userSearch(query).then(response => {
if (!response.data.items) return
console.log(response)
this.userLIstOptions = response.data.items.map(v => ({
key: v.name
}))
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.title-prompt{
position: absolute;
right: 0px;
font-size: 12px;
top:10px;
color:#ff4949;
}
.createPost-container {
position: relative;
.createPost-main-container {
padding: 40px 45px 20px 50px;
.postInfo-container {
position: relative;
@include clearfix;
margin-bottom: 10px;
.postInfo-container-item {
float: left;
}
}
.editor-container {
min-height: 500px;
margin: 0 0 30px;
.editor-upload-btn-container {
text-align: right;
margin-right: 10px;
.editor-upload-btn {
display: inline-block;
}
}
}
}
.word-counter {
width: 40px;
position: absolute;
right: -10px;
top: 0px;
}
}
</style>
<template>
<article-detail :is-edit='false'></article-detail>
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'createForm',
components: { ArticleDetail }
}
</script>
<template>
<article-detail :is-edit='true'></article-detail>
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'editForm',
components: { ArticleDetail }
}
</script>
<template>
<div class="app-container">
<el-card class="box-card">
<div slot="header" class="clearfix">
<svg-icon icon-class="international" />
<span style='margin-left:10px;'>{{$t('i18nView.title')}}</span>
</div>
<div>
<el-radio-group v-model="lang" size="small">
<el-radio label="zh" border>简体中文</el-radio>
<el-radio label="en" border>English</el-radio>
</el-radio-group>
<el-tag style='margin-top:15px;display:block;' type="info">{{$t('i18nView.note')}}</el-tag>
</div>
</el-card>
<el-row :gutter="20" style="margin:100px 50px 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"
: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>
</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="address" :label="$t('i18nView.tableAddress')"></el-table-column>
</el-table>
</el-col>
</el-row>
</div>
</template>
<script>
import local from './local'
const viewName = 'i18nView'
export default {
name: 'i18n',
data() {
return {
date: '',
currentPage: 5,
tableData: [{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles'
}]
}
},
created() {
if (!this.$i18n.getLocaleMessage('en')[viewName]) {
this.$i18n.mergeLocaleMessage('en', local.en)
this.$i18n.mergeLocaleMessage('zh', local.zh)
}
},
computed: {
lang: {
get() {
return this.$store.state.app.language
},
set(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
}
}
}
}
</script>
<style scoped>
.box-card {
width: 600px;
margin: 20px auto;
}
.block {
padding: 25px;
}
</style>
export default {
zh: {
i18nView: {
title: '切换语言',
note: '目前只翻译了当前页面和侧边栏和导航,未完待续,敬请期待...',
datePlaceholder: '请选择日期',
tableDate: '日期',
tableName: '姓名',
tableAddress: '地址',
default: '默认按钮',
primary: '主要按钮',
success: '成功按钮',
info: '信息按钮',
warning: '警告按钮',
danger: '危险按钮'
}
},
en: {
i18nView: {
title: 'Switch Language',
note: 'Currently only translated the i18n page and the sidebar and levelbar, please look forword to...',
datePlaceholder: 'Pick a day',
tableDate: 'tableDate',
tableName: 'tableName',
tableAddress: 'tableAddress',
default: 'default:',
primary: 'primary',
success: 'success',
info: 'info',
warning: 'warning',
danger: 'danger'
}
}
}
<template>
<div class="app-container">
<div class="wrapper">
<code>
这半年来一直在用vue写管理后台,目前后台已经有百来个个页面,十几种权限,但维护成本依然很低,所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios由webpack2打包.由于是个人项目,所以数据请求都是用了mockjs模拟。注意:在次项目基础上改造开发时请移除mock文件。
写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
<ul>
<li><a target='_blank' class='lin' href="https://github.com/PanJiaChen/vue-element-admin/">项目地址</a></li>
<li><a target='_blank' class='lin' href="https://github.com/PanJiaChen/vue-element-admin/wiki">wiki</a></li>
<li><a target='_blank' href="https://juejin.im/post/59097cd7a22b9d0065fb61d2">手摸手,带你用 vue 撸后台 系列一(基础篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/591aa14f570c35006961acac">手摸手,带你用 vue 撸后台 系列二(登录权限篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/593121aa0ce4630057f70d35">手摸手,带你用 vue 撸后台 系列三 (实战篇)</a></li>
<li><a target='_blank' href="https://juejin.im/post/595b4d776fb9a06bbe7dba56">手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)</a></li>
<li><a target='_blank' href="https://segmentfault.com/a/1190000009090836">手摸手,带你封装一个vue component</a></li>
</ul>
</code>
</div>
</div>
</template>
<style scoped>
.wrapper{
width: 800px;
margin: 30px auto;
}
</style>
<!--
SPDX-FileCopyrightText: 2019-2022 Pleroma Authors <https://pleroma.social>
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="invites-container">
<div class="invites-header-container">
<h1>{{ $t('invites.inviteTokens') }}</h1>
<reboot-button/>
</div>
<div class="actions-container">
<el-button class="create-invite-token" @click="createTokenDialogVisible = true">
<span>
<i class="icon el-icon-plus"/>
{{ $t('invites.createInviteToken') }}
</span>
</el-button>
<el-button class="invite-via-email" @click="inviteUserDialogVisible = true">
<span>
<i class="icon el-icon-message"/>
{{ $t('invites.inviteUserViaEmail') }}
</span>
</el-button>
</div>
<el-dialog
:visible.sync="createTokenDialogVisible"
:show-close="false"
:title="$t('invites.createInviteToken')"
:width="isTokenCreated ? '60%' : '30%'"
custom-class="create-new-token-dialog">
<el-form ref="newTokenForm" :model="newTokenForm" :label-width="getLabelWidth" status-icon>
<el-form-item :label="$t('invites.maxUse')">
<el-input-number
v-model="newTokenForm.maxUse"
:min="0"
:size="isDesktop ? 'medium' : 'small'"
name="maxUse"/>
</el-form-item>
<el-form-item :label="$t('invites.expiresAt')">
<el-date-picker
v-model="newTokenForm.expiresAt"
:placeholder="$t('invites.pickDate')"
class="pick-date"
type="date"
name="date"
value-format="yyyy-MM-dd"/>
</el-form-item>
</el-form>
<span slot="footer">
<el-button class="invites-close-dialog" @click="closeDialogWindow">{{ $t('invites.cancel') }}</el-button>
<el-button type="primary" @click="createToken">{{ $t('invites.create') }}</el-button>
</span>
<el-card v-if="'token' in newToken">
<div slot="header" class="clearfix">
<span>{{ $t('invites.tokenCreated') }}</span>
</div>
<el-form label-width="85px" class="new-token-card">
<el-form-item :label="$t('invites.inviteLink')">
<div class="invite-link-container">
<el-link :href="inviteLink" :underline="false" target="_blank">
{{ inviteLink }}
</el-link>
<el-button type="text" size="small" @click="handleCopy($event)">{{ $t('invites.copyLink') }}</el-button>
</div>
</el-form-item>
<el-form-item :label="$t('invites.token')">
{{ newToken.token }}
</el-form-item>
<el-form-item :label="$t('invites.maxUse')">
{{ newToken.maxUse }}
</el-form-item>
<el-form-item :label="$t('invites.expiresAt')">
{{ newToken.expiresAt || '(not set)' }}
</el-form-item>
</el-form>
</el-card>
</el-dialog>
<el-dialog
:visible.sync="inviteUserDialogVisible"
:show-close="false"
:title="$t('invites.sendRegistration')"
custom-class="invite-via-email-dialog">
<div>
<p class="info">{{ $t('invites.inviteViaEmailAlert') }}</p>
<el-form ref="inviteUserForm" :model="inviteUserForm" :rules="rules" :label-width="getLabelWidth" status-icon>
<el-form-item :label="$t('invites.email')" prop="email">
<el-input v-model="inviteUserForm.email" name="email" type="email" autofocus/>
</el-form-item>
<el-form-item :label="$t('invites.name')" prop="name">
<el-input v-model="inviteUserForm.name" name="name"/>
</el-form-item>
</el-form>
</div>
<span slot="footer">
<el-button @click="closeDialogWindow">{{ $t('invites.cancel') }}</el-button>
<el-button type="primary" @click="inviteUserViaEmail">{{ $t('invites.create') }}</el-button>
</span>
</el-dialog>
<el-table
v-loading="loading"
:data="tokens"
:default-sort = "{prop: 'used', order: 'ascending'}"
class="invite-token-table">
<el-table-column
v-if="isDesktop"
:label="$t('invites.id')"
min-width="60"
prop="id"
sortable/>
<el-table-column
:label="$t('invites.token')"
:min-width="isDesktop ? 320 : 120"
prop="token"/>
<el-table-column
v-if="isDesktop"
:label="$t('invites.expiresAt')"
align="center"
header-align="center"
min-width="110"
prop="expires_at"
sortable/>
<el-table-column
:label="$t('invites.maxUse')"
align="center"
header-align="center"
min-width="60"
prop="max_use"
sortable/>
<el-table-column
v-if="isDesktop"
:label="$t('invites.uses')"
align="center"
header-align="center"
min-width="60"
prop="uses"/>
<el-table-column
:label="$t('invites.used')"
:min-width="isDesktop ? 60 : 50"
align="center"
header-align="center"
prop="used"
sortable>
<template slot-scope="scope">
<el-tag
:type="scope.row.used ? 'danger' : 'success'"
disable-transitions>
{{ scope.row.used ? $t('invites.used') : $t('invites.active') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('invites.actions')"
:min-width="isDesktop ? 100 : 50"
align="center"
header-align="center">
<template slot-scope="scope">
<el-button type="text" size="small" @click.native="revokeInviteToken(scope.row.token)">
{{ $t('invites.revoke') }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import clip from '@/utils/clipboard'
import RebootButton from '@/components/RebootButton'
import { mapGetters } from 'vuex'
import { baseName } from '@/api/utils'
export default {
components: { RebootButton },
data() {
return {
rules: {
email: [
{ validator: this.validateEmail, trigger: 'blur' }
]
},
newTokenForm: {
maxUse: 1,
expiresAt: ''
},
inviteUserForm: {
email: '',
name: ''
},
createTokenDialogVisible: false,
inviteUserDialogVisible: false
}
},
computed: {
...mapGetters([
'authHost'
]),
getLabelWidth() {
return this.isDesktop ? '100px' : '85px'
},
inviteLink() {
return `${baseName(this.authHost)}/registration/${this.newToken.token}`
},
isDesktop() {
return this.$store.state.app.device === 'desktop'
},
isTokenCreated() {
return 'token' in this.newToken
},
loading() {
return this.$store.state.invites.loading
},
newToken() {
return this.$store.state.invites.newToken
},
tokens() {
return this.$store.state.invites.inviteTokens
}
},
mounted() {
this.$store.dispatch('GetNodeInfo')
this.$store.dispatch('NeedReboot')
this.$store.dispatch('FetchInviteTokens')
},
methods: {
closeDialogWindow() {
this.inviteUserDialogVisible = false
this.createTokenDialogVisible = false
this.$store.dispatch('RemoveNewToken')
this.$data.inviteUserForm.email = ''
this.$data.inviteUserForm.name = ''
this.$data.newTokenForm.maxUse = 1
this.$data.newTokenForm.expiresAt = ''
},
createToken() {
this.$store.dispatch('GenerateInviteToken', this.$data.newTokenForm)
},
handleCopy(event) {
clip(this.inviteLink, event)
},
async inviteUserViaEmail() {
this.$refs['inviteUserForm'].validate(async(valid) => {
if (valid) {
await this.$store.dispatch('InviteUserViaEmail', this.$data.inviteUserForm)
this.closeDialogWindow()
} else {
this.$message({
type: 'error',
message: this.$t('invites.submitFormError')
})
return false
}
})
},
revokeInviteToken(token) {
this.$store.dispatch('RevokeToken', token)
},
validateEmail(rule, value, callback) {
if (value === '') {
return callback(new Error(this.$t('invites.emptyEmailError')))
} else if (!this.validEmail(value)) {
return callback(new Error(this.$t('invites.invalidEmailError')))
} else {
return callback()
}
},
validEmail(email) {
const re = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
return re.test(email)
}
}
}
</script>
<style rel='stylesheet/scss' lang='scss'>
.invites-container {
.actions-container {
display: flex;
height: 36px;
justify-content: space-between;
align-items: center;
margin: 15px 15px 15px 15px;
}
.create-invite-token {
text-align: left;
width: 350px;
padding: 10px;
}
.create-new-token-dialog {
a {
margin-bottom: 3px;
}
.el-card__body {
padding: 10px 20px;
}
}
.el-dialog__body {
padding: 5px 20px 0 20px
}
h1 {
margin: 0;
}
.icon {
margin-right: 5px;
}
.invite-link-container {
display: flex;
justify-content: space-between;
align-items: baseline;
button {
margin-left: 15px;
}
}
.invite-token-table {
width: 100%;
margin: 0 15px;
}
.invite-via-email {
text-align: left;
width: 350px;
padding: 10px;
}
.invite-via-email-dialog {
width: 50%
}
.invites-header-container {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 15px;
}
.info {
color: #666666;
font-size: 13px;
line-height: 22px;
margin: 0 0 10px 0;
}
.new-token-card {
.el-form-item {
margin: 0;
}
}
.reboot-button {
padding: 10px;
margin: 0;
width: 145px;
}
}
@media only screen and (max-width:480px) {
.invites-container {
.actions-container {
display: flex;
height: 82px;
flex-direction: column;
align-items: center;
margin: 15px 10px 7px 10px;
}
.cell {
padding: 0;
}
.create-invite-token {
width: 100%;
}
.create-new-token-dialog {
width: 85%
}
.el-date-editor {
width: 150px;
}
.el-dialog__body {
padding: 5px 15px 0 15px
}
h1 {
margin: 0;
}
.invite-token-table {
width: 100%;
margin: 0 5px;
font-size: 12px;
font-weight: 500;
}
.invite-via-email {
width: 100%;
margin: 10px 0 0 0;
}
.invite-via-email-dialog {
width: 85%
}
.invites-header-container {
margin: 0 10px;
}
.info {
margin: 0 0 10px 5px;
}
th {
.cell {
padding: 0;
}
}
}
.create-invite-token {
width: 100%
}
.invite-via-email {
width: 100%
}
}
</style>
<!--
SPDX-FileCopyrightText: 2017-2019 PanJiaChen <https://github.com/PanJiaChen/vue-element-admin>
SPDX-License-Identifier: MIT
SPDX-FileCopyrightText: 2019-2022 Pleroma Authors <https://pleroma.social>
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="app-wrapper" :class="{hideSidebar:!sidebar.opened}">
<sidebar class="sidebar-container"></sidebar>
<div class="main-container">
<navbar></navbar>
<tabs-view></tabs-view>
<app-main></app-main>
</div>
</div>
<div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar class="sidebar-container"/>
<div class="main-container">
<navbar/>
<app-main/>
</div>
</div>
</template>
<script>
import { Navbar, Sidebar, AppMain, TabsView } from 'views/layout/components'
import { Navbar, Sidebar, AppMain, TagsView } from './components'
import ResizeMixin from './mixin/ResizeHandler'
export default {
name: 'layout',
name: 'Layout',
components: {
Navbar,
Sidebar,
AppMain,
TabsView
TagsView
},
mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
openSidebar: this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
},
methods: {
handleClickOutside() {
this.$store.dispatch('closeSideBar', { withoutAnimation: false })
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
}
@import "~@/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
&.mobile.openSidebar{
position: fixed;
top: 0;
}
}
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
</style>