vue官方文檔給出,遞歸組件介紹
但是如何使用钢属,以及實際應用的案例網(wǎng)上很少出現(xiàn)徘熔,今天介紹一下工作中遇到的問題,以及使用方法
1淆党,應用遞歸組件遍歷樹形題目
需求:有多種類型的題目酷师,有多級題目。分別根據(jù)類型顯示不同題目
2, 后端給的結構
{
"data":{
"cause_id":1,
"id":3,
"name":"相識結婚",
"node_id":1,
"points":[
{
"id":19,
"level":1,
"module_id":3,
"must":false,
"name":"二人相識方式",
"reasons":[
{
"has_sub_reason":true,
"id":19,
"level":1,
"name":"經(jīng)人介紹",
"point_id":19,
"status":1,
"type_op":1
},
{
"has_sub_reason":false,
"id":20,
"level":1,
"name":"自由戀愛",
"point_id":19,
"status":1,
"type_op":1
},
{
"has_sub_reason":false,
"id":21,
"level":1,
"name":"婚戀網(wǎng)站",
"point_id":19,
"status":1,
"type_op":1
}
],
"status":1,
"type":1,
"type_op":0
},
{
"id":20,
"level":1,
"module_id":3,
"must":false,
"name":"請問您是否存在受脅迫結婚的情形宁否?",
"reasons":[
{
"has_sub_reason":true,
"id":22,
"level":1,
"name":"是",
"point_id":20,
"status":1,
"type_op":1
},
{
"has_sub_reason":false,
"id":23,
"level":1,
"name":"否",
"point_id":20,
"status":1,
"type_op":1
}
],
"status":1,
"type":1,
"type_op":0
},
{
"id":21,
"level":1,
"module_id":3,
"must":false,
"name":"雙方之間是否進行了結婚登記窒升?",
"reasons":[
{
"child_node":[
{
"id":1,
"level":2,
"must":false,
"name":"雙方是再婚嗎?",
"parent_reasons_id":24,
"reasons":[
{
"has_sub_reason":true,
"id":1,
"level":2,
"name":"原告再婚",
"point_id":1,
"status":1,
"type_op":1
},
{
"has_sub_reason":true,
"id":2,
"level":2,
"name":"被告再婚",
"point_id":1,
"status":1,
"type_op":1
},
{
"has_sub_reason":false,
"id":3,
"level":2,
"name":"雙方系再婚",
"point_id":1,
"status":1,
"type_op":1
}
],
"status":1,
"type":1,
"type_op":0
},
{
"id":2,
"level":2,
"must":false,
"name":"結婚登記日期為慕匠?",
"parent_reasons_id":24,
"reasons":[
{
"description":"請選擇結婚登記日期為饱须?",
"has_sub_reason":false,
"id":4,
"level":2,
"name":"結婚登記日期為?",
"point_id":2,
"status":1,
"type_op":1
}
],
"status":1,
"type":3,
"type_op":0
}
],
"description":"",
"has_sub_reason":true,
"id":24,
"level":1,
"name":"是",
"point_id":21,
"status":1,
"type_op":1
},
{
"has_sub_reason":false,
"id":25,
"level":1,
"name":"否",
"point_id":21,
"status":1,
"type_op":1
}
],
"status":1,
"type":1,
"type_op":0
}
],
"status":1
},
"message":"",
"status":200,
"success":true
}
3, 前端代碼展示
首先解釋一下台谊,RadioComponent蓉媳,CheckComponent等組件設置成全局組件譬挚,form_item.type===1 為題目類型,為1 時為單選按鈕組件顯示酪呻。其他如同减宣。
這里組件用的時iview-design :prop="points.${form_index}.reasons
"為設置是否必填,根據(jù)must判斷的玩荠。
此頁面所有代碼為
<style lang="less" scoped>
.writeComplaint {
flex: 1;
display: flex;
padding-bottom: 30px;
.writeComplaintContent {
display: flex;
flex: 1;
.leftMenu {
width: 200px;
display: flex;
}
.writeComplaintCard {
margin-left: 20px;
flex: 1;
.submit {
/deep/ .ivu-form-item-content {
display: flex;
justify-content: center;
}
}
}
.reference {
width: 300px;
}
.wordContent {
flex: 1;
margin-left: 20px;
}
}
}
</style>
<template>
<div class="writeComplaint">
<div v-if="isCreated" class="writeComplaintContent">
<Card class="reference"></Card>
<Card class="wordContent">
<div v-for="(word_item,word_index) in word" :key="word_index">
<div v-if="word_item.title" class="title">{{word_item.title}}:</div>
<div class="content">{{word_item.content}}</div>
</div>
</Card>
</div>
<div v-else class="writeComplaintContent">
<div class="leftMenu">
<LeftMenu ref="menus" v-model="activeIndex" :data="modulesData" label="name"></LeftMenu>
</div>
<Card dis-hover class="writeComplaintCard">
<Form :model="formdata" ref="formValidate" label-position="top">
<FormItem
v-for="(form_item,form_index) in moduleForm.points"
:rules="rules(form_item)"
:label="form_item.name"
:prop="`points.${form_index}.reasons`"
:key="form_index">
<RadioComponent v-if="form_item.type===1" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></RadioComponent>
<CheckComponent v-if="form_item.type===2" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></CheckComponent>
<TimeComponent v-if="form_item.type===3" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></TimeComponent>
<!-- <SelectComponent v-if="form_item.type === 4" v-model="formdata.points[form_index].reasons"-->
<!-- :options="form_item.reasons"></SelectComponent>-->
<CityComponent v-if="form_item.type === 5" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></CityComponent>
<InputComponent v-if="form_item.type === 6" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></InputComponent>
<FormGroComponent v-if="form_item.type === 7" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></FormGroComponent>
<TextAreaComponent v-if="form_item.type === 8" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></TextAreaComponent>
</FormItem>
<FormItem class="submit">
<Button type="primary" @click="prev" v-if="activeIndex">上一步</Button>
<Button type="primary" @click="save('formValidate')" style="margin: 0 30px">保存</Button>
<Button type="primary" @click="createComplaint('formValidate')"
v-if="activeIndex===modulesData.length-1">生成訴狀
</Button>
<Button type="primary" @click="next('formValidate')" v-else>下一步</Button>
</FormItem>
</Form>
</Card>
</div>
</div>
</template>
<script type="text/javascript">
import LeftMenu from '@/components/LeftMenu.vue';
import { created, moduleDetail, modules, save } from '@/api/lawsuit.js';
export default {
data () {
var _this = this;
return {
formdata: {
points: []
},
moduleForm: {},
activeIndex: 0,
modulesData: [],
isSave: false,
wordOrder: [],
isCreated: false,
word: [],
points: []
};
},
components: {
LeftMenu
},
watch: {
activeIndex (newvalue, oldvalue) {
this.getModuleDetail(this.modulesData[ newvalue ])
}
},
created () {
this.getModules();
},
methods: {
async getModules () {
const { data } = await modules({
node_id: this.$route.query.step,
cause_id: this.$route.query.cause_id
})
this.modulesData = data.data;
const [ first ] = this.modulesData;
this.getModuleDetail(first);
},
async getModuleDetail (module_data) {
const { data } = await moduleDetail({
module_id: module_data.id
})
this.moduleForm = data.data;
this.formdata.points = [];
this.moduleForm.points.map((module_item) => {
this.formdata.points.push({
point_id: module_item.id,
reasons: []
})
// this.points =
})
},
save () {
this.savaLoad().then((data) => {
// this.isSave = true
})
},
async createComplaint (name) {
const invalidate = await this.validate(name)
if (!invalidate) return
const data = await this.savaLoad(name);
const createData = await created({
document_id: this.$route.query.document_id
});
this.isCreated = true
const wordData = createData.data.data;
this.wordOrder.map((item) => {
this.word.push({
title: wordData[ item.title ],
content: wordData[ item.content ]
})
})
},
async next (name) {
if (this.activeIndex < this.modulesData.length - 1) {
if (this.isSave) {
const invalidate = await this.validate(name)
if (!invalidate) return
this.activeIndex += 1
} else {
this.savaLoad(name).then((data) => {
if (data) {
this.activeIndex += 1
}
})
}
}
},
prev () {
this.activeIndex -= 1
},
async savaLoad () {
const invalidate = await this.validate('formValidate')
if (!invalidate) return
const params = Object.assign({}, {
document_id: this.$route.query.document_id,
module_id: this.moduleForm.id,
points: this.formdata.points
})
const { data } = await save(params)
if (data.status === 200) {
this.$Message.success('保存成功');
return true
}
},
validate (name) {
return new Promise((resolve) => {
this.$refs[ name ].validate((valid) => {
if (valid) {
resolve(true)
} else {
resolve(false)
}
})
})
},
rules (form_item) {
return [ {
required: form_item.must,
message: '不能為空'
} ]
}
}
};
</script>
4漆腌,只寫其中一個單選的例子,其他類似
radio組件所有代碼如下
<template>
<div class="mock-wrapper flex-sb-top" :style="[{display: 'block'}]">
<div v-for="(form_item, index) in options"
:key="index"
>
<div>
<div v-if="form_item.type_op === 0" class="name-box" @click="getDownOrUp($event,index)">
<Icon type="ios-arrow-down" />
<span class="question-name">{{form_item.question}},{{form_item.name}}</span>
</div>
<div v-if="form_item.type_op === 1" class="content-box">
<Radio v-model="form_item.checked" :label="form_item.name" @on-change="handleChange($event,form_item)"></Radio>
</div>
</div>
<RadioComponent v-if="form_item.type === 1" :options="form_item.reasons"></RadioComponent>
<CheckComponent v-if="form_item.type === 2" :options="form_item.reasons"></CheckComponent>
<TimeComponent v-if="form_item.type === 3" :options="form_item.reasons"></TimeComponent>
<!-- <SelectComponent v-if="form_item.type === 4" :options="form_item"></SelectComponent>-->
<CityComponent v-if="form_item.type === 5" :options="form_item.reasons"></CityComponent>
<InputComponent v-if="form_item.type === 6" :options="form_item.reasons"></InputComponent>
<FormGroComponent v-if="form_item.type === 7" :options="form_item.reasons"></FormGroComponent>
<TextAreaComponent v-if="form_item.type === 8" :options="form_item.reasons"></TextAreaComponent>
<div v-if="form_item.checked && form_item.child_node" class="sub-item-box">
<RadioComponent :options="form_item.child_node"></RadioComponent>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'RadioComponent',
props: {
options: {
type: Array,
default () {
return []
}
}
},
methods: {
handleChange (checked, option) {
this.options = this.options.map(val => {
val.checked = val.id === option.id;
val.reason_id = val.id
return val
});
this.$emit('input', this.options)
},
getDownOrUp (index) {
let dom = null;
if (index.toElement.className === 'name-box') {
dom = index.path[ 1 ].parentNode.children[ 1 ]
} else {
dom = index.path[ 1 ].parentNode.parentNode.children[ 1 ]
}
const self = index.path[ 0 ].parentNode;
if (dom.style.display === 'block') {
dom.style.display = 'none';
self.getElementsByTagName('i')[ 0 ].classList.replace('ivu-icon-ios-arrow-down', 'ivu-icon-ios-arrow-forward')
} else {
dom.style.display = 'block';
self.getElementsByTagName('i')[ 0 ].classList.replace('ivu-icon-ios-arrow-forward', 'ivu-icon-ios-arrow-down')
}
}
}
}
</script>
<style lang="less">
@import "index";
</style>
其他組件類似
5阶冈,這樣就會出現(xiàn)效果闷尿,其中有一個三級城市組件給的數(shù)據(jù)不符合要求,組件需要label 和vaule 現(xiàn)在給的是name, id所以需要轉一下女坑,用到遞歸函數(shù)
handleCity: function (tree) {
for (let i = 0; i < tree.length; i++) {
tree[ i ].label = tree[ i ].name;
tree[ i ].value = tree[ i ].id;
tree[ i ].reason_id = tree[ i ].id;
if (tree[ i ].children && tree[ i ].children.length > 0) {
this.handleCity(tree[ i ].children)
}
}
return tree
},
6填具,全篇有個知識點這里有個 子組件用 this.$emit('input', selectedData),
組件調用 使用 <RadioComponent v-if="form_item.type===1" v-model="formdata.points[form_index].reasons"
:options="form_item.reasons"></RadioComponent>
組件調用使用v-model 接受改變過的值
vue中子組件不能直接改變通過props 傳過來的值的會報錯
這里沒有找到好方法匆骗,只能屏蔽警告提示劳景,不影響效果
Vue.config.warnHandler = function (msg) {
if (!msg.includes('Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.')) { // uniApp bug: https://ask.dcloud.net.cn/question/71966
return console.warn && console.warn(msg)
}
}
至此vue遞歸組件遍歷不同類型不同級別的題目問題寫好了。
如果喜歡歡迎點贊留言碉就,關注本人維護的公眾號---- 程序員蝸牛盟广,有免費學習資料贈送//