Commit 2170c951 authored by 郝聪敏's avatar 郝聪敏

feature: 项目拆分

parent 91acb5e9
# egg-vue-typescript-boilerplate
# quantum-blocks
基于 Egg + Vue + Webpack SSR 服务端渲染和 CSR 前端渲染工程骨架项目。
基于 Egg + Vue + Webpack SSR 服务端渲染和 CSR 前端渲染工程项目。
Single Page Application Isomorphic Example for Egg + Vue, Front-End and Node of The Application are Written in TypeScript.
......
import { Controller, Context } from 'egg';
export default class ActivityController extends Controller {
public async home(ctx: Context) {
await ctx.render('activity.js', { url: ctx.url });
}
}
\ No newline at end of file
......@@ -18,5 +18,4 @@ export default (application: Application) => {
router.get('/editor/login', controller.editor.login);
router.get('/editor', controller.editor.home);
router.get('/editor/*', controller.editor.home);
router.get('/activity/:id', controller.activity.home);
};
\ No newline at end of file
app/web/asset/images/favicon.ico

4.19 KB | W: | H:

app/web/asset/images/favicon.ico

2.3 KB | W: | H:

app/web/asset/images/favicon.ico
app/web/asset/images/favicon.ico
app/web/asset/images/favicon.ico
app/web/asset/images/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
import { Vue, Component, Prop } from 'vue-property-decorator';
import Raven from 'raven-js';
import RavenVue from 'raven-js/plugins/vue';
import { release } from '@/.sentryclirc';
import '@/service/qg.service';
// 初始化sentry
if (process.env.SENTRY_ENV !== 'test' && process.env.NODE_ENV === 'production') {
Raven.config('http://0785298052fd46128e201f30ca649102@sentry.q-gp.com/64', {
release,
environment: 'production'
})
.addPlugin(RavenVue, Vue)
.install();
}
@Component({
name: 'Layout'
})
export default class Layout extends Vue {
@Prop({ type: String, default: 'egg' }) title?: string;
@Prop({ type: String, default: 'Vue TypeScript Framework, Server Side Render' }) description?: string;
@Prop({ type: String, default: 'Vue,TypeScript,Isomorphic' }) keywords?: string;
isNode: boolean = EASY_ENV_IS_NODE;
created() {
console.log('>>EASY_ENV_IS_NODE create', EASY_ENV_IS_NODE);
}
}
\ No newline at end of file
<template>
<html v-if="isNode">
<head>
<title>{{title}}</title>
<meta name="keywords" :content="keywords">
<meta name="description" :content="description">
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="/public/asset/css/reset.css">
</head>
<body>
<div id="app"><slot></slot></div>
</body>
</html>
<div v-else-if="!isNode" id="app"><slot></slot></div>
</template>
<style>
html {
font-size: 10vw !important;
}
@media screen and (min-width: 768Px) {
html {
font-size: 37.5Px !important;
}
body {
max-width: 375Px;
max-height: 667Px;
margin: 0 auto !important;
}
}
</style>
<script lang="ts" src="./index.ts"></script>
\ No newline at end of file
const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'http';
export default {
apiHost: `http://localhost:7001/`,
apiHost: `http://localhost:7002/`,
// apiHost: `http://192.168.28.199:7001/`,
// apiHost: 'https://quantum-vcc2.liangkebang.net/',
h5Host: 'https://quantum-h5-vcc2.liangkebang.net/',
qiniuHost: `https://appsync.lkbang.net/`,
shenceUrl: `${protocol}//bn.xyqb.com/sa?project=default`,
opapiHost: `https://opapi-vcc2.liangkebang.net`,
......
......@@ -2,6 +2,7 @@ const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'https';
export default {
apiHost: `https://quantum-blocks.q-gp.com/`,
h5Host: 'https://quantum-h5-vcc2.liangkebang.net/',
qiniuHost: `https://appsync.lkbang.net/`,
shenceUrl: `${protocol}//bn.xyqb.com/sa?project=production`,
opapiHost: `${protocol}//opapi.q-gp.com`,
......
......@@ -2,6 +2,7 @@ const protocol = EASY_ENV_IS_BROWSER ? window.location.protocol : 'https';
export default {
apiHost: `https://quantum-vcc2.liangkebang.net/`,
h5Host: 'https://quantum-h5-vcc2.liangkebang.net/',
opapiHost: 'https://opapi-vcc2.liangkebang.net',
qiniuHost: `https://appsync.lkbang.net/`,
shenceUrl: `${protocol}//bn.xyqb.com/sa?project=production`,
......
......@@ -29,7 +29,6 @@
type: Number,
default: 1
},
loop: Boolean,
autoplay: Boolean,
animation: Boolean
},
......@@ -40,9 +39,8 @@
let lastProgress = 0;
return {
showSwiper: true,
// style: {},
swiperOptions: {
loop: this.loop,
loop: this.slidesPerColumn === 1 ? true : false,
slidesPerView: 4,
slidesPerColumn: this.slidesPerColumn,
spaceBetween: 8,
......@@ -117,8 +115,10 @@
return new Array(length).fill(0).map((v, i) => [0, 3].includes(i % baseNumber) ? 1 : 0);
},
style() {
return {
return this.animation ? {
transition: 'all .2s cubic-bezier(.4, 0, .2, 1)'
} : {
transform: 'none'
};
}
},
......
......@@ -550,11 +550,6 @@ export const businessComponents = [
name: '行数',
type: 'InputNumber'
},
{
key: 'loop',
name: '循环',
type: 'checkbox'
},
{
key: 'autoplay',
name: '自动播放',
......@@ -622,7 +617,6 @@ export const businessComponents = [
link: 'http://activitystatic.q-gp.com'
}],
slidesPerColumn: 1,
loop: false,
autoplay: false,
animation: false
}
......
import { cloneDeep } from 'lodash';
import { Component, Prop, Vue, Mixins } from 'vue-property-decorator';
import { Action, Mutation, State, Getter } from 'vuex-class';
import TransformStyleMixin from '@/page/mixins/transformStyle.mixin';
import CustomMarquee from '@/lib/Marquee/index.vue';
import { resizeDiv } from '@/service/utils.service';
@Component({ components: { CustomMarquee }, name: 'FreedomContainer' })
export default class FreedomContainer extends Mixins(TransformStyleMixin) {
@Getter('pageData') pageData;
@State(state => state.editor.curChildIndex) curChildIndex;
@Mutation('UPDATE_PAGE_INFO') updatePageInfo;
@Prop({type: Object, default: () => ({ child: [] })}) childItem;
@Prop(String) backgroundImage;
mounted() {
// 根据背景图设置元素高度
const index = this.pageData?.elements?.findIndex(v => v.point?.responsive);
const { props: { backgroundImage }, point } = this.pageData?.elements[index] || { props: {}};
if (backgroundImage) {
resizeDiv(backgroundImage, null, null, (height) => {
this.updatePageInfo({ containerIndex: index, data: { ...this.pageData?.elements[index], point: { ...point, h: height ?? point.h } } });
});
}
}
}
\ No newline at end of file
<template>
<div class="freedom">
<div class="freedom-body">
<component :class="['freedom-body-item', { 'Fb-item_selected': curChildIndex === index }]" v-for="(item, index) in childItem.child" :style="transformStyle(item.commonStyle, 'container')" :is="item.name" :key="index" v-bind="item.props"></component>
</div>
</div>
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="less" scoped>
.freedom {
height: 100%;
width: 100%;
header {
width: 100%;
height: 48px;
line-height: 48px;
text-align: center;
border-bottom: 1px solid #f0f0f0;
}
&-body {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
&_full {
height: calc(100% - 48px);
}
}
}
</style>
\ No newline at end of file
<template>
<div ref="item"
class="vue-grid-item"
:class="classObj"
:style="style"
>
<slot></slot>
<span v-if="resizableAndNotStatic" ref="handle" :class="resizableHandleClass"></span>
<!--<span v-if="draggable" ref="dragHandle" class="vue-draggable-handle"></span>-->
</div>
</template>
<style>
.vue-grid-item {
transition: all 200ms ease;
transition-property: left, top, right;
/* add right for rtl */
}
.vue-grid-item.no-touch {
-ms-touch-action: none;
touch-action: none;
}
.vue-grid-item.cssTransforms {
transition-property: transform;
left: 0;
right: auto;
}
.vue-grid-item.cssTransforms.render-rtl {
left: auto;
right: 0;
}
.vue-grid-item.resizing {
opacity: 0.6;
z-index: 3;
}
.vue-grid-item.vue-draggable-dragging {
transition:none;
z-index: 3;
}
.vue-grid-item.vue-grid-placeholder {
background: red;
opacity: 0.2;
transition-duration: 100ms;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.vue-grid-item > .vue-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
bottom: 0;
right: 0;
background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4=');
background-position: bottom right;
padding: 0 3px 3px 0;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
cursor: se-resize;
}
.vue-grid-item > .vue-rtl-resizable-handle {
bottom: 0;
left: 0;
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAuMDAwMDAwMDAwMDAwMDAyIiBoZWlnaHQ9IjEwLjAwMDAwMDAwMDAwMDAwMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDwhLS0gQ3JlYXRlZCB3aXRoIE1ldGhvZCBEcmF3IC0gaHR0cDovL2dpdGh1Yi5jb20vZHVvcGl4ZWwvTWV0aG9kLURyYXcvIC0tPgogPGc+CiAgPHRpdGxlPmJhY2tncm91bmQ8L3RpdGxlPgogIDxyZWN0IGZpbGw9Im5vbmUiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgaGVpZ2h0PSIxMiIgd2lkdGg9IjEyIiB5PSItMSIgeD0iLTEiLz4KICA8ZyBkaXNwbGF5PSJub25lIiBvdmVyZmxvdz0idmlzaWJsZSIgeT0iMCIgeD0iMCIgaGVpZ2h0PSIxMDAlIiB3aWR0aD0iMTAwJSIgaWQ9ImNhbnZhc0dyaWQiPgogICA8cmVjdCBmaWxsPSJ1cmwoI2dyaWRwYXR0ZXJuKSIgc3Ryb2tlLXdpZHRoPSIwIiB5PSIwIiB4PSIwIiBoZWlnaHQ9IjEwMCUiIHdpZHRoPSIxMDAlIi8+CiAgPC9nPgogPC9nPgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxsaW5lIGNhbnZhcz0iI2ZmZmZmZiIgY2FudmFzLW9wYWNpdHk9IjEiIHN0cm9rZS1saW5lY2FwPSJ1bmRlZmluZWQiIHN0cm9rZS1saW5lam9pbj0idW5kZWZpbmVkIiBpZD0ic3ZnXzEiIHkyPSItNzAuMTc4NDA3IiB4Mj0iMTI0LjQ2NDE3NSIgeTE9Ii0zOC4zOTI3MzciIHgxPSIxNDQuODIxMjg5IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlPSIjMDAwIiBmaWxsPSJub25lIi8+CiAgPGxpbmUgc3Ryb2tlPSIjNjY2NjY2IiBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z181IiB5Mj0iOS4xMDY5NTciIHgyPSIwLjk0NzI0NyIgeTE9Ii0wLjAxODEyOCIgeDE9IjAuOTQ3MjQ3IiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz4KICA8bGluZSBzdHJva2UtbGluZWNhcD0idW5kZWZpbmVkIiBzdHJva2UtbGluZWpvaW49InVuZGVmaW5lZCIgaWQ9InN2Z183IiB5Mj0iOSIgeDI9IjEwLjA3MzUyOSIgeTE9IjkiIHgxPSItMC42NTU2NCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiM2NjY2NjYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+);
background-position: bottom left;
padding-left: 3px;
background-repeat: no-repeat;
background-origin: content-box;
cursor: sw-resize;
right: auto;
}
.vue-grid-item.disable-userselect {
user-select: none;
}
</style>
<script>
import {setTopLeft, setTopRight, setTransformRtl, setTransform} from './helpers/utils';
import {getControlPosition, createCoreData} from './helpers/draggableUtils';
import {getColsFromBreakpoint} from './helpers/responsiveUtils';
import {getDocumentDir} from "./helpers/DOM";
// var eventBus = require('./eventBus');
import '@interactjs/auto-start'
import '@interactjs/actions/drag'
import '@interactjs/actions/resize'
import '@interactjs/modifiers'
import '@interactjs/dev-tools'
import interact from '@interactjs/interact'
export default {
name: "GridItem",
props: {
/*cols: {
type: Number,
required: true
},*/
/*containerWidth: {
type: Number,
required: true
},
rowHeight: {
type: Number,
required: true
},
margin: {
type: Array,
required: true
},
maxRows: {
type: Number,
required: true
},*/
isDraggable: {
type: Boolean,
required: false,
default: null
},
isResizable: {
type: Boolean,
required: false,
default: null
},
/*useCssTransforms: {
type: Boolean,
required: true
},
*/
static: {
type: Boolean,
required: false,
default: false
},
minH: {
type: Number,
required: false,
default: 1
},
minW: {
type: Number,
required: false,
default: 1
},
maxH: {
type: Number,
required: false,
default: Infinity
},
maxW: {
type: Number,
required: false,
default: Infinity
},
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
},
w: {
type: Number,
required: true
},
h: {
type: Number,
required: true
},
i: {
required: true
},
dragIgnoreFrom: {
type: String,
required: false,
default: 'a, button'
},
dragAllowFrom: {
type: String,
required: false,
default: null
},
resizeIgnoreFrom: {
type: String,
required: false,
default: 'a, button'
},
},
inject: ["eventBus", "layout"],
data: function () {
return {
cols: 1,
containerWidth: 100,
rowHeight: 30,
margin: [10, 10],
maxRows: Infinity,
draggable: null,
resizable: null,
useCssTransforms: true,
useStyleCursor: true,
isDragging: false,
dragging: null,
isResizing: false,
resizing: null,
lastX: NaN,
lastY: NaN,
lastW: NaN,
lastH: NaN,
style: {},
rtl: false,
dragEventSet: false,
resizeEventSet: false,
previousW: null,
previousH: null,
previousX: null,
previousY: null,
innerX: this.x,
innerY: this.y,
innerW: this.w,
innerH: this.h
}
},
created () {
let self = this;
// Accessible refernces of functions for removing in beforeDestroy
self.updateWidthHandler = function (width) {
self.updateWidth(width);
};
self.compactHandler = function (layout) {
self.compact(layout);
};
self.setDraggableHandler = function (isDraggable) {
if (self.isDraggable === null) {
self.draggable = isDraggable;
}
};
self.setResizableHandler = function (isResizable) {
if (self.isResizable === null) {
self.resizable = isResizable;
}
};
self.setRowHeightHandler = function (rowHeight) {
self.rowHeight = rowHeight;
};
self.setMaxRowsHandler = function (maxRows) {
self.maxRows = maxRows;
};
self.directionchangeHandler = () => {
this.rtl = getDocumentDir() === 'rtl';
this.compact();
};
self.setColNum = (colNum) => {
self.cols = parseInt(colNum);
}
this.eventBus.$on('updateWidth', self.updateWidthHandler);
this.eventBus.$on('compact', self.compactHandler);
this.eventBus.$on('setDraggable', self.setDraggableHandler);
this.eventBus.$on('setResizable', self.setResizableHandler);
this.eventBus.$on('setRowHeight', self.setRowHeightHandler);
this.eventBus.$on('setMaxRows', self.setMaxRowsHandler);
this.eventBus.$on('directionchange', self.directionchangeHandler);
this.eventBus.$on('setColNum', self.setColNum)
this.rtl = getDocumentDir() === 'rtl';
},
beforeDestroy: function(){
let self = this;
//Remove listeners
this.eventBus.$off('updateWidth', self.updateWidthHandler);
this.eventBus.$off('compact', self.compactHandler);
this.eventBus.$off('setDraggable', self.setDraggableHandler);
this.eventBus.$off('setResizable', self.setResizableHandler);
this.eventBus.$off('setRowHeight', self.setRowHeightHandler);
this.eventBus.$off('setMaxRows', self.setMaxRowsHandler);
this.eventBus.$off('directionchange', self.directionchangeHandler);
this.eventBus.$off('setColNum', self.setColNum);
if (this.interactObj) {
this.interactObj.unset() // destroy interact intance
}
},
mounted: function () {
if (this.layout.responsive && this.layout.lastBreakpoint) {
this.cols = getColsFromBreakpoint(this.layout.lastBreakpoint, this.layout.cols);
} else {
this.cols = this.layout.colNum;
}
this.rowHeight = this.layout.rowHeight;
this.containerWidth = this.layout.width !== null ? this.layout.width : 100;
this.margin = this.layout.margin !== undefined ? this.layout.margin : [10, 10];
this.maxRows = this.layout.maxRows;
if (this.isDraggable === null) {
this.draggable = this.layout.isDraggable;
} else {
this.draggable = this.isDraggable;
}
if (this.isResizable === null) {
this.resizable = this.layout.isResizable;
} else {
this.resizable = this.isResizable;
}
this.useCssTransforms = this.layout.useCssTransforms;
this.useStyleCursor = this.layout.useStyleCursor;
this.createStyle();
},
watch: {
isDraggable: function () {
this.draggable = this.isDraggable;
},
static: function () {
this.tryMakeDraggable();
this.tryMakeResizable();
},
draggable: function () {
this.tryMakeDraggable();
},
isResizable: function () {
this.resizable = this.isResizable;
},
resizable: function () {
this.tryMakeResizable();
},
rowHeight: function () {
this.createStyle();
this.emitContainerResized();
},
cols: function () {
this.tryMakeResizable();
this.createStyle();
this.emitContainerResized();
},
containerWidth: function () {
this.tryMakeResizable();
this.createStyle();
this.emitContainerResized();
},
x: function (newVal) {
this.innerX = newVal;
this.createStyle();
},
y: function (newVal) {
this.innerY = newVal;
this.createStyle();
},
h: function (newVal) {
this.innerH = newVal
this.createStyle();
// this.emitContainerResized();
},
w: function (newVal) {
this.innerW = newVal;
this.createStyle();
// this.emitContainerResized();
},
renderRtl: function () {
// console.log("### renderRtl");
this.tryMakeResizable();
this.createStyle();
},
minH: function () {
this.tryMakeResizable();
},
maxH: function () {
this.tryMakeResizable();
},
minW: function () {
this.tryMakeResizable();
},
maxW: function () {
this.tryMakeResizable();
},
"$parent.margin": function (margin) {
if (!margin || (margin[0] == this.margin[0] && margin[1] == this.margin[1])) {
return;
}
this.margin = margin.map(m => Number(m));
this.createStyle();
this.emitContainerResized();
},
},
computed: {
classObj() {
return {
'vue-resizable' : this.resizableAndNotStatic,
'static': this.static,
'resizing' : this.isResizing,
'vue-draggable-dragging' : this.isDragging,
'cssTransforms' : this.useCssTransforms,
'render-rtl' : this.renderRtl,
'disable-userselect': this.isDragging,
'no-touch': this.isAndroid && this.draggableOrResizableAndNotStatic
}
},
resizableAndNotStatic(){
return this.resizable && !this.static;
},
draggableOrResizableAndNotStatic(){
return (this.draggable || this.resizable) && !this.static;
},
isAndroid() {
return true;
// return navigator.userAgent.toLowerCase().indexOf("android") !== -1;
},
renderRtl() {
return (this.layout.isMirrored) ? !this.rtl : this.rtl;
},
resizableHandleClass() {
if (this.renderRtl) {
return 'vue-resizable-handle vue-rtl-resizable-handle';
} else {
return 'vue-resizable-handle';
}
}
},
methods: {
createStyle: function () {
if (this.x + this.w > this.cols) {
this.innerX = 0;
this.innerW = (this.w > this.cols) ? this.cols : this.w
} else {
this.innerX = this.x;
this.innerW = this.w;
}
let pos = this.calcPosition(this.innerX, this.innerY, this.innerW, this.innerH);
if (this.isDragging) {
pos.top = this.dragging.top;
// Add rtl support
if (this.renderRtl) {
pos.right = this.dragging.left;
} else {
pos.left = this.dragging.left;
}
}
if (this.isResizing) {
pos.width = this.resizing.width;
pos.height = this.resizing.height;
}
let style;
// CSS Transforms support (default)
if (this.useCssTransforms) {
// Add rtl support
if (this.renderRtl) {
style = setTransformRtl(pos.top, pos.right, pos.width, pos.height);
} else {
style = setTransform(pos.top, pos.left, pos.width, pos.height);
}
} else { // top,left (slow)
// Add rtl support
if (this.renderRtl) {
style = setTopRight(pos.top, pos.right, pos.width, pos.height);
} else {
style = setTopLeft(pos.top, pos.left, pos.width, pos.height);
}
}
this.style = style;
},
emitContainerResized() {
// this.style has width and height with trailing 'px'. The
// resized event is without them
let styleProps = {};
for (let prop of ['width', 'height']) {
let val = this.style[prop];
let matches = val.match(/^(\d+)px$/);
if (! matches)
return;
styleProps[prop] = matches[1];
}
this.$emit("container-resized", this.i, this.h, this.w, styleProps.height, styleProps.width);
},
handleResize: function (event) {
if (this.static) return;
const position = getControlPosition(event);
// Get the current drag point from the event. This is used as the offset.
if (position == null) return; // not possible but satisfies flow
const {x, y} = position;
const newSize = {width: 0, height: 0};
let pos;
switch (event.type) {
case "resizestart": {
this.previousW = this.innerW;
this.previousH = this.innerH;
pos = this.calcPosition(this.innerX, this.innerY, this.innerW, this.innerH);
newSize.width = pos.width;
newSize.height = pos.height;
this.resizing = newSize;
this.isResizing = true;
break;
}
case "resizemove": {
// console.log("### resize => " + event.type + ", lastW=" + this.lastW + ", lastH=" + this.lastH);
const coreEvent = createCoreData(this.lastW, this.lastH, x, y);
if (this.renderRtl) {
newSize.width = this.resizing.width - coreEvent.deltaX;
} else {
newSize.width = this.resizing.width + coreEvent.deltaX;
}
newSize.height = this.resizing.height + coreEvent.deltaY;
///console.log("### resize => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
this.resizing = newSize;
break;
}
case "resizeend": {
//console.log("### resize end => x=" +this.innerX + " y=" + this.innerY + " w=" + this.innerW + " h=" + this.innerH);
pos = this.calcPosition(this.innerX, this.innerY, this.innerW, this.innerH);
newSize.width = pos.width;
newSize.height = pos.height;
// console.log("### resize end => " + JSON.stringify(newSize));
this.resizing = null;
this.isResizing = false;
break;
}
}
// Get new WH
pos = this.calcWH(newSize.height, newSize.width);
if (pos.w < this.minW) {
pos.w = this.minW;
}
if (pos.w > this.maxW) {
pos.w = this.maxW;
}
if (pos.h < this.minH) {
pos.h = this.minH;
}
if (pos.h > this.maxH) {
pos.h = this.maxH;
}
if (pos.h < 1) {
pos.h = 1;
}
if (pos.w < 1) {
pos.w = 1;
}
this.lastW = x;
this.lastH = y;
if (this.innerW !== pos.w || this.innerH !== pos.h) {
this.$emit("resize", this.i, pos.h, pos.w, newSize.height, newSize.width);
}
if (event.type === "resizeend" && (this.previousW !== this.innerW || this.previousH !== this.innerH)) {
this.$emit("resized", this.i, pos.h, pos.w, newSize.height, newSize.width);
}
this.eventBus.$emit("resizeEvent", event.type, this.i, this.innerX, this.innerY, pos.h, pos.w);
},
handleDrag(event) {
if (this.static) return;
if (this.isResizing) return;
const position = getControlPosition(event);
// Get the current drag point from the event. This is used as the offset.
if (position === null) return; // not possible but satisfies flow
const {x, y} = position;
// let shouldUpdate = false;
let newPosition = {top: 0, left: 0};
switch (event.type) {
case "dragstart": {
this.previousX = this.innerX;
this.previousY = this.innerY;
let parentRect = event.target.offsetParent.getBoundingClientRect();
let clientRect = event.target.getBoundingClientRect();
if (this.renderRtl) {
newPosition.left = (clientRect.right - parentRect.right) * -1;
} else {
newPosition.left = clientRect.left - parentRect.left;
}
newPosition.top = clientRect.top - parentRect.top;
this.dragging = newPosition;
this.isDragging = true;
break;
}
case "dragend": {
if (!this.isDragging) return;
let parentRect = event.target.offsetParent.getBoundingClientRect();
let clientRect = event.target.getBoundingClientRect();
// Add rtl support
if (this.renderRtl) {
newPosition.left = (clientRect.right - parentRect.right) * -1;
} else {
newPosition.left = clientRect.left - parentRect.left;
}
newPosition.top = clientRect.top - parentRect.top;
// console.log("### drag end => " + JSON.stringify(newPosition));
// console.log("### DROP: " + JSON.stringify(newPosition));
this.dragging = null;
this.isDragging = false;
// shouldUpdate = true;
break;
}
case "dragmove": {
const coreEvent = createCoreData(this.lastX, this.lastY, x, y);
// Add rtl support
if (this.renderRtl) {
newPosition.left = this.dragging.left - coreEvent.deltaX;
} else {
newPosition.left = this.dragging.left + coreEvent.deltaX;
}
newPosition.top = this.dragging.top + coreEvent.deltaY;
// console.log("### drag => " + event.type + ", x=" + x + ", y=" + y);
// console.log("### drag => " + event.type + ", deltaX=" + coreEvent.deltaX + ", deltaY=" + coreEvent.deltaY);
// console.log("### drag end => " + JSON.stringify(newPosition));
this.dragging = newPosition;
break;
}
}
// Get new XY
let pos;
if (this.renderRtl) {
pos = this.calcXY(newPosition.top, newPosition.left);
} else {
pos = this.calcXY(newPosition.top, newPosition.left);
}
this.lastX = x;
this.lastY = y;
if (this.innerX !== pos.x || this.innerY !== pos.y) {
this.$emit("move", this.i, pos.x, pos.y);
}
if (event.type === "dragend" && (this.previousX !== this.innerX || this.previousY !== this.innerY)) {
this.$emit("moved", this.i, pos.x, pos.y);
}
this.eventBus.$emit("dragEvent", event.type, this.i, pos.x, pos.y, this.innerH, this.innerW);
},
calcPosition: function (x, y, w, h) {
const colWidth = this.calcColWidth();
// add rtl support
let out;
if (this.renderRtl) {
out = {
right: Math.round(colWidth * x + (x + 1) * this.margin[0]),
top: Math.round(this.rowHeight * y + (y + 1) * this.margin[1]),
// 0 * Infinity === NaN, which causes problems with resize constriants;
// Fix this if it occurs.
// Note we do it here rather than later because Math.round(Infinity) causes deopt
width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
height: h === Infinity ? h : Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])
};
} else {
out = {
left: Math.round(colWidth * x + (x + 1) * this.margin[0]),
top: Math.round(this.rowHeight * y + (y + 1) * this.margin[1]),
// 0 * Infinity === NaN, which causes problems with resize constriants;
// Fix this if it occurs.
// Note we do it here rather than later because Math.round(Infinity) causes deopt
width: w === Infinity ? w : Math.round(colWidth * w + Math.max(0, w - 1) * this.margin[0]),
height: h === Infinity ? h : Math.round(this.rowHeight * h + Math.max(0, h - 1) * this.margin[1])
};
}
return out;
},
/**
* Translate x and y coordinates from pixels to grid units.
* @param {Number} top Top position (relative to parent) in pixels.
* @param {Number} left Left position (relative to parent) in pixels.
* @return {Object} x and y in grid units.
*/
// TODO check if this function needs change in order to support rtl.
calcXY(top, left) {
const colWidth = this.calcColWidth();
// left = colWidth * x + margin * (x + 1)
// l = cx + m(x+1)
// l = cx + mx + m
// l - m = cx + mx
// l - m = x(c + m)
// (l - m) / (c + m) = x
// x = (left - margin) / (coldWidth + margin)
let x = Math.round((left - this.margin[0]) / (colWidth + this.margin[0]));
let y = Math.round((top - this.margin[1]) / (this.rowHeight + this.margin[1]));
// Capping
x = Math.max(Math.min(x, this.cols - this.innerW), 0);
y = Math.max(Math.min(y, this.maxRows - this.innerH), 0);
return {x, y};
},
// Helper for generating column width
calcColWidth() {
const colWidth = (this.containerWidth - (this.margin[0] * (this.cols + 1))) / this.cols;
// console.log("### COLS=" + this.cols + " COL WIDTH=" + colWidth + " MARGIN " + this.margin[0]);
return colWidth;
},
/**
* Given a height and width in pixel values, calculate grid units.
* @param {Number} height Height in pixels.
* @param {Number} width Width in pixels.
* @return {Object} w, h as grid units.
*/
calcWH(height, width) {
const colWidth = this.calcColWidth();
// width = colWidth * w - (margin * (w - 1))
// ...
// w = (width + margin) / (colWidth + margin)
let w = Math.round((width + this.margin[0]) / (colWidth + this.margin[0]));
let h = Math.round((height + this.margin[1]) / (this.rowHeight + this.margin[1]));
// Capping
w = Math.max(Math.min(w, this.cols - this.innerX), 0);
h = Math.max(Math.min(h, this.maxRows - this.innerY), 0);
return {w, h};
},
updateWidth: function (width, colNum) {
this.containerWidth = width;
if (colNum !== undefined && colNum !== null) {
this.cols = colNum;
}
},
compact: function () {
this.createStyle();
},
tryMakeDraggable: function(){
const self = this;
if (this.interactObj === null || this.interactObj === undefined) {
this.interactObj = interact(this.$refs.item);
if (!this.useStyleCursor) {
this.interactObj.styleCursor(false);
}
}
if (this.draggable && !this.static) {
const opts = {
ignoreFrom: this.dragIgnoreFrom,
allowFrom: this.dragAllowFrom
};
this.interactObj.draggable(opts);
/*this.interactObj.draggable({allowFrom: '.vue-draggable-handle'});*/
if (!this.dragEventSet) {
this.dragEventSet = true;
this.interactObj.on('dragstart dragmove dragend', function (event) {
self.handleDrag(event);
});
}
} else {
this.interactObj.draggable({
enabled: false
});
}
},
tryMakeResizable: function(){
const self = this;
if (this.interactObj === null || this.interactObj === undefined) {
this.interactObj = interact(this.$refs.item);
if (!this.useStyleCursor) {
this.interactObj.styleCursor(false);
}
}
if (this.resizable && !this.static) {
let maximum = this.calcPosition(0,0,this.maxW, this.maxH);
let minimum = this.calcPosition(0,0, this.minW, this.minH);
// console.log("### MAX " + JSON.stringify(maximum));
// console.log("### MIN " + JSON.stringify(minimum));
const opts = {
preserveAspectRatio: true,
// allowFrom: "." + this.resizableHandleClass.trim().replace(" ", "."),
edges: {
left: false,
right: "." + this.resizableHandleClass.trim().replace(" ", "."),
bottom: "." + this.resizableHandleClass.trim().replace(" ", "."),
top: false
},
ignoreFrom: this.resizeIgnoreFrom,
restrictSize: {
min: {
height: minimum.height,
width: minimum.width
},
max: {
height: maximum.height,
width: maximum.width
}
}
};
this.interactObj.resizable(opts);
if (!this.resizeEventSet) {
this.resizeEventSet = true;
this.interactObj
.on('resizestart resizemove resizeend', function (event) {
self.handleResize(event);
});
}
} else {
this.interactObj.resizable({
enabled: false
});
}
},
autoSize: function() {
// ok here we want to calculate if a resize is needed
this.previousW = this.innerW;
this.previousH = this.innerH;
let newSize=this.$slots.default[0].elm.getBoundingClientRect();
let pos = this.calcWH(newSize.height, newSize.width);
if (pos.w < this.minW) {
pos.w = this.minW;
}
if (pos.w > this.maxW) {
pos.w = this.maxW;
}
if (pos.h < this.minH) {
pos.h = this.minH;
}
if (pos.h > this.maxH) {
pos.h = this.maxH;
}
if (pos.h < 1) {
pos.h = 1;
}
if (pos.w < 1) {
pos.w = 1;
}
// this.lastW = x; // basically, this is copied from resizehandler, but shouldn't be needed
// this.lastH = y;
if (this.innerW !== pos.w || this.innerH !== pos.h) {
this.$emit("resize", this.i, pos.h, pos.w, newSize.height, newSize.width);
}
if (this.previousW !== pos.w || this.previousH !== pos.h) {
this.$emit("resized", this.i, pos.h, pos.w, newSize.height, newSize.width);
this.eventBus.$emit("resizeEvent", "resizeend", this.i, this.innerX, this.innerY, pos.h, pos.w);
}
}
},
}
</script>
<template>
<div ref="item" class="vue-grid-layout" :style="mergedStyle">
<slot></slot>
<grid-item class="vue-grid-placeholder"
v-show="isDragging"
:x="placeholder.x"
:y="placeholder.y"
:w="placeholder.w"
:h="placeholder.h"
:i="placeholder.i"></grid-item>
</div>
</template>
<style>
.vue-grid-layout {
position: relative;
transition: height 200ms ease;
}
</style>
<script>
import Vue from 'vue';
const elementResizeDetectorMaker = require("element-resize-detector");
import {bottom, compact, getLayoutItem, moveElement, validateLayout, cloneLayout, getAllCollisions} from './helpers/utils';
import {getBreakpointFromWidth, getColsFromBreakpoint, findOrGenerateResponsiveLayout} from "./helpers/responsiveUtils";
//var eventBus = require('./eventBus');
import GridItem from './GridItem.vue'
import {addWindowEventListener, removeWindowEventListener} from "./helpers/DOM";
export default {
name: "GridLayout",
provide() {
return {
eventBus: null,
layout: this
}
},
components: {
GridItem,
},
props: {
// If true, the container height swells and contracts to fit contents
autoSize: {
type: Boolean,
default: true
},
colNum: {
type: Number,
default: 12
},
rowHeight: {
type: Number,
default: 150
},
maxRows: {
type: Number,
default: Infinity
},
margin: {
type: Array,
default: function () {
return [10, 10];
}
},
isDraggable: {
type: Boolean,
default: true
},
isResizable: {
type: Boolean,
default: true
},
isMirrored: {
type: Boolean,
default: false
},
useCssTransforms: {
type: Boolean,
default: true
},
verticalCompact: {
type: Boolean,
default: true
},
layout: {
type: Array,
required: true,
},
responsive: {
type: Boolean,
default: false
},
responsiveLayouts: {
type: Object,
default: function() {
return {};
}
},
breakpoints:{
type: Object,
default: function(){return{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
},
cols:{
type: Object,
default: function(){return{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }},
},
preventCollision: {
type: Boolean,
default: false
},
useStyleCursor: {
type: Boolean,
default: true
}
},
data: function () {
return {
width: null,
mergedStyle: {},
lastLayoutLength: 0,
isDragging: false,
placeholder: {
x: 0,
y: 0,
w: 0,
h: 0,
i: -1
},
layouts: {}, // array to store all layouts from different breakpoints
lastBreakpoint: null, // store last active breakpoint
originalLayout: null, // store original Layout
};
},
created () {
const self = this;
// Accessible refernces of functions for removing in beforeDestroy
self.resizeEventHandler = function(eventType, i, x, y, h, w) {
self.resizeEvent(eventType, i, x, y, h, w);
};
self.dragEventHandler = function(eventType, i, x, y, h, w) {
self.dragEvent(eventType, i, x, y, h, w);
};
self._provided.eventBus = new Vue();
self.eventBus = self._provided.eventBus;
self.eventBus.$on('resizeEvent', self.resizeEventHandler);
self.eventBus.$on('dragEvent', self.dragEventHandler);
self.$emit('layout-created', self.layout);
},
beforeDestroy: function(){
//Remove listeners
this.eventBus.$off('resizeEvent', this.resizeEventHandler);
this.eventBus.$off('dragEvent', this.dragEventHandler);
this.eventBus.$destroy();
removeWindowEventListener("resize", this.onWindowResize);
this.erd.uninstall(this.$refs.item);
},
beforeMount: function() {
this.$emit('layout-before-mount', this.layout);
},
mounted: function() {
this.$emit('layout-mounted', this.layout);
this.$nextTick(function () {
validateLayout(this.layout);
this.originalLayout = this.layout;
const self = this;
this.$nextTick(function() {
self.onWindowResize();
self.initResponsiveFeatures();
//self.width = self.$el.offsetWidth;
addWindowEventListener('resize', self.onWindowResize);
compact(self.layout, self.verticalCompact);
self.$emit('layout-updated',self.layout)
self.updateHeight();
self.$nextTick(function () {
this.erd = elementResizeDetectorMaker({
strategy: "scroll", //<- For ultra performance.
// See https://github.com/wnr/element-resize-detector/issues/110 about callOnAdd.
callOnAdd: false,
});
this.erd.listenTo(self.$refs.item, function () {
self.onWindowResize();
});
});
});
});
},
watch: {
width: function (newval, oldval) {
const self = this;
this.$nextTick(function () {
//this.$broadcast("updateWidth", this.width);
this.eventBus.$emit("updateWidth", this.width);
if (oldval === null) {
/*
If oldval == null is when the width has never been
set before. That only occurs when mouting is
finished, and onWindowResize has been called and
this.width has been changed the first time after it
got set to null in the constructor. It is now time
to issue layout-ready events as the GridItems have
their sizes configured properly.
The reason for emitting the layout-ready events on
the next tick is to allow for the newly-emitted
updateWidth event (above) to have reached the
children GridItem-s and had their effect, so we're
sure that they have the final size before we emit
layout-ready (for this GridLayout) and
item-layout-ready (for the GridItem-s).
This way any client event handlers can reliably
invistigate stable sizes of GridItem-s.
*/
this.$nextTick(() => {
this.$emit('layout-ready', self.layout);
});
}
this.updateHeight();
});
},
layout: function () {
this.layoutUpdate();
},
colNum: function (val) {
this.eventBus.$emit("setColNum", val);
},
rowHeight: function() {
this.eventBus.$emit("setRowHeight", this.rowHeight);
},
isDraggable: function() {
this.eventBus.$emit("setDraggable", this.isDraggable);
},
isResizable: function() {
this.eventBus.$emit("setResizable", this.isResizable);
},
responsive() {
if (!this.responsive) {
this.$emit('update:layout', this.originalLayout);
this.eventBus.$emit("setColNum", this.colNum);
}
this.onWindowResize();
},
maxRows: function() {
this.eventBus.$emit("setMaxRows", this.maxRows);
},
margin() {
this.updateHeight();
}
},
methods: {
layoutUpdate() {
if (this.layout !== undefined && this.originalLayout !== null) {
if (this.layout.length !== this.originalLayout.length) {
// console.log("### LAYOUT UPDATE!", this.layout.length, this.originalLayout.length);
let diff = this.findDifference(this.layout, this.originalLayout);
if (diff.length > 0){
// console.log(diff);
if (this.layout.length > this.originalLayout.length) {
this.originalLayout = this.originalLayout.concat(diff);
} else {
this.originalLayout = this.originalLayout.filter(obj => {
return !diff.some(obj2 => {
return obj.i === obj2.i;
});
});
}
}
this.lastLayoutLength = this.layout.length;
this.initResponsiveFeatures();
}
compact(this.layout, this.verticalCompact);
this.eventBus.$emit("updateWidth", this.width);
this.updateHeight();
this.$emit('layout-updated',this.layout)
}
},
updateHeight: function () {
this.mergedStyle = {
height: this.containerHeight()
};
},
onWindowResize: function () {
if (this.$refs !== null && this.$refs.item !== null && this.$refs.item !== undefined) {
this.width = this.$refs.item.offsetWidth;
}
this.eventBus.$emit("resizeEvent");
},
containerHeight: function () {
if (!this.autoSize) return;
// console.log("bottom: " + bottom(this.layout))
// console.log("rowHeight + margins: " + (this.rowHeight + this.margin[1]) + this.margin[1])
const containerHeight = bottom(this.layout) * (this.rowHeight + this.margin[1]) + this.margin[1] + 'px';
return containerHeight;
},
dragEvent: function (eventName, id, x, y, h, w) {
//console.log(eventName + " id=" + id + ", x=" + x + ", y=" + y);
let l = getLayoutItem(this.layout, id);
//GetLayoutItem sometimes returns null object
if (l === undefined || l === null){
l = {x:0, y:0}
}
if (eventName === "dragmove" || eventName === "dragstart") {
this.placeholder.i = id;
this.placeholder.x = l.x;
this.placeholder.y = l.y;
this.placeholder.w = w;
this.placeholder.h = h;
this.$nextTick(function() {
this.isDragging = true;
});
//this.$broadcast("updateWidth", this.width);
this.eventBus.$emit("updateWidth", this.width);
} else {
this.$nextTick(function() {
this.isDragging = false;
});
}
// Move the element to the dragged location.
this.layout = moveElement(this.layout, l, x, y, true, this.preventCollision);
compact(this.layout, this.verticalCompact);
// needed because vue can't detect changes on array element properties
this.eventBus.$emit("compact");
this.updateHeight();
if (eventName === 'dragend') this.$emit('layout-updated', this.layout);
},
resizeEvent: function (eventName, id, x, y, h, w) {
let l = getLayoutItem(this.layout, id);
//GetLayoutItem sometimes return null object
if (l === undefined || l === null){
l = {h:0, w:0}
}
let hasCollisions;
if (this.preventCollision) {
const collisions = getAllCollisions(this.layout, { ...l, w, h }).filter(
layoutItem => layoutItem.i !== l.i
);
hasCollisions = collisions.length > 0;
// If we're colliding, we need adjust the placeholder.
if (hasCollisions) {
// adjust w && h to maximum allowed space
let leastX = Infinity,
leastY = Infinity;
collisions.forEach(layoutItem => {
if (layoutItem.x > l.x) leastX = Math.min(leastX, layoutItem.x);
if (layoutItem.y > l.y) leastY = Math.min(leastY, layoutItem.y);
});
if (Number.isFinite(leastX)) l.w = leastX - l.x;
if (Number.isFinite(leastY)) l.h = leastY - l.y;
}
}
if (!hasCollisions) {
// Set new width and height.
l.w = w;
l.h = h;
}
if (eventName === "resizestart" || eventName === "resizemove") {
this.placeholder.i = id;
this.placeholder.x = x;
this.placeholder.y = y;
this.placeholder.w = l.w;
this.placeholder.h = l.h;
this.$nextTick(function() {
this.isDragging = true;
});
//this.$broadcast("updateWidth", this.width);
this.eventBus.$emit("updateWidth", this.width);
} else {
this.$nextTick(function() {
this.isDragging = false;
});
}
if (this.responsive) this.responsiveGridLayout();
compact(this.layout, this.verticalCompact);
this.eventBus.$emit("compact");
this.updateHeight();
if (eventName === 'resizeend') this.$emit('layout-updated', this.layout);
},
// finds or generates new layouts for set breakpoints
responsiveGridLayout(){
let newBreakpoint = getBreakpointFromWidth(this.breakpoints, this.width);
let newCols = getColsFromBreakpoint(newBreakpoint, this.cols);
// save actual layout in layouts
if(this.lastBreakpoint != null && !this.layouts[this.lastBreakpoint])
this.layouts[this.lastBreakpoint] = cloneLayout(this.layout);
// Find or generate a new layout.
let layout = findOrGenerateResponsiveLayout(
this.originalLayout,
this.layouts,
this.breakpoints,
newBreakpoint,
this.lastBreakpoint,
newCols,
this.verticalCompact
);
// Store the new layout.
this.layouts[newBreakpoint] = layout;
if (this.lastBreakpoint !== newBreakpoint) {
this.$emit('breakpoint-changed', newBreakpoint, layout);
}
// new prop sync
this.$emit('update:layout', layout);
this.lastBreakpoint = newBreakpoint;
this.eventBus.$emit("setColNum", getColsFromBreakpoint(newBreakpoint, this.cols));
},
// clear all responsive layouts
initResponsiveFeatures(){
// clear layouts
this.layouts = Object.assign({}, this.responsiveLayouts);
},
// find difference in layouts
findDifference(layout, originalLayout){
//Find values that are in result1 but not in result2
let uniqueResultOne = layout.filter(function(obj) {
return !originalLayout.some(function(obj2) {
return obj.i === obj2.i;
});
});
//Find values that are in result2 but not in result1
let uniqueResultTwo = originalLayout.filter(function(obj) {
return !layout.some(function(obj2) {
return obj.i === obj2.i;
});
});
//Combine the two arrays of unique entries#
return uniqueResultOne.concat(uniqueResultTwo);
}
},
}
</script>
let currentDir = 'auto';
// let currentDir = "auto";
function hasDocument() {
return (typeof document !== 'undefined');
}
function hasWindow() {
return (typeof window !== 'undefined');
}
export function getDocumentDir() {
if (!hasDocument()) {
return currentDir;
}
const direction = (typeof document.dir !== 'undefined') ?
document.dir :
document.getElementsByTagName('html')[0].getAttribute('dir');
return direction;
}
export function setDocumentDir(dir) {
// export function setDocumentDir(dir){
if (!hasDocument) {
currentDir = dir;
return;
}
const html = document.getElementsByTagName('html')[0];
html.setAttribute('dir', dir);
}
export function addWindowEventListener(event, callback) {
if (!hasWindow) {
callback();
return;
}
window.addEventListener(event, callback);
}
export function removeWindowEventListener(event, callback) {
if (!hasWindow) {
return;
}
window.removeEventListener(event, callback);
}
// Get {x, y} positions from event.
export function getControlPosition(e) {
return offsetXYFromParentOf(e);
}
// Get from offsetParent
export function offsetXYFromParentOf(evt) {
const offsetParent = evt.target.offsetParent || document.body;
const offsetParentRect = evt.offsetParent === document.body ? {left: 0, top: 0} : offsetParent.getBoundingClientRect();
const x = evt.clientX + offsetParent.scrollLeft - offsetParentRect.left;
const y = evt.clientY + offsetParent.scrollTop - offsetParentRect.top;
/*const x = Math.round(evt.clientX + offsetParent.scrollLeft - offsetParentRect.left);
const y = Math.round(evt.clientY + offsetParent.scrollTop - offsetParentRect.top);*/
return {x, y};
}
// Create an data object exposed by <DraggableCore>'s events
export function createCoreData(lastX, lastY, x, y) {
// State changes are often (but not always!) async. We want the latest value.
const isStart = !isNum(lastX);
if (isStart) {
// If this is our first move, use the x and y as last coords.
return {
deltaX: 0, deltaY: 0,
lastX: x, lastY: y,
x, y
};
} else {
// Otherwise calculate proper values.
return {
deltaX: x - lastX, deltaY: y - lastY,
lastX, lastY,
x, y
};
}
}
function isNum(num) {
return typeof num === 'number' && !isNaN(num);
}
// @flow
import {cloneLayout, compact, correctBounds} from './utils';
// import type {Layout} from './utils';
// export type ResponsiveLayout = {lg, md, sm, xs, xxs};
// type Breakpoint = string;
// type Breakpoints = {lg, md, sm, xs, xxs};
/**
* Given a width, find the highest breakpoint that matches is valid for it (width > breakpoint).
*
* @param {Object} breakpoints Breakpoints object (e.g. {lg: 1200, md: 960, ...})
* @param {Number} width Screen width.
* @return {String} Highest breakpoint that is less than width.
*/
export function getBreakpointFromWidth(breakpoints, width) {
const sorted = sortBreakpoints(breakpoints);
let matching = sorted[0];
for (let i = 1, len = sorted.length; i < len; i++) {
const breakpointName = sorted[i];
if (width > breakpoints[breakpointName]) { matching = breakpointName; }
}
return matching;
}
/**
* Given a breakpoint, get the # of cols set for it.
* @param {String} breakpoint Breakpoint name.
* @param {Object} cols Map of breakpoints to cols.
* @return {Number} Number of cols.
*/
export function getColsFromBreakpoint(breakpoint, cols) {
if (!cols[breakpoint]) {
throw new Error('ResponsiveGridLayout: `cols` entry for breakpoint ' + breakpoint + ' is missing!');
}
return cols[breakpoint];
}
/**
* Given existing layouts and a new breakpoint, find or generate a new layout.
*
* This finds the layout above the new one and generates from it, if it exists.
*
* @param {Array} orgLayout Original layout.
* @param {Object} layouts Existing layouts.
* @param {Array} breakpoints All breakpoints.
* @param {String} breakpoint New breakpoint.
* @param {String} breakpoint Last breakpoint (for fallback).
* @param {Number} cols Column count at new breakpoint.
* @param {Boolean} verticalCompact Whether or not to compact the layout
* vertically.
* @return {Array} New layout.
*/
export function findOrGenerateResponsiveLayout(orgLayout, layouts, breakpoints,
breakpoint, lastBreakpoint,
cols, verticalCompact) {
// If it already exists, just return it.
if (layouts[breakpoint]) { return cloneLayout(layouts[breakpoint]); }
// Find or generate the next layout
let layout = orgLayout;
const breakpointsSorted = sortBreakpoints(breakpoints);
const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint));
for (let i = 0, len = breakpointsAbove.length; i < len; i++) {
const b = breakpointsAbove[i];
if (layouts[b]) {
layout = layouts[b];
break;
}
}
layout = cloneLayout(layout || []); // clone layout so we don't modify existing items
return compact(correctBounds(layout, {cols}), verticalCompact);
}
export function generateResponsiveLayout(layout, breakpoints,
breakpoint, lastBreakpoint,
cols, verticalCompact) {
// If it already exists, just return it.
/*if (layouts[breakpoint]) return cloneLayout(layouts[breakpoint]);
// Find or generate the next layout
let layout = layouts[lastBreakpoint];*/
/*const breakpointsSorted = sortBreakpoints(breakpoints);
const breakpointsAbove = breakpointsSorted.slice(breakpointsSorted.indexOf(breakpoint));
for (let i = 0, len = breakpointsAbove.length; i < len; i++) {
const b = breakpointsAbove[i];
if (layouts[b]) {
layout = layouts[b];
break;
}
}*/
layout = cloneLayout(layout || []); // clone layout so we don't modify existing items
return compact(correctBounds(layout, {cols}), verticalCompact);
}
/**
* Given breakpoints, return an array of breakpoints sorted by width. This is usually
* e.g. ['xxs', 'xs', 'sm', ...]
*
* @param {Object} breakpoints Key/value pair of breakpoint names to widths.
* @return {Array} Sorted breakpoints.
*/
export function sortBreakpoints(breakpoints) {
const keys = Object.keys(breakpoints);
return keys.sort(function(a, b) {
return breakpoints[a] - breakpoints[b];
});
}
// tslint:disable
// @flow
// export type LayoutItemRequired = {w, h, x, y, i};
// export type LayoutItem = LayoutItemRequired &
// {minW?, minH?, maxW?, maxH?,
// moved?, static?,
// isDraggable?, isResizable?};
// export type Layout = Array<LayoutItem>;
// export type Position = {left, top, width, height};
/*
export type DragCallbackData = {
node: HTMLElement,
x, y,
deltaX, deltaY,
lastX, lastY
};
*/
// export type DragEvent = {e: Event} & DragCallbackData;
// export type Size = {width, height};
// export type ResizeEvent = {e: Event, node: HTMLElement, size: Size};
// const isProduction = process.env.NODE_ENV === 'production';
/**
* Return the bottom coordinate of the layout.
*
* @param {Array} layout Layout array.
* @return {Number} Bottom coordinate.
*/
export function bottom(layout) {
let max = 0, bottomY;
for (let i = 0, len = layout.length; i < len; i++) {
bottomY = layout[i]. y + layout[i].h;
if (bottomY > max) { max = bottomY; }
}
return max;
}
export function cloneLayout(layout) {
const newLayout = Array(layout.length);
for (let i = 0, len = layout.length; i < len; i++) {
newLayout[i] = cloneLayoutItem(layout[i]);
}
return newLayout;
}
// Fast path to cloning, since this is monomorphic
export function cloneLayoutItem(layoutItem) {
/*return {
w.w, h.h, x.x, y.y, i.i,
minW.minW, maxW.maxW, minH.minH, maxH.maxH,
moved(layoutItem.moved), static(layoutItem.static),
// These can be null
isDraggable.isDraggable, isResizable.isResizable
};*/
return JSON.parse(JSON.stringify(layoutItem));
}
/**
* Given two layoutitems, check if they collide.
*
* @return {Boolean} True if colliding.
*/
export function collides(l1, l2) {
if (l1 === l2) { return false; } // same element
if (l1.x + l1.w <= l2.x) { return false; } // l1 is left of l2
if (l1.x >= l2.x + l2.w) { return false; } // l1 is right of l2
if (l1.y + l1.h <= l2.y) { return false; } // l1 is above l2
if (l1.y >= l2.y + l2.h) { return false; } // l1 is below l2
return true; // boxes overlap
}
/**
* Given a layout, compact it. This involves going down each y coordinate and removing gaps
* between items.
*
* @param {Array} layout Layout.
* @param {Boolean} verticalCompact Whether or not to compact the layout
* vertically.
* @return {Array} Compacted Layout.
*/
export function compact(layout, verticalCompact) {
// Statics go in the compareWith array right away so items flow around them.
const compareWith = getStatics(layout);
// We go through the items by row and column.
const sorted = sortLayoutItemsByRowCol(layout);
// Holding for new items.
const out = Array(layout.length);
for (let i = 0, len = sorted.length; i < len; i++) {
let l = sorted[i];
// Don't move static elements
if (!l.static) {
l = compactItem(compareWith, l, verticalCompact);
// Add to comparison array. We only collide with items before this one.
// Statics are already in this array.
compareWith.push(l);
}
// Add to output array to make sure they still come out in the right order.
out[layout.indexOf(l)] = l;
// Clear moved flag, if it exists.
l.moved = false;
}
return out;
}
/**
* Compact an item in the layout.
*/
export function compactItem(compareWith, l, verticalCompact) {
if (verticalCompact) {
// Move the element up as far as it can go without colliding.
while (l.y > 0 && !getFirstCollision(compareWith, l)) {
l.y--;
}
}
// Move it down, and keep moving it down if it's colliding.
let collides;
while ((collides = getFirstCollision(compareWith, l))) {
l.y = collides.y + collides.h;
}
return l;
}
/**
* Given a layout, make sure all elements fit within its bounds.
*
* @param {Array} layout Layout array.
* @param {Number} bounds Number of columns.
*/
export function correctBounds(layout, bounds) {
const collidesWith = getStatics(layout);
for (let i = 0, len = layout.length; i < len; i++) {
const l = layout[i];
// Overflows right
if (l.x + l.w > bounds.cols) { l.x = bounds.cols - l.w; }
// Overflows left
if (l.x < 0) {
l.x = 0;
l.w = bounds.cols;
}
if (!l.static) { collidesWith.push(l); } else {
// If this is static and collides with other statics, we must move it down.
// We have to do something nicer than just letting them overlap.
while (getFirstCollision(collidesWith, l)) {
l.y++;
}
}
}
return layout;
}
/**
* Get a layout item by ID. Used so we can override later on if necessary.
*
* @param {Array} layout Layout array.
* @param {String} id ID
* @return {LayoutItem} Item at ID.
*/
export function getLayoutItem(layout, id) {
for (let i = 0, len = layout.length; i < len; i++) {
if (layout[i].i === id) { return layout[i]; }
}
}
/**
* Returns the first item this layout collides with.
* It doesn't appear to matter which order we approach this from, although
* perhaps that is the wrong thing to do.
*
* @param {Object} layoutItem Layout item.
* @return {Object|undefined} A colliding layout item, or undefined.
*/
export function getFirstCollision(layout, layoutItem) {
for (let i = 0, len = layout.length; i < len; i++) {
if (collides(layout[i], layoutItem)) { return layout[i]; }
}
}
export function getAllCollisions(layout, layoutItem) {
return layout.filter((l) => collides(l, layoutItem));
}
/**
* Get all static elements.
* @param {Array} layout Array of layout objects.
* @return {Array} Array of static layout items..
*/
export function getStatics(layout) {
// return [];
return layout.filter((l) => l.static);
}
/**
* Move an element. Responsible for doing cascading movements of other elements.
*
* @param {Array} layout Full layout to modify.
* @param {LayoutItem} l element to move.
* @param {Number} [x] X position in grid units.
* @param {Number} [y] Y position in grid units.
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is
* being dragged/resized by th euser.
*/
export function moveElement(layout, l, x, y, isUserAction, preventCollision) {
if (l.static) { return layout; }
// Short-circuit if nothing to do.
// if (l.y === y && l.x === x) return layout;
const oldX = l.x;
const oldY = l.y;
const movingUp = y && l.y > y;
// This is quite a bit faster than extending the object
if (typeof x === 'number') { l.x = x; }
if (typeof y === 'number') { l.y = y; }
l.moved = true;
// If this collides with anything, move it.
// When doing this comparison, we have to sort the items we compare with
// to ensure, in the case of multiple collisions, that we're getting the
// nearest collision.
let sorted = sortLayoutItemsByRowCol(layout);
if (movingUp) { sorted = sorted.reverse(); }
const collisions = getAllCollisions(sorted, l);
if (preventCollision && collisions.length) {
l.x = oldX;
l.y = oldY;
l.moved = false;
return layout;
}
// Move each item that collides away from this element.
for (let i = 0, len = collisions.length; i < len; i++) {
const collision = collisions[i];
// console.log('resolving collision between', l.i, 'at', l.y, 'and', collision.i, 'at', collision.y);
// Short circuit so we can't infinite loop
if (collision.moved) { continue; }
// This makes it feel a bit more precise by waiting to swap for just a bit when moving up.
if (l.y > collision.y && l.y - collision.y > collision.h / 4) { continue; }
// Don't move static items - we have to move *this* element away
if (collision.static) {
layout = moveElementAwayFromCollision(layout, collision, l, isUserAction);
} else {
layout = moveElementAwayFromCollision(layout, l, collision, isUserAction);
}
}
return layout;
}
/**
* This is where the magic needs to happen - given a collision, move an element away from the collision.
* We attempt to move it up if there's room, otherwise it goes below.
*
* @param {Array} layout Full layout to modify.
* @param {LayoutItem} collidesWith Layout item we're colliding with.
* @param {LayoutItem} itemToMove Layout item we're moving.
* @param {Boolean} [isUserAction] If true, designates that the item we're moving is being dragged/resized
* by the user.
*/
export function moveElementAwayFromCollision(layout, collidesWith,
itemToMove, isUserAction) {
const preventCollision = false; // we're already colliding
// If there is enough space above the collision to put this element, move it there.
// We only do this on the main collision as this can get funky in cascades and cause
// unwanted swapping behavior.
if (isUserAction) {
// Make a mock item so we don't modify the item here, only modify in moveElement.
const fakeItem = {
x: itemToMove.x,
y: itemToMove.y,
w: itemToMove.w,
h: itemToMove.h,
i: '-1'
};
fakeItem.y = Math.max(collidesWith.y - itemToMove.h, 0);
if (!getFirstCollision(layout, fakeItem)) {
return moveElement(layout, itemToMove, undefined, fakeItem.y, preventCollision);
}
}
// Previously this was optimized to move below the collision directly, but this can cause problems
// with cascading moves, as an item may actually leapflog a collision and cause a reversal in order.
return moveElement(layout, itemToMove, undefined, itemToMove.y + 1, preventCollision);
}
/**
* Helper to convert a number to a percentage string.
*
* @param {Number} num Any number
* @return {String} That number as a percentage.
*/
export function perc(num) {
return num * 100 + '%';
}
export function setTransform(top, left, width, height) {
// Replace unitless items with px
const translate = 'translate3d(' + left + 'px,' + top + 'px, 0)';
return {
transform: translate,
WebkitTransform: translate,
MozTransform: translate,
msTransform: translate,
OTransform: translate,
width: width + 'px',
height: height + 'px',
position: 'absolute'
};
}
/**
* Just like the setTransform method, but instead it will return a negative value of right.
*
* @param top
* @param right
* @param width
* @param height
* @returns {{transform, WebkitTransform, MozTransform, msTransform, OTransform, width, height, position}}
*/
export function setTransformRtl(top, right, width, height) {
// Replace unitless items with px
const translate = 'translate3d(' + right * -1 + 'px,' + top + 'px, 0)';
return {
transform: translate,
WebkitTransform: translate,
MozTransform: translate,
msTransform: translate,
OTransform: translate,
width: width + 'px',
height: height + 'px',
position: 'absolute'
};
}
export function setTopLeft(top, left, width, height) {
return {
top: top + 'px',
left: left + 'px',
width: width + 'px',
height: height + 'px',
position: 'absolute'
};
}
/**
* Just like the setTopLeft method, but instead, it will return a right property instead of left.
*
* @param top
* @param right
* @param width
* @param height
* @returns {{top, right, width, height, position}}
*/
export function setTopRight(top, right, width, height) {
return {
top: top + 'px',
right: right + 'px',
width: width + 'px',
height: height + 'px',
position: 'absolute'
};
}
/**
* Get layout items sorted from top left to right and down.
*
* @return {Array} Array of layout objects.
* @return {Array} Layout, sorted static items first.
*/
export function sortLayoutItemsByRowCol(layout) {
return [].concat(layout).sort(function(a, b) {
if (a.y === b.y && a.x === b.x) {
return 0;
}
if (a.y > b.y || (a.y === b.y && a.x > b.x)) {
return 1;
}
return -1;
});
}
/**
* Generate a layout using the initialLayout and children as a template.
* Missing entries will be added, extraneous ones will be truncated.
*
* @param {Array} initialLayout Layout passed in through props.
* @param {String} breakpoint Current responsive breakpoint.
* @param {Boolean} verticalCompact Whether or not to compact the layout vertically.
* @return {Array} Working layout.
*/
/*
export function synchronizeLayoutWithChildren(initialLayout, children: Array<React.Element>|React.Element,
cols, verticalCompact) {
// ensure 'children' is always an array
if (!Array.isArray(children)) {
children = [children];
}
initialLayout = initialLayout || [];
// Generate one layout item per child.
let layout = [];
for (let i = 0, len = children.length; i < len; i++) {
let newItem;
const child = children[i];
// Don't overwrite if it already exists.
const exists = getLayoutItem(initialLayout, child.key || "1" /!* FIXME satisfies Flow *!/);
if (exists) {
newItem = exists;
} else {
const g = child.props._grid;
// Hey, this item has a _grid property, use it.
if (g) {
if (!isProduction) {
validateLayout([g], 'ReactGridLayout.children');
}
// Validated; add it to the layout. Bottom 'y' possible is the bottom of the layout.
// This allows you to do nice stuff like specify {y: Infinity}
if (verticalCompact) {
newItem = cloneLayoutItem({...g, y: Math.min(bottom(layout), g.y), i: child.key});
} else {
newItem = cloneLayoutItem({...g, y: g.y, i: child.key});
}
}
// Nothing provided: ensure this is added to the bottom
else {
newItem = cloneLayoutItem({w: 1, h: 1, x: 0, y: bottom(layout), i: child.key || "1"});
}
}
layout[i] = newItem;
}
// Correct the layout.
layout = correctBounds(layout, {cols: cols});
layout = compact(layout, verticalCompact);
return layout;
}
*/
/**
* Validate a layout. Throws errors.
*
* @param {Array} layout Array of layout items.
* @param {String} [contextName] Context name for errors.
* @throw {Error} Validation error.
*/
export function validateLayout(layout, contextName) {
contextName = contextName || 'Layout';
const subProps = ['x', 'y', 'w', 'h'];
if (!Array.isArray(layout)) { throw new Error(contextName + ' must be an array!'); }
for (let i = 0, len = layout.length; i < len; i++) {
const item = layout[i];
for (let j = 0; j < subProps.length; j++) {
if (typeof item[subProps[j]] !== 'number') {
throw new Error('VueGridLayout: ' + contextName + '[' + i + '].' + subProps[j] + ' must be a number!');
}
}
if (item.i && typeof item.i !== 'string') {
// number is also ok, so comment the error
// TODO confirm if commenting the line below doesn't cause unexpected problems
// throw new Error('VueGridLayout: ' + contextName + '[' + i + '].i must be a string!');
}
if (item.static !== undefined && typeof item.static !== 'boolean') {
throw new Error('VueGridLayout: ' + contextName + '[' + i + '].static must be a boolean!');
}
}
}
// Flow can't really figure this out, so we just use Object
export function autoBindHandlers(el, fns) {
fns.forEach((key) => el[key] = el[key].bind(el));
}
/**
* Convert a JS object to CSS string. Similar to React's output of CSS.
* @param obj
* @returns {string}
*/
export function createMarkup(obj) {
const keys = Object.keys(obj);
if (!keys.length) { return ''; }
let i, len = keys.length;
let result = '';
for (i = 0; i < len; i++) {
const key = keys[i];
const val = obj[key];
result += hyphenate(key) + ':' + addPx(key, val) + ';';
}
return result;
}
/* The following list is defined in React's core */
export let IS_UNITLESS = {
animationIterationCount: true,
boxFlex: true,
boxFlexGroup: true,
boxOrdinalGroup: true,
columnCount: true,
flex: true,
flexGrow: true,
flexPositive: true,
flexShrink: true,
flexNegative: true,
flexOrder: true,
gridRow: true,
gridColumn: true,
fontWeight: true,
lineClamp: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
tabSize: true,
widows: true,
zIndex: true,
zoom: true,
// SVG-related properties
fillOpacity: true,
stopOpacity: true,
strokeDashoffset: true,
strokeOpacity: true,
strokeWidth: true
};
/**
* Will add px to the end of style values which are Numbers.
* @param name
* @param value
* @returns {*}
*/
export function addPx(name, value) {
if (typeof value === 'number' && !IS_UNITLESS[ name ]) {
return value + 'px';
} else {
return value;
}
}
/**
* Hyphenate a camelCase string.
*
* @param {String} str
* @return {String}
*/
export let hyphenateRE = /([a-z\d])([A-Z])/g;
export function hyphenate(str) {
return str.replace(hyphenateRE, '$1-$2').toLowerCase();
}
export function findItemInArray(array, property, value) {
for (let i = 0; i < array.length; i++) {
if (array[i][property] == value) {
return true;
}
}
return false;
}
export function findAndRemove(array, property, value) {
array.forEach(function(result, index) {
if (result[property] === value) {
// Remove from array
array.splice(index, 1);
}
});
}
'use strict';
import App from '../../framework/app';
import createStore from '../store/index';
import createRouter from './router/index';
import entry from './view/home/index.vue';
export default new App({ entry, createStore, createRouter }).bootstrap();
\ No newline at end of file
import Vue from 'vue';
import VueRouter from 'vue-router';
import Activity from '../view/activity/index.vue';
Vue.use(VueRouter);
export default function createRouter() {
return new VueRouter({
mode: 'history',
routes: [
{
path: '/activity/:pageId',
component: Activity
}
]
});
}
import { kebabCase } from 'lodash';
import { Vue, Component, Watch, Provide, Mixins } from 'vue-property-decorator';
import eleConfig from '../../../editor/utils/config';
import FreedomContainer from '../../component/FreedomContainer/index.vue';
import GridLayout from '../../component/VueGridLayout/GridLayout.vue';
import GridItem from '../../component/VueGridLayout/GridItem.vue';
// import LoginForm from '@/lib/Form/index.vue';
import DownloadGuide from '@/lib/DownloadGuide/index.vue';
import { Getter, State, Mutation } from 'vuex-class';
import GuideCube from '@/lib/GuideCube/index.vue';
import GoodsTabs from '@/lib/GoodsTabs/index.vue';
import Coupon from '@/lib/Coupon/index.vue';
import Advertisement from '@/lib/Advertisement/index.vue';
import TransformStyleMixin from '@/page/mixins/transformStyle.mixin';
import { getStyle } from '@/service/utils.service';
@Component({ components: { FreedomContainer, GridLayout, GridItem, DownloadGuide, GoodsTabs, GuideCube, Advertisement, Coupon }, name: 'Activity'})
export default class Activity extends Mixins(TransformStyleMixin) {
@Getter('pageData') pageData;
@State(state => state.editor.pageInfo.pageName) pageName;
@State(state => state.editor.gridLayout.rowHeight) rowHeight;
@Provide('editor');
isLayoutComReady = false;
showBackTop = false;
targetEle: HTMLElement | null = null;
get layout() {
return this.pageData && this.pageData.elements.map(v => v.point) || [];
}
@Watch('pageName', { immediate: true })
onPageNameChange(newVal) {
if (EASY_ENV_IS_BROWSER && newVal) {
document.title = newVal;
// 如果是 iOS 设备,则使用如下 hack 的写法实现页面标题的更新
if (navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {
const hackIframe = document.createElement('iframe');
hackIframe.style.display = 'none';
hackIframe.src = '/public/fixIosTitle.html?r=' + Math.random();
document.body.appendChild(hackIframe);
setTimeout(() => {
document.body.removeChild(hackIframe);
}, 300);
}
}
}
mounted() {
this.targetEle = document.querySelector('body');
this.showBackTop = true;
setTimeout(() => {
this.modfiTabsStyle();
}, 300);
}
fetchApi(options) {
const { store, route } = options;
const { pageId } = route.params;
console.log('fetchApi', route);
return store.dispatch('getPageDate', { pageId });
}
createStyle({h}) {
return EASY_ENV_IS_NODE ? {
height: `${h * this.rowHeight}px`,
} : {};
}
modfiTabsStyle() {
const tabsEle = document.querySelector('.tabs');
if (tabsEle) {
const gridItemEle = tabsEle?.parentNode;
if (gridItemEle?.classList.contains('vue-grid-item')) {
// 处理transform
const transform = getStyle(gridItemEle, 'transform');
const transformY = transform.split('(')[1].split(')')[0].split(',')[5];
gridItemEle.style.transform = 'none';
gridItemEle.style.top = `${transformY}px`;
// 处理backgroundColor
const backgroundColor = getStyle(tabsEle, 'backgroundColor');
const crTabs = tabsEle.childNodes[0];
crTabs.style.backgroundColor = backgroundColor;
const stickyEle = crTabs?.childNodes[0];
if (stickyEle?.classList.contains('cr-sticky') && stickyEle?.childNodes) {
stickyEle.childNodes[0]?.style.backgroundColor = backgroundColor;
}
}
}
}
}
\ No newline at end of file
<template>
<div class="activity" :style="transformStyle(pageData.commonStyle)">
<grid-layout
:layout.sync="layout"
:isDraggable="false"
:isResizable="false"
:col-num="12"
:row-height="rowHeight"
:margin="[0, 0]"
:is-draggable="true"
:is-resizable="true"
:is-mirrored="false"
:vertical-compact="true"
:use-css-transforms="true"
@layout-ready="modfiTabsStyle"
>
<grid-item :style="createStyle(item.point)" v-for="(item, index) in pageData.elements"
:x="item.point.x"
:y="item.point.y"
:w="item.point.w"
:h="item.point.h"
:i="item.point.i"
:key="item.point.i">
<component :style="transformStyle(item.commonStyle)" :data-index="index" :containerIndex="index" :childItem="item" :is="item.name" :key="index" v-bind="item.props"></component>
</grid-item>
</grid-layout>
<cr-back-top v-if="showBackTop && pageData.props.showBackTop" />
</div>
</template>
<script lang="ts" src="./index.ts"></script>
<style lang="less" scoped>
html,
body,
#app {
height: 100%;
/deep/ .cr-popup {
.cr-notify {
font-size: 14px;
}
}
}
.activity {
width: 100%;
height: 100%;
min-height: 100%;
background-color: rgb(244, 244, 244);
box-shadow: 2px 0px 10px rgba(0, 0, 0, 0.2);
/deep/ .vue-grid-layout {
min-height: 667px;
// transform: translateY(-10px);
transition-property: none;
.vue-grid-item {
transition-property: none;
display: flex;
justify-content: center;
align-items: center;
&>*:first-child {
height: 100%;
}
}
}
}
</style>
import { Vue, Component, Emit } from 'vue-property-decorator';
import Layout from 'component/layout/activity/index.vue';
@Component({
components: {
Layout
}
})
export default class Home extends Vue {}
\ No newline at end of file
<template>
<Layout>
<router-view></router-view>
</Layout>
</template>
<script lang="ts" src="./index.ts"></script>
......@@ -6,7 +6,7 @@
<Tooltip placement="top" content="两列">
<Button type="ghost" icon="ios-pause" @click="select(2)"></Button>
</Tooltip>
<Tooltip placement="top" content="列">
<Tooltip placement="top" content="列">
<Button type="ghost" icon="navicon-round" @click="select(3)" ></Button>
</Tooltip>
</div>
......
......@@ -93,7 +93,7 @@ export default class DashBoard extends Mixins(ContextMenuMixin, GoodsTabsMixin,
await this.savePageData({ pageInfo, pageData: this.pageData });
this.showSubmitPopup = false;
if (type === 'preview') {
window.open(`${config.apiHost}activity/${this.pageId}`);
window.open(`${config.h5Host}activity/${this.pageId}`);
}
}
}
......
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/src/notify';
const ERR_MESSAGE_MAP = {
status: {
......
import Vue from 'vue';
import {
Button,
Image,
Icon,
Cell,
CellGroup,
Row,
Col,
Dialog,
Popup,
Overlay,
Divider,
Loading,
Picker,
NavBar,
Field,
Checkbox,
CardList,
List,
Form,
Sticky,
Tab,
Tabs,
Notify,
Swipe,
SwipeItem,
Toast,
BackTop
} from '@qg/cherry-ui';
// import {
// Button,
// Image,
// Icon,
// Cell,
// CellGroup,
// Row,
// Col,
// Dialog,
// Popup,
// Overlay,
// Divider,
// Loading,
// Picker,
// NavBar,
// Field,
// Checkbox,
// CardList,
// List,
// Form,
// Sticky,
// Tab,
// Tabs,
// Notify,
// Swipe,
// SwipeItem,
// Toast,
// BackTop
// } from '@qg/cherry-ui';
import Button from '@qg/cherry-ui/src/button';
import Image from '@qg/cherry-ui/src/image';
import Field from '@qg/cherry-ui/src/field';
import Form from '@qg/cherry-ui/src/form';
import Tab from '@qg/cherry-ui/src/tab';
import Tabs from '@qg/cherry-ui/src/tabs';
import BackTop from '@qg/cherry-ui/src/back-top';
import Notify from '@qg/cherry-ui/src/notify';
import Toast from '@qg/cherry-ui/src/toast';
import { KaLoginForm } from '@qg/citrus-ui';
Vue.use(Button);
......
......@@ -36,5 +36,10 @@ export default (appInfo: EggAppConfig) => {
'access'
];
config.cors = {
origin: '*',
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH'
};
return config;
};
......@@ -2,12 +2,10 @@
// Do not modify this file!!!!!!!!!
import 'egg';
import ExportActivity from '../../../app/controller/activity';
import ExportEditor from '../../../app/controller/editor';
declare module 'egg' {
interface IController {
activity: ExportActivity;
editor: ExportEditor;
}
}
......@@ -8,7 +8,6 @@ module.exports = {
entry: {
'login': 'app/web/page/login/index.vue',
'editor': 'app/web/page/editor/index.ts',
'activity': 'app/web/page/activity/index.ts',
},
resolve: {
alias:{
......
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