From 9416e64e7e4eb5430eddbdaa9125adc325ead3a7 Mon Sep 17 00:00:00 2001 From: maplemei Date: Wed, 16 Oct 2019 23:22:54 +0800 Subject: [PATCH] v1.0.8 --- CHANGELOG.md | 23 +++++++ dist/static/2.js | 2 +- dist/static/3.js | 2 +- dist/static/docs.js | 4 +- dist/xm-select.js | 2 +- docs/assets/common.less | 9 ++- docs/mds/XM05.md | 28 ++++++++- docs/mds/XM16.md | 16 +++-- docs/mds/XM21.md | 35 +++++++++++ docs/mds/ZM01.md | 18 +++++- docs/mds/ZM03.md | 14 ++++- docs/mds/ZM04.md | 35 +++++++++++ docs/mds/ZM05.md | 56 +++++++++++++++++ docs/mds/ZM06.md | 33 ++++++++++ docs/mds/options.md | 42 +++++++++++-- docs/router.js | 5 +- src/components/common/util.js | 24 ++++++++ src/components/config/options.js | 1 + src/components/core/index.js | 31 ++++++++-- src/components/element/framework.js | 80 ++++++++++++++++++++----- src/components/element/model/general.js | 42 ++++++++++--- src/style/index.less | 11 +--- 22 files changed, 448 insertions(+), 65 deletions(-) create mode 100644 docs/mds/ZM04.md create mode 100644 docs/mds/ZM05.md create mode 100644 docs/mds/ZM06.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e9cc3..a09ca60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ ## 更新日志 +### 1.0.8 + +*2019-10-16* + +#### 兼容提示 + +- 此版本的on方法结构调整, 升级请注意 + +#### 新增 + +- 新增分组单击事件 click, 可选值 `SELECT`, `CLEAR`, `AUTO`, `自定义` +- 新增`append`方法追加赋值, `delete`方法删除赋值 +- 新增搜索完成回调`filterDone` + +#### Bug fixes + +- 修复全选和请空不走on监听的问题 +- 修复`autoRow`模式下, 无选项时的css样式错误 +- 修复`update`后, 下拉框显示状态被重置为隐藏 +- 优化`setValue`方法, 可自行判断下拉框的显示状态 +- 修复文档错误, 实例没有`render`方法 + + ### 1.0.7 *2019-10-16* diff --git a/dist/static/2.js b/dist/static/2.js index 2d5063a..d0fa844 100644 --- a/dist/static/2.js +++ b/dist/static/2.js @@ -1 +1 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{361:function(e,t,n){e.exports=n.p+"static/wx.f391ad4.jpg"},363:function(e,t,n){"use strict";n.r(t);var a=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("section",{staticClass:"content element-doc"},[e._m(0),e._m(1),n("demo-block",[n("template",{slot:"source"},[n("element-demo0")],1),n("template",{slot:"highlight"},[n("pre",{pre:!0},[n("code",{pre:!0,attrs:{class:"html"}},[e._v("
\n\n ``` ::: + + +### 搜索完成回调 + +:::demo +```html +
+ + +``` +::: diff --git a/docs/mds/XM16.md b/docs/mds/XM16.md index fefe105..ca7ff65 100644 --- a/docs/mds/XM16.md +++ b/docs/mds/XM16.md @@ -12,8 +12,8 @@ ``` ::: + + +### 分组的单击事件 + +:::demo +```html +
+ + +``` +::: diff --git a/docs/mds/ZM01.md b/docs/mds/ZM01.md index 8d5f485..5f5be95 100644 --- a/docs/mds/ZM01.md +++ b/docs/mds/ZM01.md @@ -8,9 +8,13 @@ :::demo ```html
+

- + +

+ +

 
 
+```
+:::
diff --git a/docs/mds/ZM05.md b/docs/mds/ZM05.md
new file mode 100644
index 0000000..41d1a35
--- /dev/null
+++ b/docs/mds/ZM05.md
@@ -0,0 +1,56 @@
+## 动态数据
+
+
+### 本地数据动态赋值
+
+:::demo 
+```html
+
+ + +``` +::: + + +### 远程数据动态赋值 + +:::demo +```html +
+ + +``` +::: diff --git a/docs/mds/ZM06.md b/docs/mds/ZM06.md new file mode 100644 index 0000000..c82ea3b --- /dev/null +++ b/docs/mds/ZM06.md @@ -0,0 +1,33 @@ +## 远程搜索 + + +### layer弹出框 + +:::demo +```html + + + +``` +::: diff --git a/docs/mds/options.md b/docs/mds/options.md index 5605159..d8aabbe 100644 --- a/docs/mds/options.md +++ b/docs/mds/options.md @@ -15,6 +15,7 @@ | searchTips | 搜索提示 | string | - | 请选择 | | delay | 搜索延迟 ms | int | - | 500 | | filterMethod | 搜索回调函数 | function(val, item, index, prop) val: 当前搜索值, item: 每个option选项, index: 位置数据中的下标, prop: 定义key | - | - | +| filterDone | 搜索完成函数 | function(val) val: 当前搜索值 | - | - | | remoteSearch | 是否开启自定义搜索 (远程搜索)| boolean | true / false | false | | remoteMethod | 自定义搜索回调函数 | function(val, cb, show) val: 当前搜索值, cb: 回调函数, 需要回调一个数组, 结构同data, show: 下拉框显示状态 | - | - | | direction | 下拉方向| string | auto / up / down | auto | @@ -31,7 +32,7 @@ | show | 展开下拉的回调 | function | - | - | | hide | 隐藏下拉的回调 | function | - | - | | template | 自定义渲染选项 | function({ item, sels, name, value }) | - | - | -| on | 监听选中变化 | function({ arr, item, selected }) | - | - | +| on | 监听选中变化 | function({ arr, change, isAdd }) | - | - | | max | 设置多选选中上限 | int | - | 0 | | maxMethod | 达到选中上限的回到 | function(sels, item), sels: 已选中数据, item: 当前选中的值 | - | - | | name | 表单提交时的name | string | - | select | @@ -45,13 +46,41 @@ | 参数 | 说明 | 类型 | 可选值 | 默认值 | | ----------------- | ------------------------------ | --------------- | ------ | ------ | | name | 显示名称 | string | - | name | -| value | 选中值 | string | - | value | +| value | 选中值, 当前多选唯一 | string | - | value | | selected | 是否选中 | string | - | selected | | disabled | 是否禁用 | string | - | disabled | | children | 分组children | string | - | children | | optgroup | 分组optgroup | string | - | optgroup | +### 分组说明 + +如果children属性为数组, 或者optgroup=true的时候开启分组模式 + +``` +//平级结构下面的数据为一组 +{name: '城市', optgroup: true}, + +//children下的数组为一组 +{name: '销售员', children: [ + {name: '李四', value: 4, selected: true}, + {name: '王五', value: 5}, +]}, + +//可在分组上定义click属性, 来定义点击事件 +//这里以optgroup模式为例, children模式同理 +{name: '选中', optgroup: true, click: 'SELECT'}, +{name: '清空', optgroup: true, click: 'CLEAR'}, +{name: '自动', optgroup: true, click: 'AUTO'}, +{name: '自定义', optgroup: true, click: function(item){ + alert('自定义的, 想干嘛干嘛'); +}}, + + + +``` + + ### theme | 参数 | 说明 | 类型 | 可选值 | 默认值 | @@ -109,7 +138,7 @@ model: { | showIcon | 是否显示工具图标 | boolean | true / false | true | | list | 工具条数组 (默认有 全选/清空, 可以自定义) | array | - | [ "ALL", "CLEAR" ] | -- 自定义方式 +> 自定义方式 ``` @@ -141,9 +170,10 @@ xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用 | 事件名 | 说明 | 参数 | | ------ | ------------------ | -------- | | getValue | 获取当前选中的数据 | (type: 类型), 可选值: name, nameStr, value, valueStr | -| setValue | 动态设置数据 | (array: 选中的数据, show: 是否展开下拉) | +| setValue | 动态设置数据 | (array: 选中的数据, show: 是否展开下拉, 不传默认当前显示状态, 取值: true/false) | +| append | 追加赋值 | (array: 追加的数据) | +| delete | 删除赋值 | (array: 删除的数据) | | opened | 主动展开下拉 | - | | closed | 主动关闭下拉 | - | -| render | 重新渲染多选 | (options: 见配置项) | | reset | 重置为上一次的render状态 | - | -| update | 更新多选选中, reset不保留 | - | +| update | 更新多选选中, reset不保留 | (options: 见配置项) | diff --git a/docs/router.js b/docs/router.js index 5d426d7..874d702 100644 --- a/docs/router.js +++ b/docs/router.js @@ -78,7 +78,10 @@ export default [{ 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/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/ZTEST', name: '测试', component: importMd('/ZTEST') }, ] }, { diff --git a/src/components/common/util.js b/src/components/common/util.js index ffebe3c..2707ad0 100644 --- a/src/components/common/util.js +++ b/src/components/common/util.js @@ -169,3 +169,27 @@ export function findSelected(arr, data, prop){ } }); } + +export function addGroupLabel(arr, prop){ + const { disabled, children, optgroup, value } = prop; + let group; + for(let i = 0; i < arr.length; i++){ + let item = arr[i]; + if(item[optgroup]){ + group = item; + group.__value = []; + continue; + } + let child = item[children]; + if(child && child.length > 0){ + group = null; + item.__value = child.filter(c => !c[disabled]).map(c => c[value]); + continue; + } + if(!group || item[disabled]){ + continue; + } + group.__value.push(item[value]); + } + return arr; +} diff --git a/src/components/config/options.js b/src/components/config/options.js index 3c32f05..e1e5824 100644 --- a/src/components/config/options.js +++ b/src/components/config/options.js @@ -71,6 +71,7 @@ export default function (lan = 'zn') { disabled: 'disabled', children: 'children', optgroup: 'optgroup', + click: 'click', }, //主题配置 theme: { diff --git a/src/components/core/index.js b/src/components/core/index.js index d9a3bb6..4b76439 100644 --- a/src/components/core/index.js +++ b/src/components/core/index.js @@ -94,7 +94,7 @@ class xmOptions { */ getValue(type){ let arr = safety(childs[this.options.el].state.sels); - + if(type === 'name'){ return arr.map(item => item[this.options.prop.name]); }else @@ -107,7 +107,7 @@ class xmOptions { if(type === 'valueStr'){ return arr.map(item => item[this.options.prop.value]).join(','); } - + return arr; } @@ -119,10 +119,33 @@ class xmOptions { warn('请传入数组结构...') return ; } - childs[this.options.el].value(sels, !!show); + childs[this.options.el].value(sels, show); return this; } - + + /** + * 追加赋值 + */ + append(sels){ + if(!isArray(sels)){ + warn('请传入数组结构...') + return ; + } + childs[this.options.el].append(sels); + return this; + } + + /** + * 删除赋值 + */ + delete(sels){ + if(!isArray(sels)){ + warn('请传入数组结构...') + return ; + } + childs[this.options.el].del(sels); + return this; + } } diff --git a/src/components/element/framework.js b/src/components/element/framework.js index 8f24fff..c915c05 100644 --- a/src/components/element/framework.js +++ b/src/components/element/framework.js @@ -24,7 +24,7 @@ class Framework extends Component{ //用于多选上限的边框颜色变化 this.updateBorderColor(''); this.resetDate(props.data); - this.value(props.initValue ? props.initValue : this.findValue(this.state.data), false); + this.value(props.initValue ? props.initValue : this.findValue(this.state.data), !!this.state.show); } findValue(data){ @@ -33,22 +33,58 @@ class Framework extends Component{ return list; } + resetSelectValue(sels = [], change = [], isAdd){ + let on = this.props.on; + if(isFunction(on)){ + on({ arr: sels, change, isAdd }); + } + this.setState({ sels }); + } + resetDate(data = []){ this.setState({ data }); } value(sels, show){ - let data = this.state.data; - let value = this.props.prop.value; + if(show !== false && show !== true){ + show = this.state.show; + } + let changeData = this.exchangeValue(sels); + this.resetSelectValue(changeData, changeData, true); + this.setState({ show }) + } + exchangeValue(sels){ + let data = this.state.data; + let value = this.props.prop.value; let list = []; filterGroupOption(list, data, this.props.prop); - this.setState({ - sels: sels.map(sel => typeof sel === 'object' ? sel[value] : sel).map(val => list.find(item => item[value] == val)).filter(a => a), - //下拉框是否展开 - show, - }) - } + return sels.map(sel => typeof sel === 'object' ? sel[value] : sel).map(val => list.find(item => item[value] == val)).filter(a => a); + } + + append(arr){ + let changeData = this.exchangeValue(arr); + this.resetSelectValue(mergeArr(changeData, this.state.sels, this.props.prop), changeData, true); + } + + del(arr){ + let value = this.props.prop.value; + let sels = this.state.sels; + arr = this.exchangeValue(arr); + arr.forEach(v => { + let index = sels.findIndex(item => item[value] === v[value]); + if(index != -1){ + sels.splice(index, 1); + } + }); + this.resetSelectValue(sels, arr, false); + } + + auto(arr){ + let value = this.props.prop.value; + let sels = arr.filter(v => this.state.sels.findIndex(item => item[value] === v) != -1); + sels.length == arr.length ? this.del(arr) : this.append(arr); + } updateBorderColor(tmpColor){ this.setState({ tmpColor }); @@ -57,11 +93,25 @@ class Framework extends Component{ onReset(data, type){ //重置数据 if(type === 'data'){ - this.setState({ sels: mergeArr(this.findValue(data), this.state.sels, this.props.prop), data }); + let changeData = this.findValue(data); + this.resetSelectValue(mergeArr(changeData, this.state.sels, this.props.prop), changeData, true); + this.setState({ data }); }else //重置选中数据 if(type === 'sels'){ - this.setState({ sels: data }); + this.resetSelectValue(data, data, true); + }else + //追加数据 + if(type === 'append'){ + this.append(data); + }else + //清理数据 + if(type === 'delete'){ + this.del(data); + }else + //自动判断模式 + if(type === 'auto'){ + this.auto(data); } } @@ -157,7 +207,7 @@ class Framework extends Component{ let index = sels.findIndex(sel => sel[valueProp] == item[valueProp]) if(index != -1){ sels.splice(index, 1); - this.setState({ sels }); + this.resetSelectValue(sels, [item], !selected); } }else{ //查看是否设置了多选上限 @@ -173,14 +223,12 @@ class Framework extends Component{ //如果是单选模式 if(radio){ - this.setState({ sels: [item] }); + this.resetSelectValue([item], [item], !selected); }else{ - this.setState({ sels: [...sels, item] }); + this.resetSelectValue([...sels, item], [item], !selected); } } - on && on({ arr: this.state.sels, item, selected: !selected }); - //检查是否为选择即关闭状态, 强制删除情况下不做处理 clickClose && !mandatoryDelete && this.onClick(); }; diff --git a/src/components/element/model/general.js b/src/components/element/model/general.js index cc999a2..98d634d 100644 --- a/src/components/element/model/general.js +++ b/src/components/element/model/general.js @@ -1,5 +1,5 @@ import { h, Component, render } from '@/components/preact' -import { isFunction, isArray, safety, mergeArr, IEVersion, filterGroupOption } from '@/components/common/util' +import { isFunction, isArray, safety, deepMerge, mergeArr, IEVersion, filterGroupOption, addGroupLabel } from '@/components/common/util' /** * 普通的多选渲染 @@ -28,6 +28,21 @@ class General extends Component{ this.blockClick(e); } + groupClick(item, e){ + let m = item[this.props.prop.click]; + if(m === 'SELECT'){ + this.props.onReset(item.__value, 'append'); + }else if(m === 'CLEAR'){ + this.props.onReset(item.__value, 'delete'); + }else if(m === 'AUTO'){ + this.props.onReset(item.__value, 'auto'); + }else if(isFunction(m)){ + m(item); + } + //阻止父组件上的事件冒泡 + this.blockClick(e); + } + blockClick(e){ e.stopPropagation(); } @@ -69,6 +84,7 @@ class General extends Component{ this.searchCid = setTimeout(() => this.setState({ filterValue: this.__value, remote: true, + callback: true, }), this.props.delay); } } @@ -107,13 +123,24 @@ class General extends Component{ } } + componentDidUpdate(){ + if(this.state.callback){ + this.setState({ callback: false }); + + let done = this.props.filterDone; + if(isFunction(done)){ + done(this.state.filterValue); + } + } + } + render(config) { let { data, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips } = config const { name, value, disabled, children, optgroup } = prop; - let arr = safety(data); + let arr = deepMerge([], data); //是否开启了搜索 if(filterable){ if(remoteSearch){//是否进行远程搜索 @@ -232,7 +259,8 @@ class General extends Component{ } } - let safetyArr = safety(arr); + let safetyArr = deepMerge([], arr); + //工具条操作 const toolbar = (
@@ -294,28 +322,28 @@ class General extends Component{
) } + const renderGroup = item => { const isGroup = item[optgroup]; if(isGroup){//分组模式 return (
-
{ item[name] }
+
{ item[name] }
) } - const child = item[children]; if(isArray(child) && child.length > 0){//分组模式 return (
-
{ item[name] }
+
{ item[name] }
{ child.map(renderItem) }
) } return renderItem(item); } - arr = arr.map(renderGroup); + arr = addGroupLabel(arr, prop).map(renderGroup); if(!arr.length){ arr.push( diff --git a/src/style/index.less b/src/style/index.less index d27686c..f86b7e7 100644 --- a/src/style/index.less +++ b/src/style/index.less @@ -68,7 +68,7 @@ xm-select{ & > .xm-tips{ color: #999999; padding: 0 10px; - height: 100%; + position: absolute; } & > .xm-icon{ @@ -116,16 +116,9 @@ xm-select{ .scroll{ .label-content{ - line-height: @heightLabel; - height: calc(100% - 20px); display: flex; - align-items: baseline; - white-space: pre; + line-height: @heightLabel; padding: 3px 30px 3px 10px; - &:after{ - content: '-'; - opacity: 0; - } } }