#### 新增

- 新增`disabled`配置, 可以禁用多选
- 新增`create`配置, 可以创建条目, 具体见 [创建条目](https://maplemei.gitee.io/xm-select/#/example/XM27)
- 方法`warning`新增参数`sustain`, 可以配置是否持续显示
- 新增全局`get`方法, 可以获取多选渲染后的对象
- 新增全局`batch`方法, 可以批量给渲染后的多选执行方法

#### Bug fixes

- 修复`update`方法, 会使已选中数据丢失的问题
- 修复`Safari`浏览器下, 搜索框无法聚焦的问题
This commit is contained in:
maplemei 2019-10-23 14:48:04 +08:00
parent 6d23654a43
commit 20e535ac90
24 changed files with 529 additions and 34 deletions

View File

@ -1,5 +1,23 @@
## 更新日志 ## 更新日志
### 1.0.11
*2019-10-23*
#### 新增
- 新增`disabled`配置, 可以禁用多选
- 新增`create`配置, 可以创建条目, 具体见 [创建条目](https://maplemei.gitee.io/xm-select/#/example/XM27)
- 方法`warning`新增参数`sustain`, 可以配置是否持续显示
- 新增全局`get`方法, 可以获取多选渲染后的对象
- 新增全局`batch`方法, 可以批量给渲染后的多选执行方法
#### Bug fixes
- 修复`update`方法, 会使已选中数据丢失的问题
- 修复`Safari`浏览器下, 搜索框无法聚焦的问题
### 1.0.10 ### 1.0.10
*2019-10-20* *2019-10-20*

2
dist/static/2.js vendored

File diff suppressed because one or more lines are too long

2
dist/static/3.js vendored

File diff suppressed because one or more lines are too long

10
dist/static/docs.js vendored

File diff suppressed because one or more lines are too long

2
dist/xm-select.js vendored

File diff suppressed because one or more lines are too long

View File

@ -177,6 +177,7 @@ button, input, select, textarea {
vertical-align: top; vertical-align: top;
display: inline-block; display: inline-block;
width: 300px; width: 300px;
margin-bottom: 10px;
} }
.xm-select-size{ .xm-select-size{
vertical-align: top; vertical-align: top;
@ -204,6 +205,7 @@ button, input, select, textarea {
vertical-align: top; vertical-align: top;
margin-left: 10px; margin-left: 10px;
margin-bottom: 10px;
&:hover{ &:hover{
opacity: .8; opacity: .8;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -5,6 +5,14 @@
放下她, 让我来!!! 默认颜色`#e54d42`, 多选上限的样色 放下她, 让我来!!! 默认颜色`#e54d42`, 多选上限的样色
```
/*
* COLOR: 自定义颜色, 默认是 options.theme.maxColor的颜色
* SUSTAIN: 是否持续显示, 默认为false
*/
xmSelectObj.warning(COLOR, SUSTAIN);
```
:::demo :::demo
```html ```html
@ -31,7 +39,6 @@ document.getElementById('demo1-warning').onclick = function(){
### 自定义闪烁颜色 ### 自定义闪烁颜色
:::demo :::demo
```html ```html
<div id="demo2" class="xm-select-demo"></div> <div id="demo2" class="xm-select-demo"></div>
@ -53,3 +60,29 @@ document.getElementById('demo2-warning').onclick = function(){
</script> </script>
``` ```
::: :::
### 持续显示
:::demo
```html
<div id="demo3" class="xm-select-demo"></div>
<button class="btn" id="demo3-warning">警告</button>
<script>
var demo3 = xmSelect.render({
el: '#demo3',
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3, disabled: true},
]
})
document.getElementById('demo3-warning').onclick = function(){
demo3.warning('#6739b6', true);
}
</script>
```
:::

90
docs/mds/XM26.md Normal file
View File

@ -0,0 +1,90 @@
## 禁用
### 渲染禁用
```
xmSelect.render({
//...
disabled: true
})
```
:::demo
```html
<div id="demo1" class="xm-select-demo"></div>
<script>
var demo1 = xmSelect.render({
el: '#demo1',
disabled: true,
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3, disabled: true},
]
})
</script>
```
:::
### 动态启用禁用
:::demo
```html
<div id="demo2" class="xm-select-demo"></div>
<button class="btn" id="demo21">禁用</button>
<button class="btn" id="demo22">启用</button>
<script>
var demo2 = xmSelect.render({
el: '#demo2',
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3, disabled: true},
]
})
document.getElementById('demo21').onclick = function(){
demo2.update({ disabled: true });
}
document.getElementById('demo22').onclick = function(){
demo2.update({ disabled: false });
}
</script>
```
:::
### 选完`张三`后禁用
:::demo
```html
<div id="demo3" class="xm-select-demo"></div>
<button class="btn" id="demo3-disabled">启用</button>
<script>
var demo3 = xmSelect.render({
el: '#demo3',
data: [
{name: '张三', value: 1},
{name: '李四', value: 2},
{name: '王五', value: 3},
],
on({ arr, change, isAdd }){
var hasZS = change.find(item => item.name === '张三');
if(isAdd && hasZS){
demo3.update({ disabled: true });
}
}
})
//没有做不到, 只有想不到, 把多选玩出花来吧
document.getElementById('demo3-disabled').onclick = function(){
demo3.update({ disabled: false });
}
</script>
```
:::

78
docs/mds/XM27.md Normal file
View File

@ -0,0 +1,78 @@
## 创建条目
### 搜索不存在则创建条目
```
//想创建就必须要开启本地搜索
xmSelect.render({
//...
filterable: true,
create: function(val){
//返回一个创建成功的对象, val是搜索的数据
return {
name: '创建-' + val,
value: val
}
}
})
```
:::demo
```html
<div id="demo1" class="xm-select-demo"></div>
<script>
var demo1 = xmSelect.render({
el: '#demo1',
filterable: true,
create: function(val){
return {
name: '创建-' + val,
value: val
}
},
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3, disabled: true},
]
})
</script>
```
:::
### 单选创建
:::demo
```html
<div id="demo2" class="xm-select-demo"></div>
<script>
var demo1 = xmSelect.render({
el: '#demo2',
radio: true,
clickClose: true,
filterable: true,
create: function(val){
return {
name: '创建-' + val,
value: val
}
},
model: {
icon: 'hidden',
label: {
type: 'text',
}
},
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3},
]
})
</script>
```
:::

66
docs/mds/ZM07.md Normal file
View File

@ -0,0 +1,66 @@
## 获取实例对象
### 全局方法 get
```
//所有
xmSelect.get();
//字符串
xmSelect.get('#demo2');
//正则
xmSelect.get(/demo.*/);
//过滤方法
xmSelect.get((el) => {
return el == '#demo1' || el == '#xm3'
});
```
:::demo
```html
<div id="demo1" class="xm-select-demo"></div>
<div id="demo2" class="xm-select-demo"></div>
<div id="xm3" class="xm-select-demo"></div>
<div><button class="btn" id="btn1">get(所有)</button></div>
<div><button class="btn" id="btn2">get(字符串)</button></div>
<div><button class="btn" id="btn3">get(正则)</button></div>
<div><button class="btn" id="btn4">get(过滤方法)</button></div>
<script>
['#demo1', '#demo2', '#xm3'].forEach((el, index) => {
xmSelect.render({
el,
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3, disabled: true},
]
});
});
document.getElementById('btn1').onclick = function(){
var xmList = xmSelect.get();
alert('当前页面多选个数: ' + xmList.length)
}
document.getElementById('btn2').onclick = function(){
var xmList = xmSelect.get('#demo2');
alert('获取 #demo2 实例: ' + xmList.length)
}
document.getElementById('btn3').onclick = function(){
var xmList = xmSelect.get(/demo.*/);
alert('获取匹配正则 /demo.*/ 的实例: ' + xmList.length)
}
document.getElementById('btn4').onclick = function(){
var xmList = xmSelect.get((el) => {
return el == '#demo1' || el == '#xm3'
});
alert('自定义方法的实例: ' + xmList.length)
}
</script>
```
:::

61
docs/mds/ZM08.md Normal file
View File

@ -0,0 +1,61 @@
## 批量操作
### 批量操作已渲染实例
```
//filter, 同get方法
//method, 需要执行的方法
//args, 执行方法的参数
xmSelect.batch(filter, method, ...args);
```
:::demo
```html
<div id="demo1" class="xm-select-demo"></div>
<div id="demo2" class="xm-select-demo"></div>
<div id="xm3" class="xm-select-demo"></div>
<div><button class="btn" id="btn1">batch禁用</button></div>
<div><button class="btn" id="btn2">batch启用</button></div>
<div><button class="btn" id="btn3">batch警告</button></div>
<div><button class="btn" id="btn4">batch获取已选中数据</button></div>
<pre id="demo-value"></pre>
<script>
['#demo1', '#demo2', '#xm3'].forEach((el, index) => {
xmSelect.render({
el,
data: [
{name: '张三', value: 1, selected: true},
{name: '李四', value: 2},
{name: '王五', value: 3, disabled: true},
]
});
});
document.getElementById('btn1').onclick = function(){
xmSelect.batch('', 'update', {
disabled: true
});
}
document.getElementById('btn2').onclick = function(){
xmSelect.batch('', 'update', {
disabled: false
});
}
document.getElementById('btn3').onclick = function(){
xmSelect.batch(/demo.*/, 'warning', '#F00', true);
}
document.getElementById('btn4').onclick = function(){
var selectArr = xmSelect.batch(null, 'getValue', 'name');
document.getElementById('demo-value').innerHTML = JSON.stringify(selectArr, null, 2);
}
</script>
```
:::

View File

@ -10,8 +10,15 @@
//先渲染多选 //先渲染多选
var demo1 = xmSelect.render({ var demo1 = xmSelect.render({
el: '#demo1', el: '#demo1',
paging: true, autoRow: true,
pageSize: 2, filterable: true,
create: val => {
console.log(val)
return {
name: val,
value: val,
}
},
data(){ data(){
return [ return [
{name: '销售员', children: [ {name: '销售员', children: [

View File

@ -41,6 +41,8 @@
| showCount | 展示在下拉框中的最多选项数量 | int | - | 0 | | showCount | 展示在下拉框中的最多选项数量 | int | - | 0 |
| autoRow | 是否开启自动换行(选项过多时) | boolean | true / false | false | | autoRow | 是否开启自动换行(选项过多时) | boolean | true / false | false |
| size | 尺寸 | string | large / medium / small / mini | medium | | size | 尺寸 | string | large / medium / small / mini | medium |
| disabled | 是否禁用多选 | boolean | true / false | false |
| create | 创建条目 | function(val), val: 搜索的数据 | - | null |
### prop ### prop
@ -163,7 +165,38 @@ list: [ "ALL", "CLEAR",
``` ```
### 方法 ### 全局方法
| 事件名 | 说明 | 参数 | 返回值 |
| ------ | ------------------ | -------- | -------- |
| render | 渲染多选 | (options: 配置项) | 实例对象 |
| get | 获取页面中已经渲染的多选 | (filter: 过滤`el`) | 符合条件的实例数组 |
| batch | 批量操作已渲染的多选 | (filter: 过滤`el`, method: 方法, ...方法参数) | 符合条件的实例数组 |
```
//render 使用方式
xmSelect.render(OPTIONS);
//get 使用方式
xmSelect.get('#demo1'); //指定某一个获取
xmSelect.get(/.*demo1.*/); //正则获取
//自定义方法获取
xmSelect.get(function(el){
return el == '#demo1' || el == '#demo2';
});
//batch 使用方式
//批量执行禁用
xmSelect.batch(/.*demo/, 'update', {
disabled: true,
});
//批量执行warning
xmSelect.batch(/.*demo/, 'warning', '#F00', true);
```
### 实例方法
:::warning :::warning
xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用 xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用
@ -179,4 +212,4 @@ xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用
| closed | 主动关闭下拉 | - | | closed | 主动关闭下拉 | - |
| reset | 重置为上一次的render状态 | - | | reset | 重置为上一次的render状态 | - |
| update | 更新多选选中, reset不保留 | (options: 见配置项) | | update | 更新多选选中, reset不保留 | (options: 见配置项) |
| warning | 警告 | (color: 默认同theme.maxColor) | | warning | 警告 | (color: 默认同theme.maxColor, sustain: 是否持续显示) |

View File

@ -27,3 +27,34 @@
### 4.占位标签为什么是div ### 4.占位标签为什么是div
演示中使用的是div, 不限制标签, 但是不建议使用`select`, 因为`layui`会渲染`select`标签 演示中使用的是div, 不限制标签, 但是不建议使用`select`, 因为`layui`会渲染`select`标签
### 5.动态数据渲染报错
![](../assets/dataNotArray.png)
检查设置的data数据是否为数组类型
```
var demo1 = xmSelect.render({
el: '#demo1',
data: []
})
//....N多操作以后
var arr = data;//这里的data可能是ajax返回的数据
//这里必须是 [object Array]
console.log(Object.prototype.toString.call(arr));
//如果是 [object String]
//1. JSON数据
arr = JSON.parse(arr);
//2. 类似JSON的数据
arr = eval('(' + arr + ')');
demo1.update({
data: arr,
})
```

View File

@ -72,6 +72,8 @@ export default [{
{ path: '/example/XM23', name: 'HiddenIcon 隐藏图标', component: importMd('/XM23') }, { path: '/example/XM23', name: 'HiddenIcon 隐藏图标', component: importMd('/XM23') },
{ path: '/example/XM24', name: 'Size 尺寸', component: importMd('/XM24') }, { path: '/example/XM24', name: 'Size 尺寸', component: importMd('/XM24') },
{ path: '/example/XM25', name: 'Warning 警告', component: importMd('/XM25') }, { path: '/example/XM25', name: 'Warning 警告', component: importMd('/XM25') },
{ path: '/example/XM26', name: 'Disabled 禁用', component: importMd('/XM26') },
{ path: '/example/XM27', name: 'Create 创建条目', component: importMd('/XM27') },
] ]
}, { }, {
path: '/example-custom', path: '/example-custom',
@ -85,6 +87,8 @@ export default [{
{ path: '/example-custom/ZM04', name: '远程搜索', component: importMd('/ZM04') }, { path: '/example-custom/ZM04', name: '远程搜索', component: importMd('/ZM04') },
{ path: '/example-custom/ZM05', name: '动态数据', component: importMd('/ZM05') }, { path: '/example-custom/ZM05', name: '动态数据', component: importMd('/ZM05') },
{ path: '/example-custom/ZM06', name: '弹框中的多选', component: importMd('/ZM06') }, { path: '/example-custom/ZM06', name: '弹框中的多选', component: importMd('/ZM06') },
{ path: '/example-custom/ZM07', name: '获取实例对象', component: importMd('/ZM07') },
{ path: '/example-custom/ZM08', name: '批量操作', component: importMd('/ZM08') },
] ]
}, { }, {
path: '/example-plugin', path: '/example-plugin',

View File

@ -1,6 +1,6 @@
{ {
"name": "xm-select", "name": "xm-select",
"version": "1.0.10", "version": "1.0.11",
"description": "始于Layui的select多选解决方案", "description": "始于Layui的select多选解决方案",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@ -15,8 +15,12 @@ export default function (lan = 'zn') {
name: 'select', name: 'select',
//尺寸 //尺寸
size: 'medium', size: 'medium',
//是否禁用多选
disabled: false,
//默认选中数据, 优先级大于selected //默认选中数据, 优先级大于selected
initValue: null, initValue: null,
//创建条目
create: null,
//默认提示 //默认提示
tips: setting.tips, tips: setting.tips,
//空数据提示 //空数据提示

View File

@ -162,11 +162,18 @@ class xmOptions {
/** /**
* 闪烁警告边框 * 闪烁警告边框
*/ */
warning(color){ warning(color, sustain = false){
childs[this.options.el].updateBorderColor(color || this.options.theme.maxColor); let showColor = color || this.options.theme.maxColor;
sustain === true ? (
childs[this.options.el].base.style.borderColor = showColor
) : (
childs[this.options.el].updateBorderColor(showColor)
)
return this; return this;
} }
} }
export default xmOptions; export default xmOptions;

View File

@ -24,8 +24,9 @@ class Framework extends Component{
reset(props){ reset(props){
//用于多选上限的边框颜色变化 //用于多选上限的边框颜色变化
this.updateBorderColor(''); this.updateBorderColor('');
let old = this.state.data;
this.resetDate(props.data); this.resetDate(props.data);
this.value(props.initValue ? props.initValue : this.findValue(this.state.data), !!this.state.show); JSON.stringify(props.data) !== JSON.stringify(old) && this.value(props.initValue ? props.initValue : this.findValue(this.state.data), !!this.state.show);
} }
findValue(data){ findValue(data){
@ -115,6 +116,11 @@ class Framework extends Component{
} }
onClick(e){ onClick(e){
if(this.props.disabled){
this.state.show !== false && this.setState({ show: false });
return ;
}
let show = !this.state.show; let show = !this.state.show;
if(show){ if(show){
@ -197,6 +203,9 @@ class Framework extends Component{
this.updateBorderColor('') this.updateBorderColor('')
}, 300); }, 300);
} }
if(config.disabled){
show = false;
}
//右边下拉箭头的变化class //右边下拉箭头的变化class
const iconClass = show ? 'xm-icon xm-icon-expand' : 'xm-icon'; const iconClass = show ? 'xm-icon xm-icon-expand' : 'xm-icon';
@ -265,6 +274,7 @@ class Framework extends Component{
<General { ...bodyProps } /> <General { ...bodyProps } />
) } ) }
</div> </div>
{ config.disabled && <div class="xm-select-disabled"></div> }
</xm-select> </xm-select>
); );
} }

View File

@ -14,11 +14,11 @@ class Custom extends Component{
} }
shouldComponentUpdate(){ shouldComponentUpdate(){
return !this.already; return !this.prepare;
} }
render(config) { render(config) {
this.already = true; this.prepare = true;
return ( return (
<div onClick={ this.blockClick } class="xm-body-custom" dangerouslySetInnerHTML={{ __html: config.content }}> <div onClick={ this.blockClick } class="xm-body-custom" dangerouslySetInnerHTML={{ __html: config.content }}>

View File

@ -137,11 +137,11 @@ class General extends Component{
render(config) { render(config) {
let { data, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips } = config let { data, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips, create } = config
const { name, value, disabled, children, optgroup } = prop; const { name, value, disabled, children, optgroup } = prop;
let arr = deepMerge([], data); let arr = deepMerge([], data), creator;
//是否开启了搜索 //是否开启了搜索
if(filterable){ if(filterable){
if(remoteSearch){//是否进行远程搜索 if(remoteSearch){//是否进行远程搜索
@ -181,6 +181,8 @@ class General extends Component{
arr[arr.length - 1].__del = true; arr[arr.length - 1].__del = true;
} }
arr = arr.filter(item => !item.__del); arr = arr.filter(item => !item.__del);
//创建条目
creator = this.state.filterValue && isFunction(create) && create(this.state.filterValue);
} }
} }
@ -352,9 +354,9 @@ class General extends Component{
arr = arr.map(renderGroup); arr = arr.map(renderGroup);
if(!arr.length){ if(!arr.length){
arr.push( arr.push( creator ? renderItem(creator) : (
<div class="xm-select-empty">{ empty }</div> <div class="xm-select-empty">{ empty }</div>
) ))
} }
return ( return (

View File

@ -3,13 +3,45 @@ import '@/components/common/expand'
import Core from '@/components/core' import Core from '@/components/core'
import '@/style/index.less' import '@/style/index.less'
import '@/style/iconfont.less' import '@/style/iconfont.less'
import { selector, warn } from '@/components/common/util'
const object = {}
const xmSelect = { const xmSelect = {
name, name,
version, version,
render(options) { render(options) {
return new Core(options); let instance = new Core(options);
if(instance){
let { el } = options;
let select = object[el];
select !== undefined && (delete object[el]);
object[el] = instance;
}
return instance;
},
get(filter){
let type = Object.prototype.toString.call(filter);
let method;
switch (type){
case '[object String]':
filter && (method = item => item === filter);
break;
case '[object RegExp]':
method = item => filter.test(item);
break;
case '[object Function]':
method = filter;
break;
default:
break;
}
let keys = Object.keys(object)
return (method ? keys.filter(method) : keys).map(key => object[key]).filter(instance => selector(instance.options.el));
},
batch(filter, method){
let args = [ ...arguments ];
args.splice(0, 2);
return this.get(filter).map(instance => instance[method](...args));
} }
} }

View File

@ -33,6 +33,13 @@
} }
} }
.userselect(@v){
user-select: @v;
-ms-user-select: @v;
-moz-user-select: @v;
-webkit-user-select: @v;
}
xm-select{ xm-select{
*{ *{
margin: 0; margin: 0;
@ -42,10 +49,7 @@ xm-select{
font-weight: 400; font-weight: 400;
// color: @fontColor; // color: @fontColor;
text-overflow: ellipsis; text-overflow: ellipsis;
user-select: none; .userselect(none);
-ms-user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
} }
background-color: #FFF; background-color: #FFF;
@ -363,6 +367,7 @@ xm-select{
line-height: 1.3; line-height: 1.3;
padding-left: 10px; padding-left: 10px;
outline: 0; outline: 0;
.userselect(text);
} }
.dis{ .dis{
@ -401,6 +406,18 @@ xm-select{
display: none !important; display: none !important;
} }
.xm-select-disabled{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
cursor: no-drop;
z-index: 2;
opacity: 0.3;
background-color: #FFF;
}
} }
//不同尺寸下的数据调整 //不同尺寸下的数据调整