您的位置:首页 > 产品设计 > UI/UE

使用vue+elementUI编写运营活动生成工具

2017-06-26 14:40 791 查看
前端切图的对象,很大一部分是公司营销活动类和纯展示类的页面,这一类页面通常有以下特点:

1.页面:切片(cuts的添加和删除)、功能区域(map、area或absolute操作对象)、模块(include的固定代码)、组件(components的调用和配置);

2:组件: 一份代码 + 一份数据,可复用组装。

3.有规则的数据监控和上线配置;

4.实时预览及快速发布上线。

以下为采用vue+elementUI制作运营活动工具的实践。

源码地址: JZ_Activity

活动上线流程:



1.新建活动页面,配置信息(页面标题,生成URL,分享图标及描述)





2.上传切好的各个切片(暂存于临时目录,活动生成后自动删除)



3.进入编辑页后选择组件(如:链接,功能性按钮,表单等等,可以根据需求自行开发),框选组件位置和尺寸



4.生成代码 =>

1)一键上传到服务器(需配置好对应上传目录及权限)

2)下载代码,编写无法用编辑器实现的特定需求后上传(活动代码压缩包);

组件开发流程:

1.新建vue组件.

2.进入活动编辑页

3.添加 - 选择组件 , 框选组件位置和尺寸

实现编辑页功能:

import Vue from 'vue'
export default {
components: {},
data () {
return {
defaults:{
width:0,height:0,left:0,top:0,
memorySize : true,//记住上一次尺寸
currentCutName:''
},
pageUrl:null,//生成地址
info:{},//活动信息
fileList : [],//切片列表
currentMoveArea : null,//补充未快速响应的移动事件
currentChangeArea : null,//补充未快速响应的移动事件
areaTypeList :[
{
name:'组件库',
type:2,
data:[
{ key : 'slider-zhucai' , icon : 'menu' , text : '主材轮播',width:300,height:200},
{ key : 'slider-fangan' , icon : 'menu' , text : '整装方案轮播',width:300,height:200},
{ key : 'slider-ybj' , icon : 'menu' , text : '样板间轮播',width:300,height:200}
]
},
{
name:'热区元素',
type:3,
data:[
{ key : 'open-form' , icon : 'view' , text : '弹出表单',width:100,height:50},
{ key : 'open-link' , icon : 'view' , text : '超链接',width:200,
c680
height:50}
]
},
{
name:'表单录入',
type:3,
data:[
{ key : 'input-name' , icon : 'edit' , text : '姓名',width:200,height:50},
{ key : 'input-telephone' , icon : 'edit' , text : '电话',width:200,height:50},
{ key : 'input-mianji' , icon : 'edit' , text : '面积',width:200,height:50},
{ key : 'input-text' , icon : 'edit' , text : '文本',width:200,height:50},
{ key : 'input-submit' , icon : 'check' , text : '提交',width:200,height:50}
]
}
]
}
},
filters: {
imgUrl (name) {
return Vue.globalOptions.root + '/static/server/custom/images/' + name;
}
},
methods: {
init (info,fileList){
this.info = info
this.fileList = fileList
},
add (item,type){
var width,height,left,top;
if(this.defaults.memorySize && this.defaults.left != 0 && item.name == this.defaults.currentCutName){
width = this.defaults.width;
height = this.defaults.height;
left = this.defaults.left;
this.defaults.top += this.defaults.height + 10;//跟上一个area加点间距
top = this.defaults.top
}else{
width = this.defaults.width = type.width;
height = this.defaults.height = type.height;
left = this.defaults.left = 100;
top = this.defaults.top = 20;
}
this.defaults.currentCutName = item.name;
item.areaList.push({
type:type,
drag:{
move_flag:false,
change_flag:false,
mousePosition:{x:0,y:0},
areaPosition:{x:0,y:0},
areaSize:{width:0,height:0}
},
left:left,
top:top,
width:width,
height:height
});
},
clear (item){
item.areaList = [];
this.defaults.width = 0;
this.defaults.height = 0;
this.defaults.left = 0;
this.defaults.top = 0;
},
move_down (event,area){
if(!area.drag.move_flag){
this.currentMoveArea = area;
area.drag.move_flag = true;
area.drag.mousePosition = {x:event.clientX,y:event.clientY};
area.drag.areaPosition = {x:area.left,y:area.top};
}
},
move_move (event,area){
if(area.drag.move_flag){
let left = area.drag.areaPosition.x + event.clientX - area.drag.mousePosition.x;
let top  = area.drag.areaPosition.y + event.clientY - area.drag.mousePosition.y;
area.left = left > 0 ? left : 0;
area.top = top > 0 ? top : 0;
if(this.defaults.memorySize){
this.defaults.left = left;
this.defaults.top = top;
this.defaults.width = area.width;
this.defaults.height = area.height;
}
}
},
move_up (event,area){
if(area.drag.move_flag){
area.drag.move_flag = false;
}
},
remove (event,area,item){
removeObjWithArr(item.areaList,area);
//设置最后一个area为默认尺寸
if(item.areaList.length>0){
let lastArea = item.areaList[item.areaList.length-1];
this.defaults.width = lastArea.width;
this.defaults.height = lastArea.height;
this.defaults.left = lastArea.left;
this.defaults.top = lastArea.top;
}else{
this.defaults.width = 0;
this.defaults.height = 0;
this.defaults.left = 0;
this.defaults.top = 0;
}
},
change_down (event,area){
event.cancelBubble = true;
if(!area.drag.change_flag){
this.currentChangeArea = area;
area.drag.change_flag = true;
area.drag.mousePosition = {x:event.clientX,y:event.clientY};
area.drag.areaSize = {width:area.width,height:area.height};
}
},
change_move (event,area){
if(area.drag.change_flag){
var width = area.drag.areaSize.width + event.clientX - area.drag.mousePosition.x;
var height = area.drag.areaSize.height + event.clientY - area.drag.mousePosition.y;
area.width = width > 50 ? width : 50;
area.height = height > 30 ? height : 30;
if(this.defaults.memorySize){
this.defaults.width = width;
this.defaults.height = height;
this.defaults.left = area.left;
this.defaults.top = area.top;
}
}
},
change_up (event,area){
if(area.drag.change_flag){
this.currentMoveArea = null;
area.drag.change_flag = false;
}
},
cut_move (event){//补充未快速响应的移动事件
if(this.currentMoveArea != null){
this.move_move(event,this.currentMoveArea);
}else if(this.currentChangeArea != null){
this.change_move(event,this.currentChangeArea);//补充未快速响应的移动事件
}
},
cut_up (event){//结束未快速响应的移动事件
if(this.currentMoveArea != null){
this.currentMoveArea.drag.move_flag = false;
this.currentMoveArea = null;
}else if(this.currentChangeArea != null){
this.currentChangeArea.drag.change_flag = false;
this.currentChangeArea = null;
}
},
save (){
var form = {
info:this.info,
cutList:this.fileList
};
var container = this.$refs.container;
var width;
if (window.getComputedStyle) {
width = window.getComputedStyle(container, null).width;
} else {
width = container.currentStyle.width;
}
form.ratio = (parseFloat(width)/1920).toFixed(2);
this.$post('activity/build', form, { emulateJSON: false }).then(function(data){
data = JSON.parse(data.bodyText);
if(data.code == 0 ){
this.$message({
message: '已存在的页面地址(请确认活动路径和页面名称是否重复)!',
type: 'warning'
});
}else{
this.pageUrl = "//localhost:8999/preview/" + this.info.url + '/' + (this.info.type=="1"?"pc":"mobile");
}
//this.$emit('close');
//this.$emit('reload');
})
}
}
}




生成数据结构:



代码生成:



点击生成活动时:提交到本地nodejs server,实现以下步骤:

1.新建活动代码目录build;

2.匹配活动模板,拼接HTML字符串,生成至build目录

3.拷贝默认携带的js,images等资源至build目录

4.生成build压缩包或上线代码

5.清空暂存目录


module.exports.build = function(req,res,next){
var info = req.body.info;
var cutList = req.body.cutList;
var ratio = req.body.ratio;
var type = info.type == "1"? "pc":"mobile";
var formIndex = 1;
var calculate = function(value){
if(type=="pc"){
return (value/ratio).toFixed(2) + 'px';
}else{
return (value/ratio/1920*100/10).toFixed(2) + 'rem';
}
};

//check dir exist
var output = Config.root + '/server/custom/output/' + info.url;
if(!fsExtra.pathExistsSync(output)){
fsExtra.ensureDirSync(output);
}
if(fsExtra.pathExistsSync(output + '/' + type )){
res.json({success:true,code:0});
return;
}else{
fsExtra.ensureDirSync(output);
}
//make html
var html_content = '';
for(let c = 0 ; c < cutList.length ; c++){
let cut = cutList[c];
let initForm = false;
if(cut.areaList.length == 0)
html_content += '<img src="<%= images(\'' + cut.name + '\') %>" alt="家装季" />\n';
else{
html_content += '<div class="activity-module">\n';
html_content += '<img src="<%= images(\'' + cut.name + '\') %>"  alt="家装季" />\n';
for(let a = 0 ; a < cut.areaList.length ; a++){
let area = cut.areaList[a];
let styles = 'top:' + calculate(area.top) + ';left:' + calculate(area.left) + ';width:' + calculate(area.width) + ';height:' + calculate(area.height) + ';';
switch(area.type.key){
case 'open-form':
html_content += '<button class="activity-button" style="'+ styles +'"></button>\n';
break;
case 'input-name':
if(!initForm){
initForm = true;
html_content += '<div id="j_form' + formIndex++ + '" form-baoming="' + c + '" >\n';
}
html_content += '<input type="text" class="c1" data-placeholder="请输入您的姓名" style="'+ styles +'" />\n';
break;
case 'input-telephone':
if(!initForm){
initForm = true;
html_content += '<div id="j_form' + formIndex++ + '" form-baoming="" >\n';
}
html_content += '<input type="tel" class="c2" data-placeholder="请输入您的联系方式" maxlength="11" style="'+ styles +'" />\n';
break;
case 'input-mianji':
if(!initForm){
initForm = true;
html_content += '<div id="j_form' + formIndex++ + '" form-baoming="" >\n';
}
html_content += '<input type="tel" class="c3" data-placeholder="请输入您的房屋面积" maxlength="11" style="'+ styles +'" />\n';
html_content = html_content.replace(new RegExp('form-baoming=\"' + c + '\"'),'form-baoming="baojia"');
break;
case 'input-text':
if(!initForm){
initForm = true;
html_content += '<div id="j_form' + formIndex++ + '" form-baoming="" >\n';
}
html_content += '<input type="text" data-placeholder="请输入您的房屋面积" maxlength="11" style="'+ styles +'" />\n';
break;
case 'input-submit':
if(!initForm){
initForm = true;
html_content += '<div id="j_form' + formIndex++ + '" form-baoming="" >\n';
}
html_content += '<button class="btn_submit" style="'+ styles +'"></button>\n';
break;

}
}
if(initForm) html_content += '</div>\n';
html_content += '</div>\n';

}
}
//build html
var html_template_url = Config.root + '/server/custom/template/' + type + '.html';
var html_build_url = Config.root + '/server/custom/build/' + type + '/' + type + '.html';
var html = fs.readFileSync(html_template_url,"utf-8");
html = html.replace(/\[node-build-activity-title\]/g,info.title);
html = html.replace(/\[node-build-activity-modules-placeholder\]/g,html_content);
fs.writeFile(html_build_url,html,function(err){
if (err) throw err;

//build js

//build images
var images_build_url = Config.root + '/server/custom/build/' + type + '/' + type + '/images';
fsExtra.removeSync(images_build_url);
fsExtra.copySync(imagesUploadDir,images_build_url);

//copy default images
var images_default_url = Config.root + '/server/custom/template/' + type + '/images';
fsExtra.copySync(images_default_url,images_build_url);

//copy all to custom
var all_build_url = Config.root + '/server/custom/build/';
fsExtra.copySync(all_build_url + type , output);

//clear pool
fsExtra.removeSync(imagesUploadDir);
res.json({success:true,code:1});
});

};


源码地址: JZ_Activity

原文链接:http://www.tliangl.com/article67.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息