This commit is contained in:
maplemei 2020-05-04 18:56:31 +08:00
parent 308e0b64e1
commit 1ae32b8ed1
15 changed files with 176 additions and 98 deletions

View File

@ -1,5 +1,22 @@
## 更新日志 ## 更新日志
### 1.1.9
*2020-05-04*
#### 新增
- tree新增配置`simple`, 代表极简模式, 子级全部被选中后只会显示父级
#### Bug fixes
- 设置远程模式`totalSize`默认为1
- 修复普通多选模式下设置`max`配置后, 工具条的全选和反选 选中数据错误
- 修复`getValue`方法获取到的部分数据中携带`__node`参数无法进行序列化
- 修复同时开启远程搜索和远程分页的时候会出发两次`remoteMethod`
- 优化`remoteMethod`的内部回调机制
### 1.1.8 ### 1.1.8
*2020-02-10* *2020-02-10*

4
dist/static/2.js vendored

File diff suppressed because one or more lines are too long

4
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

4
dist/xm-select.js vendored

File diff suppressed because one or more lines are too long

View File

@ -18,6 +18,8 @@ tree: {
expandedKeys: [], expandedKeys: [],
//是否严格遵守父子模式 //是否严格遵守父子模式
strict: true, strict: true,
//是否开启极简模式
simple: false,
}, },
``` ```
@ -32,6 +34,7 @@ tree: {
<input type="checkbox" name="showFolderIcon" lay-filter="showFolderIcon" lay-skin="primary" title="是否展示三角图标" checked> <input type="checkbox" name="showFolderIcon" lay-filter="showFolderIcon" lay-skin="primary" title="是否展示三角图标" checked>
<input type="checkbox" name="showLine" lay-filter="showLine" lay-skin="primary" title="是否显示虚线" checked> <input type="checkbox" name="showLine" lay-filter="showLine" lay-skin="primary" title="是否显示虚线" checked>
<input type="checkbox" name="strict" lay-filter="strict" lay-skin="primary" title="严格父子结构" checked> <input type="checkbox" name="strict" lay-filter="strict" lay-skin="primary" title="严格父子结构" checked>
<input type="checkbox" name="simple" lay-filter="simple" lay-skin="primary" title="极简模式">
</div> </div>
<div style="margin-top: 20px">间距</div> <div style="margin-top: 20px">间距</div>
@ -41,7 +44,7 @@ tree: {
<script> <script>
layui.form.render(); layui.form.render();
['showFolderIcon', 'showLine', 'strict'].forEach(function(key){ ['showFolderIcon', 'showLine', 'strict', 'simple'].forEach(function(key){
layui.form.on('checkbox('+key+')', function(data){ layui.form.on('checkbox('+key+')', function(data){
var treeConfig = {}; var treeConfig = {};
treeConfig[key] = data.elem.checked; treeConfig[key] = data.elem.checked;

View File

@ -8,35 +8,29 @@
var demo1 = xmSelect.render({ var demo1 = xmSelect.render({
el: '#demo1', el: '#demo1',
autoRow: true, autoRow: true,
filterable: true, height: '300px',
cascader: { tree: {
show: true, show: false,
indent: 100, simple: true,
expandedKeys: [-1],
}, },
height: '100px',
toolbar: { toolbar: {
show: true, show: true,
list: ['ALL', 'REVERSE', 'CLEAR'] list: ['ALL', 'REVERSE', 'CLEAR']
}, },
filterable: true, filterable: true,
paging: true,
pageRemote: true,
remoteSearch: true,
remoteMethod(val, cb, show, pageIndex){
cb([
{name: '张三11111111111', value: 1, selected: true, children: []},
{name: '李四1', value: 2, selected: true},
{name: '王五1', value: 3, disabled: false},
])
},
data(){ data(){
return [ return []
{name: '销售员', value: -1, disabled: true, children: [
{name: '张三11111111111', value: 1, selected: true, children: []},
{name: '李四1', value: 2, selected: true},
{name: '王五1', value: 3, disabled: true},
]},
{name: '奖品', value: -2, children: [
{name: '奖品3333333333', value: -3, children: [
{name: '苹果3', value: 14, selected: true},
{name: '香蕉3', value: 15},
{name: '葡萄3', value: 16},
]},
{name: '苹果2', value: 4, selected: true, disabled: true},
{name: '香蕉2', value: 5},
{name: '葡萄2', value: 6},
]},
]
} }
}) })

View File

@ -172,8 +172,9 @@ list: [ "ALL", "CLEAR",
| showFolderIcon | 是否显示节点前的三角图标 | boolean | true / false | true | | showFolderIcon | 是否显示节点前的三角图标 | boolean | true / false | true |
| showLine | 是否显示虚线 | boolean | true / false | true | | showLine | 是否显示虚线 | boolean | true / false | true |
| indent | 间距 | int | - | 20 | | indent | 间距 | int | - | 20 |
| expandedKeys | 默认展开的节点数组, 为true时展开所有节点 | array / boolean | - | [ ] | | expandedKeys | 默认展开的节点数组, 为true时展开所有节点 | array / boolean | - | [ ] |
| strict | 是否遵循严格父子结构 | boolean | true / false | true | | strict | 是否遵循严格父子结构 | boolean | true / false | true |
| simple | 是否开启极简模式 | boolean | true / false | false |
### cascader ### cascader

View File

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

View File

@ -164,3 +164,30 @@ export function exchangeOptionsData(arr, { prop }){
} }
return newArr; return newArr;
} }
export function toSimple(data, sels, list, prop){
if(!data || !isArray(data)){
return;
}
let { children, selected, value } = prop;
data.forEach(item => {
if(item.__node[selected] || sels.find(i => i[value] === item[value])){
list.push(item);
}else{
toSimple(item[children], sels, list, prop);
}
})
}
export function delProp(data, children, props){
if(!data || !isArray(data)){
return;
}
return data.map(item => {
item = { ...item };
props.forEach(prop => delete item[prop]);
item[children] = delProp(item[children], children, props);
return item;
})
}

View File

@ -283,7 +283,6 @@ class Framework extends Component{
if(type === 'data'){ if(type === 'data'){
let changeData = data.filter(item => item[this.props.prop.selected] === true); let changeData = data.filter(item => item[this.props.prop.selected] === true);
this.resetSelectValue(mergeArr(changeData, this.state.sels, this.props.prop), changeData, true); this.resetSelectValue(mergeArr(changeData, this.state.sels, this.props.prop), changeData, true);
let dataObj = {}, flatData = []; let dataObj = {}, flatData = [];
this.load(data, dataObj, flatData); this.load(data, dataObj, flatData);
this.setState({ data, flatData }); this.setState({ data, flatData });

View File

@ -1,5 +1,5 @@
import { h, Component, render } from 'preact' import { h, Component, render } from 'preact'
import { isFunction } from '@/common/util' import { isFunction, toSimple } from '@/common/util'
/** /**
* 标签的渲染 * 标签的渲染
@ -43,7 +43,7 @@ class Label extends Component{
} }
render(config) { render(config) {
const { data, prop, theme, model, sels, autoRow } = config; const { data, prop, theme, model, sels, autoRow, tree } = config;
const { name, disabled } = prop; const { name, disabled } = prop;
//获取配置项 //获取配置项
@ -51,17 +51,24 @@ class Label extends Component{
const type = label.type; const type = label.type;
const conf = label[type]; const conf = label[type];
let list = sels;
//树结构开启极简显示
if(tree.show && tree.strict && tree.simple){
list = []
toSimple(data, sels, list, prop);
}
//渲染结果 //渲染结果
let html = '', innerHTML = true; let html = '', innerHTML = true;
//悬浮显示已选择 //悬浮显示已选择
let title = sels.map(item => item[name]).join(',') let title = list.map(item => item[name]).join(',')
if(type === 'text'){ if(type === 'text'){
html = sels.map(sel => `${conf.left}${sel[name]}${conf.right}`).join(conf.separator) html = list.map(sel => `${conf.left}${sel[name]}${conf.right}`).join(conf.separator)
}else if(type === 'block'){ }else if(type === 'block'){
innerHTML = false; innerHTML = false;
//已选择的数据 //已选择的数据
let arr = [...sels]; let arr = [...list];
const style = { backgroundColor: theme.color } const style = { backgroundColor: theme.color }
//显示的个数 //显示的个数
@ -91,10 +98,10 @@ class Label extends Component{
) )
} }
}else{ }else{
if(sels.length && conf && conf.template){ if(list.length && conf && conf.template){
html = conf.template(data, sels); html = conf.template(data, list);
}else{ }else{
html = sels.map(sel => sel[name]).join(',') html = list.map(sel => sel[name]).join(',')
} }
} }

View File

@ -125,13 +125,15 @@ class General extends Component{
this.setState({ loading: true, remote: false }); this.setState({ loading: true, remote: false });
//让输入框失去焦点 //让输入框失去焦点
this.blur(); this.blur();
this.props.remoteMethod(this.state.filterValue, (result, totalSize) => { this.props.remoteMethod(this.state.filterValue, (result, totalSize = 1) => {
//回调后可以重新聚焦 //这里同步修改为异步
this.focus(); setTimeout(() => {
//回调后可以重新聚焦
this.callback = true; this.focus();
this.setState({ loading: false, totalSize }); this.callback = true;
this.props.onReset(result, 'data'); this.setState({ loading: false, totalSize });
this.props.onReset(result, 'data');
}, 10);
}, this.props.show, pageIndex); }, this.props.show, pageIndex);
} }
} }
@ -215,46 +217,42 @@ class General extends Component{
} }
render(config) { render(config) {
let { data, flatData, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips, create, pageRemote } = config let { data, flatData, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips, create, pageRemote, max } = config
const { name, value, disabled, children, optgroup } = prop; const { name, value, disabled, children, optgroup } = prop;
let arr = deepMerge([], flatData), creator; let arr = deepMerge([], flatData), creator;
//是否开启了搜索 //远程分页 或者 远程搜索
if(filterable){ if(pageRemote || filterable && remoteSearch){
if(remoteSearch){//是否进行远程搜索 this.postData();
this.postData();
}else{
const filterData = (item, index) => {
const isGroup = item[optgroup];
if(isGroup){
delete item.__del;
return true;
}
return filterMethod(this.state.filterValue, item, index, prop);
}
arr = arr.filter(filterData);
for(let i = 0; i < arr.length - 1; i++){
let a = arr[i];
let b = arr[i + 1];
if(a[optgroup] && b[optgroup]){
arr[i].__del = true;
}
}
if(arr.length && arr[arr.length - 1][optgroup]){
arr[arr.length - 1].__del = true;
}
arr = arr.filter(item => !item.__del);
//创建条目
creator = this.state.filterValue && isFunction(create);
}
} }
//远程分页 //本地搜索
if(pageRemote){ if(filterable && !remoteSearch){
this.postData(); const filterData = (item, index) => {
const isGroup = item[optgroup];
if(isGroup){
delete item.__del;
return true;
}
return filterMethod(this.state.filterValue, item, index, prop);
}
arr = arr.filter(filterData);
for(let i = 0; i < arr.length - 1; i++){
let a = arr[i];
let b = arr[i + 1];
if(a[optgroup] && b[optgroup]){
arr[i].__del = true;
}
}
if(arr.length && arr[arr.length - 1][optgroup]){
arr[arr.length - 1].__del = true;
}
arr = arr.filter(item => !item.__del);
//创建条目
creator = this.state.filterValue && isFunction(create);
} }
const search = ( const search = (
@ -355,7 +353,19 @@ class General extends Component{
info = { icon: 'xm-iconfont xm-icon-quanxuan', name, method: (pageData) => { info = { icon: 'xm-iconfont xm-icon-quanxuan', name, method: (pageData) => {
const { optgroup, disabled } = prop; const { optgroup, disabled } = prop;
const list = pageData.filter(item => !item[optgroup]).filter(item => !item[disabled]) const list = pageData.filter(item => !item[optgroup]).filter(item => !item[disabled])
this.props.onReset(radio ? list.slice(0, 1) : mergeArr(list, sels, prop), 'sels');
const disSels = sels.filter(item => item[prop.disabled]);
let result = [];
//单选的处理
if(radio){
result = disSels.length ? disSels : list.slice(0, 1)
}else if(max > 0){
result = disSels.length >= max ? disSels : mergeArr(list.slice(0, max - disSels.length), disSels, prop)
}else{
result = mergeArr(list, sels, prop)
}
this.props.onReset(result, 'sels');
} }; } };
}else if(tool === 'CLEAR'){ }else if(tool === 'CLEAR'){
info = { icon: 'xm-iconfont xm-icon-qingkong', name, method: (pageData) => { info = { icon: 'xm-iconfont xm-icon-qingkong', name, method: (pageData) => {
@ -375,7 +385,19 @@ class General extends Component{
list.splice(index, 1); list.splice(index, 1);
} }
}) })
this.props.onReset(radio ? selectedList.slice(0, 1) : mergeArr(list, selectedList, prop), 'sels');
const disSels = selectedList.filter(item => item[prop.disabled]);
let result = [];
//单选的处理
if(radio){
result = disSels.length ? disSels : list.slice(0, 1)
}else if(max > 0){
result = disSels.length >= max ? disSels : mergeArr(list.slice(0, max - disSels.length), disSels, prop)
}else{
result = mergeArr(list, selectedList, prop)
}
this.props.onReset(result, 'sels');
} }; } };
}else { }else {
info = tool info = tool

View File

@ -1,6 +1,6 @@
import { h, Component, render } from 'preact' import { h, Component, render } from 'preact'
import { datas, optionData, childData } from '@/index.js'; import { datas, optionData, childData } from '@/index.js';
import { warn, listenerClose, isArray, deepMerge, exchangeOptionsData } from '@/common/util' import { warn, listenerClose, isArray, deepMerge, exchangeOptionsData, toSimple, delProp } from '@/common/util'
import Framework from '@/components/framework' import Framework from '@/components/framework'
import defaultOptions from '@/config/options' import defaultOptions from '@/config/options'
@ -84,11 +84,17 @@ class xmOptions {
* 获取多选选中的数据 * 获取多选选中的数据
*/ */
getValue(type){ getValue(type){
let arr = childData[this.options.el].state.sels.map(item => { const { tree, prop, data } = this.options;
item = { ...item }; let sels = childData[this.options.el].state.sels;
delete item.__node; let list = sels;
return item;
}); //树结构开启极简显示
if(tree.show && tree.strict && tree.simple){
list = []
toSimple(data, sels, list, prop);
}
let arr = delProp(list, prop.children, [ '__node' ]);;
if(type === 'name'){ if(type === 'name'){
return arr.map(item => item[this.options.prop.name]); return arr.map(item => item[this.options.prop.name]);

View File

@ -101,6 +101,8 @@ export default function (lan = 'zn') {
lazy: false, lazy: false,
//懒加载回调 //懒加载回调
load: null, load: null,
//是否开启极简模式
simple: false,
}, },
//级联结构 //级联结构
cascader: { cascader: {