场景
不同的平台写基础组件,写累了,对于单选复选这种逻辑闭合的组件,javascript 控制脚本是可以完全独立出来的,仅对外暴露状态即可根据不同的平台和环境进行视图的渲染
效果
使用
初始化
let selector = new Selector({key: 'value',multiple: true,limit: 2,changeHandle: (selection, selectionMap) => {this.status = selectionMap},invalidHandle: (msg) => {console.log(msg)}})
invalidHandle
为触发但是没有实际变更内容的动作的回调
changeHandle
为选项发生变化时触发
selectionMap
为选中状态字典,将值赋给status
后在视图中按照如下方式渲染
<template v-for="(item,index) in ops"><span class="item" :class="{'active':status[item.value]}"@click="toggle(item.value)">{{item.label}}</span></template>
常用的方法
toggle(key,status)
单选或者复选模式下切换指定key的状态
toggleMulti(data:[{key:Any,status:Boolean}])
复选模式下批量修改key的状态
getSelection
获取选中项
setData(Array)
动态设置可选项的时候会用到,如果需要保留变化前的状态,只需要再
toggle或者 toggleMulti 一下就行了
代码
/*
* 选择器
* 限制必须使用 key 指明选项的唯一索引,常理下各项一定是互相不同的,即使原始数据存在相同项也要尝试给添加一个唯一标识
* */
const TIP_NO_CHANGE = '未变化的值',TIP_TOGGLE_MULTI_NO_MATCH = '非多选模式不可调用',TIP_OVER_LIMIT = '超出上限';export class Selector {data = []multiple = falselimit = InfinitynodeMap = nullkey = nullchangeHandle = nullinvalidHandle = nullconstructor(option = {}) {this.multiple = option.multiple || falsethis.key = option.key || 'value'this.limit = +(option.limit) || Infinitythis.changeHandle = option.changeHandlethis.invalidHandle = option.invalidHandlethis.setData(option.data)}/*** 设置选项值,根据key生成节点索引*/setData(data = []) {const vm = this, { key } = vmvm.data = datalet nodeMapTemp = {}for (let i = 0, item; (item = data[i]) != null; i++) {nodeMapTemp[item[key]] = { index: i, status: false }}vm.nodeMap = nodeMapTemp}/*** 单选|复选 通过指定key 变更选项状态** 单选模式下对于触发的选项无论如何只会有 false => true 一种行为* @param key* @param status*/toggle(key, status) {const vm = this, { multiple } = vmlet keyNode = vm.nodeMap[key]if (keyNode) {if (multiple) {vm.toggleMulti({ key, status })} else {let active = vm.getSelection()[0]if (active && active[vm.key] != key) {vm.nodeMap[active[vm.key]].status = false}if (keyNode.status != true) {keyNode.status = truevm.change()} else {vm.invalid(TIP_NO_CHANGE)}}}}/*** 批量变更指定状态,必须使用key指明选项* @param data:[{key:Any,status:Boolean}]*/toggleMulti(data) {const vm = this, { limit, multiple } = vmif (!multiple) {vm.invalid(TIP_TOGGLE_MULTI_NO_MATCH)return}if (!(data instanceof Array)) {data = [data]}let hasChange = falselet nextSelection = []for (let i = 0, item; (item = data[i]) != null; i++) {let keyData = this.nodeMap[item.key]if (keyData) {if (item.status == null) {item.status = !(keyData.status)}//1) 若选项为不可编辑,跳过if (keyData.disable === true) {continue}//2) 若选项新状态为true且不在超过已选上限,跳过if (item.status) {nextSelection.push(item)} else {if (vm.nodeMap[item.key].status) {hasChange = true}vm.nodeMap[item.key].status = false}}}let selectionLen = vm.getSelection().lengthfor (let i = 0, item; (item = nextSelection[i]) != null; i++) {if (selectionLen >= limit) {vm.invalid(TIP_OVER_LIMIT)break} else {if (!vm.nodeMap[item.key].status) {hasChange = true}vm.nodeMap[item.key].status = trueselectionLen++}}if (hasChange) {vm.change()}}getSelection() {const vm = this, { key } = vm;return vm.data.filter(d => vm.nodeMap[d[key]].status)}invalid(msg) {if (this.invalidHandle) {this.invalidHandle(msg)}}change() {const vm = thislet selection = vm.getSelection()let statusMap = {}selection.forEach(d => statusMap[d[vm.key]] = true)vm.changeHandle(selection, statusMap)}
}