Commit c567b1a1 authored by 郝聪敏's avatar 郝聪敏

feature: 优化

parent 9ebd2881
......@@ -43,4 +43,9 @@ export default class EditorController extends Controller {
offset: (+pageNo - 1) * +pageSize || 0 });
ctx.body = ctx.helper.ok({ total, data });
}
public async getTemplateList(ctx: Context) {
const list = await ctx.model.PageInfo.findAll({ where: { isTemplate: 1 } });
ctx.body = ctx.helper.ok(list);
}
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ export default (application: Application) => {
router.post('/editor/save', controller.editor.save);
router.post('/editor/update', controller.editor.update);
router.get('/editor/get/list', controller.editor.getList);
router.get('/editor/get/template', controller.editor.getTemplateList);
router.get('/editor/get/:pageId', controller.editor.get);
// router.get('/', controller.admin.login);
// router.get('/admin', controller.admin.home);
......
import DB from '../lib/db/base';
declare module 'egg' {
interface Application {
db: DB;
}
interface Context {
db: DB;
}
}
\ No newline at end of file
......@@ -3,15 +3,18 @@ import config from '../config';
const testReq = 0;
export default {
getPageList(params) {
return http.get(config.apiHost + 'editor/get/list', { params });
return http.get('editor/get/list', { params });
},
getPageById(params) {
return http.get(`/editor/get/${params.pageId}`);
return http.get(`editor/get/${params.pageId}`);
},
updatePage(params) {
return http.post(`/editor/update`, params);
return http.post(`editor/update`, params);
},
savePage(params) {
return http.post(`/editor/save`, params);
}
return http.post(`editor/save`, params);
},
getTemplateList() {
return http.get('editor/get/template');
},
};
\ No newline at end of file
export default {
apiHost: `http://localhost:7001/`,
apiHost: `http://172.20.3.77:80/`,
qiniuHost: `https://appsync.lkbang.net/`,
};
......@@ -2,8 +2,8 @@ import Http from '@/service/http.service.ts';
import Cookies from '@/service/cookieStorage.service.ts';
import localStorage from '@/service/localStorage.service.ts';
import { getParameterByName } from '@/service/utils.service.ts';
import qs from 'qs';
const qs = require('qs');
const ApiBaseUrl = 'http://passportapi.q-gp.com';
const qApi = 'http://qapi.q-gp.com';
......
......@@ -48,7 +48,7 @@
</a>
</template>
</cr-field>
<cr-button :color="btnColor" shape='circle' block native-type="submit" class="login-form-submit">
<cr-button :color="btnColor" :style="styles" shape='circle' block native-type="submit" class="login-form-submit">
{{ btnTxt }}
</cr-button>
</cr-form>
......@@ -84,7 +84,8 @@ export default {
type: String,
default: '#000'
},
registerFrom: String
btnImage: String,
registerFrom: String,
},
data() {
return {
......@@ -117,6 +118,11 @@ export default {
},
captchaBtnText() {
return this.showCount ? `剩余${this.count}秒` : '获取验证码';
},
styles() {
return this.btnImage ? {
background: `url(${this.btnImage}) no-repeat 0 0 / cover`,
} : {};
}
},
mounted() {
......
// tslint:disable
/* initGeetest 1.0.0
* 用于加载id对应的验证码库,并支持宕机模式
* 暴露 initGeetest 进行验证码的初始化
......
......@@ -45,11 +45,7 @@ export const basicComponents = [
color: '#07c160',
text: '按钮1'
},
commonStyle: {
position: 'absolute',
top: 5,
left: 5
}
commonStyle: {}
},
{
eleName: 'cr-field',
......@@ -62,7 +58,7 @@ export const basicComponents = [
},
{
key: 'label',
name: 'label文案',
name: 'label',
type: 'Input'
}
],
......@@ -70,11 +66,7 @@ export const basicComponents = [
placeholder: '请输入',
label: 'label'
},
commonStyle: {
position: 'absolute',
top: 5,
left: 5
}
commonStyle: {}
},
{
eleName: 'cr-image',
......@@ -102,11 +94,7 @@ export const basicComponents = [
src: 'https://appsync.lkbang.net/Fs0qmUsMry39AjHDf_W-qgn8XEy6',
fit: 'contain'
},
commonStyle: {
position: 'absolute',
top: 5,
left: 5
}
commonStyle: {}
}
];
......@@ -124,8 +112,13 @@ export const businessComponents = [
{
key: 'btnColor',
name: '按钮颜色',
type: 'ColorPicker'
type: 'ColorSelector'
},
{
key: 'btnImage',
name: '按钮图片',
type: 'Input'
}
{
key: 'registerFrom',
name: '渠道号',
......@@ -135,12 +128,9 @@ export const businessComponents = [
value: {
btnTxt: '登陆',
btnColor: '#ee0a24',
btnImage: '',
registerFrom: ''
},
commonStyle: {
position: 'absolute',
top: 5,
left: 5
}
commonStyle: {}
}
];
\ No newline at end of file
// tslint:disable
// @flow
// export type LayoutItemRequired = {w, h, x, y, i};
// export type LayoutItem = LayoutItemRequired &
......@@ -568,8 +569,8 @@ export function hyphenate(str) {
export function findItemInArray(array, property, value) {
for (let i = 0; i < array.length; i++) {
if (array[i][property] == value) {
return true;
}
return true;
}
}
return false;
......
......@@ -18,8 +18,8 @@ export default class DynamicForm extends Vue {
@Watch('pageData', { immediate: true })
onPageDataChange(newVal) {
const { pageName, pageDescribe, coverImage, showDownload, isPublish } = this.pageData;
this.formCustom = { pageName, pageDescribe, coverImage, showDownload: !!showDownload, isPublish: !!isPublish };
const { pageName, pageDescribe, coverImage, showDownload, isPublish, isTemplate } = this.pageData;
this.formCustom = { pageName, pageDescribe, coverImage, showDownload: !!showDownload, isPublish: !!isPublish, isTemplate: !!isTemplate };
}
@Watch('value')
......@@ -31,7 +31,7 @@ export default class DynamicForm extends Vue {
handleSubmit(type) {
this.$refs.formCustom.validate((valid) => {
if (valid) {
this.$emit('submit', type, { ...this.formCustom, showDownload: this.formCustom.showDownload ? 1 : 0, isPublish: this.formCustom.isPublish ? 1 : 0 });
this.$emit('submit', type, { ...this.formCustom, showDownload: this.formCustom.showDownload ? 1 : 0, isPublish: this.formCustom.isPublish ? 1 : 0, isTemplate: this.formCustom.isTemplate ? 1 : 0 });
}
});
},
......
......@@ -16,6 +16,9 @@
<FormItem label="是否发布" prop="isPublish">
<Checkbox v-model="formCustom.isPublish"></Checkbox>
</FormItem>
<FormItem label="是否模板" prop="isTemplate" v-if="formCustom.isPublish">
<Checkbox v-model="formCustom.isTemplate"></Checkbox>
</FormItem>
</Form>
<div slot="footer">
<Button type="success" @click="handleSubmit('save')">保存</Button>
......@@ -23,52 +26,4 @@
</div>
</Modal>
</template>
<script lang="ts" src="./index.ts">
// import Upload from '@editor/component/DynamicForm/component/Upload/index.vue';
// export default {
// name: 'BasicPageForm',
// components: {
// Upload,
// },
// props: {
// value: Boolean,
// defaultForm: Object
// },
// data() {
// return {
// showPopup: false,
// formCustom: {
// pageName: '',
// pageDescribe: '',
// coverImage: 'http://desk.fd.zol-img.com.cn/g5/M00/00/07/ChMkJ1ZqMb2IWITEAAbRDaofaNIAAGBHwO3hh0ABtEl380.jpg',
// showDownload: false,
// isPublish: false,
// ...this.defaultForm
// },
// ruleCustom: {
// pageName: [
// { required: true, message: '请输入页面名称', trigger: 'blur' }
// ]
// },
// }
// },
// watch: {
// value(val) {
// this.showPopup = val;
// },
// },
// methods: {
// handleSubmit(type) {
// this.$refs.formCustom.validate((valid) => {
// if (valid) {
// this.$emit('submit', type, this.formCustom);
// }
// });
// },
// change(val) {
// this.$emit('input', val);
// }
// }
// }
</script>
\ No newline at end of file
<script lang="ts" src="./index.ts"></script>
\ No newline at end of file
......@@ -7,7 +7,7 @@ import { COMPONENT_NAME } from './constant';
const ContextmenuConstructor = Vue.extend(Contextmenu);
Vue.component(COMPONENT_NAME, Submenu);
function install(Vue) {
function install(vue) {
let lastInstance = null;
const ContextmenuProxy = function(options) {
const instance = new ContextmenuConstructor();
......@@ -19,8 +19,8 @@ function install(Vue) {
instance.position.y = options.event.clientY;
}
instance.customClass = options.customClass;
options.minWidth && (instance.style.minWidth = options.minWidth);
options.zIndex && (instance.style.zIndex = options.zIndex);
if (options.minWidth) { instance.style.minWidth = options.minWidth; }
if (options.zIndex) { instance.style.zIndex = options.zIndex; }
ContextmenuProxy.destroy();
lastInstance = instance;
instance.$mount();
......@@ -31,7 +31,7 @@ function install(Vue) {
lastInstance = null;
}
};
Vue.prototype.$contextmenu = ContextmenuProxy;
vue.prototype.$contextmenu = ContextmenuProxy;
}
if (window && window.Vue) {
......
// tslint:disable
export function hasClass(el, className) {
if (!className) {
return true;
......
import { Component, Vue, Prop } from 'vue-property-decorator';
import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
import components from '@qg/cherry-ui/src/index.js';
import FreedomContainer from '../../component/FreedomContainer/index.vue';
import { kebabCase } from 'lodash';
......@@ -8,17 +8,22 @@ import { chunk, flatten } from 'lodash';
export default class DynamicComponent extends Vue {
@Prop({ default: () => ([]), type: Array }) data;
eleConfig: array = chunk(this.data, 2);
eleConfig: array = [];
created() {
console.log(this.eleConfig, flatten(this.eleConfig));
@Watch('data', { immediate: true })
onDataChange(newVal) {
this.eleConfig = chunk(newVal, 2);
}
dragstart(event, eleName) {
this.$emit('dragstart');
const eleConfig = flatten(this.eleConfig).find(config => config.eleName === eleName);
const props = this.getProps(eleName);
if (eleName === 'freedom-container') {
if (eleName.includes('template')) {
event.dataTransfer.setData('text', JSON.stringify({
template: eleConfig.page
}));
} else if (eleName === 'freedom-container') {
event.dataTransfer.setData('text', JSON.stringify({
name: eleName,
point: {x: 0, y: 2, w: 12, h: 5, i: '0'},
......@@ -36,7 +41,6 @@ export default class DynamicComponent extends Vue {
commonStyle: eleConfig.commonStyle
}));
}
console.log('getData', event.dataTransfer.getData('text'));
event.dataTransfer.effectAllowed = 'copyMove';
}
......
......@@ -6,9 +6,8 @@
<p slot="title">{{ config.title }}</p>
<div class="dynamic-row-card">
<img
src="http://desk.fd.zol-img.com.cn/g5/M00/00/07/ChMkJ1ZqMb2IWITEAAbRDaofaNIAAGBHwO3hh0ABtEl380.jpg"
/>
src="http://desk.fd.zol-img.com.cn/g5/M00/00/07/ChMkJ1ZqMb2IWITEAAbRDaofaNIAAGBHwO3hh0ABtEl380.jpg"
/>
</div>
</Card>
</Col>
......@@ -29,11 +28,21 @@
display: flex;
justify-content: center;
img {
width: 64px;
width: 100%;
object-fit: fill;
-webkit-user-drag: none;
}
}
/deep/ .ivu-card {
&-head {
padding: 5px;
}
&-body {
padding: 10px;
}
}
}
}
</style>
<template>
<div class="color-selector">
<Input class="color-selector-input" v-model="color" placeholder="请输入"></Input>
<ColorPicker v-model="color" />
</div>
</template>
<script>
export default {
props: {
value: String,
},
data() {
return {
color: this.value,
}
},
watch: {
color(val) {
console.log('color', val);
this.$emit('input', val);
}
}
}
</script>
<style lang="less">
.color-selector {
display: flex;
align-items: center;
justify-content: space-between;
&-input {
flex-basis: 150px;
}
}
</style>
\ No newline at end of file
......@@ -3,8 +3,9 @@ import { Getter, State } from 'vuex-class';
import { reduce, ceil, subtract, divide } from 'lodash';
import { ContextMenu } from '@editor/mixins/contextMenu.mixin';
import Upload from './component/Upload/index.vue';
import ColorSelector from './component/ColorSelector/index.vue';
@Component({ components: { Upload }, name: 'DynamicForm' })
@Component({ components: { Upload, ColorSelector }, name: 'DynamicForm' })
export default class DynamicForm extends Mixins(ContextMenu) {
@State(state => state.editor.curEleIndex) curEleIndex;
@State(state => state.editor.curChildIndex) curChildIndex;
......@@ -16,10 +17,14 @@ export default class DynamicForm extends Mixins(ContextMenu) {
@Watch('curElement', { immediate: true, deep: true })
onElementChange(newVal) {
this.form = reduce(newVal.schame, (obj, param) => {
obj[param.key] = newVal.props[param.key];
return obj;
}, {});
newVal?.schame?.forEach(schame => {
this.$set(this.form, schame.key, newVal.props[schame.key]);
});
}
@Watch('form', { immediate: true, deep: true })
onFormChange(newVal) {
this.$emit('modProps', this.form);
}
get eleName() {
......@@ -30,13 +35,8 @@ export default class DynamicForm extends Mixins(ContextMenu) {
return result;
}
change() {
this.$emit('modProps', this.form);
}
resizedEvent(h, w) {
const elements = this.pageData.elements[this.curEleIndex];
console.log('resizedEvent', this.curEleIndex, elements);
this.updatePageInfo({ containerIndex: this.curEleIndex, data: { ...elements, point: { ...elements.point, w: w ?? elements.point.w, h: h ?? elements.point.h } } });
}
......
......@@ -4,7 +4,7 @@
<h4>组件属性</h4>
<Form ref="formCustom" :label-width="60" :model="form">
<FormItem :label="item.name" :key="index" v-for="(item, index) in curElement.schame">
<component :is="item.type" v-model="form[item.key]" @on-change="change" />
<component :is="item.type" v-model="form[item.key]" />
</FormItem>
</Form>
</template>
......
......@@ -2,11 +2,13 @@ import { Component, Prop, Mixins } from 'vue-property-decorator';
import LoginForm from '@/lib/Form/index.vue';
import { ContextMenu } from '@editor/mixins/contextMenu.mixin';
import { cloneDeep } from 'lodash';
import { Action, Mutation } from 'vuex-class';
import { Action, Mutation, State } from 'vuex-class';
@Component({ components: { LoginForm }, name: 'FreedomContainer' })
export default class FreedomContainer extends Mixins(ContextMenu) {
@Action('setDragable') setDragable;
@State(state => state.editor.curChildIndex) curChildIndex;
@Prop({type: Object, default: () => ({ child: [] })}) childItem;
@Prop({type: Number, default: 0}) containerIndex;
@Prop({ type: Boolean, default: true }) showHeader;
......
......@@ -2,7 +2,7 @@
<div class="freedom" @click.stop="handleElementClick(containerIndex)">
<header v-if="showHeader">{{ title }}</header>
<div :class="['freedom-body', { 'freedom-body_full': showHeader }]" :style="{background: `url(${backgroundImage}) no-repeat 0 0 / cover`}">
<component v-for="(item, index) in childItem.child" :style="transformStyle(item.commonStyle)" :is="item.name" :key="index" @click.stop.native="handleElementClick(containerIndex, index)" @mousedown.native.stop="mousedown(index, $event)" v-bind="item.props" @contextmenu.native.prevent.stop="show($event, containerIndex, index)"></component>
<component :class="['freedom-body-item', { 'Fb-item_selected': curChildIndex === index }]" v-for="(item, index) in childItem.child" :style="transformStyle(item.commonStyle)" :is="item.name" :key="index" @click.stop.native="handleElementClick(containerIndex, index)" @mousedown.native.stop="mousedown(index, $event)" v-bind="item.props" @contextmenu.native.prevent.stop="show($event, containerIndex, index)"></component>
</div>
</div>
</template>
......@@ -26,6 +26,14 @@
&_full {
height: calc(100% - 48px);
}
.freedom-body-item {
&:hover {
border: 2px dashed #0c0c0c !important;
}
}
.Fb-item_selected {
border: 2px dashed #0c0c0c !important;
}
}
}
</style>
\ No newline at end of file
import { kebabCase } from 'lodash';
import { kebabCase, maxBy } from 'lodash';
import { Getter, Action, State, Mutation } from 'vuex-class';
import { Mixins, Component, Watch } from 'vue-property-decorator';
import DynamicComponent from '@editor/component/DynamicComponent/index.vue';
......@@ -19,42 +19,36 @@ export default class DashBoard extends Mixins(ContextMenu) {
@Mutation('ADD_ELEMENTS') addElements;
@Mutation('SET_CUR_ELE_INDEX') setCurEleIndex;
@Mutation('SET_CUR_CHILD_INDEX') setCurChildIndex;
@Mutation('SET_PAGE_INFO') setPageInfo;
@Mutation('SET_PAGE_DATA') setPageData;
@Action('resetPageData') resetPageData;
@Action('savePageData') savePageData;
@Action('getPageDate') getPageDate;
@Action('getTemplateList') getTemplateList;
@Action('setTemplateInfo') setTemplateInfo;
@Getter('pageId') pageId;
@Getter('pageData') pageData;
@State(state => state.editor.draggable) draggable;
@State(state => state.editor.curEleIndex) curEleIndex;
@State(state => state.editor.curChildIndex) curChildIndex;
@State(state => state.editor.templateList) templateList;
activeName: string = '1';
activeTab: string = '0';
isCollapsed: boolean = false;
isCollapsed: boolean = true;
isDragIn: boolean = false;
isDraging: boolean = false;
// curEleIndex: number | null = null;
// curChildIndex: number | null = null;
resources: object = { basicComponents, businessComponents };
showSubmitPopup: boolean = false;
// todo:
// 1. grid拖拽进去后的顺序
// 2. init height
created() {
const { pageId } = this.$route.params;
async created() {
const { pageId, templateId } = this.$route.params;
this.resetPageData();
if (pageId) {
this.getPageDate({ pageId });
} else {
this.resetPageData();
} else if (templateId) {
this.setTemplateInfo({ pageId: templateId });
}
}
mounted() {
this.$nextTick(() => {
this.activeTab = '2';
});
console.log(this.activeTab);
this.getTemplateList();
}
get layout() {
......@@ -83,8 +77,8 @@ export default class DashBoard extends Mixins(ContextMenu) {
this.showSubmitPopup = true;
} else {
this.pageData.elements.sort((a, b) => a.point.y - b.point.y);
const { pageName, pageDescribe, coverImage, showDownload, isPublish } = pageConfig;
const pageInfo = { page: JSON.stringify(this.pageData), author: 'congmin.hao', isPublish, pageName, pageDescribe, coverImage, showDownload };
const { pageName, pageDescribe, coverImage, showDownload, isPublish, isTemplate } = pageConfig;
const pageInfo = { page: JSON.stringify(this.pageData), author: 'congmin.hao', isPublish, pageName, pageDescribe, coverImage, showDownload, isTemplate };
if (+this.pageId) { pageInfo.id = this.pageId; }
await this.savePageData(pageInfo);
this.showSubmitPopup = false;
......@@ -99,13 +93,16 @@ export default class DashBoard extends Mixins(ContextMenu) {
}
handleElementClick(curEleIndex = null, curChildIndex = null) {
console.log(curEleIndex, curChildIndex);
this.toggle(false);
this.setCurEleIndex(curEleIndex);
this.setCurChildIndex(curChildIndex);
}
toggle(val) {
if (val) {
this.setCurEleIndex(null);
this.setCurChildIndex(null);
}
this.isCollapsed = val;
}
......@@ -147,16 +144,33 @@ export default class DashBoard extends Mixins(ContextMenu) {
}
drops(event) {
console.log(event.target);
this.isDragIn = false;
this.isCollapsed = false;
const data = JSON.parse(event.dataTransfer.getData('text'));
const { layerX: left, layerY: top } = event;
event.dataTransfer.clearData();
console.log({...data, i: this.pageData.elements.length}, this.pageData.elements[event.target.dataset.index]);
if (event.target.classList.contains('freedom')) {
this.addElements({ containerIndex: event.target.dataset.index, data });
// template
if (data.template) {
this.setPageData(JSON.parse(data.template));
this.handleElementClick(null, null);
// freedom
} else if (event.target.classList.contains('freedom')) {
const { y: curY } = this.pageData.elements[event.target.dataset.index].point;
const scrollTop = this.layout.reduce((pre, cur) => {
if (cur.y < curY) {
return pre + cur.h * 55.09;
}
return pre;
}, 0);
this.addElements({ containerIndex: event.target.dataset.index, data: { ...data, commonStyle: { position: 'absolute', left, top: top - scrollTop } } });
this.handleElementClick(+event.target.dataset.index, this.pageData.elements[event.target.dataset.index].child.length - 1);
// component
} else {
this.addElements({ data: {...data, point: { ...data.point, i: this.pageData.elements.length}} });
const { i } = maxBy(this.layout, 'i') || {};
const y = Math.floor(top / 55.09);
console.log('drops', i);
this.addElements({ data: {...data, point: { ...data.point, i: i || i === 0 ? String(+i + 1) : '0', y } });
this.handleElementClick(this.pageData.elements.length - 1, null);
}
}
......@@ -172,7 +186,6 @@ export default class DashBoard extends Mixins(ContextMenu) {
}
}
console.log(props);
return props;
}
......
......@@ -12,7 +12,7 @@
</Col>
<Col span="7" @click.native="exit">
<Icon type="monitor"></Icon>
<span>退出</span>
<span>返回</span>
</Col>
</Row>
</Col>
......@@ -21,26 +21,27 @@
<Col class="dashboard-container-left" span="6">
<Menu class="Dc-left-menu" active-name="1" @on-select="selectMaterial">
<MenuItem name="1">
<Icon type="ios-paper"></Icon>
</MenuItem>
<MenuItem name="2">
<Icon type="ios-people"></Icon>
</MenuItem>
<MenuItem name="3">
<Icon type="stats-bars"></Icon>
</MenuItem>
<Icon type="ios-paper"></Icon>
</MenuItem>
<MenuItem name="2">
<Icon type="ios-people"></Icon>
</MenuItem>
<MenuItem name="3">
<Icon type="stats-bars"></Icon>
</MenuItem>
</Menu>
<div class="Dc-left-content">
<div v-show="activeName === '1'">
<h4>基础库</h4>
<h2>基础库</h2>
<dynamic-component :data="resources.basicComponents" @dragstart="dragstart" @dragend="dragend"></dynamic-component>
</div>
<div v-show="activeName === '2'">
<h4>业务库</h4>
<h2>业务库</h2>
<dynamic-component :data="resources.businessComponents" @dragstart="dragstart" @dragend="dragend"></dynamic-component>
</div>
<div v-show="activeName === '3'">
<h4>模板</h4>
<h2>模板</h2>
<dynamic-component :data="templateList" @dragstart="dragstart" @dragend="dragend"></dynamic-component>
</div>
</div>
</Col>
......@@ -68,8 +69,9 @@
:i="item.point.i"
:key="item.point.i"
@contextmenu.native.prevent="show($event, index)"
@resized="resizedEvent">
<component class="Dcmc-panel-com" @handleElementClick="handleElementClick" :data-index="index" :containerIndex="index" :childItem="item" :is="item.name" :key="index" v-bind="item.props"></component>
@resized="resizedEvent"
:class="{'Dcmcp-item_selected': curEleIndex === index && curChildIndex === null}">
<component class="Dcmcp-item-com" @handleElementClick="handleElementClick" :data-index="index" :containerIndex="index" :childItem="item" :is="item.name" :key="index" v-bind="item.props"></component>
</grid-item>
</grid-layout>
</div>
......@@ -126,6 +128,7 @@
.Dc-left-content {
flex: 1;
padding: 10px;
background: #fff;
}
.tabs-position();
}
......@@ -148,23 +151,22 @@
background-color: rgb(244, 244, 244);
box-shadow: 2px 0px 10px rgba(0, 0, 0, 0.2);
/deep/ .vue-grid-layout {
// transform: translateY(-10px);
.vue-grid-item {
display: flex;
justify-content: center;
align-items: center;
background: #fff;
overflow: hidden;
&:hover {
border: 1px dashed #0c0c0c !important;
}
}
.Dcmcp-item_selected {
border: 1px dashed #0c0c0c !important;
}
}
.Dcmc-panel-com {
// height: 38px;
}
// &_in {
// opacity: 0.7;
// }
&_draging {
.Dcmc-panel-com {
.Dcmcp-item-com {
* {
pointer-events: none;
}
......@@ -177,6 +179,7 @@
height: 100%;
background: #fff;
transition: width ease-in-out 0.5s;
overflow: hidden;
.Dc-middle-editing {
height: 100%;
min-width: 320px;
......@@ -184,6 +187,8 @@
}
}
.Dcm-sider_none {
position: absolute;
right: 0;
width: 0;
}
}
......
......@@ -11,6 +11,7 @@
<script>
import editorApi from '@api/editor.api';
import QGTable from '@editor/component/QgTable/index.vue';
import config from '@/config/index';
export default {
components: {
QGTable,
......@@ -29,6 +30,11 @@ export default {
title: '名称',
formType: 'input',
},
{
key: 'pageDescribe',
title: '描述',
formType: 'input',
},
{
key: 'coverImage',
title: '封面',
......@@ -59,6 +65,13 @@ export default {
);
},
},
{
key: 'id',
title: '链接',
render: (h, params) => {
return h('span', `${config.apiHost}activity/${params.row.id}`)
}
},
{
key: 'isPublish',
title: '是否发布',
......
......@@ -3,6 +3,7 @@
<QGTable
:columns="columns"
:request="query"
@newBtnClick="addPage"
>
</QGTable>
</div>
......@@ -10,6 +11,7 @@
<script>
import editorApi from '@api/editor.api';
import QGTable from '@editor/component/QgTable/index.vue';
import config from '@/config/index';
export default {
components: {
QGTable,
......@@ -23,6 +25,16 @@ export default {
formType: 'input',
hideSearch: true
},
{
key: 'pageName',
title: '名称',
formType: 'input',
},
{
key: 'pageDescribe',
title: '描述',
formType: 'input',
},
{
key: 'author',
title: '作者',
......@@ -58,6 +70,13 @@ export default {
);
},
},
{
key: 'id',
title: '链接',
render: (h, params) => {
return h('span', `${config.apiHost}activity/${params.row.id}`)
}
},
{
key: 'isTemplate',
title: '是否模板',
......@@ -76,6 +95,9 @@ export default {
async query(data) {
return editorApi.getPageList({ type: 'list', ...data });
},
addPage() {
this.$router.push('/detail');
}
},
mounted(){}
}
......
......@@ -3,7 +3,7 @@
<QGTable
:columns="columns"
:request="query"
:toolBar="gettoolBar"
@newBtnClick="addPage"
>
</QGTable>
</div>
......@@ -11,6 +11,7 @@
<script>
import editorApi from '@api/editor.api';
import QGTable from '@editor/component/QgTable/index.vue';
import config from '@/config/index';
export default {
components: {
QGTable,
......@@ -24,6 +25,16 @@ export default {
formType: 'input',
hideSearch: true
},
{
key: 'pageName',
title: '名称',
formType: 'input',
},
{
key: 'pageDescribe',
title: '描述',
formType: 'input',
},
{
key: 'author',
title: '作者',
......@@ -32,36 +43,79 @@ export default {
{
key: 'coverImage',
title: '封面',
hideSearch: true
hideSearch: true,
render: (h, params) => {
return h(
'div',
{
style: {
textAlign: 'left',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '10px 0'
},
},
[h(
'img',{
attrs: {
src: params.row.coverImage,
},
style: {
width: '60px',
height: '60px'
}
}
)]
);
},
},
{
key: 'isTemplate',
title: '是否模板',
formType: 'select',
number: true,
hideSearch: true,
valueEnum: {
0: '',
1: '',
key: 'id',
title: '链接',
render: (h, params) => {
return h('span', `${config.apiHost}activity/${params.row.id}`)
}
},
{
key: 'action',
title: '操作',
width: 200,
render: (h, params) => {
const props = {
type: 'primary',
};
const style = {
display: 'inline-block',
margin: '5px',
};
return h(
'Button',
{
props,
style: {
...style,
},
on: {
click: () => {
this.$router.push({ name: 'detail', params: { templateId: params.row.id } });
},
},
},
'立即使用'
);
},
}
],
}
},
methods: {
gettoolBar(){
return [<Button type="primary" onClick={() => this.newElement}>新增</Button>]
},
requestFn(data){
console.log(data);
return {
data: [{page: 1},{}],
total: 100,
}
},
async query(data) {
return editorApi.getPageList({ type: 'template' });
},
addPage() {
this.$router.push('/detail');
}
},
mounted(){}
}
......
// import http from '../../../../service/http.service';
import api from '@/api/editor.api';
import { Module, GetterTree, ActionTree, MutationTree } from 'vuex';
import {
......@@ -9,11 +8,13 @@ import {
ADD_ELEMENTS
SET_CUR_ELE_INDEX,
SET_CUR_CHILD_INDEX,
RESET_PAGE_DATA
RESET_PAGE_DATA,
SET_TEMPLATE_LIST,
SET_PAGE_DATA,
} from './type';
import RootState from '../../state';
import EditorState, { PageInfo, defaultState, Page } from './state';
import EditorState, { PageInfo, defaultState, Page, PageElement } from './state';
export default class EditorModule implements Module<EditorState, RootState> {
state: EditorState;
......@@ -33,22 +34,31 @@ export default class EditorModule implements Module<EditorState, RootState> {
actions: ActionTree<EditorState, RootState> = {
async savePageData({ commit }, condition) {
if (condition.id) {
// await http.post(`/editor/update`, condition);
await api.updatePage(condition);
commit(SET_PAGE_INFO, { ...condition, page: JSON.parse(condition.page as string) });
} else {
// const res = await http.post(`/editor/save`, condition);
const res = await api.savePage(condition);
const { page, ...rest } = res as PageInfo;
commit(SET_PAGE_INFO, { ...rest, page: JSON.parse(page as string) });
}
},
async getPageDate({ commit }, condition) {
// const res = await http.get(`/editor/get/${condition.pageId}`);
const res = await api.getPageById(condition);
const { page, ...rest } = res as PageInfo;
commit(SET_PAGE_INFO, { ...rest, page: JSON.parse(page as string) });
},
async getTemplateList({ commit }) {
const res = await api.getTemplateList({ type: 'template' });
const handledData = res.map((v, idx) => ({ eleName: `template${idx}`, title: v.pageName, coverImage: v.coverImage, page: v.page }));
// todo 解析数据
commit(SET_TEMPLATE_LIST, handledData);
},
async setTemplateInfo({commit}, condition) {
const res = await api.getPageById(condition);
const { page } = res as PageInfo;
const { pageInfo } = { ...defaultState };
commit(SET_PAGE_INFO, { ...pageInfo, page: JSON.parse(page as string) });
},
setDragable({ commit }, condition) {
commit(SET_DRAGABLE, condition);
},
......@@ -73,6 +83,12 @@ export default class EditorModule implements Module<EditorState, RootState> {
[SET_CUR_CHILD_INDEX](state, curChildIndex) {
state.curChildIndex = curChildIndex;
},
[SET_TEMPLATE_LIST](state, data) {
state.templateList = data;
},
[SET_PAGE_DATA](state, data) {
state.pageInfo.page = data;
},
[COPY_OR_DELETE_PAGE_INFO](state, { type, containerIndex, childIndex }) {
const page = (state.pageInfo.page as Page).elements;
if (type === 'delete') {
......@@ -82,7 +98,7 @@ export default class EditorModule implements Module<EditorState, RootState> {
page.splice(containerIndex, 1);
}
} else if (type === 'copy') {
let eleCopyed = {};
let eleCopyed = {} as PageElement;
if (childIndex || childIndex === 0) {
eleCopyed = page[containerIndex].child[childIndex];
const { left, top } = eleCopyed.commonStyle;
......
interface Schame {
key: string;
name: string;
type: string;
}
interface Point {
x: number;
y: number;
w: number;
moved: boolean;
h: number;
i: number | string;
}
interface CommonStyle {
left: number;
top: number;
}
export interface PageElement {
name: string;
schame: Schame[];
props: object;
point: Point;
commonStyle: CommonStyle;
child: PageElement[];
}
export interface Page {
elements: any[];
elements: PageElement[];
}
export interface PageInfo {
......@@ -9,14 +38,7 @@ export interface PageInfo {
author?: string;
coverImage?: string;
isTemplate?: number;
isPublish?: number;
}
export default interface EditorState {
pageInfo: PageInfo;
draggable: boolean;
curEleIndex: number | null;
curChildIndex: number | null;
isPublish?: number | boolean;
}
export const defaultState = {
......@@ -33,5 +55,14 @@ export const defaultState = {
page: {
elements: [],
}
}
};
\ No newline at end of file
},
templateList: [],
};
export default interface EditorState {
pageInfo: PageInfo;
draggable: boolean;
curEleIndex: number | null;
curChildIndex: number | null;
templateList: any[];
}
\ No newline at end of file
......@@ -6,4 +6,6 @@ export const UPDATE_PAGE_INFO = 'UPDATE_PAGE_INFO';
export const ADD_ELEMENTS = 'ADD_ELEMENTS';
export const SET_CUR_ELE_INDEX = 'SET_CUR_ELE_INDEX';
export const SET_CUR_CHILD_INDEX = 'SET_CUR_CHILD_INDEX';
export const RESET_PAGE_DATA = 'RESET_PAGE_DATA';
\ No newline at end of file
export const RESET_PAGE_DATA = 'RESET_PAGE_DATA';
export const SET_TEMPLATE_LIST = 'SET_TEMPLATE_LIST';
export const SET_PAGE_DATA = 'SET_PAGE_DATA';
\ No newline at end of file
import axios from 'axios';
import basicConfig from '../config';
import localStorage from './localStorage.service';
// import { Notify } from '@qg/cherry-ui';
import { Notify } from '@qg/cherry-ui';
const ERR_MESSAGE_MAP = {
status: {
......@@ -22,7 +22,6 @@ const ERR_MESSAGE_MAP = {
const CancelToken = axios.CancelToken;
const pending = {};
const testReq = 0;
let reqNum = 0;
axios.defaults.baseURL = basicConfig.apiHost;
axios.defaults.timeout = 30000;
......@@ -54,8 +53,6 @@ const instance = axios.create();
// 请求拦截器
instance.interceptors.request.use(
config => {
testReq++;
console.log('config.url', config.url, testReq, JSON.stringify(pending));
// beforeRequest();
// 发起请求时,取消掉当前正在进行的相同请求
if (pending[config.url as string]) {
......@@ -78,32 +75,26 @@ instance.interceptors.request.use(
// 响应拦截器即异常处理
instance.interceptors.response.use(
response => {
console.log('response.data.code', JSON.stringify(response && response.data));
if (response && response.config) {
let notifyType = 'danger';
// afterRequest();
delete pending[response.config.url as string];
if (response.data.code === '0000' && response.data.businessCode === '0000') { return response.data.data; }
// 后端返回异常信息时提出警告
if (response.data.code && response.data.msg) { notifyType = 'warning'; }
// Notify({
// type: notifyType,
// message: response.data.msg || '后端服务异常',
// duration: notifyType === 'warning' ? 6000 : 3000
// });
if (response.data.code === '40100') {
localStorage.remove('Token');
}
return Promise.reject(response.data);
} else {
console.log('response.data.code1', JSON.stringify(response));
let notifyType = 'danger';
// afterRequest();
delete pending[response.config.url as string];
if (response.data.code === '0000' && response.data.businessCode === '0000') { return response.data.data; }
// 后端返回异常信息时提出警告
if (response.data.code && response.data.msg) { notifyType = 'warning'; }
Notify({
type: notifyType,
message: response.data.msg || '后端服务异常',
duration: notifyType === 'warning' ? 6000 : 3000
});
if (response.data.code === '40100') {
localStorage.remove('Token');
}
return Promise.reject(response.data);
},
async err => {
console.log('err.response', JSON.stringify(err));
// afterRequest();
// 判断是否取消请求
if (err?.message === '取消重复请求') {
......@@ -132,7 +123,7 @@ instance.interceptors.response.use(
} else {
message = '连接到服务器失败';
}
// Notify({ type: 'danger', message });
Notify({ type: 'danger', message });
return Promise.reject(err);
}
);
......
......@@ -23,7 +23,8 @@ export default (appInfo: EggAppConfig) => {
};
exports.webpack = {
webpackConfigList: getWebpackConfig()
webpackConfigList: getWebpackConfig(),
browser: 'http://localhost:7001/editor/list'
};
return exports;
......
......@@ -3,7 +3,8 @@
"version": "4.0.3",
"description": "Egg + Vue + TypeScript Server Side Render(SSR) 服务端渲染骨架项目",
"scripts": {
"start": "egg-scripts start --port 7001 --workers 4",
"start": "egg-scripts start --port 80 --daemon --workers 4",
"stop": "egg-scripts stop",
"backend": "nohup egg-scripts start --port 7001 --workers 4 &",
"dev": "egg-bin dev -r egg-ts-helper/register",
"debug": "egg-bin debug -r egg-ts-helper/register",
......@@ -68,7 +69,6 @@
"egg-bin": "^4.9.0",
"egg-scripts": "^2.10.0",
"egg-ts-helper": "^1.13.0",
"imagemin-webpack-plugin": "^2.4.2",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"node-tool-utils": "^1.1.1",
......
......@@ -56,7 +56,8 @@ module.exports = {
from: 'app/web/asset',
to: 'asset'
}]
}
},
{ imagemini: false }
],
devtool:'source-map',
customize(webpackConfig){
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment