Commit 0d45399360777777d4666d35d50d5804fb3a6d4f
1 parent
13edea6e
feat: add work list page
Showing
9 changed files
with
324 additions
and
23 deletions
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 './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> | ... | ... |