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 42 ...mapState('editor', {
43 43 editingElement: state => state.editingElement,
44 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 48 ...mapState('loading', {
48 49 saveWork_loading: state => state.saveWork_loading
... ... @@ -98,7 +99,7 @@ export default {
98 99 },
99 100 render (h) {
100 101 return (
101   - <a-layout id="luban-layout" style={{ height: '100vh' }}>
  102 + <a-layout id="luban-editor-layout" style={{ height: '100vh' }}>
102 103 <a-layout-header class="header">
103 104 <div class="logo">鲁班 H5</div>
104 105 {/* TODO we can show the plugins shortcuts here */}
... ... @@ -190,14 +191,16 @@ export default {
190 191 </a-tabs>
191 192 </a-layout-sider>
192 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 197 </a-layout>
195 198 )
196 199 },
197 200 created () {
198   - let workId = this.$route.query.workId
  201 + let workId = this.$route.params.workId
  202 + console.log(workId)
199 203 if (workId) {
200   - // this.$store.dispatch('getWorkById', workId)
201 204 this.fetchWork(workId)
202 205 } else {
203 206 this.createWork()
... ...
front-end/h5/src/components/core/editor/modals/preview.vue
1 1 <script>
2   -import { mapActions, mapState } from 'vuex'
  2 +import { mapActions } from 'vuex'
3 3 import QRCode from 'qrcode'
4 4 import { API_ORIGIN } from '../../../../constants/api.js'
5 5  
... ... @@ -12,12 +12,16 @@ export default {
12 12 handleClose: {
13 13 type: Function,
14 14 default: () => {}
  15 + },
  16 + work: {
  17 + type: Object,
  18 + default: () => {}
15 19 }
16 20 },
17 21 computed: {
18   - ...mapState('editor', {
19   - work: state => state.work
20   - }),
  22 + // ...mapState('editor', {
  23 + // work: state => state.work
  24 + // }),
21 25 releaseUrl () {
22 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 3 .header {
3 4 padding: 0 10px;
4 5  
... ... @@ -92,4 +93,10 @@
92 93  
93 94 .no-border {
94 95 border: none !important;
  96 +}
  97 +
  98 +.flex-center {
  99 + display: flex !important;
  100 + align-items: center;
  101 + justify-content: center;
95 102 }
96 103 \ No newline at end of file
... ...
front-end/h5/src/router.js
1 1 import Vue from 'vue'
2 2 import Router from 'vue-router'
3 3 // import Home from './views/Home.vue'
  4 +import Home from './views/work-manager/index.vue'
4 5  
5 6 Vue.use(Router)
6 7  
7 8 export default new Router({
  9 + // mode: 'history',
8 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 31 path: '/about',
16 32 name: 'about',
17 33 component: () => import('./views/About.vue')
18 34 },
19 35 {
20   - path: '/', // #!zh 编辑器页面,核心功能部分
  36 + path: '/editor/:workId', // #!zh 编辑器页面,核心功能部分
21 37 name: 'editor',
22 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 5 import { actions as workActions, mutations as workMutations } from './work'
6 6  
7 7 const state = {
  8 + works: [],
8 9 work: new Work(),
9 10 editingPage: { elements: [] },
10 11 editingElement: null,
... ...
front-end/h5/src/store/modules/work.js
1   -// import Work from '../../components/core/models/work'
2 1 import Element from '../../components/core/models/element'
3 2 import strapi from '../../utils/strapi'
4 3 import Page from '../../components/core/models/page'
5 4 import { AxiosWrapper } from '../../utils/http.js'
  5 +import router from '@/router.js'
6 6  
7 7 export const actions = {
8 8 previewWork ({ commit }, payload = {}) {
... ... @@ -13,7 +13,8 @@ export const actions = {
13 13 },
14 14 createWork ({ commit }, payload) {
15 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 19 // commit('createWork')
19 20 // commit('pageManager', { type: 'add' })
... ... @@ -46,11 +47,19 @@ export const actions = {
46 47 commit('setWork', entry)
47 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 58 // mutations
53 59 export const mutations = {
  60 + setWorks (state, works) {
  61 + state.works = works
  62 + },
54 63 setWork (state, work) {
55 64 work.pages = work.pages.map(page => {
56 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>
... ...