Commit 14571770fc947cdae489fefebcea11c578316adc
1 parent
c532b500
refactor(ajax)
Showing
9 changed files
with
228 additions
and
27 deletions
README.en.md
| @@ -36,17 +36,16 @@ English | [简体中文](./README.md) | @@ -36,17 +36,16 @@ English | [简体中文](./README.md) | ||
| 36 | - [ ] Photo Gallery | 36 | - [ ] Photo Gallery |
| 37 | - [ ] Third Party uncopyrighted Image Search | 37 | - [ ] Third Party uncopyrighted Image Search |
| 38 | 38 | ||
| 39 | -4. Data Statistics | 39 | + |
| 40 | +4. back-end API | ||
| 41 | + - [x] create work | ||
| 42 | + - [x] save work | ||
| 43 | + - [x] update work | ||
| 40 | - [ ] Form Statistics | 44 | - [ ] Form Statistics |
| 45 | + - [x] Online Preview | ||
| 46 | + - [x] QR Code Preview | ||
| 41 | 47 | ||
| 42 | -5. Others | ||
| 43 | - - [ ] Online preview | ||
| 44 | - - [ ] QR Code Preview | ||
| 45 | - | ||
| 46 | -### Technology Stack (current) | ||
| 47 | - 1. Front end: Vue.js | ||
| 48 | - 2. Back End: Strapi | ||
| 49 | - 3. Storage: Sqlite | 48 | +--- |
| 50 | 49 | ||
| 51 | ### Installs | 50 | ### Installs |
| 52 | > proj: project root path | 51 | > proj: project root path |
| @@ -54,5 +53,14 @@ English | [简体中文](./README.md) | @@ -54,5 +53,14 @@ English | [简体中文](./README.md) | ||
| 54 | 1. Front End | 53 | 1. Front End |
| 55 | Editor: please refer to: [`project/front-end/h5/README.md`](https://github.com/ly525/luban-h5/blob/dev/front-end/h5/README.md) | 54 | Editor: please refer to: [`project/front-end/h5/README.md`](https://github.com/ly525/luban-h5/blob/dev/front-end/h5/README.md) |
| 56 | 55 | ||
| 57 | -### Front-End Component Explanation | ||
| 58 | -1. `lbp-`: `lu-ban-plugin-`, means `Luban H5 plugin`, location: `proj/front-end/h5/src/components/plugins` | ||
| 59 | \ No newline at end of file | 56 | \ No newline at end of file |
| 57 | + | ||
| 58 | +--- | ||
| 59 | +### More Explanation | ||
| 60 | +#### Front-End Component Explanation | ||
| 61 | +1. `lbp-`: `lu-ban-plugin-`, means `Luban H5 plugin`, location: `proj/front-end/h5/src/components/plugins` | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +#### Technology Stack (current) | ||
| 65 | + 1. Front end: Vue.js | ||
| 66 | + 2. Back End: Strapi | ||
| 67 | + 3. Storage: Sqlite | ||
| 60 | \ No newline at end of file | 68 | \ No newline at end of file |
README.md
back-end/h5-api/README.md
| @@ -13,3 +13,19 @@ strapi develop | @@ -13,3 +13,19 @@ strapi develop | ||
| 13 | # default database is sqlite3(h5-api/.tmp/data.db) | 13 | # default database is sqlite3(h5-api/.tmp/data.db) |
| 14 | # visit http://locahost:1337/admin | 14 | # visit http://locahost:1337/admin |
| 15 | ``` | 15 | ``` |
| 16 | + | ||
| 17 | + | ||
| 18 | +## Features(zh) | ||
| 19 | + - [x] 创建、保存、更新作品 | ||
| 20 | + - [x] 在线预览 | ||
| 21 | + - [x] 二维码预览 | ||
| 22 | + - [ ] 表单数据收集 | ||
| 23 | + - [ ] 表单数据展示 | ||
| 24 | + | ||
| 25 | +## Features(en) | ||
| 26 | + - [x] create work | ||
| 27 | + - [x] save work | ||
| 28 | + - [x] update work | ||
| 29 | + - [x] Online Preview | ||
| 30 | + - [x] QR Code Preview | ||
| 31 | + - [ ] Form Statistics | ||
| 16 | \ No newline at end of file | 32 | \ No newline at end of file |
front-end/h5/src/components/core/editor/index.js
| @@ -43,6 +43,9 @@ export default { | @@ -43,6 +43,9 @@ export default { | ||
| 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 | + }), | ||
| 47 | + ...mapState('loading', { | ||
| 48 | + saveWork_loading: state => state.saveWork_loading | ||
| 46 | }) | 49 | }) |
| 47 | }, | 50 | }, |
| 48 | methods: { | 51 | methods: { |
| @@ -112,7 +115,7 @@ export default { | @@ -112,7 +115,7 @@ export default { | ||
| 112 | </a-button-group> | 115 | </a-button-group> |
| 113 | </a-menu-item> | 116 | </a-menu-item> |
| 114 | <a-menu-item key="1" class="transparent-bg"><a-button type="primary" size="small" onClick={() => { this.previewVisible = true }}>预览</a-button></a-menu-item> | 117 | <a-menu-item key="1" class="transparent-bg"><a-button type="primary" size="small" onClick={() => { this.previewVisible = true }}>预览</a-button></a-menu-item> |
| 115 | - <a-menu-item key="2" class="transparent-bg"><a-button size="small" onClick={() => this.saveWork()}>保存</a-button></a-menu-item> | 118 | + <a-menu-item key="2" class="transparent-bg"><a-button size="small" onClick={() => this.saveWork()} loading={this.saveWork_loading}>保存</a-button></a-menu-item> |
| 116 | <a-menu-item key="3" class="transparent-bg"><a-button size="small">发布</a-button></a-menu-item> | 119 | <a-menu-item key="3" class="transparent-bg"><a-button size="small">发布</a-button></a-menu-item> |
| 117 | </a-menu> | 120 | </a-menu> |
| 118 | </a-layout-header> | 121 | </a-layout-header> |
front-end/h5/src/constants/api.js
| 1 | -export const API_HOST = 'https://radiant-depths-79548.herokuapp.com' | ||
| 2 | -export const API_PORT = '' | ||
| 3 | -export const API_ORIGIN = `${API_HOST}:${API_PORT}` | 1 | +// export const API_HOST = 'https://radiant-depths-79548.herokuapp.com' |
| 2 | +// export const API_PORT = '' | ||
| 3 | +// export const API_ORIGIN = `${API_HOST}:${API_PORT}` | ||
| 4 | +export const API_ORIGIN = 'http://localhost:1337' |
front-end/h5/src/store/modules/loading.js
| 1 | // initial state | 1 | // initial state |
| 2 | const state = { | 2 | const state = { |
| 3 | - | 3 | + saveWork_loading: false |
| 4 | } | 4 | } |
| 5 | 5 | ||
| 6 | // getters | 6 | // getters |
| @@ -10,12 +10,16 @@ const getters = { | @@ -10,12 +10,16 @@ const getters = { | ||
| 10 | 10 | ||
| 11 | // actions | 11 | // actions |
| 12 | const actions = { | 12 | const actions = { |
| 13 | - | 13 | + update ({ commit }, payload) { |
| 14 | + commit('update', payload) | ||
| 15 | + } | ||
| 14 | } | 16 | } |
| 15 | 17 | ||
| 16 | // mutations | 18 | // mutations |
| 17 | const mutations = { | 19 | const mutations = { |
| 18 | - | 20 | + update (state, { type, payload }) { |
| 21 | + state[type] = payload | ||
| 22 | + } | ||
| 19 | } | 23 | } |
| 20 | 24 | ||
| 21 | export default { | 25 | export default { |
front-end/h5/src/store/modules/work.js
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | import Element from '../../components/core/models/element' | 2 | import Element from '../../components/core/models/element' |
| 3 | import strapi from '../../utils/strapi' | 3 | import strapi from '../../utils/strapi' |
| 4 | import Page from '../../components/core/models/page' | 4 | import Page from '../../components/core/models/page' |
| 5 | +import { AxiosWrapper } from '../../utils/http.js' | ||
| 5 | 6 | ||
| 6 | export const actions = { | 7 | export const actions = { |
| 7 | previewWork ({ commit }, payload = {}) { | 8 | previewWork ({ commit }, payload = {}) { |
| @@ -26,13 +27,19 @@ export const actions = { | @@ -26,13 +27,19 @@ export const actions = { | ||
| 26 | } | 27 | } |
| 27 | commit('setWork', work) | 28 | commit('setWork', work) |
| 28 | }, | 29 | }, |
| 29 | - saveWork ({ commit, state }, payload = {}) { | 30 | + saveWork ({ commit, dispatch, state }, payload = {}) { |
| 30 | // update work with strapi | 31 | // update work with strapi |
| 31 | const work = { | 32 | const work = { |
| 32 | ...state.work, | 33 | ...state.work, |
| 33 | ...payload | 34 | ...payload |
| 34 | } | 35 | } |
| 35 | - strapi.updateEntry('works', state.work.id, work) | 36 | + |
| 37 | + new AxiosWrapper({ | ||
| 38 | + dispatch, | ||
| 39 | + loading_name: 'saveWork_loading', | ||
| 40 | + successMsg: '保存作品成功', | ||
| 41 | + customRequest: strapi.updateEntry.bind(strapi) | ||
| 42 | + }).put('works', state.work.id, work) | ||
| 36 | }, | 43 | }, |
| 37 | fetchWork ({ commit, state }, workId) { | 44 | fetchWork ({ commit, state }, workId) { |
| 38 | strapi.getEntry('works', workId).then(entry => { | 45 | strapi.getEntry('works', workId).then(entry => { |
front-end/h5/src/utils/http.js
0 → 100644
| 1 | +import axios from 'axios' | ||
| 2 | +import { message } from 'ant-design-vue' | ||
| 3 | + | ||
| 4 | +message.config({ | ||
| 5 | + maxCount: 3 | ||
| 6 | +}) | ||
| 7 | + | ||
| 8 | +export const myMessage = message | ||
| 9 | + | ||
| 10 | +export const client = axios.create({ | ||
| 11 | + baseURL: '/v1', | ||
| 12 | + responseType: 'json' | ||
| 13 | +}) | ||
| 14 | +export const baseClient = axios.create({ | ||
| 15 | + responseType: 'json' | ||
| 16 | +}) | ||
| 17 | + | ||
| 18 | +export class AxiosWrapper { | ||
| 19 | + // eslint-disable-next-line camelcase | ||
| 20 | + constructor ({ name = 'default', loading_name, responseType = 'json', headers, dispatch, router, successMsg, failMsg, successCallback, failCallback, customRequest }) { | ||
| 21 | + this.name = name | ||
| 22 | + // eslint-disable-next-line camelcase | ||
| 23 | + this.loading_name = loading_name | ||
| 24 | + // eslint-disable-next-line camelcase | ||
| 25 | + this.responseType = responseType | ||
| 26 | + this.dispatch = dispatch | ||
| 27 | + this.router = router | ||
| 28 | + this.successMsg = successMsg | ||
| 29 | + this.failMsg = failMsg | ||
| 30 | + this.customRequest = customRequest | ||
| 31 | + this.successCallback = successCallback | ||
| 32 | + this.failCallback = failCallback | ||
| 33 | + this.source = axios.CancelToken.source() | ||
| 34 | + this.instance = axios.create({ | ||
| 35 | + // baseURL: '/v1', | ||
| 36 | + responseType, | ||
| 37 | + headers, | ||
| 38 | + cancelToken: this.source.token | ||
| 39 | + }) | ||
| 40 | + this.instance.interceptors.response.use(response => { | ||
| 41 | + // Do something with response data | ||
| 42 | + return Promise.resolve(response) | ||
| 43 | + }, error => { | ||
| 44 | + // Do something with response error | ||
| 45 | + return Promise.reject(error) | ||
| 46 | + }) | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + get (...args) { | ||
| 50 | + this.setDefaultLoadingName(args) | ||
| 51 | + | ||
| 52 | + this.setLoadingValue(true) | ||
| 53 | + return this.instance.get(...args).then(response => { | ||
| 54 | + const handler = this.getCommonResponseHandler({ failMsg: 'Query Failed.' }) | ||
| 55 | + handler.call(this, response) | ||
| 56 | + }).catch(error => { | ||
| 57 | + // handle error | ||
| 58 | + myMessage.error(error.message) | ||
| 59 | + }).finally(() => this.setLoadingValue(false)) | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + post (...args) { | ||
| 63 | + this.setDefaultLoadingName(args) | ||
| 64 | + | ||
| 65 | + this.setLoadingValue(true) | ||
| 66 | + return this.instance.post(...args).then(response => { | ||
| 67 | + const handler = this.getCommonResponseHandler({ failMsg: 'Save Failed.' }) | ||
| 68 | + handler.call(this, response) | ||
| 69 | + }).catch(error => { | ||
| 70 | + // handle error | ||
| 71 | + myMessage.error(error.message) | ||
| 72 | + }).finally(() => this.setLoadingValue(false)) | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + put (...args) { | ||
| 76 | + this.setDefaultLoadingName(args) | ||
| 77 | + | ||
| 78 | + this.setLoadingValue(true) | ||
| 79 | + if (this.customRequest) { | ||
| 80 | + return this.customRequest(...args) | ||
| 81 | + .then(data => { | ||
| 82 | + const handler = this.getCommonResponseHandler({ failMsg: 'Save Failed.' }) | ||
| 83 | + handler.call(this, { data: { status: 200, data } }) | ||
| 84 | + }) | ||
| 85 | + .finally(() => this.setLoadingValue(false)) | ||
| 86 | + } | ||
| 87 | + return this.instance.put(...args).then(response => { | ||
| 88 | + const handler = this.getCommonResponseHandler({ failMsg: 'Save Failed.' }) | ||
| 89 | + // handler.call(this, response) | ||
| 90 | + handler() | ||
| 91 | + }).catch(error => { | ||
| 92 | + // handle error | ||
| 93 | + myMessage.error(error.message) | ||
| 94 | + }).finally(() => this.setLoadingValue(false)) | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + delete (...args) { | ||
| 98 | + this.setDefaultLoadingName(args) | ||
| 99 | + | ||
| 100 | + this.setLoadingValue(true) | ||
| 101 | + return this.instance.delete(...args).then(response => { | ||
| 102 | + const handler = this.getCommonResponseHandler({ failMsg: 'Save Failed.' }) | ||
| 103 | + handler.call(this, response) | ||
| 104 | + }).catch(error => { | ||
| 105 | + // handle error | ||
| 106 | + myMessage.error(error.message) | ||
| 107 | + }).finally(() => this.setLoadingValue(false)) | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + cancel (reason) { | ||
| 111 | + this.source.cancel(reason) | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + setLoadingValue (payload) { | ||
| 115 | + this.dispatch('loading/update', { type: this.loading_name, payload }, { root: true }) | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + setDefaultLoadingName (...args) { | ||
| 119 | + if (!this.loading_name) { | ||
| 120 | + let url = args[0] | ||
| 121 | + if (url.indexOf('/') !== -1) { | ||
| 122 | + let us = url.split('/') | ||
| 123 | + url = us[us.length - 1] | ||
| 124 | + } | ||
| 125 | + if (url.indexOf('?') !== -1) { | ||
| 126 | + let us = url.split('?') | ||
| 127 | + url = us[0] | ||
| 128 | + } | ||
| 129 | + this.loading_name = `${url}_loading` | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + getCommonResponseHandler ({ failMsg } = {}) { | ||
| 134 | + return (response) => { | ||
| 135 | + if (!response.data) { | ||
| 136 | + myMessage.warn(this.failMsg || failMsg) | ||
| 137 | + } else if (response.data.status === 200) { | ||
| 138 | + this.successMsg && myMessage.success(this.successMsg) | ||
| 139 | + if (this.successCallback) { | ||
| 140 | + this.successCallback(response) | ||
| 141 | + } else { | ||
| 142 | + // this.dispatch({ type: this.name, payload: response.data.data }) | ||
| 143 | + } | ||
| 144 | + } else if (this.responseType === 'json') { | ||
| 145 | + myMessage.error(response.data.msg) | ||
| 146 | + if (response.data.status === 401) { | ||
| 147 | + if (this.router) { | ||
| 148 | + this.router.push('/login') | ||
| 149 | + } | ||
| 150 | + } else { | ||
| 151 | + alert(response.data.msg) | ||
| 152 | + if (this.failCallback) { | ||
| 153 | + this.failCallback(response) | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + } else { | ||
| 157 | + if (this.successCallback) { | ||
| 158 | + this.successCallback(response) | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + } | ||
| 162 | + } | ||
| 163 | +} |
front-end/h5/vue.config.js
| 1 | const ProxyAgent = require('proxy-agent') | 1 | const ProxyAgent = require('proxy-agent') |
| 2 | const isProd = process.env.NODE_ENV === 'production' | 2 | const isProd = process.env.NODE_ENV === 'production' |
| 3 | 3 | ||
| 4 | -const API_ORIGIN = 'https://radiant-depths-79548.herokuapp.com' | ||
| 5 | -const publicPath = isProd ? '/luban-h5/' : '/' | ||
| 6 | -// const proxy = isProd ? API_ORIGIN : 'http://localhost:1337' | 4 | +const PROD_API_ORIGIN = 'https://radiant-depths-79548.herokuapp.com' |
| 5 | +const DEV_API_ORIGIN = 'http://localhost:1337' | ||
| 7 | 6 | ||
| 8 | module.exports = { | 7 | module.exports = { |
| 9 | runtimeCompiler: true, | 8 | runtimeCompiler: true, |
| 10 | - publicPath, | 9 | + publicPath: isProd ? '/luban-h5/' : '/', |
| 11 | devServer: { | 10 | devServer: { |
| 12 | // proxy: API_ORIGIN | 11 | // proxy: API_ORIGIN |
| 13 | proxy: { | 12 | proxy: { |
| 14 | '/': { | 13 | '/': { |
| 15 | agent: new ProxyAgent('socks5://127.0.0.1:1086'), | 14 | agent: new ProxyAgent('socks5://127.0.0.1:1086'), |
| 16 | - target: API_ORIGIN, | 15 | + target: isProd ? PROD_API_ORIGIN : DEV_API_ORIGIN, |
| 17 | changeOrigin: true | 16 | changeOrigin: true |
| 18 | } | 17 | } |
| 19 | } | 18 | } |