Commit 0d45399360777777d4666d35d50d5804fb3a6d4f

Authored by ly525
1 parent 13edea6e

feat: add work list page

front-end/h5/src/components/core/editor/index.js
@@ -42,7 +42,8 @@ export default { @@ -42,7 +42,8 @@ export default {
42 ...mapState('editor', { 42 ...mapState('editor', {
43 editingElement: state => state.editingElement, 43 editingElement: state => state.editingElement,
44 elements: state => state.editingPage.elements, 44 elements: state => state.editingPage.elements,
45 - pages: state => state.work.pages 45 + pages: state => state.work.pages,
  46 + work: state => state.work
46 }), 47 }),
47 ...mapState('loading', { 48 ...mapState('loading', {
48 saveWork_loading: state => state.saveWork_loading 49 saveWork_loading: state => state.saveWork_loading
@@ -98,7 +99,7 @@ export default { @@ -98,7 +99,7 @@ export default {
98 }, 99 },
99 render (h) { 100 render (h) {
100 return ( 101 return (
101 - <a-layout id="luban-layout" style={{ height: '100vh' }}> 102 + <a-layout id="luban-editor-layout" style={{ height: '100vh' }}>
102 <a-layout-header class="header"> 103 <a-layout-header class="header">
103 <div class="logo">鲁班 H5</div> 104 <div class="logo">鲁班 H5</div>
104 {/* TODO we can show the plugins shortcuts here */} 105 {/* TODO we can show the plugins shortcuts here */}
@@ -190,14 +191,16 @@ export default { @@ -190,14 +191,16 @@ export default {
190 </a-tabs> 191 </a-tabs>
191 </a-layout-sider> 192 </a-layout-sider>
192 </a-layout> 193 </a-layout>
193 - <PreviewDialog visible={this.previewVisible} handleClose={() => { this.previewVisible = false }} /> 194 + {
  195 + this.previewVisible && <PreviewDialog work={this.work} visible={this.previewVisible} handleClose={() => { this.previewVisible = false }} />
  196 + }
194 </a-layout> 197 </a-layout>
195 ) 198 )
196 }, 199 },
197 created () { 200 created () {
198 - let workId = this.$route.query.workId 201 + let workId = this.$route.params.workId
  202 + console.log(workId)
199 if (workId) { 203 if (workId) {
200 - // this.$store.dispatch('getWorkById', workId)  
201 this.fetchWork(workId) 204 this.fetchWork(workId)
202 } else { 205 } else {
203 this.createWork() 206 this.createWork()
front-end/h5/src/components/core/editor/modals/preview.vue
1 <script> 1 <script>
2 -import { mapActions, mapState } from 'vuex' 2 +import { mapActions } from 'vuex'
3 import QRCode from 'qrcode' 3 import QRCode from 'qrcode'
4 import { API_ORIGIN } from '../../../../constants/api.js' 4 import { API_ORIGIN } from '../../../../constants/api.js'
5 5
@@ -12,12 +12,16 @@ export default { @@ -12,12 +12,16 @@ export default {
12 handleClose: { 12 handleClose: {
13 type: Function, 13 type: Function,
14 default: () => {} 14 default: () => {}
  15 + },
  16 + work: {
  17 + type: Object,
  18 + default: () => {}
15 } 19 }
16 }, 20 },
17 computed: { 21 computed: {
18 - ...mapState('editor', {  
19 - work: state => state.work  
20 - }), 22 + // ...mapState('editor', {
  23 + // work: state => state.work
  24 + // }),
21 releaseUrl () { 25 releaseUrl () {
22 return `${API_ORIGIN}/works/preview/${this.work.id}` 26 return `${API_ORIGIN}/works/preview/${this.work.id}`
23 } 27 }
front-end/h5/src/components/core/styles/index.scss
1 -#luban-layout { 1 +#luban-editor-layout,
  2 +#luban-work-manager-layout {
2 .header { 3 .header {
3 padding: 0 10px; 4 padding: 0 10px;
4 5
@@ -92,4 +93,10 @@ @@ -92,4 +93,10 @@
92 93
93 .no-border { 94 .no-border {
94 border: none !important; 95 border: none !important;
  96 +}
  97 +
  98 +.flex-center {
  99 + display: flex !important;
  100 + align-items: center;
  101 + justify-content: center;
95 } 102 }
96 \ No newline at end of file 103 \ No newline at end of file
front-end/h5/src/router.js
1 import Vue from 'vue' 1 import Vue from 'vue'
2 import Router from 'vue-router' 2 import Router from 'vue-router'
3 // import Home from './views/Home.vue' 3 // import Home from './views/Home.vue'
  4 +import Home from './views/work-manager/index.vue'
4 5
5 Vue.use(Router) 6 Vue.use(Router)
6 7
7 export default new Router({ 8 export default new Router({
  9 + // mode: 'history',
8 routes: [ 10 routes: [
9 - // {  
10 - // path: '/',  
11 - // name: 'home',  
12 - // component: Home  
13 - // }, 11 + {
  12 + path: '/work-manager',
  13 + component: Home,
  14 + name: 'work-manager',
  15 + redirect: '/work-manager/list',
  16 + alias: '/',
  17 + children: [
  18 + {
  19 + path: '/work-manager/list',
  20 + name: 'work-manager-list',
  21 + component: () => import('@/views/work-manager/list.vue')
  22 + },
  23 + {
  24 + path: '/work-manager/form-stat',
  25 + name: 'form-stat',
  26 + component: () => import('@/views/work-manager/form-stat.vue')
  27 + }
  28 + ]
  29 + },
14 { 30 {
15 path: '/about', 31 path: '/about',
16 name: 'about', 32 name: 'about',
17 component: () => import('./views/About.vue') 33 component: () => import('./views/About.vue')
18 }, 34 },
19 { 35 {
20 - path: '/', // #!zh 编辑器页面,核心功能部分 36 + path: '/editor/:workId', // #!zh 编辑器页面,核心功能部分
21 name: 'editor', 37 name: 'editor',
22 component: () => import('./views/Editor.vue') 38 component: () => import('./views/Editor.vue')
23 - },  
24 - {  
25 - path: '/form-stat', // #!zh 表单统计页面  
26 - name: 'form-stat',  
27 - component: () => import('./views/About.vue')  
28 } 39 }
29 ] 40 ]
30 }) 41 })
front-end/h5/src/store/modules/editor.js
@@ -5,6 +5,7 @@ import { actions as elementActions, mutations as elementMutations } from &#39;./elem @@ -5,6 +5,7 @@ import { actions as elementActions, mutations as elementMutations } from &#39;./elem
5 import { actions as workActions, mutations as workMutations } from './work' 5 import { actions as workActions, mutations as workMutations } from './work'
6 6
7 const state = { 7 const state = {
  8 + works: [],
8 work: new Work(), 9 work: new Work(),
9 editingPage: { elements: [] }, 10 editingPage: { elements: [] },
10 editingElement: null, 11 editingElement: null,
front-end/h5/src/store/modules/work.js
1 -// import Work from '../../components/core/models/work'  
2 import Element from '../../components/core/models/element' 1 import Element from '../../components/core/models/element'
3 import strapi from '../../utils/strapi' 2 import strapi from '../../utils/strapi'
4 import Page from '../../components/core/models/page' 3 import Page from '../../components/core/models/page'
5 import { AxiosWrapper } from '../../utils/http.js' 4 import { AxiosWrapper } from '../../utils/http.js'
  5 +import router from '@/router.js'
6 6
7 export const actions = { 7 export const actions = {
8 previewWork ({ commit }, payload = {}) { 8 previewWork ({ commit }, payload = {}) {
@@ -13,7 +13,8 @@ export const actions = { @@ -13,7 +13,8 @@ export const actions = {
13 }, 13 },
14 createWork ({ commit }, payload) { 14 createWork ({ commit }, payload) {
15 strapi.createEntry('works').then(entry => { 15 strapi.createEntry('works').then(entry => {
16 - window.location = `${window.location.origin}/#/?workId=${entry.id}` 16 + router.replace({ name: 'editor', params: { workId: entry.id } })
  17 + // window.location = `${window.location.origin}/#/editor/${entry.id}`
17 }) 18 })
18 // commit('createWork') 19 // commit('createWork')
19 // commit('pageManager', { type: 'add' }) 20 // commit('pageManager', { type: 'add' })
@@ -46,11 +47,19 @@ export const actions = { @@ -46,11 +47,19 @@ export const actions = {
46 commit('setWork', entry) 47 commit('setWork', entry)
47 commit('setEditingPage') 48 commit('setEditingPage')
48 }) 49 })
  50 + },
  51 + fetchWorks ({ commit, state }, workId) {
  52 + strapi.getEntries('works', {}).then(entries => {
  53 + commit('setWorks', entries)
  54 + })
49 } 55 }
50 } 56 }
51 57
52 // mutations 58 // mutations
53 export const mutations = { 59 export const mutations = {
  60 + setWorks (state, works) {
  61 + state.works = works
  62 + },
54 setWork (state, work) { 63 setWork (state, work) {
55 work.pages = work.pages.map(page => { 64 work.pages = work.pages.map(page => {
56 page.elements = page.elements.map(element => new Element(element)) 65 page.elements = page.elements.map(element => new Element(element))
front-end/h5/src/views/work-manager/form-stat.vue 0 → 100644
front-end/h5/src/views/work-manager/index.vue 0 → 100644
  1 +<script>
  2 +// import PreView from '@/pages/preview';
  3 +// import Sidebar from './components/sidebar.vue'
  4 +import '@/components/core/styles/index.scss'
  5 +
  6 +const sidebarMenus = [
  7 + {
  8 + label: '我的作品',
  9 + value: 'workManager',
  10 + antIcon: 'bars',
  11 + key: '1'
  12 + },
  13 + {
  14 + label: '数据中心',
  15 + value: 'dataCenter',
  16 + antIcon: 'snippets',
  17 + key: '2',
  18 + children: [
  19 + {
  20 + label: '基础数据',
  21 + value: 'basicData',
  22 + antIcon: 'snippets',
  23 + key: '2-1'
  24 + },
  25 + {
  26 + label: '表单统计',
  27 + value: 'formData',
  28 + antIcon: 'snippets',
  29 + key: '2-2'
  30 + }
  31 + ]
  32 + },
  33 + {
  34 + label: '模板中心',
  35 + value: 'templateCenter',
  36 + antIcon: 'snippets',
  37 + key: '3',
  38 + children: [
  39 + {
  40 + label: '免费模板',
  41 + value: 'freeTemplates',
  42 + antIcon: 'snippets',
  43 + key: '3-1'
  44 + }
  45 + ]
  46 + },
  47 + {
  48 + label: '账号中心',
  49 + value: 'freeTemplate',
  50 + antIcon: 'appstore',
  51 + key: '4'
  52 + }
  53 +]
  54 +
  55 +export default {
  56 + components: {
  57 + // PreView,
  58 + // Sidebar
  59 + },
  60 + render (h) {
  61 + return (
  62 + <a-layout id="luban-work-manager-layout" style={{ height: '100vh' }}>
  63 + <a-layout-header class="header">
  64 + <div class="logo">鲁班 H5</div>
  65 + {/* TODO we can show the plugins shortcuts here */}
  66 + <a-dropdown style={{ float: 'right', background: 'transparent', margin: '16px 28px 16px 0' }}>
  67 + <a-menu slot="overlay" onClick={() => {}}>
  68 + <a-menu-item key="1">
  69 + <span>someone@luban</span>
  70 + </a-menu-item>
  71 + <a-menu-divider />
  72 + <a-menu-item key="2"><a-icon type="setting" />账号设置</a-menu-item>
  73 + <a-menu-item key="3"><a-icon type="logout" />退出登录</a-menu-item>
  74 + </a-menu>
  75 + <a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
  76 + </a-dropdown>
  77 + </a-layout-header>
  78 + <a-layout>
  79 + <a-layout-sider width="160" style="background: #fff">
  80 + <a-menu
  81 + mode="inline"
  82 + // defaultSelectedKeys={['1']}
  83 + defaultOpenKeys={['1', '2', '3']}
  84 + style="height: 100%"
  85 + >
  86 + {
  87 + sidebarMenus.map(menu => (
  88 + menu.children
  89 + ? <a-sub-menu key={menu.key}>
  90 + <span slot="title"><a-icon type={menu.antIcon} />{menu.label}</span>
  91 + {
  92 + (menu.children).map(submenu => (<a-menu-item key={submenu.key}>{submenu.label}</a-menu-item>))
  93 + }
  94 + </a-sub-menu>
  95 + : <a-menu-item key={menu.key}>
  96 + <a-icon type={menu.antIcon} />
  97 + <span>{menu.label}</span>
  98 + </a-menu-item>
  99 + ))
  100 + }
  101 + </a-menu>
  102 + </a-layout-sider>
  103 + <a-layout style="padding: 0 24px 24px">
  104 + <a-layout-content style={{ padding: '24px', margin: 0, minHeight: '280px' }}>
  105 + <router-view />
  106 + </a-layout-content>
  107 + </a-layout>
  108 + </a-layout>
  109 + {/** <PreviewDialog visible={this.previewVisible} handleClose={() => { this.previewVisible = false }} /> */}
  110 + </a-layout>)
  111 + }
  112 +}
  113 +</script>
front-end/h5/src/views/work-manager/list.vue 0 → 100644
  1 +<script>
  2 +import { mapState, mapActions } from 'vuex'
  3 +import QRCode from 'qrcode'
  4 +
  5 +import { API_ORIGIN } from '@/constants/api.js'
  6 +import PreviewDialog from '@/components/core/editor/modals/preview.vue'
  7 +
  8 +const ListItemCard = {
  9 + props: {
  10 + work: {
  11 + type: Object,
  12 + default: () => {}
  13 + },
  14 + handleClickEdit: {
  15 + type: Function,
  16 + default: () => {}
  17 + },
  18 + handleClickPreview: {
  19 + type: Function,
  20 + default: () => {}
  21 + }
  22 + },
  23 + data: () => ({
  24 + qrcodeUrl: ''
  25 + }),
  26 + methods: {
  27 + timeFmt (date) {
  28 + const dateTime = new Date(date)
  29 + const displayTime = `${dateTime.getFullYear()}-${dateTime.getMonth() +
  30 + 1}-${dateTime.getDate()}`
  31 + return displayTime
  32 + },
  33 + genQRCodeUrl (work) {
  34 + const url = `${API_ORIGIN}/works/preview/${work.id}`
  35 + QRCode.toDataURL(url, (err, url) => {
  36 + if (err) console.log(err)
  37 + this.qrcodeUrl = url
  38 + })
  39 + }
  40 + },
  41 + render (h) {
  42 + return (
  43 + <a-card hoverable >
  44 + <div slot="cover" class="flex-center" style="height: 200px;font-size: 24px;border: 1px dashed #eee;color: #aaa;background: #f7f5f557;" >
  45 + { this.qrcodeUrl ? <img src={this.qrcodeUrl} /> : <span>Luban H5</span> }
  46 + </div>
  47 + <template class="ant-card-actions" slot="actions">
  48 + <a-tooltip effect="dark" placement="bottom" title="编辑">
  49 + <router-link to={{ name: 'editor', params: { workId: this.work.id }}} target="_blank">
  50 + <a-icon type="edit" title="编辑"/>
  51 + </router-link>
  52 + </a-tooltip>
  53 + <a-tooltip effect="dark" placement="bottom" title="预览">
  54 + <a-icon type="eye" title="预览" onClick={this.handleClickPreview} />
  55 + </a-tooltip>
  56 + {
  57 + this.qrcodeUrl
  58 + ? <a-icon type="close-circle" onClick={() => { this.qrcodeUrl = '' }} />
  59 + : <a-icon type="qrcode" onClick={() => this.genQRCodeUrl(this.work)} />
  60 + }
  61 + {/**
  62 + <a-icon type="setting" />
  63 + <a-icon type="ellipsis" />
  64 + */}
  65 + </template>
  66 + <a-card-meta
  67 + >
  68 + <div slot="title" class="ant-card-meta-title" style="font-size: 14px;">
  69 + {this.work.title}({this.work.id})
  70 + </div>
  71 + <div slot="description" style="font-size: 12px;">
  72 + <div>描述:{this.work.description}</div>
  73 + <div>时间:{this.timeFmt(this.work.created_at)}</div>
  74 + </div>
  75 + </a-card-meta>
  76 + </a-card>
  77 + )
  78 + }
  79 +}
  80 +
  81 +const AddNewCard = {
  82 + functional: true,
  83 + render (h, { props }) {
  84 + return (
  85 + <a-card hoverable>
  86 + <div slot="cover" class="flex-center" style="height: 305px;background: #f7f5f557;" onClick={props.handleCreate}>
  87 + <a-icon type="plus" />
  88 + </div>
  89 + <template class="ant-card-actions" slot="actions">
  90 + <span onClick={props.handleCreate}>创建新作品</span>
  91 + </template>
  92 + </a-card>
  93 + )
  94 + }
  95 +}
  96 +
  97 +export default {
  98 + components: {
  99 + ListItemCard,
  100 + AddNewCard
  101 + },
  102 + data: () => ({
  103 + activeWork: null,
  104 + previewVisible: false
  105 + }),
  106 + computed: {
  107 + ...mapState('editor', ['works'])
  108 + },
  109 + methods: {
  110 + ...mapActions('editor', [
  111 + 'fetchWorks'
  112 + ]),
  113 + deleteWork (item) {
  114 + // TODO delete work from work list
  115 + },
  116 + createWork () {
  117 + this.$router.push({ name: 'editor' })
  118 + }
  119 + },
  120 + render (h) {
  121 + return (
  122 + <div class="works-wrapper">
  123 + <a-row gutter={24}>
  124 + <a-col span={6} style="margin-bottom: 10px;">
  125 + <AddNewCard handleCreate={this.createWork} />
  126 + </a-col>
  127 + {
  128 + this.works.map(work => (
  129 + <a-col span={6} key={work.id} style="margin-bottom: 20px;">
  130 + <ListItemCard work={work} handleClickPreview={e => {
  131 + this.previewVisible = true
  132 + this.activeWork = work
  133 + }} />
  134 + </a-col>
  135 + ))
  136 + }
  137 + </a-row>
  138 + {
  139 + this.previewVisible &&
  140 + <PreviewDialog
  141 + work={this.activeWork}
  142 + visible={this.previewVisible}
  143 + handleClose={() => { this.previewVisible = false }}
  144 + />
  145 + }
  146 + </div>
  147 + )
  148 + },
  149 + created () {
  150 + this.fetchWorks()
  151 + }
  152 +}
  153 +</script>