theme: fancy
个人笔记,如有错误请谅解。
需求
所有需求大多数用于展示目的,真实值不被修改。
主要需求
- 用户输入
正整数
例如 1-4 则自动选择option里的1,2,3,4。label展示为1-4 (数字连续要分开,例如 1,2,3 6,7,8)
- 用户手动单击选择option里的1,2,3,4。label展示为1-4
- 服务端返回[1,2,3,4],label展示为1-4
- 处理数据错误。例如用户输入非法字符或格式错误等
10-4、xx-3、好-我、1---3
可选option只有1,2,4,6 结果用户输入1-7等...
- 兼容程序。
回车
、空格
均作为确认快捷键。
- 删除要做二次确认,和选中效果。
- 用户输入快捷方式的时候,并不能影响到原option数据展示。
效果图

以下代码均做简化处理,只保留重要部分。
先简单的理一下思路,我们只需要做到表面上的修改甚至可以直接使用jQuery来操纵真实dom。
用户输入,以及v-model的内容,我们都处理一遍就好了。对我个人来讲,v-model对这种需求并不友好。我还是喜欢
React /(ㄒoㄒ)/~~
首先加载组件
<el-select
v-model="dataModel"
allow-create
default-first-option
automatic-dropdown
:multiple="true"
:filterable="true"
:fit-input-width="true"
:filter-method="(value) => do_not_searchFillterData(value, oldOptions)"
@change="(e)=>{selectValue_changle(this,e)}"
@focus="init_input_keyup"
>
<el-option
v-for="item in do_not_searchInit(formData.options.options)"
:key="item.value"
:value="item.value"
:label="item.value"
>
<span>{
{item.value }}</span>
</el-option>
</el-select>
<Select
mode="tags"
options={options}
onChange={(e)=>{selectValue_changle(this,e)}}
filterOption={(input, option) =>{do_not_searchInit(input,oldOptions)}}
onFocus={init_input_keyup}
/>
就从上面结构代码来挨个挨个解释吧。
multiple、filterable、default-first-option
必须为true
这是创建条目,多选的必备属性。
filter-method
这个属性里我们使用了do_not_searchFillterData
方法目的是解决需求 8 用户输入快捷...
下面方法很简单,既然不要影响options
里的数据,那么我们直接不做处理,返回原数组就好了。
do_not_searchInit
do_not_searchInit(e) {
this.oldOptions = e
return e;
}
const [oldOptions,setOldOptions]=useState([])
const do_not_searchInit=(e)=>{
setOldOptions(e)
return e;
}
onChange
这个我们需要处理的就比较多了,解决需求 1、2、3、4、5
因为这是我们功能的主要方法,完成了他,基本就写完了。
下面代码我们首先进行校验,使用正则表达式,让用户只能输入正整数-正整数
表达式为/^([0-9]+-[0-9]+)$/
,然后我们处理好其他校验后,进行数据处理,分为两种处理方式。
方式一、用户输入xx-xx
方式二、用户选择数字
我直接用最简单最笨的方法,使用includes
进行判断是否存在字符串-
如果存在,走方式一
然后把拿到的例如1-3 转换成1,2,3 Array map结合起来使用
,然后我们得数组[1,2,3] 别着急,我们还要进行一次数据处理,要把不连续的数组分开,排序,输出为[[1,2,3],[7,8,9]]
这个时候就通过我们的sortArr
方法了。
selectValue_changle
data() {
return {
dataModel: [],
}
selectValue_changle(el, val) {
if (this.formData.options.options.length >= 1) {
if (val.at(-1) > this.formData.options.options.at(-1).value) {
console.error('数值大小有误')
this.dataModel.length = this.dataModel.length - 1
return;
}
if (val.length >= 1 && val.at(-1).toString().includes('-')) {
const newVal = val.at(-1).split('-')
const start = parseInt(newVal[0]);
const end = parseInt(newVal[1]);
const regE = /^([0-9]+-[0-9]+)$/;
const regES = new RegExp(regE);
if (regES.test(val.at(-1)) && start < end) {
const inveres = Array(end - start + 1).fill(0).map((v, i) => i + start)
const visibleArraydata = []
for (let is = 0; is < this.formData.options.options.length; is++) {
const item = this.formData.options.options[is];
visibleArraydata.push(item.value)
}
if (inveres.filter(item => !visibleArraydata.includes(item)).length > 0) {
console.error('所选范围包含无效值')
this.dataModel.length = this.dataModel.length - 1
return;
}
if (inveres[0] < this.formData.options.options[0].value) {
console.error('数值大小有误')
this.dataModel.length = this.dataModel.length - 1
return;
}
if (inveres.at(-1) > this.formData.options.options.at(-1).value) {
console.error('数值大小有误')
this.dataModel.length = this.dataModel.length - 1
return;
} else {
if (this.dataModel.length === 0) {
this.dataModel = []
} else {
this.dataModel.length = this.dataModel.length - 1
}
this.dataModel.push(...inveres)
this.create_dom_node(el, this.sortArr(this.dataModel))
}
} else {
console.error('数值表达式有误')
this.dataModel.length = this.dataModel.length - 1
return;
}
} else {
this.create_dom_node(el, this.sortArr(val))
}
} else {
console.error('数值大小有误')
this.dataModel.length = this.dataModel.length - 1
return;
}
}
const [dataModel,setDataModel]=useState([])
const selectValue_changle = (el, val) => {
if (formData.options.options.length >= 1) {
if (val.at(-1) > formData.options.options.at(-1).value) {
console.error('数值大小有误')
dataModel.length = dataModel.length - 1
return;
}
if (val.length >= 1 && val.at(-1).toString().includes('-')) {
const newVal = val.at(-1).split('-')
const start = parseInt(newVal[0]);
const end = parseInt(newVal[1]);
const regE = /^([0-9]+-[0-9]+)$/;
const regES = new RegExp(regE);
if (regES.test(val.at(-1)) && start < end) {
const inveres = Array(end - start + 1).fill(0).map((v, i) => i + start)
const visibleArraydata = []
for (let is = 0; is < formData.options.options.length; is++) {
const item = formData.options.options[is];
visibleArraydata.push(item.value)
}
if (inveres.filter(item => !visibleArraydata.includes(item)).length > 0) {
console.error('所选范围包含无效值')
setDataModel.length - 1
return;
}
if (inveres[0] < formData.options.options[0].value) {
console.error('数值大小有误')
setDataModel.length - 1
return;
}
if (inveres.at(-1) > formData.options.options.at(-1).value) {
console.error('数值大小有误')
setDataModel.length - 1
return;
} else {
if (dataModel.length === 0) {
setDataModel([])
} else {
setDataModel.length - 1
}
setDataModel(...inveres)
create_dom_node(el, sortArr(dataModel))
}
} else {
console.error('数值表达式有误')
setDataModel.length - 1
return;
}
} else {
create_dom_node(el, sortArr(val))
}
} else {
console.error('数值大小有误')
setDataModel.length - 1
return;
}
}
sortArr 数据处理为 [[1,2,3],[7,8,9]]
这边把数据排序处理为指定格式,是为了方便我们后期使用,处理完成后,直接使用create_dom_node(el, sortArr(dataModel))
方法 进行dom的绘制处理. (el 是上方的dom节点,this)
sortArr(res) {
if (res.length >= 1) {
var arrays = []
for (let i = 0; i < this.turnNum(res).length; i++) {
const it = this.turnNum(res)[i]
if (!isNaN(parseInt(it))) {
arrays.push(parseInt(it))
} else {
console.error('数值格式有误')
this.dataModel.length = this.dataModel.length - 1
return;
}
}
arrays = Array.from(new Set(arrays))
const result = [];
let i = 0;
const list = arrays.sort((a, b) => a - b);
list.forEach((item, index) => {
if (index === 0) {
result[0] = [item];
} else if (item - list[index - 1] === 1) {
result[i].push(item);
} else {
result[++i] = [item];
}
})
this.dataModel = Array.from(new Set(result.flat()))
return result;
}
}
const sortArr = (res) => {
if (res.length >= 1) {
let arrays = []
for (let i = 0; i < this.turnNum(res).length; i++) {
const it = this.turnNum(res)[i]
if (!isNaN(parseInt(it))) {
arrays.push(parseInt(it))
} else {
console.error('数值格式有误')
setDataModel.length - 1
return;
}
}
arrays = Array.from(new Set(arrays))
const result = [];
let i = 0;
const list = arrays.sort((a, b) => a - b);
list.forEach((item, index) => {
if (index === 0) {
result[0] = [item];
} else if (item - list[index - 1] === 1) {
result[i].push(item);
} else {
result[++i] = [item];
}
})
setDataModel(Array.from(new Set(result.flat())))
return result;
}
}
绘制DOM create_dom_node
这边写的最多的无非就是 使用jQuery
选中我要操作的节点,进行add增加标签而已。因为我的结构较多,所以写的就比较多。如图。

create_dom_node(el, vals, isDisplay) {
const visibleArraydata = []
for (let is = 0; is < this.formData.options.options.length; is++) {
const item = this.formData.options.options[is];
visibleArraydata.push(item.value)
}
if (vals) {
if (vals.at(-1).filter(item => !visibleArraydata.includes(item)).length > 0 && !isDisplay) {
openMsg(this, '所选范围包含无效值', 'error')
this.dataModel.length = this.dataModel.length - 1
return;
}
const _this = this
{
mathJaxContainer[0]}el).find('.el-select__tags span').remove()
for (let i = 0; i < vals.length; i++) {
const item = vals[i];
var tagStr = ''
if (item.length > 1) {
tagStr = item[0] + '-' + item[item.length - 1];
} else {
tagStr = item[0];
}
const ptag = '<span class="el-tag el-tag--info el-tag--mini el-tag--light"><span class="el-select__tags-text">' + tagStr + '</span> <i class="el-tag__close el-icon-close"></i> </span>';
const spana = $('<span value="' + JSON.stringify(item) + '">' + ptag + '</span>');
spana.find('i').click(function (e) {
var span = $(this).parents('.el-tag--light').parents('span')
if (span.length > 0) {
var value = span.attr('value');
$(this).parent().remove();
_this.getNewDataModel(_this.dataModel, value)
}
e.stopPropagation();
{
mathJaxContainer[1]}el).find('.el-select__tags input').focus();
return false;
})
{
mathJaxContainer[2]}el).find('.el-select__tags').append(spana)
{
mathJaxContainer[3]}el).find('.el-select__tags input').appendTo({
mathJaxContainer[4]}el).find('.el-select__tags'))
if ($('.el-tag--light').length >= 2) {
this.isDelete = true
}
}
} else {
{
mathJaxContainer[5]}el).find('.el-select__tags span').remove()
}
},
const create_dom_node = (el, vals, isDisplay) => {
const visibleArraydata = []
for (let is = 0; is < formData.options.options.length; is++) {
const item = formData.options.options[is];
visibleArraydata.push(item.value)
}
if (vals) {
if (vals.at(-1).filter(item => !visibleArraydata.includes(item)).length > 0 && !isDisplay) {
openMsg(this, '所选范围包含无效值', 'error')
dataModel.length = dataModel.length - 1
return;
}
{
mathJaxContainer[6]}el).find('.el-select__tags span').remove()
for (let i = 0; i < vals.length; i++) {
const item = vals[i];
var tagStr = ''
if (item.length > 1) {
tagStr = item[0] + '-' + item[item.length - 1];
} else {
tagStr = item[0];
}
const ptag = '<span class="el-tag el-tag--info el-tag--mini el-tag--light"><span class="el-select__tags-text">' + tagStr + '</span> <i class="el-tag__close el-icon-close"></i> </span>';
const spana = $('<span value="' + JSON.stringify(item) + '">' + ptag + '</span>');
spana.find('i').click(function (e) {
var span = $(this).parents('.el-tag--light').parents('span')
if (span.length > 0) {
var value = span.attr('value');
$(this).parent().remove();
_getNewDataModel(_dataModel, value)
}
e.stopPropagation();
{
mathJaxContainer[7]}el).find('.el-select__tags input').focus();
return false;
})
{
mathJaxContainer[8]}el).find('.el-select__tags').append(spana)
{
mathJaxContainer[9]}el).find('.el-select__tags input').appendTo({
mathJaxContainer[10]}el).find('.el-select__tags'))
}
} else {
{
mathJaxContainer[11]}el).find('.el-select__tags span').remove()
}
},
本文同步 我的笔记
End