使用Vue渲染可配置表單--記一次問卷平臺項目

近幾天來了個緊急項目,想要作一個內部版本的問卷星。至關於能夠編輯問卷並提供問卷展現,數據統計的這麼一個平臺。整個項目耗時不長,本着積澱和積累的原則,將過程當中的思路和收穫進行一下沉澱。因爲公司緣由,代碼還沒有開源。html

不過沉澱了個動態配置表單的嘗試: github,用於後臺快速開發表單等需求,搭配element-ui進行使用,同時可經過後臺進行配置生成表單等。vue

功能和效果

問卷編輯功能大概須要一下幾點:git

  • 根據不一樣題型添加問題github

  • 區分問題的必選性ajax

  • 問題排序,刪除,複製功能vuex

  • 選擇題的選項編輯,排序,刪除功能element-ui

  • 問卷渲染數組

  • 生成問卷二維碼數據結構

效果

圖片
or
預覽ui

技術方案

Vue + VueRouter + ElementUI

使用element進行後臺以及問卷表單渲染是再合適不過的了。極大的節省了須要進行表單樣式修改的時間,同時,讓動態渲染表單成爲一件可能且容易的事情。

表單動態渲染

恰好在項目以前,有過一次動態配置表單的嘗試: github 經過字段自動生成表單及驗證。但此時的數據格式至關於在後臺已經肯定好的,針對可變切頻繁變更的表單結構,肯定數據結構以下:

數據結構

data: {
  title: 問卷名稱
  desc: 問卷描述
  questionList: [
    {
      type: 問題類型,
      label: 問題描述,
      required: 必選性,
      options: [ //選項
        {
          label: 選項內容,
          value: 選項值
        }
        ...
      ] 
    }
    ...
  ]
}

表單渲染

最簡單的 v-if 模式來知足咱們的需求,以前有想過使用is進行渲染,可是不一樣表單配置項相差很大,很難進行通用。所以採用相似如下這種方式,配置詳情可見element官網。

<!-- 填空題 -->
<div v-if="question.type === 'input' || question.type === 'textarea'" class="question-content-wrap">
  <el-row>
    <el-col :xs="8" :sm="10">
      <el-input
        v-model="question.value"
        :autosize="{ minRows: 2, maxRows: 4}"
        class="question-input"
        :type="question.type">
      </el-input>
    </el-col>
  </el-row>
</div>

很簡單就能夠將表單根據配置渲染出來啦:

clipboard.png

實現過程

思路理清楚了,就能夠動手實踐啦!

添加問題

首先,我須要各個問題的基本配置模板,以便於每次直接向questionList中直接添加相應的內容,爲了方便存儲及使用,將其放在store中,當

const state = {
  baseSet: {
    radio: {
      type: 'radio',
      label: '單選題',
      required: true,
      options: [...]
    },
    checkbox: ...
    input: ...
  }
}

//添加問題時,直接push進數組便可
const mutations = [
  //添加問題
  ADDQUESTIONLIST(state, data) {
    state.qss.questionList.push(data);
  }
]

//添加問題方法
addQuestion(type) {
  this.addQuestionList(this. baseSet[type]);
},

注意

使用getter獲取到咱們對應的baseSet對象時,此對象爲引用類型,而且,對象的屬性,如options也一樣爲引用類型。咱們若不進行處理,則會出現,建立兩個相同類型的問題時,對其中某一問題選項進行修改,另外一個選項也會進行修改。 所以咱們須要對base對象進行簡單的拷貝(只進行到數組內容便可)

export const clone = function(obj) {
  var newObj = {};

  for (let key in obj) {
    var target = obj[key];

    if (Object.prototype.toString.call(target) === "[object Object]") {
      newObj[key] = clone(target);
    } else {
      if (Object.prototype.toString.call(target) === "[object Array]") {
        newObj[key] = target.slice(0);
      } else {
        newObj[key] = target;
      }
    }
  }

  return newObj;
}

addQuestion(type) {
  this.addQuestionList(clone(this. baseSet[type]));
},

排序/刪除/複製

這三點基本就是簡單的數組操做啦,此時的問題數據依舊是引用類型,直接對引用數組進行操做便可。簡單的上移,下移排序,使用splice便可實現。其實這三點都是用splice實現的哈。

deleteQuestion(index) {
  this.data.questionList.splice(index, 1);
},

copyQuestion(index) {
  let list = this.data.questionList;
  //複製時,一樣須要對引用對象進行深拷貝
  list.splice(index, 1, list[index], clone(list[index]));
},

moveQuestion(index, direct) {
  let list = this.data.questionList;

  if(direct === 'up') {
    if(index < 1) {
      this.$toast('已是第一項!');
      return;
    }

    list.splice(index - 1, 2, list[index], list[index - 1]);
  } else {
    if(index >= list.length - 1) {
      this.$toast('已是最後一項!');
      return;
    }

    list.splice(index, 2, list[index + 1], list[index]);
  }
}

生成二維碼

使用qrcode.js,感謝大佬們爲小輩們造出這麼多好用的輪子,讓咱們站在巨人的肩膀上前行!

其餘點

對於Vuex,使用computed獲取getters or state,如何配合v-model使用?

咱們都知道,針對Vue2.0後,使用computed獲取getters or state,而針對計算屬性,咱們是沒法進行寫操做的,像這樣

computed: {
  ...mapState({
    qss: state => state.qss,
    base: state => state.base
  })
},

//如下代碼是無效的
this.qss = 2;

所以,咱們更沒法將qss屬性直接綁定在v-model上,非常苦惱。同事的通常處理方式是在data中書寫相同的屬性,在路由進入時對其進行初始化,當其修改時再寫回store。這樣寫起來未免有點麻煩且不穩當。那麼,該如何解決呢?

其實很簡單,能夠交給父組件呀。

咱們經常會聽到一個詞,單向數據流,大概意思就是讓數據單一方向流動,咱們只對數據源進行修改,再讓數據從數據源依次流動到子組件進行UI渲染。

其實就像咱們使用ajax獲取數據時,統一交給父組件同樣,咱們將統一獲取到的數據,使用props進行向下分發便可,使用vuex亦是如此。子組件值進行對應值的修改。而針對props,v-model能夠很方便的對其進行修改了。固然這些只是個人一點理解,若是有異議,能夠一塊兒討論哈。

相關文章
相關標籤/搜索