Incentive Page
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Lukas Bachschwell 2020-01-03 13:33:47 +01:00
parent 8e7029f5fd
commit 125f6b80d8
Signed by: lbsadmin
GPG Key ID: CCC6AA87CC8DF425
11 changed files with 572 additions and 7 deletions

View File

@ -1,24 +1,35 @@
import Mock from 'mockjs' import Mock from 'mockjs'
const List = [] const List = []
const count = 100 const count = 10
const baseContent = '<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>' const baseContent = '<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3' const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3'
const titles = [
'Special Coffee ☕️☕️☕️',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei',
'VR Experience im Vrei'
]
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
List.push(Mock.mock({ List.push(Mock.mock({
id: '@increment', id: i * 5 + 5,
timestamp: +Mock.Random.date('T'), timestamp: +Mock.Random.date('T'),
author: '@first', author: '@first',
reviewer: '@first', reviewer: '@first',
title: '@title(5, 10)', title: titles[i],
content_short: 'mock data', content_short: 'mock data',
content: baseContent, content: baseContent,
forecast: '@float(0, 100, 2, 2)', forecast: '@float(0, 100, 2, 2)',
importance: '@integer(1, 3)', importance: '@integer(1, 3)',
'type|1': ['CN', 'US', 'JP', 'EU'], 'type|1': ['CN', 'US', 'JP', 'EU'],
'status|1': ['published', 'draft', 'deleted'], 'status|1': ['Aktiv', 'Entwurf', 'Gelöscht'],
display_time: '@datetime', display_time: '@datetime',
comment_disabled: true, comment_disabled: true,
pageviews: '@integer(300, 5000)', pageviews: '@integer(300, 5000)',

View File

@ -120,7 +120,7 @@ export const constantRoutes = [
*/ */
export const asyncRoutes = [ export const asyncRoutes = [
{ {
path: '/find', path: '/', // find
component: Layout, component: Layout,
alwaysShow: false, alwaysShow: false,
children: [ children: [
@ -137,7 +137,7 @@ export const asyncRoutes = [
] ]
}, },
{ {
path: '/', path: '/dashboard',
component: Layout, component: Layout,
redirect: '/dashboard', redirect: '/dashboard',
children: [ children: [
@ -161,6 +161,41 @@ export const asyncRoutes = [
} }
] ]
}, },
{
path: '/incentives',
component: Layout,
redirect: '/example/list',
name: 'Example Articles',
meta: {
title: 'Incentives',
icon: 'example'
},
children: [
{
path: 'create',
component: () => import('@/views/incentives/create'),
name: 'CreateIncentive',
meta: { title: 'Incentive anlegen', icon: 'edit' }
},
{
path: 'list',
component: () => import('@/views/incentives/list'),
name: 'IncentiveList',
meta: { title: 'Incentive Liste', icon: 'list' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/incentives/edit'),
name: 'EditIncentive',
meta: {
title: 'Incentive bearbeiten',
noCache: true,
activeMenu: '/example/list'
},
hidden: true
}
]
},
{ {
path: '/permission', path: '/permission',
component: Layout, component: Layout,

View File

@ -0,0 +1,249 @@
<template>
<div class="createPost-container">
<el-form ref="postForm" :model="postForm" :rules="rules" class="form-container">
<sticky :z-index="10" :class-name="'sub-navbar '+postForm.status">
<CommentDropdown v-model="postForm.comment_disabled" />
<PlatformDropdown v-model="postForm.platforms" />
<SourceUrlDropdown v-model="postForm.source_uri" />
<el-button v-loading="loading" style="margin-left: 10px;" type="success" @click="submitForm">
Aktiv schalten
</el-button>
<el-button v-loading="loading" type="warning" @click="draftForm">
Entwurf speichern
</el-button>
</sticky>
<div class="createPost-main-container">
<el-row>
<el-col :span="24">
<el-form-item style="margin-bottom: 40px;" prop="title">
<MDinput v-model="postForm.title" :maxlength="100" name="name" required>
Incentive Titel
</MDinput>
</el-form-item>
<div class="postInfo-container">
<el-row>
<el-col :span="8">
<el-form-item label-width="100px" label="Münzkosten:" class="postInfo-container-item">
<el-select v-model="postForm.author" :remote-method="getRemoteUserList" filterable default-first-option remote placeholder="Kosten">
<el-option v-for="(item,index) in userListOptions" :key="item+index" :label="item" :value="item" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label-width="120px" label="Gültig Bis:" class="postInfo-container-item">
<el-date-picker v-model="displayTime" type="datetime" format="yyyy-MM-dd HH:mm:ss" placeholder="Wähle ein Datum" />
</el-form-item>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-form-item style="margin-bottom: 40px;" label-width="70px" label="Summary:">
<el-input v-model="postForm.content_short" :rows="1" type="textarea" class="article-textarea" autosize placeholder="Beschreibung des Incentives" />
<span v-show="contentShortLength" class="word-counter">{{ contentShortLength }}words</span>
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import MDinput from '@/components/MDinput'
import Sticky from '@/components/Sticky'
import { fetchArticle } from '@/api/article'
import { searchUser } from '@/api/remote-search'
const defaultForm = {
status: 'draft',
title: '', //
content: '', //
content_short: '', //
source_uri: '', //
image_uri: '', //
display_time: undefined, //
id: undefined,
platforms: ['a-platform'],
comment_disabled: false,
importance: 0
}
export default {
name: 'ArticleDetail',
components: { MDinput, Sticky },
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
const validateRequire = (rule, value, callback) => {
if (value === '') {
this.$message({
message: rule.field + ' ist erforderlich',
type: 'error'
})
callback(new Error(rule.field + ' erforderlich'))
} else {
callback()
}
}
return {
postForm: Object.assign({}, defaultForm),
loading: false,
userListOptions: [],
rules: {
image_uri: [{ validator: validateRequire }],
title: [{ validator: validateRequire }],
content: [{ validator: validateRequire }]
},
tempRoute: {}
}
},
computed: {
contentShortLength() {
return this.postForm.content_short.length
},
displayTime: {
// set and get is useful when the data
// returned by the back end api is different from the front end
// back end return => "2013-06-25 06:59:25"
// front end need timestamp => 1372114765000
get() {
return (+new Date(this.postForm.display_time))
},
set(val) {
this.postForm.display_time = new Date(val)
}
}
},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
}
// Why need to make a copy of this.$route here?
// Because if you enter this page and quickly switch tag, may be in the execution of the setTagsViewTitle function, this.$route is no longer pointing to the current page
// https://github.com/PanJiaChen/vue-element-admin/issues/1221
this.tempRoute = Object.assign({}, this.$route)
},
methods: {
fetchData(id) {
fetchArticle(id).then(response => {
this.postForm = response.data
// just for test
this.postForm.title += ` Article Id:${this.postForm.id}`
this.postForm.content_short += ` Article Id:${this.postForm.id}`
// set tagsview title
this.setTagsViewTitle()
// set page title
this.setPageTitle()
}).catch(err => {
console.log(err)
})
},
setTagsViewTitle() {
const title = 'Edit Article'
const route = Object.assign({}, this.tempRoute, { title: `${title}-${this.postForm.id}` })
this.$store.dispatch('tagsView/updateVisitedView', route)
},
setPageTitle() {
const title = 'Edit Article'
document.title = `${title} - ${this.postForm.id}`
},
submitForm() {
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) {
searchUser(query).then(response => {
if (!response.data.items) return
this.userListOptions = response.data.items.map(v => v.name)
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
.createPost-container {
position: relative;
.createPost-main-container {
padding: 40px 45px 20px 50px;
.postInfo-container {
position: relative;
@include clearfix;
margin-bottom: 10px;
.postInfo-container-item {
float: left;
}
}
}
.word-counter {
width: 40px;
position: absolute;
right: 10px;
top: 0px;
}
}
.article-textarea /deep/ {
textarea {
padding-right: 40px;
resize: none;
border: none;
border-radius: 0px;
border-bottom: 1px solid #bfcbd9;
}
}
</style>

View File

@ -0,0 +1,41 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
{{ !comment_disabled?'Comment: opened':'Comment: closed' }}
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding">
<el-dropdown-item>
<el-radio-group v-model="comment_disabled" style="padding: 10px;">
<el-radio :label="true">
Close comment
</el-radio>
<el-radio :label="false">
Open comment
</el-radio>
</el-radio-group>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
default: false
}
},
computed: {
comment_disabled: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

View File

@ -0,0 +1,46 @@
<template>
<el-dropdown :hide-on-click="false" :show-timeout="100" trigger="click">
<el-button plain>
Platfroms({{ platforms.length }})
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-border">
<el-checkbox-group v-model="platforms" style="padding: 5px 15px;">
<el-checkbox v-for="item in platformsOptions" :key="item.key" :label="item.key">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
required: true,
default: () => [],
type: Array
}
},
data() {
return {
platformsOptions: [
{ key: 'a-platform', name: 'a-platform' },
{ key: 'b-platform', name: 'b-platform' },
{ key: 'c-platform', name: 'c-platform' }
]
}
},
computed: {
platforms: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

View File

@ -0,0 +1,38 @@
<template>
<el-dropdown :show-timeout="100" trigger="click">
<el-button plain>
Link
<i class="el-icon-caret-bottom el-icon--right" />
</el-button>
<el-dropdown-menu slot="dropdown" class="no-padding no-border" style="width:400px">
<el-form-item label-width="0px" style="margin-bottom: 0px" prop="source_uri">
<el-input v-model="source_uri" placeholder="Please enter the content">
<template slot="prepend">
URL
</template>
</el-input>
</el-form-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
}
},
computed: {
source_uri: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
}
}
</script>

View File

@ -0,0 +1,3 @@
export { default as CommentDropdown } from './Comment'
export { default as PlatformDropdown } from './Platform'
export { default as SourceUrlDropdown } from './SourceUrl'

View File

@ -0,0 +1,13 @@
<template>
<aside>
Creating and editing pages cannot be cached by keep-alive because keep-alive include does not currently support
caching based on routes, so it is currently cached based on component name. If you want to achieve a similar caching
effect, you can use a browser caching scheme such as localStorage. Or do not use keep-alive include to cache all
pages directly. See details
<a
href="https://panjiachen.github.io/vue-element-admin-site/guide/essentials/tags-view.html"
target="_blank"
>Document</a>
</aside>
</template>

View File

@ -0,0 +1,13 @@
<template>
<article-detail :is-edit="false" />
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'CreateArticle',
components: { ArticleDetail }
}
</script>

View File

@ -0,0 +1,13 @@
<template>
<article-detail :is-edit="true" />
</template>
<script>
import ArticleDetail from './components/ArticleDetail'
export default {
name: 'EditForm',
components: { ArticleDetail }
}
</script>

View File

@ -0,0 +1,103 @@
<template>
<div class="app-container">
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column min-width="300px" label="Titel">
<template slot-scope="{row}">
<router-link :to="'/incentive/edit/'+row.id" class="link-type">
<span>{{ row.title }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column align="center" label="Münzen" width="100">
<template slot-scope="scope">
<span>{{ scope.row.id }}</span>
</template>
</el-table-column>
<el-table-column width="180px" align="center" label="Gültig bis">
<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="Erstellt von">
<template slot-scope="scope">
<span>{{ scope.row.author }}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="Status" width="110">
<template slot-scope="{row}">
<el-tag :type="row.status | statusFilter">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="Actions" width="120">
<template slot-scope="scope">
<router-link :to="'/incentive/edit/'+scope.row.id">
<el-button type="primary" size="small" icon="el-icon-edit">
Bearbeiten
</el-button>
</router-link>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { fetchList } from '@/api/article'
export default {
name: 'IncentiveList',
filters: {
statusFilter(status) {
const statusMap = {
Aktiv: 'success',
Entwurf: 'info',
Gelöscht: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 20
}
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.items
this.total = response.data.total
this.listLoading = false
})
}
}
}
</script>
<style scoped>
.edit-input {
padding-right: 100px;
}
.cancel-btn {
position: absolute;
right: 15px;
top: 10px;
}
</style>