以前一直用Jquery+Jquery-ui來作這個項目,那個時候沒有設計稿,沒有項目需求,就由於BOSS一句話,要作這樣的東西,當時就...好吧!我認可,其實已經習慣了,無所謂了(也是無奈,哎)!!!
在以後的一段時間裏,作了一個demo出來,BOSS很滿意了,因此本身接下來就慢慢作吧,差很少兩三個月吧,就悶頭作這個,後來項目上線了,固然由於產品的不完善,仍是有點問題了!
不過基本能知足公司的需求了,能編輯的均可以編輯,組件的background(包括背景圖片) color border box-shadow margin padding width height 對齊方式(字體和組件內部元素) border-radius font(font-size/font-family)等等這些基礎的均可以隨心變動,固然考慮到可能知足不了公司的使用,就加了一個自定義樣式的功能,而這個只有懂前端的人才能使用了,沒辦法,需求永遠趕不上變化,這樣保險一點。由於你們都知道,需求的知足和變動永遠跑在現成需求的前面
除了這些基礎的可更改,各個組件的特有可變動的功能也基本齊全的,好比輪播圖圖片變動,輪播方式,控制是否輪播等等這些功能,這裏就不一一介紹了
包括後來,由於有組件內部個別元素不能修改,又增長了[綁定修改]功能,就是這個功能選中以後,在視圖界面,選中須要修改的元素,即可以進行修改了,這個功能仍是有點意思的
說了這麼多,其實當時由於作的倉促,存儲的時候存的是HTML,你們不要鄙視(要臉0.0),這個也是我內心一直的梗,最近加上BOSS從新提出了一些想法,有蠻多東西要加,思前想後,決定將項目重構一下
考慮到vue響應式與基本是純數據操做,因此決定使用vue從新構建這個項目。javascript
一、使用vue-cli,下載下來配置好的東西
二、由於中間牽涉了拖拽生成組件的操做,因此使用了vuedraggable和sortablejs。
html
安裝vuedraggable sortablejs前端
npm install vuedraggable
npm install sortablejs
複製代碼
項目中咱們只須要引入vuedraggable就能夠了,牽涉了sortablejs東西的時候,vuedraggable會去本身加載調用sortablejs裏面的方法的,這個就不是咱們須要關注的(你若是想了解,能夠本身去看看);
三、安裝vuex,由於裏面牽涉到了大量的數據交互,不少組件都須要一些公用的數據,不使用vuex去管理,將會爲開發帶來更多沒必要要的麻煩;
vue
安裝vuexjava
npm install --save vuex
複製代碼
四、由於沒有設計稿的緣故,因此大膽使用了第三方UI庫 element-ui;
element-ui官網地址
ios
安裝elememtgit
npm install element-ui
//爲何是element-ui而不是element?由於當時npm上已經有了element包了(我當時還以爲挺有意思的,0.0 好冷啊!!!)
複製代碼
五、axios安裝,後面與後臺數據交互會用到
github
安裝axiosweb
npm install --save axios
複製代碼
差很少準備工做就這些了,接下來咱們看項目實施;vuex
在組件當中,存在一個佈局的問題,因此要有佈局組件,讓組件能夠放到佈局組建中,這樣才更加的靈活
說明:
一、由於用戶在拖拽以後要實時保存到sessionStorage中, 因此再初始的時候要到sessionStroage中去取數據,防止忽然刷新頁面,尚未保存到數據庫中,用戶剛剛編輯的數據所有丟失的狀況;
二、這裏說明一下,可能考慮到用於已經提交了數據,因此用戶關閉窗口以後,再次進來的時候,要結合後臺給出的用戶以前的數據,一塊兒存儲到sessionStorage中去,相信這一點你們確定想的到的,這裏善意提醒一下 0.0;
三、我這這裏暫時放了四個參數,圖中都有說明,我主要是將基本編輯作成了一個組件,會根據用戶點擊時哪一個組件,而從新渲染數據給到編輯組件,從而能夠實時對應到點擊的組件去編輯;
四、editShow的做用就是控制編輯組件顯示與否的,主要刪除組件的時候,讓編輯組件隱藏一下;點擊其餘組件的顯示一下;
基本的配置就這些了,接下來就是真正的開發了;
<template>
<!--用的element-ui-->
<el-container>
<el-aside>
<Draggable class="app-aside-drag" :options="dragOption">
<div class="app-aside-list" v-for="(dragList,index) in dragData" :type="dragList.type" :key="dragList.type">
<div class="aside-item-body">
<i class="aside-item-ele"></i>
<span class="aside-item-ele">{{ list.title }}</span>
</div>
</div>
</Draggable>
<el-aside>
<el-main class="app-main">
<section class="app-phone">
<div class="app-phone-header">
<span class="phone-camera"></span>
<span class="phone-ls"></span>
</div>
<!--頁面view區 -->
<Sort class="app-phone-body"></Sort>
<div class="app-phone-footer">
<button class="app-phone-menu">RS</button>
</div>
</section>
</el-main>
<el-aside class="app-right">
<!--組件編輯區域-->
<BaseEdit></BaseEdit>
</el-aside>
</el-container>
</template>
<script> import DragApi from "@/dragapi/dragapi.js"; import Draggable from "vuedraggable"; import Sort from "@/view/Sort"; import BaseEdit from "@/view/BaseEdit"; export default { name: 'app', data(){ return{ dragData: {}, dragOption: { group: { name: 'components', //這個很重要,其餘的與之能產生關聯的拖拽框就靠這name 必定要一致 pull: 'clone', put: false }, sort: false //默然爲true。這裏咱們只須要他拖拽,無需能拖動排序 } } }, components: { Draggable, Sort, BaseEdit }, created(){ //側邊欄拖拽列表數據 //這裏我只寫了組件的數據進來,佈局的暫時沒放 this.dragData = DragApi.configList[1].content; } } </script>
複製代碼
<template>
<Draggable :options="sortOption"
@sort="onSort"
@add="onAdd"
class="app-sort">
<!-- ui組件 -->
<!--這裏不懂的人,能夠去vue官網看看動態組件-->
<div v-for="(appUi,index) in sortApi" //循環組件
:is="appUi.component" //根據存在的組件渲染出來
:content="appUi.content"
:oStyle="appUi.style"
:editPartShow="appUi.editPartShow"
:aIndex="index"
//組件想要點擊生效,只須要@click.native就好了
@click.native="getIndex(index)"
//key值必定要給出來,否則相同組件的排序可能會不成功
:key="appUi.content.code">
</div>
</Draggable>
</template>
<script>
//利用vuex 輔助函數來操做vuexjs中的數據
import { mapState,mapMutations } from 'vuex';
//拖拽插件引入
import Draggable from 'vuedraggable';
//各個組件引入
import Carousel from "@/components/Carousel.vue";
import Btn from "@/components/Btn.vue";
export default {
name: 'Sort',
components: {
Draggable,Btn,Carousel
},
data(){
return {
sortOption: {
group: {
name: 'components', //前面說的name,在這裏就起了做用,不同,是不能放入的
pull: true,
put: true
},
sort: true,
animation: 300 //給了個動畫,看起來舒服些
}
}
},
computed:{
...mapState(['editIndex','sortApi']),
},
watch:{
sortApi:{
handler(newVal,oldVal){
window.sessionStorage.setItem('localData',JSON.stringify(newVal));
},
deep: true
}
},
methods:{
...mapMutations(['sortCp','addCp','setStyle','setCommon']),
onSort(res){ //排序產生的事件
if(res.from === res.to){
this.sortCp(res);
}
},
onAdd(res){//組件增長產生的事件
this.addCp(res);
},
getIndex(index){
this.setCommon({index: index,flag: true});
}
}
}
</script>
複製代碼
<template>
<transition name="slide-right">
<div v-if="sortApi.length > 0 && editShow === true">
//組件特有編輯
<el-tabs v-model="activeName">
<el-tab-pane label="組件設置" name="first">
<div v-for="(appUi,index) in sortApi"
:is="appUi.component+'Edit'"
:content="appUi.content"
:oStyle="appUi.style"
:editPartShow="appUi.editPartShow"
:aIndex="index"
:currentIndex="editIndex"
:key="appUi.content.code">
</div>
</el-tab-pane>
<el-tab-pane label="樣式設置" name="second">
//公共樣式編輯
<el-collapse v-model="colorPicker.name" class="base-edit" accordion>
<el-collapse-item class="tititt" :title="colorPicker.type" :name="colorPicker.type">
<el-form ref="form" :model="colorPicker" size="mini">
<el-form-item class="cui-inline-reset"
v-for="(item,index) in colorPicker.content"
:label="item.title"
:key="item.style">
<el-color-picker
//在element-ui框架中,有不少@change @active-change事件,直接寫事件發現不能傳入參數,
//固然,辦法總比問題多,咱們換成一下這種寫法就好了,他的默然參數寫在前面
//這裏顏色拾取器 返回的是實時的顏色值
//我這裏主要想傳一個對應的style
@active-change=" (value) => setStyle(value,item.style)"
v-model="sortApi[editIndex].style[item.style]"
show-alpha>
</el-color-picker>
<span class="black-text-shadow"
:style="{color: sortApi[editIndex].style[item.style]}">
{{ sortApi[editIndex].style[item.style] }}
</span>
</el-form-item>
</el-form>
</el-collapse-item>
</el-collapse>
</el-tab-pane>
</el-tabs>
</div>
</transition>
</template>
<script>
import { mapState,mapMutations } from 'vuex';
//這裏我將組建特有的編輯欄,寫成了一個組件,爲何不寫在相應的組件一塊兒了?
//這裏必須說明一下,主要是我沒有想到方法,讓他在同一組件內分離出來,單獨將dom結構放在編輯欄這裏,若是有大神知道
//還望不吝賜教
import BtnEdit from "@/components/BtnEdit.vue";
export default{
name: 'BaseEdit',
components: {
BtnEdit
},
data(){
return{
colorPicker: {
type: '顏色設置',
name: 'Picker',
content:[
{
title: '背景顏色',
style: 'background'
},
{
title: '字體顏色',
style: 'color'
}
]
},
activeName: 'first'
}
},
computed:{
...mapState(['editIndex','sortApi','editShow'])
},
methods:{
setStyle(value,style){
//根據上面傳入的style屬性,實時改變現有的值
this.$set(this.sortApi[this.editIndex].style,style,value);
}
}
}
</script>
複製代碼
//按鈕組件,其實裏面很簡單
//組件的對應的編輯組件,裏面內容和這個也差很少,下面就不寫了
<template>
<div class="btn-box ui-sortable" :data-code="content.code">
<el-button class="ui-btn" :style="oStyle">
{{ content.text }}
</el-button>
//由於每一個組件都有刪除功能,因此寫成了一個組件
<DeleteCp :aIndex="aIndex"></DeleteCp>
</div>
</template>
<script> import DeleteCp from "@/components/DeleteCp"; export default { name: 'Btn', props: { //父組件傳入的參數 content: Object, oStyle: Object, aIndex: Number }, components: { DeleteCp }, data(){ return{ btnModel: 'btn-model' } } } </script>
複製代碼
<template>
<div class="delete-compontent-box">
<div class="el-icon-delete remove-component" @click.stop="dailogStatu"></div>
<el-dialog
title="提示"
:visible.sync="dialogVisible"
:append-to-body="appendToBody"
width="430px">
<div class="el-message-box__content">
<div class="el-message-box__status el-icon-warning"></div>
<div class="el-message-box__message dialog-message">此操做將刪除該模塊, 是否繼續?</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small">取 消</el-button>
<el-button type="primary" @click="onRemove(aIndex)" size="small">確 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
name: 'oText',
props: {
aIndex: Number
},
data(){
return{
//這兩個參數是彈框的參數
dialogVisible: false,
appendToBody: true
}
},
methods:{
...mapMutations(['deleteCp','setCommon']),
dailogStatu(){
//主要是控制彈窗出來,而且顯示該組件對應的編輯欄
this.dialogVisible = true;
this.setCommon({flag: true,index: this.aIndex})
},
onRemove(index){
//點擊肯定刪除對應的組件
let flag = false;
this.deleteCp(index);
this.dialogVisible = false;
this.$message({
message: '該模塊已刪除 !',
type: 'success'
});
this.setCommon({flag: false,index: 0})
}
}
}
</script>
複製代碼
好了,今天寫了不少了,最後咱們來梳理一下思路:
一、首先配置左側的拖拽組件
二、配置vuex中的數據
三、app.vue中配置
四、編輯組件的配置
五、各類數據的傳遞與依賴
其實每一個項目,都須要一個清晰的路線,這樣才能很好的開發下去,因此個人建議是,在拿到項目的時候,千萬不要一股腦的去寫,必定要想好怎麼作,以及突發事情的發生(好比突來的需求變動),這樣既方便了咱們本身,也方便了後來維護的人,也阻止了沒必要要的麻煩
謝謝你們的耐心的閱讀,畢竟這只是一個大概的介紹,確定存在不少不足,若是你們有建議,歡迎留言交流
最後:歡迎你們關注個人我的公衆號:大前端js,固然爲了回饋你們關注,裏面我放了一些學習資源,熱烈歡迎你們關注交流前端方面但不侷限前端方面的知識;
是時候兌現以前說的話了,開源了,固然刪掉了不少東西,就留了一個本文給到的思路下的一個大框架,若是你要加本身的功能仍是須要本身去在這思路基礎上增刪改查哦!廢話很少說,項目地址奉上: 項目地址,若是喜歡多多給star哦 23333
原創不易,轉載時請註明出處與原文連接,謝謝!