background
Because the company has a lot of landing pages, as well as buried data reporting and other requirements (data reporting directly inserted into the corresponding components), we need to make a tool to simplify the work of colleagues, but also reduce the frequency of looking for development students, each happy ~
Analyze requirements
Because it is the landing page, so need to consider a certain amount of concurrency and the stability of the site, in order to prevent accidents directly generated static pages is better. The end result is a complete HTML page, which is written by Vue, so the easiest way to render the components is through JSON
The technology stack I’m using here is Vite + TS + VUe-Router
The technical implementation
- Left base Component, click Add Base Component
The base component is stored as an object. Each click pushes the current component type into an array (for display of the landing page, the landing page renders the component from this array). Each click on the base component generates a unique random ID (to store configuration information for each component). This random ID is mapped to the intermediate render page components so that when editing components, they can be mapped to each of them.
{currentComponent: 'button', listComponents: ['image-9592', 'button-317', 'video-2387']}Copy the code
- In the middle render page, click component to pop up configuration information
When each component is clicked on the middle page, the ID of the clicked component is recorded in real time. The corresponding configuration information is displayed on the right
{
currentSetting: 'setting'
}
Copy the code
- Edit component on the right, edit component configuration
Configure basic information about each component.
Vuex configuration information state: {value: ", setting: {}, button: {'button-317': {}... },... },Copy the code
- Save all configuration information to generate a page
The configuration information of the page is sent to the backend students by request, the backend generates the page, and adds the configuration information to the HTML page
The final HTML page code can be accessed on the server
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" href="/favicon.ico" /> <meta name="viewport" content="width=device-width, Initial-scale =1.0" /> <title> test </title> <script> let data = {"componentsSetting":{"currentComponent":"button","listComponents":["image-9592","button-317","video-2387"],"saveHtml":" ","previewVal":""},"currentCompenentsSetting":{"currentSetting":"setting"},"editSetting":{"value":"setting","setting":{" LandPageName ":" test ", "title" : ""}," upon maturity of lingzhi only text ": {}," button ": {" button - 317" : {" button ":" click to enter the shop jingdong ", "color" : "# F2B00A", "bgcolor" : "# B80314","size":"20","align":"center","layoutStyle":"","sizeStyle":"margin-top:0px; margin-right:0px; margin-bottom:0px; margin-left:0px; width:100%; height:39px; line-height:39px; border-radius:27px; color:#F2B00A; font-size:20px; background:#B80314; float:none","h5Url":"https://shop.m.jd.com/?shopId=119921&utm_user=plusmember&gx=RnEwyjVYbjKMy9RG_sYoAXcAFA&ad_od=share& utm_source=androidapp&utm_medium=appshare&utm_campaign=t_335139774&utm_term=Wxfriends","schemesUrl":"","transform":[{"tr AnslationId ":100004,"translationName":" click the button ","translationTarget":"buy"}],"transformValue":" click the button ","dividerSize":[{"title" : "width", "label" : "width", "value" : 100, the "unit", "%"}, {" title ":" high ", "label" : "height", "value" : 39, "min" : 32}, {" title ":" round ", "label" : "border-radius","value":27,"min":0,"max":50}]}}},"userSetting":{"userInfo":{},"advPageInfo":{}},"post":{"id":"23","advAc countId":"670"}} window.localStorage.setItem('lp',JSON.stringify(data)) </script> </head> <body> <div id="app"></div> <script type="module" src="/src/main.ts"></script> </body> </html>Copy the code
The main code paste, vuex will not paste, I believe that we will use ~
basic button code
<template> <div style="display:inline-block; Text - align: center: "id =" value ": style =" sizeStyle @ click "=" handleTranslate "> {{post. The button | | 'this is the button'}} < / div > < / template > <script lang="ts"> export default defineComponent({ props: { value: { type: String, default: () => '', }, }, setup(props) { const store = useStore() const route = useRoute() const state = reactive({ style: '', button: '', post: {}, sizeStyle:'', value: props.value }) const getData = (d: any) => { let { value } = props let res = d && d[value] if(! res) return state.post = res state.sizeStyle = res.sizeStyle } const handleTranslate = () => { let{ h5Url, schemesUrl } = state.post nextTick(() => { if(Object.keys(state.post).includes('transform') && (state.post as any).transform){ let { translationId, translationTarget } = (state.post as any).transform[0] transformEvent(translationTarget, translationId) } if(h5Url || schemesUrl){ openApp(state.value, h5Url,schemesUrl) } }) } watch(store.state.editSetting, (val) => { let { button } = val getData(button) }); onMounted(() => { let { button } = store.state.editSetting getData(button) }) return { ... toRefs(state), handleTranslate } }, }) </script>Copy the code
setting button code
<template>
<div class="text">
<h3>基础设置</h3>
<el-row :gutter="20">
<el-col :span="5">点击跳转</el-col>
<el-col :span="19">
<el-radio v-model="radio" label="1">外链</el-radio>
</el-col>
<el-col :span="5"><div class="i-right-label">网页链接</div></el-col>
<el-col :span="19">
<el-input
placeholder="http://"
v-model="post.h5Url"
@change="postDate"
/>
</el-col>
<el-col :span="5"><div class="i-right-label">直达链接</div></el-col>
<el-col :span="19">
<el-input
placeholder="mttbrowser://url=https://www.qq.com"
v-model="post.schemesUrl"
@change="postDate"
/>
</el-col>
</el-row>
<el-divider></el-divider>
<h3>样式</h3>
<el-row :gutter="20">
<el-col :span="5">按钮文案</el-col>
<el-col :span="19">
<el-input
placeholder="这是按钮"
v-model="post.button"
@change="postDate"
/>
</el-col>
<el-col :span="5"><div class="i-right-label">按钮颜色</div></el-col>
<el-col :span="19">
<div class="i-right-label">文字</div>
<el-color-picker v-model="post.color" size="small" @change="postDate"> </el-color-picker>
<div class="i-right-label" style="margin-left: 30px">背景</div>
<el-color-picker v-model="post.bgcolor" size="small" @change="postDate" show-alpha> </el-color-picker>
</el-col>
<el-col :span="5"><div class="i-right-label">字号</div></el-col>
<el-col :span="19">
<el-select v-model="post.size" placeholder="请选择" style="width:70px" @change="postDate">
<el-option
v-for="item in options"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</el-col>
</el-row>
<Divider @handleDivider="getDividerSize" :data="dividerSize"/>
<el-divider></el-divider>
<h3>布局</h3>
<el-row :gutter="20">
<el-col :span="5"><div class="i-right-label">对齐方式</div></el-col>
<el-col :span="19">
<el-radio-group v-model="post.align" class="text-align" @change="postDate">
<el-radio-button v-for="item in options2" :key="item" :label="item" value="item"></el-radio-button>
</el-radio-group>
</el-col>
</el-row>
<Divider @handleDivider="getDivider" :data="dividerData"/>
<Transform @handleTranslate="getTranslate" :data="transformData"/>
</div>
</template>
<script lang="ts">
export default defineComponent({
components: { Transform, Divider },
name: 'buttonSetting',
setup() {
const { proxy } = getCurrentInstance() as any;
const store = useStore();
const route = useRoute()
const clearData = (params) => {
return {
dividerSize: [
{
title: '宽度',
label: 'width',
value: 80,
unit: '%',
},
{
title: '高度',
label: 'height',
value: 32,
min: 32,
},
{
title: '圆角',
label: 'border-radius',
value: 3,
min: 0,
max: 50,
},
],
dividerData: [
{
title: '上边距',
label: 'margin-top',
value: 0
},
{
title: '右边距',
label: 'margin-right',
value: 0
},
{
title: '下边距',
label: 'margin-bottom',
value: 0
},
{
title: '左边距',
label: 'margin-left',
value: 0
},
],
post: {
button: '这是按钮',
color: '#fff',
bgcolor: 'rgba(64, 158, 255, 1)',
size: '16',
align: 'center',
layoutStyle: '',
sizeStyle: '',
h5Url: '',
schemesUrl: '',
},
transform: []
}[params]
}
const state = reactive({
radio: '1',
options: ['12', '14', '16', '18', '20', '24'],
options1: ['normal', 'bold'],
options2: ['left', 'center', 'right'],
post: clearData('post'),
dividerSize: clearData('dividerSize'),
dividerData: clearData('dividerData'),
transformData: clearData('transform'),
})
const value = computed(() => store.state.editSetting.value);
const getData = (d: any) => {
state.dividerData = clearData('dividerData')
state.dividerSize = clearData('dividerSize')
state.transformData = clearData('transform')
state.post = clearData('post')
if(!d || !value.value || !d[value.value]) return
let data = d[value.value]
state.dividerData = data.dividerData
state.dividerSize = data.dividerSize
state.transformData = data.transformValue
state.post = data
}
watch(value, (val) => {
if(val.indexOf('button') < 0) return
let { button } = store.state.editSetting
getData(button)
postDate()
});
const postDate = () => {
let { post } = state
let { color,bgcolor, size, align } = post
state.dividerData = post.dividerData || clearData('dividerData')
state.dividerSize = post.dividerSize || clearData('dividerSize')
let layoutStyle = styleTransform(state.dividerData)
let sizeStyle = styleTransform(state.dividerSize)
let style = `${layoutStyle}${sizeStyle}color:${color};font-size:${size}px;background:${bgcolor};float:${align=='center'?'none':align}`
post.sizeStyle = style
const params = {[value.value]: post}
store.dispatch('editSetting/setButton', params);
}
const getTranslate = (val: object) => {
Object.assign(state.post,val),postDate()
}
const getDivider = (val: object) => {
let { data } = val
Object.assign(state.post,{dividerData: data})
postDate()
}
const getDividerSize = (val: object) => {
let { data } = val
Object.assign(state.post,{dividerSize: data})
postDate()
}
const styleTransform = (data) => {
let str = ''
data.forEach(element => {
str += `${element.label}:${element.value}${element.unit || 'px'};`
if(element.label == 'height') str += `line-height:${element.value}${element.unit || 'px'};`
})
return str
}
onMounted(() => {
let { button } = store.state.editSetting
getData(button)
postDate()
})
return {
...toRefs(state),
postDate,
getTranslate,
getDivider,
getDividerSize
}
},
});
</script>
Copy the code
Post a finished picture
Ha ha ha, it looks very complicated, in fact, careful analysis, it is very easy to complete, write this is mainly to record, if you have any questions, welcome to discuss ah ~