使用vue構建一個自動建站專案

深谷逸風發表於2019-03-04

寫在前面

之前一直用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重新構建這個專案。

開發準備

1、使用vue-cli,下載下來配置好的東西
2、因為中間牽涉了拖拽生成元件的操作,所以使用了vuedraggable和sortablejs。

安裝vuedraggable sortablejs

    npm install vuedraggable
    npm install sortablejs
複製程式碼

專案中我們只需要引入vuedraggable就可以了,牽涉了sortablejs東西的時候,vuedraggable會去自己載入呼叫sortablejs裡面的方法的,這個就不是我們需要關注的(你如果想了解,可以自己去看看);
3、安裝vuex,因為裡面牽涉到了大量的資料互動,很多元件都需要一些公用的資料,不使用vuex去管理,將會為開發帶來更多不必要的麻煩;

安裝vuex

    npm install --save vuex
複製程式碼

4、因為沒有設計稿的緣故,所以大膽使用了第三方UI庫 element-ui;
element-ui官網地址

安裝elememt

 npm install element-ui
 //為什麼是element-ui而不是element?因為當時npm上已經有了element包了(我當時還覺得挺有意思的,0.0 好冷啊!!!)
複製程式碼

5、axios安裝,後面與後臺資料互動會用到

安裝axios

npm install --save axios
複製程式碼

差不多準備工作就這些了,接下來我們看專案實施;

專案開始

1、各種檔案的配置

-> main.js中檔案的配置

main.js配置
圖片中都有解釋,應該可以看的懂的;

-> 側邊欄拖拽元件資料的配置

dragApi
因為檔案太長,所以刪掉了一些,這裡就是一個簡單的格式,僅供參考,不作為標準;

在元件當中,存在一個佈局的問題,所以要有佈局元件,讓元件可以放到佈局組建中,這樣才更加的靈活

-> vuexjs 狀態管理中的js配置

storejs配置

說明:
1、因為使用者在拖拽之後要實時儲存到sessionStorage中, 所以再初始的時候要到sessionStroage中去取資料,防止突然重新整理頁面,還沒有儲存到資料庫中,使用者剛剛編輯的資料全部丟失的情況;
2、這裡說明一下,可能考慮到用於已經提交了資料,所以使用者關閉視窗之後,再次進來的時候,要結合後臺給出的使用者之前的資料,一起儲存到sessionStorage中去,相信這一點大家肯定想的到的,這裡善意提醒一下 0.0;
3、我這這裡暫時放了四個引數,圖中都有說明,我主要是將基本編輯做成了一個元件,會根據使用者點選時哪個元件,而重新渲染資料給到編輯元件,從而可以實時對應到點選的元件去編輯;
4、editShow的作用就是控制編輯元件顯示與否的,主要刪除元件的時候,讓編輯元件隱藏一下;點選其他元件的顯示一下;

基本的配置就這些了,接下來就是真正的開發了;


2、專案開發開始

-> app.vue檔案中該怎麼寫?

<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>
複製程式碼

-> 來看看sort view檢視區域元件

<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>
複製程式碼

-> 來看看效果圖吧

效果圖展示

結束語

好了,今天寫了很多了,最後我們來梳理一下思路:

1、首先配置左側的拖拽元件
2、配置vuex中的資料
3、app.vue中配置
4、編輯元件的配置
5、各種資料的傳遞與依賴

其實每個專案,都需要一個清晰的路線,這樣才能很好的開發下去,所以我的建議是,在拿到專案的時候,千萬不要一股腦的去寫,一定要想好怎麼做,以及突發事情的發生(比如突來的需求變更),這樣既方便了我們自己,也方便了後來維護的人,也阻止了不必要的麻煩

謝謝大家的耐心的閱讀,畢竟這只是一個大概的介紹,肯定存在很多不足,如果大家有建議,歡迎留言交流

最後:歡迎大家關注我的個人公眾號:大前端js,當然為了回饋大家關注,裡面我放了一些學習資源,熱烈歡迎大家關注交流前端方面但不侷限前端方面的知識;

專案開源了

是時候兌現之前說的話了,開源了,當然刪掉了很多東西,就留了一個本文給到的思路下的一個大框架,如果你要加自己的功能還是需要自己去在這思路基礎上增刪改查哦!廢話不多說,專案地址奉上: 專案地址,如果喜歡多多給star哦 23333

原創不易,轉載時請註明出處與原文連結,謝謝!

相關文章