场景
- 产品经理提了一个需求:
产品标准化处理,新增产品或者编辑产品的时候 可以直接通过页面操作;
一个产品的开通只要再网站输入要求即可
产品新的属性也是页面操作
分析
现状
- 各个产品线比较混乱,隶属于不同的项目组, 所以有各自的产品表 , 产品配置表
实现
- 各个产品合并成一个产品表, 提取公共属性,其余有差异的属性浓缩到data属性中 json串存储
环境
- 切换产品使用是
sagalbot/vue-select
组件 - Vue2.5.16
- php5.5(
运维:php7.2 这辈子都不可能的
) - top-think/framework (为什么不让用laraval呢? 哈 原因是 ….)
问题
- computed product_modal属性 动态获取选定产品的配置信息
- v-for 循环配置
- v-if 渲染不同的组件
- v-model是相应的product_modal.index.val
然后 radio出现了很诡异的情况, 可以同时选中两个
解决
组件有问题 vue-devtools来调试
- 发现 product_modal.index.val属性 ,不会根据页面变化而变化;
- 在computed 用法官网 找到了原因
- computed 属性只有在依赖变化的时候 才会变化;
所以新建data 选项 product_option, 在vue-select 值发生变化的时候,作对应的修改
这里有个坑 sagalbot/vue-select对于change事件介绍极少
:on-change=function 触发事件 ;但是如果使用这个方法的话 v-select上的v-model将不在起作用 && computed自然也不会触发
源码
<template type="text/x-template" id="product_create">
<div class="container">
<div class="row">
<div class="panel panel-default">
<div class="panel-heading text-center">
<span class="font-bold">创建产品</span>
</div>
<div class="panel-body">
<prompt_modal ref="modal_prompt"></prompt_modal>
<form class="form-horizontal col-sm-12" @submit.prevent="createProduct">
<div class="form-group">
<label class="col-sm-3 control-label font-bold">账号名称:</label>
<div class="col-sm-6">
<input type="text" v-model.trim="account_name" class="form-control" disabled>
</div>
</div>
<div class="form-group">
<span class="alert-danger font-bold" v-if="!end_time"> * 必选选择</span>
<label class="col-sm-3 control-label font-bold">选择产品:</label>
<div class="col-sm-6">
<v-select :options="list_product_type" placeholder="请选择产品" v-model="product_type" label="product_name"></v-select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label font-bold">状态:</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="status_item in list_status">
<input type="radio" v-model="status" :value="status_item.value"> {{status_item.label }}
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label font-bold">签约类型:</label>
<div class="col-sm-6">
<select id="contract_status" v-model="contract_status" class="form-control">
<option :value="item.value" v-for="item in list_contract_status">{{ item.name}}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label font-bold">限额类型:</label>
<div class="col-sm-6">
<label class="radio-inline" v-for="limit_item in list_limit_type">
<input type="radio" v-model="limit_type" :value="limit_item.value"> {{ limit_item.name }}
</label>
</div>
</div>
<div class="form-group" v-if="limit_type">
<span class="alert-danger font-bold" v-if="!end_time"> * 必选填写</span>
<label class="col-sm-3 control-label font-bold">限额用量:</label>
<div class="col-sm-6">
<input type="text" class="form-control" v-model.trim="limit_number">
</div>
</div>
<div class="form-group">
<span class="alert-danger font-bold" v-if="!end_time"> * 必选填写</span>
<label class="col-sm-3 control-label font-bold">限额用量:</label>
<div class="col-sm-6">
<input type="text" class="form-control" v-model.trim="total_limit">
</div>
</div>
<div class="form-group">
<span class="alert-danger font-bold" v-if="!end_time"> * 必选选择</span>
<label class="col-sm-3 control-label font-bold" for="end_time">截至日期:</label>
<div class="col-sm-6">
<input type="date" id="end_time" v-model="end_time" class="form-control">
</div>
</div>
<!--1. 如果直接使用 product_modal 会导致radio不会在刷新,出现同时出现多个情况同时被选中的情况, 因为product_modal是只有在
依赖的产品类型发生变化的时候,再会刷新数据, 所以双向绑定的数据, ===》 so 做为v-model的对象肯定不能是computed product_modal;
所以需要建立data镜像;
2. product_modal需要触发才会启动代码
-->
<div style="display: none">
{{ product_modal }}
</div>
<!-- 产品的配置属性 -->
<div class="form-group" v-if="product_option" v-for="(item_option,index) in product_option">
<span class="alert-danger font-bold" v-if="item_option.is_need"> * 必选选择</span>
<label class="col-sm-3 control-label font-bold" >{{ item_option.cn_name}}:</label>
<div class="col-sm-6" v-if="item_option.type === 1">
<input type="text" class="form-control" v-model.trim="item_option.val">
</div>
<div class="col-sm-6" v-else-if="item_option.type === 2">
<textarea class="form-control" cols="30" rows="10" v-model.trim="item_option.val"></textarea>
</div>
<div class="col-sm-6" v-else-if="item_option.type === 3">
<label class="radio-inline" v-for="item in item_option.option">
<input type="radio" :value="item.opt_val" v-model="item_option.val" > {{item.opt_name }}
</label>
</div>
<div class="col-sm-6" v-else-if="item_option.type === 4">
<label class="checkbox-inline" v-for="item in item_option.option">
<input type="checkbox" :value="item.opt_val" v-model="item_option.val"> {{item.opt_name }}
</label>
</div>
<div class="col-sm-6" v-else-if="item_option.type === 5">
<input type="date" v-model="item_option.val" class="form-control">
</div>
</div>
<div class="form-group col-sm-9">
<ul class="list-inline pull-right">
<li><button type="submit" class="btn-primary btn-sm btn">开通产品</button></li>
<li><a @click.prevent="goBack" class="btn-default btn-sm btn">返回</a></li>
</ul>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
Vue.component('product_create_template', {
template : '#product_create',
props : ['account_info'],
data : function () {
return {
account_name : '',
account_id : '',
contract_status : 3,
list_contract_status : [
{name: '已签约已付费', value: 1},
{name: '已签约未付费', value: 2},
{name: '未签约', value: 3},
{name: '特殊客户', value: 5},
{name: '其他', value: 4}
],
end_time: '',
status : 1,
list_status : [
{ label : '正常', value : 1},
{ label : '禁用', value : 0},
],
list_limit_type : [
{name: '日限额', value: 1,},
{name: '月限额', value: 2},
{name: '年限额', value: 3},
{name: '无限制', value: 0},
], // 限额类型列表
limit_type : 1, // 限额类型
limit_number : '', // 限额用量
total_limit : '', // 限额总量
list_product_type : [], // 产品类型列表
product_type : '', // 产品类型
product_option : [],
}
},
mounted : function(){
// 初始化apikey appsecret
this.iniParams();
},
computed : {
// 产品配置项
product_modal : function () {
this.product_option = !!this.product_type ? JSON.parse(this.product_type.data) : [];
return true;
},
},
methods : {
// 初始化apikey appsecret
iniParams: function(){
let account_info = JSON.parse(this.account_info);
this.account_name = account_info.account_name;
this.account_id = account_info.account_id;
let vm = this;
// 初始化产品列表
this.$http.get('/product', {responseType: 'json'}).then(function (response) {
if (response.body.status === 0) {
vm.list_product_type = response.body.list_product_type;
} else {
this.$refs.modal_prompt.open({
title : '开通产品提示',
body : '网路故障,请稍后再试'
});
console.log(response);
}
});
},
goBack : function(){
window.history.back();
},
// 新建产品
createProduct : function () {
console.log(this.product_option);
return false;
// 检查参数
if (!this.verifyParams()){
return false;
}
let params = {
product_name : this.product_name,
apikey : this.apikey,
email : this.email,
appsecret : this.appsecret,
end_time: this.end_time,
access_ip : this.access_ip,
mark : this.mark,
status : this.status
};
this.$http.post('/product/store', params, {responseType : 'json'}).then(function (response) {
console.log(response);
if (response.body.status === 0) {
this.$refs.modal_prompt.open({
title : '产品创建提示',
body : '产品创建成功',
btn_name_left : '返回产品列表',
btn_name_right : '立即开通产品',
btn_class_left : 'btn btn-sm btn-primary',
btn_class_right : 'btn btn-sm btn-primary',
btn_url_left : '/product',
btn_url_right : ''
});
} else {
this.$refs.modal_prompt.open({
title : '创建产品失败',
body : response.body.msg,
});
}
});
},
// 检查参数
verifyParams : function () {
// 检查产品
if (!this.product_name) {
this.$refs.modal_prompt.open({
title : '创建产品提示',
body : '请填写产品名称'
});
return false;
}
// 检查apikey appsecret
if (!this.apikey) {
this.$refs.modal_prompt.open({
title : '创建产品提示',
body : '请填写产品APIKEY'
});
return false;
}
if (!this.appsecret) {
this.$refs.modal_prompt.open({
title : '创建产品提示',
body : '请填写产品APPSECRET'
});
return false;
}
// 检查email
if (!this.email) {
this.$refs.modal_prompt.open({
title : '创建产品提示',
body : '请填写邮箱'
});
return false;
}
// 检查截至时间
if (!this.end_time) {
this.$refs.modal_prompt.open({
title : '创建产品提示',
body : '请填写截至时间'
});
return false;
}
return true;
}
}
});
</script>