This commit is contained in:
maplemei 2019-11-26 12:20:02 +08:00
parent 3fcd41969c
commit c604a398ff
14 changed files with 239 additions and 113 deletions

View File

@ -1,5 +1,19 @@
## 更新日志
### 1.1.1
*2019-11-26*
#### 新增
- 键盘操作updownenter
#### Bug fixes
- 修改分组模式下第一级数据中children为空数组报错
- 修改tree模式+radio模式无法选中父节点的bug
### 1.1.0
*2019-11-25*
@ -132,7 +146,7 @@
#### 新增
- 新增`disabled`配置, 可以禁用多选
- 新增`create`配置, 可以创建条目, 具体见 [创建条目](https://maplemei.gitee.io/xm-select/#/example/XM27)
- 新增`create`配置, 可以创建条目, 具体见 [创建条目](https://maplemei.gitee.io/xm-select/#/basic/create)
- 方法`warning`新增参数`sustain`, 可以配置是否持续显示
- 新增全局`get`方法, 可以获取多选渲染后的对象
- 新增全局`batch`方法, 可以批量给渲染后的多选执行方法
@ -149,7 +163,7 @@
#### 新增
- 新增`content`配置, 可自定义下拉框HTML, 具体见 [下拉自定义](https://maplemei.gitee.io/xm-select/#/example-plugin/ZP01)
- 新增`content`配置, 可自定义下拉框HTML, 具体见 [下拉自定义](https://maplemei.gitee.io/xm-select/#/plugin/customer)
- 方法`setValue`新增参数`listenOn`, 可以设置是否通过`on`监听
#### Bug fixes

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

4
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

@ -218,36 +218,19 @@ button, input, select, textarea {
}
}
.demo-ZM03{
table{
margin-bottom: -1px !important;
border-collapse: unset !important;
line-height: unset !important;
font-size: unset !important;
background-color: unset !important;
th, td{
padding: 5px 0 !important;
}
}
}
.ly-label-center.layui-form-pane .layui-form-label{
display: flex;
align-items: center;
justify-content: center;
}
.demo-ZP01{
.demo-customer{
.xm-body-custom{
padding: 0 10px;
}
table{
margin-bottom: -1px !important;
}
}
.demo-ZP03, .demo-ZP04{
.demo-laydate, .demo-panel{
xm-select .scroll-body{
margin-top: -5px;
}
@ -258,11 +241,14 @@ button, input, select, textarea {
box-shadow: none;
}
}
.demo-ZP05{
.demo-transfer{
xm-select .scroll-body{
padding-left: 10px;
overflow: hidden;
}
.layui-transfer-data{
overflow: hidden;
}
}
.tool_bar{

View File

@ -22,10 +22,10 @@ var demo2 = xmSelect.render({
{name: '王五1', value: 3, disabled: true},
]},
{name: '奖品', children: [
{name: '苹果2', value: 4, selected: true, disabled: true},
{name: '苹果2', value: 4, selected: true, disabled: true, children: []},
{name: '香蕉2', value: 5},
{name: '葡萄2', value: 6},
]},
]},
]
})
</script>
@ -45,8 +45,10 @@ var demo4 = xmSelect.render({
toolbar:{
show: true,
},
height: '500px',
data: [
height: '300px',
data: [
{name: '开始无分组1', value: 11, children: []},
{name: '开始无分组2', value: 12, children: []},
{name: '选中', optgroup: true, click: 'SELECT', children: [
{name: '张三', value: 1},
{name: '李四', value: 2, disabled: true},
@ -58,11 +60,15 @@ var demo4 = xmSelect.render({
{name: '自动', optgroup: true, click: 'AUTO', children: [
{name: '香蕉', value: 5},
{name: '葡萄', value: 6},
]},
]},
{name: '中间无分组1', value: 21, children: []},
{name: '中间无分组2', value: 22, children: []},
{name: '自定义', optgroup: true, click: function(item){ alert('自定义的, 想干嘛干嘛') }, children: [
{name: '小米', value: 7},
{name: '华为', value: 8},
]},
]},
{name: '结尾无分组1', value: 31, children: []},
{name: '结尾无分组2', value: 32, children: []},
]
})
</script>

View File

@ -10,8 +10,8 @@ var demo1 = xmSelect.render({
autoRow: true,
filterable: true,
tree: {
strict: false,
show: true,
strict: true,
show: false,
showFolderIcon: true,
showLine: true,
indent: 20,
@ -25,23 +25,26 @@ var demo1 = xmSelect.render({
{name: item.name + 2, value: item.value + '2' },
])
}, 500)
}
},
},
toolbar: {
show: true
},
height: 'auto',
data(){
return [
{name: '销售员', value: -1, children: [
{name: '张三1', value: 100, selected: true, children: []},
{name: '李四1', value: 2, selected: true},
{name: '王五1', value: 3, disabled: true},
{name: '销售员', value: -1, selected: true, children: [
{name: '张三1', value: 100, selected: false, children: []},
{name: '李四1', value: 2, selected: false},
{name: '王五1', value: 3, disabled: false},
]},
{name: '奖品', value: -2, children: [
{name: '奖品3', value: -3, children: [
{name: '苹果3', value: 14, selected: true},
{name: '苹果3', value: 14, selected: false},
{name: '香蕉3', value: 15},
{name: '葡萄3', value: 16},
]},
{name: '苹果2', value: 4, selected: true, disabled: true},
{name: '苹果2', value: 4, selected: false, disabled: true},
{name: '香蕉2', value: 5},
{name: '葡萄2', value: 6},
]},

View File

@ -42,65 +42,65 @@ export default [{
component: importMd('/options'),
}]
}, {
path: '/example',
path: '/basic',
name: '示例',
redirect: '/example/XM01',
redirect: '/basic/base',
component: Component,
children: [
{ path: '/example/XM01', name: 'Base 基础使用', component: importMd('/XM01') },
{ path: '/example/XM02', name: 'Language 国际化', component: importMd('/XM02') },
{ path: '/example/XM03', name: 'InitValue 默认选中', component: importMd('/XM03') },
{ path: '/example/XM04', name: 'Tips 修改提示', component: importMd('/XM04') },
{ path: '/example/XM05', name: 'Filterable 搜索模式', component: importMd('/XM05') },
{ path: '/example/XM06', name: 'Direction 下拉方向', component: importMd('/XM06') },
{ path: '/example/XM07', name: 'Style 自定义样式', component: importMd('/XM07') },
{ path: '/example/XM08', name: 'Paging 分页', component: importMd('/XM08') },
{ path: '/example/XM09', name: 'Radio 单选', component: importMd('/XM09') },
{ path: '/example/XM10', name: 'Repeat 重复选', component: importMd('/XM10') },
{ path: '/example/XM11', name: 'Prop 自定义属性', component: importMd('/XM11') },
{ path: '/example/XM12', name: 'Theme 主题', component: importMd('/XM12') },
{ path: '/example/XM13', name: 'Show 显示与隐藏', component: importMd('/XM13') },
{ path: '/example/XM14', name: 'Model 显示方式', component: importMd('/XM14') },
{ path: '/example/XM15', name: 'Template 构建选项', component: importMd('/XM15') },
{ path: '/example/XM16', name: 'On 监听选择', component: importMd('/XM16') },
{ path: '/example/XM17', name: 'Test 性能测试', component: importMd('/XM17') },
{ path: '/example/XM18', name: 'Max 多选上限', component: importMd('/XM18') },
{ path: '/example/XM19', name: 'Toolbar 工具条', component: importMd('/XM19') },
{ path: '/example/XM20', name: 'ShowCount 选项数量', component: importMd('/XM20') },
{ path: '/example/XM21', name: 'Optgroup 分组', component: importMd('/XM21') },
{ path: '/example/XM22', name: 'AutoRow 自动换行', component: importMd('/XM22') },
{ path: '/example/XM23', name: 'HiddenIcon 隐藏图标', component: importMd('/XM23') },
{ path: '/example/XM24', name: 'Size 尺寸', component: importMd('/XM24') },
{ 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: '/basic/use', name: 'Base 基础使用', component: importMd('/XM01') },
{ path: '/basic/language', name: 'Language 国际化', component: importMd('/XM02') },
{ path: '/basic/initValue', name: 'InitValue 默认选中', component: importMd('/XM03') },
{ path: '/basic/tips', name: 'Tips 修改提示', component: importMd('/XM04') },
{ path: '/basic/filter', name: 'Filterable 搜索模式', component: importMd('/XM05') },
{ path: '/basic/direction', name: 'Direction 下拉方向', component: importMd('/XM06') },
{ path: '/basic/style', name: 'Style 自定义样式', component: importMd('/XM07') },
{ path: '/basic/paging', name: 'Paging 分页', component: importMd('/XM08') },
{ path: '/basic/radio', name: 'Radio 单选', component: importMd('/XM09') },
{ path: '/basic/repeat', name: 'Repeat 重复选', component: importMd('/XM10') },
{ path: '/basic/prop', name: 'Prop 自定义属性', component: importMd('/XM11') },
{ path: '/basic/theme', name: 'Theme 主题', component: importMd('/XM12') },
{ path: '/basic/show', name: 'Show 显示与隐藏', component: importMd('/XM13') },
{ path: '/basic/model', name: 'Model 显示方式', component: importMd('/XM14') },
{ path: '/basic/template', name: 'Template 构建选项', component: importMd('/XM15') },
{ path: '/basic/on', name: 'On 监听选择', component: importMd('/XM16') },
{ path: '/basic/test', name: 'Test 性能测试', component: importMd('/XM17') },
{ path: '/basic/max', name: 'Max 多选上限', component: importMd('/XM18') },
{ path: '/basic/toolbar', name: 'Toolbar 工具条', component: importMd('/XM19') },
{ path: '/basic/showCount', name: 'ShowCount 选项数量', component: importMd('/XM20') },
{ path: '/basic/optgroup', name: 'Optgroup 分组', component: importMd('/XM21') },
{ path: '/basic/autoRow', name: 'AutoRow 自动换行', component: importMd('/XM22') },
{ path: '/basic/hiddenIcon',name: 'HiddenIcon 隐藏图标', component: importMd('/XM23') },
{ path: '/basic/size', name: 'Size 尺寸', component: importMd('/XM24') },
{ path: '/basic/warning', name: 'Warning 警告', component: importMd('/XM25') },
{ path: '/basic/disabled', name: 'Disabled 禁用', component: importMd('/XM26') },
{ path: '/basic/create', name: 'Create 创建条目', component: importMd('/XM27') },
]
}, {
path: '/example-custom',
path: '/c',
name: '进阶示例',
redirect: '/example-custom/ZM01',
redirect: '/senior/getValue',
component: Component,
children: [
{ path: '/example-custom/ZM01', name: '赋值与取值', component: importMd('/ZM01') },
{ path: '/example-custom/ZM02', name: '表单提交', component: importMd('/ZM02') },
{ path: '/example-custom/ZM03', name: '表格中多选', component: importMd('/ZM03') },
{ path: '/example-custom/ZM04', name: '远程搜索', component: importMd('/ZM04') },
{ path: '/example-custom/ZM05', name: '动态数据', component: importMd('/ZM05') },
{ 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: '/senior/getValue', name: '赋值与取值', component: importMd('/ZM01') },
{ path: '/senior/form', name: '表单提交', component: importMd('/ZM02') },
{ path: '/senior/table', name: '表格中多选', component: importMd('/ZM03') },
{ path: '/senior/remoteSearch', name: '远程搜索', component: importMd('/ZM04') },
{ path: '/senior/update', name: '动态数据', component: importMd('/ZM05') },
{ path: '/senior/alert', name: '弹框中的多选', component: importMd('/ZM06') },
{ path: '/senior/get', name: '获取实例对象', component: importMd('/ZM07') },
{ path: '/senior/batch', name: '批量操作', component: importMd('/ZM08') },
]
}, {
path: '/example-plugin',
path: '/plugin',
name: '拓展中心',
redirect: '/example-plugin/ZP01',
redirect: '/plugin/customer',
component: Component,
children: [
{ path: '/example-plugin/ZP01', name: '下拉自定义', component: importMd('/ZP01') },
{ path: '/example-plugin/ZP02', name: '下拉树 Tree', component: importMd('/ZP02') },
{ path: '/example-plugin/ZP03', name: '下拉日期多选', component: importMd('/ZP03') },
{ path: '/example-plugin/ZP04', name: '下拉折叠面板', component: importMd('/ZP04') },
{ path: '/example-plugin/ZP05', name: '下拉穿梭框', component: importMd('/ZP05') },
{ path: '/plugin/customer', name: '下拉自定义', component: importMd('/ZP01') },
{ path: '/plugin/tree', name: '下拉树 Tree', component: importMd('/ZP02') },
{ path: '/plugin/laydate', name: '下拉日期多选', component: importMd('/ZP03') },
{ path: '/plugin/panel', name: '下拉折叠面板', component: importMd('/ZP04') },
{ path: '/plugin/transfer', name: '下拉穿梭框', component: importMd('/ZP05') },
]
}, {
path: '/question',
@ -113,8 +113,8 @@ export default [{
}, {
path: '/test',
name: '测试',
// hidden: true,
hidden: false,
hidden: true,
// hidden: false,
component: importMd('/ZTEST'),
},

View File

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

View File

@ -33,13 +33,16 @@ class Framework extends Component{
}
init(props, refresh){
let { data, prop, initValue } = props, sels;
let { data, prop, initValue, radio } = props, sels;
//如果新数据和旧数据不同 或者 强制刷新 才进行数据处理
if(refresh){
let dataObj = {};
let flatData = [];
this.load(data, dataObj, flatData);
sels = initValue ? this.exchangeValue(initValue, true, dataObj) : Object.values(dataObj).filter(item => item[prop.selected] === true).filter(item => item[prop.optgroup] !== true)
sels = this.exchangeValue(initValue ? initValue : Object.values(dataObj).filter(item => item[prop.selected] === true), dataObj)
if(radio && sels.length > 1){
sels = sels.slice(0, 1)
}
this.setState({ sels, dataObj, flatData });
}
@ -48,8 +51,12 @@ class Framework extends Component{
return sels;
}
exchangeValue(arr, filterGroup = true, dataObj = this.state.dataObj){
exchangeValue(arr, dataObj = this.state.dataObj){
let list = arr.map(sel => typeof sel === 'object' ? sel : dataObj[sel]).filter(a => a)
let filterGroup = true, { tree } = this.props;
if(tree.show && tree.strict === false){
filterGroup = false;
}
filterGroup && (list = list.filter(item => item[this.props.prop.optgroup] !== true))
return list;
}
@ -60,7 +67,7 @@ class Framework extends Component{
}
const { prop, tree } = this.props;
let changeData = this.exchangeValue(sels, !tree.show);
let changeData = this.exchangeValue(sels);
if(tree.show && tree.strict){
let data = this.state.data;
this.clearAndReset(data, changeData);

View File

@ -1,5 +1,7 @@
import { h, Component, render } from 'preact'
import { isFunction, isArray, safety, deepMerge, mergeArr, IEVersion } from '@/common/util'
const emptyVal = {};
/**
* 普通的多选渲染
@ -14,16 +16,19 @@ class General extends Component{
remote: true,
loading: false,
pageIndex: 1,
totalSize: 0,
totalSize: 0,
val: emptyVal,
});
this.searchCid = 0;
this.inputOver = true;
this.__value = '';
this.__value = '';
this.tempData = [];
}
optionClick(item, selected, disabled, e){
this.props.ck(item, selected, disabled);
this.props.ck(item, selected, disabled);
this.focus();
//阻止父组件上的事件冒泡
this.blockClick(e);
}
@ -129,12 +134,54 @@ class General extends Component{
}
}
//键盘事件
keydown(e){
let keyCode = e.keyCode;
const { value, optgroup, disabled } = this.props.prop;
let data = this.tempData.filter(item => !item[optgroup] && !item[disabled]);
let len = data.length - 1;
if(len === -1){
return ;
}
let index = data.findIndex(item => item[value] === this.state.val);
//Up 键
if(keyCode === 38){
if(index <= 0){
index = len
}else if(index > 0){
index -= 1;
}
let val = data[index][value];
this.setState({ val })
}else
//Down 键
if(keyCode === 40){
if(index === -1 || index === len){
index = 0
}else if(index < len){
index += 1;
}
let val = data[index][value];
this.setState({ val })
}else
//Enter 键
if(keyCode === 13){
if(this.state.val != emptyVal){
let option = data[index];
this.optionClick(option, this.props.sels.findIndex(item => item[value] === this.state.val) != -1, option[disabled], e)
}
}
}
//组件将要接收新属性
componentWillReceiveProps(props){
if(this.props.show != props.show){
if(!props.show){
//清空输入框的值
this.setState({ filterValue: '' });
this.setState({ filterValue: '', val: emptyVal });
this.__value = '';
this.searchInputRef && (this.searchInputRef.value = '');
}else{
@ -149,7 +196,8 @@ class General extends Component{
const { name, value, disabled, children, optgroup } = prop;
let arr = deepMerge([], flatData), creator;
let arr = deepMerge([], flatData), creator;
//是否开启了搜索
if(filterable){
if(remoteSearch){//是否进行远程搜索
@ -194,8 +242,8 @@ class General extends Component{
);
//如果是分组模式, 要分页先去除分组, 然后在计算分页, 最后再加上分组
let groups = [], groupInfo = {};
groups = arr.filter(item => item[optgroup]).forEach(g => {
let groupInfo = {};
arr.filter(item => item[optgroup]).forEach(g => {
g[children].forEach(item => groupInfo[item[value]] = g);
});
arr = arr.filter(item => !item[optgroup]);
@ -247,16 +295,20 @@ class General extends Component{
}
}
let newArr = [], group;
let newArr = [], group, tmpGroup = { __tmp: true };
tmpGroup[optgroup] = true;
arr.forEach(item => {
let g = groupInfo[item[value]];
if(g != group){
if(group && !g){
g = tmpGroup
}
if(g != group){
group = g;
newArr.push(group);
g && newArr.push(group);
}
newArr.push(item);
});
arr = newArr;
arr = newArr;
//查看是否创建了条目
if(creator){
@ -333,6 +385,9 @@ class General extends Component{
if(!showIcon && selected){
itemStyle.backgroundColor = theme.color;
item[disabled] && (itemStyle.backgroundColor = '#C2C2C2');
}
if(item[value] === this.state.val){
itemStyle.backgroundColor = '#f2f2f2'
}
const className = ['xm-option', (item[disabled] ? ' disabled' : ''), (selected ? ' selected' : ''), (showIcon ? 'show-icon' : 'hide-icon') ].join(' ');
const iconClass = ['xm-option-icon xm-iconfont', radio ? 'xm-icon-danx' : 'xm-icon-duox'].join(' ');
@ -347,9 +402,12 @@ class General extends Component{
const renderGroup = item => {
const isGroup = item[optgroup];
if(isGroup){//分组模式
return (
<div class="xm-group">
if(isGroup){//分组模式
if(item.__tmp){
return <div class="item--divided"></div>
}
return (
<div class="xm-group">
<div class="xm-group-item" onClick={ this.groupClick.bind(this, item) }>{ item[name] }</div>
</div>
)
@ -388,6 +446,7 @@ class General extends Component{
input.addEventListener('compositionupdate', this.handleComposition.bind(this));
input.addEventListener('compositionend', this.handleComposition.bind(this));
input.addEventListener('input', this.searchInput.bind(this));
input.addEventListener('keydown', this.keydown.bind(this));
this.searchInputRef = input;
}
}

View File

@ -1,4 +1,5 @@
import { h, Component, render } from 'preact'
import { deepMerge, isFunction } from '@/common/util'
class Tree extends Component{
@ -242,11 +243,46 @@ class Tree extends Component{
}
let arr = data.map(item => renderGroup(item, 10 - tree.indent)).filter(a => a);
let safetyArr = deepMerge([], arr);
let safetySels = deepMerge([], sels);
if(!arr.length){
//查看无数据情况下是否显示分页
arr.push(<div class="xm-select-empty">{ empty }</div>)
}
//工具条操作
const toolbar = (
<div class='xm-toolbar'>
{ config.toolbar.list.map(tool => {
const toolClass = 'toolbar-tag';
let info, name = config.languageProp.toolbar[tool];
if(tool === 'ALL'){
info = { icon: 'xm-iconfont xm-icon-quanxuan', name, method: (pageData) => {
} };
}else if(tool === 'CLEAR'){
info = { icon: 'xm-iconfont xm-icon-qingkong', name, method: (pageData) => {
} };
}else if(tool === 'REVERSE'){
info = { icon: 'xm-iconfont xm-icon-fanxuan', name, method: (pageData) => {
} };
}else {
info = tool
}
const hoverChange = e => {
if(e.type === 'mouseenter') e.target.style.color = theme.color;
if(e.type === 'mouseleave') e.target.style.color = '';
}
return (<div class={ toolClass } onClick={ () => {
isFunction(info.method) && info.method(safetyArr, safetySels)
} } onMouseEnter={ hoverChange } onMouseLeave={ hoverChange }>
{ config.toolbar.showIcon && <i class={ info.icon }></i> }
<span>{ info.name }</span>
</div>)
}).filter(a => a) }
</div>
)
const search = (
<div class='xm-search'>
@ -255,8 +291,14 @@ class Tree extends Component{
</div>
);
if(!arr.length){
//查看无数据情况下是否显示分页
arr.push(<div class="xm-select-empty">{ empty }</div>)
}
return (
<div onClick={ this.blockClick } class="xm-body-tree" >
{ config.toolbar.show && toolbar }
{ filterable && search }
<div class="scroll-body" style={ {maxHeight: config.height} }>{ arr }</div>
</div>

View File

@ -475,6 +475,12 @@ xm-select{
background-color: #FFF;
}
.item--divided{
border-top: 1px solid #ebeef5;
width: calc(100% - 20px);
cursor: initial;
}
}
//不同尺寸下的数据调整
@ -519,6 +525,9 @@ xm-select{
top: @size / 2 - 1px;
}
}
.item--divided{
margin: @size / 4;
}
}
xm-select[size='large']{
.mixin(40px)