场景
不同的平台写基础组件,写累了,对于单选复选这种逻辑闭合的组件,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 = false
limit = Infinity
nodeMap = null
key = null
changeHandle = null
invalidHandle = null
constructor(option = {
}) {
this.multiple = option.multiple || false
this.key = option.key || 'value'
this.limit = +(option.limit) || Infinity
this.changeHandle = option.changeHandle
this.invalidHandle = option.invalidHandle
this.setData(option.data)
}
/**
* 设置选项值,根据key生成节点索引
*/
setData(data = []) {
const vm = this, {
key } = vm
vm.data = data
let 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 } = vm
let 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 = true
vm.change()
} else {
vm.invalid(TIP_NO_CHANGE)
}
}
}
}
/**
* 批量变更指定状态,必须使用key指明选项
* @param data:[{key:Any,status:Boolean}]
*/
toggleMulti(data) {
const vm = this, {
limit, multiple } = vm
if (!multiple) {
vm.invalid(TIP_TOGGLE_MULTI_NO_MATCH)
return
}
if (!(data instanceof Array)) {
data = [data]
}
let hasChange = false
let 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().length
for (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 = true
selectionLen++
}
}
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 = this
let selection = vm.getSelection()
let statusMap = {
}
selection.forEach(d => statusMap[d[vm.key]] = true)
vm.changeHandle(selection, statusMap)
}
}