Compare commits
10 Commits
16de57e340
...
e7e9ee2a65
Author | SHA1 | Date | |
---|---|---|---|
e7e9ee2a65 | |||
|
05dc278d28 | ||
|
170d8ab0d1 | ||
|
8774b1f79c | ||
|
479b26fa7f | ||
|
eb4e6fa66e | ||
|
12b7f0ce0e | ||
|
f8fa44c94c | ||
|
e928c6a038 | ||
|
b47c8cfcb9 |
54
CHANGELOG.md
54
CHANGELOG.md
@ -1,5 +1,59 @@
|
|||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
|
### 1.2.4
|
||||||
|
|
||||||
|
*2021-07-22*
|
||||||
|
|
||||||
|
#### 新增
|
||||||
|
|
||||||
|
- 新增渲染完成的回调`done`
|
||||||
|
- `create`支持一次性创建多个, 返回一个数组即可 [#I40MU0](https://gitee.com/maplemei/xm-select/issues/I40MU0)
|
||||||
|
- 增加预选配置`enableHoverFirst`, 默认选中第一项可选中数据 [#I3ZCRX](https://gitee.com/maplemei/xm-select/issues/I3ZCRX)
|
||||||
|
- 新增focus后的样式提醒
|
||||||
|
- 新增自定义设置选中键盘KeyCode配置`selectedKeyCode`, 可选 `xmSelect.KeyCode.Enter`, `xmSelect.KeyCode.Space`, 也可自行定义数值 [#I41NNI](https://gitee.com/maplemei/xm-select/issues/I41NNI)
|
||||||
|
- 表单验证失败后 滚动到可视范围内 ##灰度##
|
||||||
|
|
||||||
|
#### Bug fixes
|
||||||
|
|
||||||
|
- 修复`tree`非严格模式下, 无法选中父节点
|
||||||
|
- 修复`tree`模式下搜索后无法折叠的问题 [#I3XBF1](https://gitee.com/maplemei/xm-select/issues/I3XBF1)
|
||||||
|
|
||||||
|
|
||||||
|
### 1.2.3
|
||||||
|
|
||||||
|
*2021-06-30*
|
||||||
|
|
||||||
|
#### 新增
|
||||||
|
|
||||||
|
- 新增`submitConversion`配置方法, 用于拓展表单提交数据, 默认是value数组
|
||||||
|
|
||||||
|
#### Bug fixes
|
||||||
|
|
||||||
|
- 修复级联模式下第一组数据过多时不显示滚动条
|
||||||
|
- 修复级联模式下隐藏图标背景色透明的bug
|
||||||
|
- 修复级联模式下如果子节点是空数组也显示右箭头的bug
|
||||||
|
- 修复级联/树模式下,如果子节点是空数组,然后操作选中状态异常
|
||||||
|
- 修复工具条点击清空, `on`监听到的`isAdd`为`true`的bug [#I3T2KE](https://gitee.com/maplemei/xm-select/issues/I3T2KE)
|
||||||
|
- 修复setValue时对多选上限的判断异常 [#I3SABO](https://gitee.com/maplemei/xm-select/issues/I3SABO)
|
||||||
|
|
||||||
|
|
||||||
|
### 1.2.2
|
||||||
|
|
||||||
|
*2021-01-19*
|
||||||
|
|
||||||
|
#### 新增
|
||||||
|
|
||||||
|
- 新增配置 `model.type: fixed`, 切换为`fixed`布局模式 [体验传送门](https://maplemei.gitee.io/xm-select/#/senior/table)
|
||||||
|
- 新增实例方法`calcPosition`, fixed布局模式下重新计算位置
|
||||||
|
|
||||||
|
#### Bug fixes
|
||||||
|
|
||||||
|
- 修改直接设置父节点无法选中的问题
|
||||||
|
- 修改非严格模式下设置父节点, 子节点受影响
|
||||||
|
- 修复渲染失败页面监听错误的问题
|
||||||
|
- 修改数据重复时分组错乱的问题
|
||||||
|
|
||||||
|
|
||||||
### 1.2.1
|
### 1.2.1
|
||||||
|
|
||||||
*2020-11-27*
|
*2020-11-27*
|
||||||
|
@ -26,11 +26,14 @@
|
|||||||
|
|
||||||
> 联系方式
|
> 联系方式
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
- xm-select技术群①: `660408068` (500人)
|
- xm-select技术群①: `660408068` (500人)
|
||||||
- xm-select技术群②: `938624691` (500人)
|
- xm-select技术群②: `938624691` (500人)
|
||||||
- xm-select技术群③: `1145047250` (500人)
|
- xm-select技术群③: `1145047250` (500人)
|
||||||
|
|
||||||
[issues 需求记录](https://gitee.com/maplemei/xm-select/issues/I1NSO7)
|
[issues 需求记录](https://gitee.com/maplemei/xm-select/issues)
|
||||||
|
|
||||||
|
|
||||||
[更新日志](CHANGELOG.md)
|
[更新日志](CHANGELOG.md)
|
||||||
|
@ -94,9 +94,12 @@ const webpackConfig = {
|
|||||||
new webpack.BannerPlugin(banner),
|
new webpack.BannerPlugin(banner),
|
||||||
new VueLoaderPlugin(),
|
new VueLoaderPlugin(),
|
||||||
],
|
],
|
||||||
|
optimization: {
|
||||||
|
minimize: false,//可以自行配置是否压缩
|
||||||
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 9000,
|
port: 9001,
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
hot: true
|
hot: true
|
||||||
},
|
},
|
||||||
|
13471
dist/static/2.js
vendored
13471
dist/static/2.js
vendored
File diff suppressed because one or more lines are too long
1128
dist/static/3.js
vendored
1128
dist/static/3.js
vendored
File diff suppressed because one or more lines are too long
120377
dist/static/docs.js
vendored
120377
dist/static/docs.js
vendored
File diff suppressed because one or more lines are too long
8
dist/xm-select.js
vendored
8
dist/xm-select.js
vendored
File diff suppressed because one or more lines are too long
@ -38,6 +38,8 @@
|
|||||||
this.fixedControl = bottom > document.documentElement.clientHeight &&
|
this.fixedControl = bottom > document.documentElement.clientHeight &&
|
||||||
top + 44 <= document.documentElement.clientHeight;
|
top + 44 <= document.documentElement.clientHeight;
|
||||||
this.$refs.control.style.left = this.fixedControl ? `${ left }px` : '0';
|
this.$refs.control.style.left = this.fixedControl ? `${ left }px` : '0';
|
||||||
|
|
||||||
|
xmSelect.get().forEach(xs => xs.calcPosition());
|
||||||
},
|
},
|
||||||
removeScrollHandler() {
|
removeScrollHandler() {
|
||||||
this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler);
|
this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler);
|
||||||
|
@ -4,10 +4,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>
|
<h1>
|
||||||
<router-link :to="`/`">
|
<router-link :to="`/`">
|
||||||
<!-- logo -->
|
|
||||||
<slot>
|
<slot>
|
||||||
<!-- <img src="../assets/images/element-logo.svg" alt="element-logo" class="nav-logo"> -->
|
|
||||||
<!-- <img src="../assets/images/element-logo-small.svg" alt="element-logo" class="nav-logo-small"> -->
|
|
||||||
xm-select
|
xm-select
|
||||||
</slot>
|
</slot>
|
||||||
</router-link>
|
</router-link>
|
||||||
@ -19,7 +16,7 @@
|
|||||||
<router-link active-class="active" :to="`/`">使用手册</router-link>
|
<router-link active-class="active" :to="`/`">使用手册</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href='https://gitee.com/maplemei/xm-select/issues/I1NSO7' target="_blank" style="opacity: 1;">提新需求↗</a>
|
<a href='https://gitee.com/maplemei/xm-select/issues' target="_blank" style="opacity: 1;">提新需求↗</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
@ -48,7 +48,7 @@ layui.use('table', function() {
|
|||||||
//修改一些css样式, 这里虽然能够使用, 但是还是不太友好, 努力中...
|
//修改一些css样式, 这里虽然能够使用, 但是还是不太友好, 努力中...
|
||||||
var cells = document.querySelectorAll('div[lay-id="demo"] .layui-table-cell');
|
var cells = document.querySelectorAll('div[lay-id="demo"] .layui-table-cell');
|
||||||
for(var i = 0 ; i < cells.length ; i++ ){
|
for(var i = 0 ; i < cells.length ; i++ ){
|
||||||
cells[i].style.overflow = 'unset';
|
//cells[i].style.overflow = 'unset';
|
||||||
cells[i].style.height = 'auto';
|
cells[i].style.height = 'auto';
|
||||||
}
|
}
|
||||||
//渲染多选
|
//渲染多选
|
||||||
@ -56,6 +56,7 @@ layui.use('table', function() {
|
|||||||
var xm = xmSelect.render({
|
var xm = xmSelect.render({
|
||||||
el: '#XM-' + item.id,
|
el: '#XM-' + item.id,
|
||||||
autoRow: true,
|
autoRow: true,
|
||||||
|
model: { type: 'fixed' },
|
||||||
data: [
|
data: [
|
||||||
{name: '张三', value: 1},
|
{name: '张三', value: 1},
|
||||||
{name: '李四', value: 2},
|
{name: '李四', value: 2},
|
||||||
@ -70,6 +71,13 @@ layui.use('table', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//表格滚动时 重新计算位置
|
||||||
|
document.querySelector('.layui-table-body').addEventListener('scroll', () => {
|
||||||
|
xmSelect.get().forEach(function(item){
|
||||||
|
item.calcPosition();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
@ -5,46 +5,55 @@
|
|||||||
<div id="demo1" class="xm-select-demo"></div>
|
<div id="demo1" class="xm-select-demo"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var demo1 = xmSelect.render({
|
xmSelect.render({
|
||||||
el: '#demo1',
|
el: '#demo1',
|
||||||
autoRow: true,
|
autoRow: true,
|
||||||
filterable: true,
|
|
||||||
tree: {
|
tree: {
|
||||||
show: true,
|
show: true,
|
||||||
showFolderIcon: true,
|
|
||||||
showLine: true,
|
|
||||||
indent: 20,
|
|
||||||
expandedKeys: [ -3 ],
|
|
||||||
simple: true,
|
|
||||||
clickExpand: false,
|
|
||||||
clickCheck: false,
|
|
||||||
strict: false
|
|
||||||
},
|
},
|
||||||
toolbar: {
|
height: '200px',
|
||||||
show: true,
|
maxMethod(a, item){
|
||||||
list: ['ALL', 'REVERSE', 'CLEAR']
|
console.log(item)
|
||||||
|
},
|
||||||
|
submitConversion(sels, prop){
|
||||||
|
return sels.map(item => item[prop.name]).join(',')
|
||||||
},
|
},
|
||||||
filterable: true,
|
filterable: true,
|
||||||
height: 'auto',
|
|
||||||
data(){
|
data(){
|
||||||
return [
|
return [
|
||||||
{name: '销售员', value: -1, disabled: true, children: [
|
{name: '销售员', value: -1, disabled: false, children: [
|
||||||
{name: '张三1', value: 1, selected: true, children: []},
|
{name: '张三1', value: 1, selected: true, children: []},
|
||||||
{name: '李四1', value: 2, selected: true},
|
{name: '王五1', value: 13, disabled: true},
|
||||||
{name: '王五1', value: 3, disabled: true},
|
{name: '王五1', value: 131, disabled: true},
|
||||||
|
{name: '王五1', value: 132, disabled: true},
|
||||||
|
{name: '王五1', value: 133, disabled: true},
|
||||||
|
{name: '王五1', value: 134, disabled: true},
|
||||||
|
{name: '王五1', value: 135, disabled: true},
|
||||||
|
{name: '王五1', value: 136, disabled: true},
|
||||||
|
{name: '王五1', value: 137, disabled: true},
|
||||||
|
{name: '王五1', value: 138, disabled: true},
|
||||||
]},
|
]},
|
||||||
{name: '奖品', value: -2, disabled: true, children: [
|
{name: '奖品', value: -2, children: [
|
||||||
{name: '奖品3', value: -3, children: [
|
{name: '奖品3', 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: 4, disabled: true},
|
||||||
{name: '香蕉2', value: 5},
|
{name: '香蕉2', value: 5},
|
||||||
{name: '葡萄2', value: 6},
|
{name: '葡萄2', value: 6},
|
||||||
]},
|
]},
|
||||||
|
{name: '李四1', value: 2},
|
||||||
|
{name: '王五1', value: 3, disabled: true},
|
||||||
|
{name: '王五1', value: 31, disabled: true},
|
||||||
|
{name: '王五1', value: 32, disabled: true},
|
||||||
|
{name: '王五1', value: 33, disabled: true},
|
||||||
|
{name: '王五1', value: 34, disabled: true},
|
||||||
|
{name: '王五1', value: 35, disabled: true},
|
||||||
|
{name: '王五1', value: 36, disabled: true},
|
||||||
|
{name: '王五1', value: 37, disabled: true},
|
||||||
|
{name: '王五1', value: 38, disabled: true},
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -46,12 +46,16 @@
|
|||||||
| toolbar | 工具条, 具体看下表 | object | - | - |
|
| toolbar | 工具条, 具体看下表 | object | - | - |
|
||||||
| showCount | 展示在下拉框中的最多选项数量 | int | - | 0 |
|
| showCount | 展示在下拉框中的最多选项数量 | int | - | 0 |
|
||||||
| enableKeyboard | 是否启用键盘操作选项 | boolean | true / false | true |
|
| enableKeyboard | 是否启用键盘操作选项 | boolean | true / false | true |
|
||||||
|
| enableHoverFirst | 是否默认选中第一项 | boolean | true / false | true |
|
||||||
|
| selectedKeyCode | 选中的键盘KeyCode | int | 全部KeyCode, 也可xmSelect.KeyCode.Enter,xmSelect.KeyCode.Space | 13 |
|
||||||
| 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 |
|
| disabled | 是否禁用多选 | boolean | true / false | false |
|
||||||
| create | 创建条目 | function(val, data), val: 搜索的数据, data: 当前下拉数据 | - | null |
|
| create | 创建条目 | function(val, data), val: 搜索的数据, data: 当前下拉数据 | - | null |
|
||||||
| tree | 树形结构, 具体看下表 | object | - | - |
|
| tree | 树形结构, 具体看下表 | object | - | - |
|
||||||
| cascader | 级联结构, 具体看下表 | object | - | - |
|
| cascader | 级联结构, 具体看下表 | object | - | - |
|
||||||
|
| submitConversion | 配置表单提交数据 | function(sels, prop), sels: 已选中数据, prop: 自定义的prop | - | - |
|
||||||
|
| done | 渲染完成回调 | function | - | - |
|
||||||
|
|
||||||
|
|
||||||
### prop
|
### prop
|
||||||
@ -134,7 +138,7 @@ model: {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
//展示类型, 下拉框形式: absolute, 直接显示模式: relative
|
//展示类型, 下拉框形式: absolute, 直接显示模式: relative, 浮动布局: fixed
|
||||||
type: 'absolute',
|
type: 'absolute',
|
||||||
},
|
},
|
||||||
```
|
```
|
||||||
@ -211,7 +215,7 @@ list: [ "ALL", "CLEAR",
|
|||||||
| render | 渲染多选 | (options: 配置项) | 实例对象 |
|
| render | 渲染多选 | (options: 配置项) | 实例对象 |
|
||||||
| get | 获取页面中已经渲染的多选 | (filter: 过滤`el`, single: 是否返回单实例) | 符合条件的实例数组 |
|
| get | 获取页面中已经渲染的多选 | (filter: 过滤`el`, single: 是否返回单实例) | 符合条件的实例数组 |
|
||||||
| batch | 批量操作已渲染的多选 | (filter: 过滤`el`, method: 方法, ...方法参数) | 符合条件的实例数组 |
|
| batch | 批量操作已渲染的多选 | (filter: 过滤`el`, method: 方法, ...方法参数) | 符合条件的实例数组 |
|
||||||
| arrr2tree | 把列表数据转化为树状结构 | (arr: 数据, pid: 父节点ID的key, id: 对应key, children: 对应key, topParentId: 顶级节点的ID) | 符合条件的数组 |
|
| arr2tree | 把列表数据转化为树状结构 | (arr: 数据, pid: 父节点ID的key, id: 对应key, children: 对应key, topParentId: 顶级节点的ID) | 符合条件的数组 |
|
||||||
|
|
||||||
```
|
```
|
||||||
//render 使用方式
|
//render 使用方式
|
||||||
@ -258,3 +262,4 @@ xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用
|
|||||||
| changeExpandedKeys | 树模式下更新节点展开状态, v1.2.0 新增 | (keys: true-全部展开, false-全部关闭, 数组-展开的节点值) |
|
| changeExpandedKeys | 树模式下更新节点展开状态, v1.2.0 新增 | (keys: true-全部展开, false-全部关闭, 数组-展开的节点值) |
|
||||||
| enable | 启用选项, disabled=false, v1.2.0 新增 | (array: 想要启用的选项数组) |
|
| enable | 启用选项, disabled=false, v1.2.0 新增 | (array: 想要启用的选项数组) |
|
||||||
| disable | 禁用用选项, disabled=true, v1.2.0 新增 | (array: 想要禁用的选项数组) |
|
| disable | 禁用用选项, disabled=true, v1.2.0 新增 | (array: 想要禁用的选项数组) |
|
||||||
|
| calcPosition | fixed布局模式下重新计算位置, v1.2.2 新增 | - |
|
||||||
|
9070
package-lock.json
generated
Normal file
9070
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@ -1,45 +1,46 @@
|
|||||||
{
|
{
|
||||||
"name": "xm-select",
|
"name": "xm-select",
|
||||||
"version": "1.2.1",
|
"version": "1.2.4",
|
||||||
"description": "始于Layui的select多选解决方案",
|
"description": "始于Layui的select多选解决方案",
|
||||||
|
"website": "https://maplemei.gitee.io/xm-select",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_ENV=dev node_modules/.bin/webpack-dev-server --config build/webpack.config.js",
|
"dev": "cross-env NODE_ENV=dev node_modules/.bin/webpack-dev-server --config build/webpack.config.js",
|
||||||
"build": "cross-env NODE_ENV=prod webpack --config build/webpack.config.js"
|
"build": "cross-env NODE_ENV=prod webpack --config build/webpack.config.js"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "maplemei",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel-polyfill": "^6.26.0",
|
||||||
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
|
"element-ui": "^2.12.0",
|
||||||
|
"highlight.js": "^9.15.10",
|
||||||
|
"preact": "^10.4.6",
|
||||||
|
"vue": "^2.6.14",
|
||||||
|
"vue-router": "^3.1.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
"@babel/core": "^7.4.0",
|
"@babel/core": "^7.4.0",
|
||||||
"@babel/preset-env": "^7.4.2",
|
"@babel/preset-env": "^7.4.2",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.5",
|
||||||
"babel-plugin-transform-react-jsx": "^6.24.1",
|
"babel-plugin-transform-react-jsx": "^6.24.1",
|
||||||
"babel-polyfill": "^6.26.0",
|
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
|
||||||
"cross-env": "^6.0.0",
|
"cross-env": "^6.0.0",
|
||||||
"css-loader": "^3.0.0",
|
"css-loader": "^3.0.0",
|
||||||
"element-ui": "^2.12.0",
|
|
||||||
"file-loader": "^4.2.0",
|
"file-loader": "^4.2.0",
|
||||||
"highlight.js": "^9.15.10",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"less": "^3.9.0",
|
"less": "^3.9.0",
|
||||||
"less-loader": "^4.1.0",
|
"less-loader": "^4.1.0",
|
||||||
"markdown-it": "^10.0.0",
|
"markdown-it": "^10.0.0",
|
||||||
"markdown-it-anchor": "^5.2.4",
|
"markdown-it-anchor": "^5.2.4",
|
||||||
"markdown-it-chain": "^1.3.0",
|
"markdown-it-chain": "^1.3.0",
|
||||||
"markdown-it-container": "^2.0.0",
|
"markdown-it-container": "^2.0.0",
|
||||||
"preact": "^10.4.6",
|
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"transliteration": "^2.1.7",
|
"transliteration": "^2.1.7",
|
||||||
"url-loader": "^2.1.0",
|
"url-loader": "^2.1.0",
|
||||||
"vue": "^2.6.10",
|
|
||||||
"vue-loader": "^15.7.1",
|
"vue-loader": "^15.7.1",
|
||||||
"vue-router": "^3.1.3",
|
"vue-template-compiler": "^2.6.11",
|
||||||
"vue-template-compiler": "^2.6.10",
|
|
||||||
"webpack": "^4.29.6",
|
"webpack": "^4.29.6",
|
||||||
"webpack-cli": "^3.3.0",
|
"webpack-cli": "^3.3.0",
|
||||||
"webpack-dev-server": "^3.2.1"
|
"webpack-dev-server": "^3.2.1"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"html-webpack-plugin": "^3.2.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6638
pnpm-lock.yaml
generated
Normal file
6638
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -191,3 +191,21 @@ export function delProp(data, children, props){
|
|||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function throttle(cb, time = 100 ,wait = true) {
|
||||||
|
// cb => 回调函数
|
||||||
|
// time => 定时
|
||||||
|
// wait => 是否等待 false 立即生效 true 时间到了再执行
|
||||||
|
const self = this // 保存this指向
|
||||||
|
let disable = false // 节流标志
|
||||||
|
return function (...data) {
|
||||||
|
if(disable)return // 如果是禁止状态那么直接跳出
|
||||||
|
disable = true // 如果不是禁止状态那么立即设置为禁止
|
||||||
|
!wait && cb.call(self,...data) // 如果不等待那么立即执行
|
||||||
|
setTimeout(()=>{
|
||||||
|
wait && cb.call(self,...data) // 如果要等待那么到时间后再执行
|
||||||
|
disable = false // 关闭禁止状态
|
||||||
|
},time)
|
||||||
|
}
|
||||||
|
}
|
@ -31,11 +31,12 @@ class Framework extends Component{
|
|||||||
show: false,
|
show: false,
|
||||||
tmpColor: '',
|
tmpColor: '',
|
||||||
bodyClass: '',
|
bodyClass: '',
|
||||||
|
time: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(props, refresh){
|
init(props, refresh){
|
||||||
let { data, prop, initValue, radio } = props, sels;
|
let { data, prop, initValue, radio, tree, cascader } = props, sels;
|
||||||
//如果新数据和旧数据不同 或者 强制刷新 才进行数据处理
|
//如果新数据和旧数据不同 或者 强制刷新 才进行数据处理
|
||||||
if(refresh){
|
if(refresh){
|
||||||
let dataObj = {};
|
let dataObj = {};
|
||||||
@ -47,6 +48,9 @@ class Framework extends Component{
|
|||||||
}), dataObj)
|
}), dataObj)
|
||||||
if(radio && sels.length > 1){
|
if(radio && sels.length > 1){
|
||||||
sels = sels.slice(0, 1)
|
sels = sels.slice(0, 1)
|
||||||
|
if(tree.show && tree.strict || cascader.show && cascader.strict){
|
||||||
|
this.clearAndReset(data, sels, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.setState({ sels, dataObj, flatData });
|
this.setState({ sels, dataObj, flatData });
|
||||||
}
|
}
|
||||||
@ -116,7 +120,7 @@ class Framework extends Component{
|
|||||||
return cgList;
|
return cgList;
|
||||||
}
|
}
|
||||||
|
|
||||||
value(sels, show, listenOn, jsChangeData){
|
value(sels, show, listenOn, jsChangeData, isAdd = true){
|
||||||
if(show !== false && show !== true){
|
if(show !== false && show !== true){
|
||||||
show = this.state.show;
|
show = this.state.show;
|
||||||
}
|
}
|
||||||
@ -125,7 +129,7 @@ class Framework extends Component{
|
|||||||
let changeData = this.exchangeValue(sels);
|
let changeData = this.exchangeValue(sels);
|
||||||
|
|
||||||
//检测是否超选了
|
//检测是否超选了
|
||||||
if(this.checkMax(changeData, changeData)){
|
if(this.checkMax(changeData, changeData, true)){
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,19 +138,28 @@ class Framework extends Component{
|
|||||||
this.clearAndReset(data, changeData, false);
|
this.clearAndReset(data, changeData, false);
|
||||||
changeData = this.init({ data, prop }, true);
|
changeData = this.init({ data, prop }, true);
|
||||||
}
|
}
|
||||||
this.resetSelectValue(changeData, jsChangeData ? jsChangeData : changeData, true, listenOn);
|
this.resetSelectValue(changeData, jsChangeData ? jsChangeData : changeData, isAdd, listenOn);
|
||||||
this.setState({ show })
|
this.setState({ show })
|
||||||
}
|
}
|
||||||
|
|
||||||
clearAndReset(data, changeData, parentCK){
|
clearAndReset(data, changeData, parentCK){
|
||||||
const { selected, children, value } = this.props.prop;
|
const { selected, disabled, children, value } = this.props.prop;
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
item[selected] = changeData.findIndex(c => c[value] === item[value]) != -1 || parentCK;
|
item[selected] = changeData.findIndex(c => c[value] === item[value]) != -1 || parentCK;
|
||||||
let child = item[children];
|
let child = item[children];
|
||||||
child && isArray(child) && this.clearAndReset(child, changeData, item[selected])
|
if(child && isArray(child) && child.length > 0){
|
||||||
|
this.clearAndReset(child, changeData, item[selected])
|
||||||
|
let len = child.length;
|
||||||
|
let slen = child.filter(i => i[selected] === true || i.__node.selected === true).length;
|
||||||
|
item.__node.selected = slen === len;
|
||||||
|
item.__node.half = slen > 0 && slen < len || child.filter(i => i.__node.half === true).length > 0;
|
||||||
|
item.__node.disabled = child.filter(i => i[disabled] === true || i.__node.disabled === true).length === len;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
load(data, dataObj, flatData, parent, level = 0, initValue){
|
load(data, dataObj, flatData, parent, level = 0, initValue){
|
||||||
const { prop, tree, cascader } = this.props;
|
const { prop, tree, cascader } = this.props;
|
||||||
const { children, optgroup, value, selected, disabled } = prop;
|
const { children, optgroup, value, selected, disabled } = prop;
|
||||||
@ -237,11 +250,12 @@ class Framework extends Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkMax(item, sels){
|
checkMax(item, sels, contains){
|
||||||
const { max, maxMethod, theme } = this.props
|
const { max, maxMethod, theme } = this.props
|
||||||
//查看是否设置了多选上限
|
//查看是否设置了多选上限
|
||||||
let maxCount = toNum(max);
|
let maxCount = toNum(max);
|
||||||
if(maxCount > 0 && sels.length >= maxCount){
|
let flag = (contains ? sels.length : (isArray(item) ? item.length : 1) + sels.length) > maxCount;
|
||||||
|
if(maxCount > 0 && flag){
|
||||||
this.updateBorderColor(theme.maxColor);
|
this.updateBorderColor(theme.maxColor);
|
||||||
//查看是否需要回调
|
//查看是否需要回调
|
||||||
maxMethod && isFunction(maxMethod) && maxMethod(sels, item);
|
maxMethod && isFunction(maxMethod) && maxMethod(sels, item);
|
||||||
@ -252,14 +266,14 @@ class Framework extends Component{
|
|||||||
//选项, 选中状态, 禁用状态, 是否强制删除:在label上点击删除
|
//选项, 选中状态, 禁用状态, 是否强制删除:在label上点击删除
|
||||||
itemClick(item, itemSelected, itemDisabled, mandatoryDelete){
|
itemClick(item, itemSelected, itemDisabled, mandatoryDelete){
|
||||||
|
|
||||||
const { theme, prop, radio, repeat, clickClose, max, maxMethod, tree } = this.props
|
const { theme, prop, radio, repeat, clickClose, max, maxMethod, tree, cascader, data } = this.props
|
||||||
let sels = [ ...this.state.sels ]
|
let sels = [ ...this.state.sels ]
|
||||||
const { value, selected, disabled, children, optgroup } = prop
|
const { value, selected, disabled, children, optgroup } = prop
|
||||||
|
|
||||||
//如果是禁用状态, 不能进行操作
|
//如果是禁用状态, 不能进行操作
|
||||||
if(itemDisabled) return;
|
if(itemDisabled) return;
|
||||||
|
|
||||||
if(item[optgroup] && tree.strict){
|
if(item[optgroup] && (tree.show && tree.strict || cascader.show && cascader.strict)){
|
||||||
let child = item[children], change = [], isAdd = true, handlerType;
|
let child = item[children], change = [], isAdd = true, handlerType;
|
||||||
if(item.__node.selected){
|
if(item.__node.selected){
|
||||||
handlerType = 'del';
|
handlerType = 'del';
|
||||||
@ -280,7 +294,7 @@ class Framework extends Component{
|
|||||||
this.treeHandler(sels, item, change, handlerType);
|
this.treeHandler(sels, item, change, handlerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.checkMax(change, change)){
|
if(this.checkMax(change, sels)){//TODO 这里还是有问题, 如果是取消的
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
sels = [ ...this.state.sels ], change = [];
|
sels = [ ...this.state.sels ], change = [];
|
||||||
@ -308,6 +322,7 @@ class Framework extends Component{
|
|||||||
}else{
|
}else{
|
||||||
sels = [...sels, item]
|
sels = [...sels, item]
|
||||||
}
|
}
|
||||||
|
this.clearAndReset(data, sels, itemSelected)
|
||||||
this.resetSelectValue(sels, [item], !itemSelected);
|
this.resetSelectValue(sels, [item], !itemSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -387,7 +402,7 @@ class Framework extends Component{
|
|||||||
}else
|
}else
|
||||||
//树状结构数据更新
|
//树状结构数据更新
|
||||||
if(type === 'treeData'){
|
if(type === 'treeData'){
|
||||||
this.value(data, null, true)
|
this.value(data, null, true, false, false)
|
||||||
}else
|
}else
|
||||||
//树状结构数据更新
|
//树状结构数据更新
|
||||||
if(type === 'close'){
|
if(type === 'close'){
|
||||||
@ -399,7 +414,7 @@ class Framework extends Component{
|
|||||||
}else
|
}else
|
||||||
//聚焦label搜索框
|
//聚焦label搜索框
|
||||||
if(type === 'labelSearchBlur'){
|
if(type === 'labelSearchBlur'){
|
||||||
this.labelRef.blur(data);
|
this.labelRef && this.labelRef.blur(data);
|
||||||
}else
|
}else
|
||||||
//聚焦label搜索框
|
//聚焦label搜索框
|
||||||
if(type === 'labelSearch'){
|
if(type === 'labelSearch'){
|
||||||
@ -422,7 +437,7 @@ class Framework extends Component{
|
|||||||
sels.splice(index, 1);
|
sels.splice(index, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.value(sels, this.props.show, true, changeData)
|
this.value(sels, this.props.show, true, changeData, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto(arr){
|
auto(arr){
|
||||||
@ -439,6 +454,21 @@ class Framework extends Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calcPosition(){
|
||||||
|
if(!this.base || !this.state.show){
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
let rect = this.base.getBoundingClientRect();
|
||||||
|
(Date.now() - this.state.time > 10) && this.setState({ time: Date.now() })
|
||||||
|
return {
|
||||||
|
position: 'fixed',
|
||||||
|
left: rect.x,
|
||||||
|
top: rect.y + rect.height + 4,
|
||||||
|
width: rect.width,
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
//组件将要接收新属性
|
//组件将要接收新属性
|
||||||
componentWillReceiveProps(props){
|
componentWillReceiveProps(props){
|
||||||
this.init(props, props.updateData);
|
this.init(props, props.updateData);
|
||||||
@ -450,7 +480,7 @@ class Framework extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(config, state) {
|
render(config, state) {
|
||||||
const { theme, prop, radio, repeat, clickClose, on, max, maxMethod, content, disabled, tree } = config;
|
const { theme, prop, radio, repeat, clickClose, on, max, maxMethod, content, disabled, tree, submitConversion } = config;
|
||||||
const borderStyle = { borderColor: theme.color };
|
const borderStyle = { borderColor: theme.color };
|
||||||
let { data, dataObj, flatData, sels, show, tmpColor, bodyClass } = state;
|
let { data, dataObj, flatData, sels, show, tmpColor, bodyClass } = state;
|
||||||
|
|
||||||
@ -482,6 +512,7 @@ class Framework extends Component{
|
|||||||
|
|
||||||
//渲染组件
|
//渲染组件
|
||||||
let Body = content ? <Custom { ...bodyProps } /> : tree.show ? <Tree { ...bodyProps } ref={ ref => this.treeRef = ref } /> : config.cascader.show ? <Cascader { ...bodyProps } /> : <General { ...bodyProps } ref={ ref => this.generalRef = ref } />;
|
let Body = content ? <Custom { ...bodyProps } /> : tree.show ? <Tree { ...bodyProps } ref={ ref => this.treeRef = ref } /> : config.cascader.show ? <Cascader { ...bodyProps } /> : <General { ...bodyProps } ref={ ref => this.generalRef = ref } />;
|
||||||
|
let bodyStyle = this.calcPosition();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<xm-select { ...xmSelectProps } >
|
<xm-select { ...xmSelectProps } >
|
||||||
@ -490,12 +521,12 @@ class Framework extends Component{
|
|||||||
lay-verType={ config.layVerType }
|
lay-verType={ config.layVerType }
|
||||||
lay-reqText={ config.layReqText }
|
lay-reqText={ config.layReqText }
|
||||||
name={ config.name }
|
name={ config.name }
|
||||||
value={ sels.map(item => item[prop.value]).join(',') }
|
value={ submitConversion(sels, prop) }
|
||||||
></input>
|
></input>
|
||||||
<i class={ show ? 'xm-icon xm-icon-expand' : 'xm-icon' } />
|
<i class={ show ? 'xm-icon xm-icon-expand' : 'xm-icon' } />
|
||||||
{ sels.length === 0 && <div class="xm-tips">{ config.tips }</div> }
|
{ sels.length === 0 && <div class="xm-tips">{ config.tips }</div> }
|
||||||
<Label { ...labelProps } ref={ ref => this.labelRef = ref } />
|
<Label { ...labelProps } ref={ ref => this.labelRef = ref } />
|
||||||
<div class={ ['xm-body', bodyClass, config.model.type, show ? '':'dis', ].join(' ') } ref={ ref => this.bodyView = ref}>
|
<div class={ ['xm-body', bodyClass, config.model.type, show ? '':'dis', ].join(' ') } style={ bodyStyle } ref={ ref => this.bodyView = ref}>
|
||||||
{ Body }
|
{ Body }
|
||||||
</div>
|
</div>
|
||||||
{ disabled && <div class="xm-select-disabled"></div> }
|
{ disabled && <div class="xm-select-disabled"></div> }
|
||||||
@ -510,11 +541,16 @@ class Framework extends Component{
|
|||||||
//监听键盘事件
|
//监听键盘事件
|
||||||
this.base.addEventListener('keydown', e => {
|
this.base.addEventListener('keydown', e => {
|
||||||
let keyCode = e.keyCode;
|
let keyCode = e.keyCode;
|
||||||
|
//ENTER
|
||||||
if(keyCode === 13){
|
if(keyCode === 13){
|
||||||
this.onClick()
|
this.onClick(e)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// focus 可以监听tab切换
|
||||||
|
// this.base.addEventListener('focus', e => {
|
||||||
|
// })
|
||||||
|
|
||||||
//表单验证
|
//表单验证
|
||||||
this.input = this.base.querySelector('.xm-select-default');
|
this.input = this.base.querySelector('.xm-select-default');
|
||||||
//监听class的变化, 然后进行边框变色处理, 或者更多的处理
|
//监听class的变化, 然后进行边框变色处理, 或者更多的处理
|
||||||
@ -528,7 +564,7 @@ class Framework extends Component{
|
|||||||
this.input.className = 'xm-select-default';
|
this.input.className = 'xm-select-default';
|
||||||
this.base.style.borderColor = this.props.theme.maxColor;
|
this.base.style.borderColor = this.props.theme.maxColor;
|
||||||
//这里可以自己新增一个回调, 也许看到源码的你能够看到
|
//这里可以自己新增一个回调, 也许看到源码的你能够看到
|
||||||
// this.base.scrollIntoView({ behavior: "smooth" });
|
this.base.scrollIntoView && this.base.scrollIntoView({ behavior: "smooth" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -548,6 +584,8 @@ class Framework extends Component{
|
|||||||
dom = dom.parentElement;
|
dom = dom.parentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { done } = this.props;
|
||||||
|
done && done();
|
||||||
}
|
}
|
||||||
|
|
||||||
//此时页面又被重新渲染了
|
//此时页面又被重新渲染了
|
||||||
@ -558,7 +596,12 @@ class Framework extends Component{
|
|||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(model.type === 'fixed'){
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
let rect = this.base.getBoundingClientRect();
|
let rect = this.base.getBoundingClientRect();
|
||||||
|
let bodyViewHeight;
|
||||||
if(direction === 'auto'){
|
if(direction === 'auto'){
|
||||||
//用于控制js获取下拉框的高度
|
//用于控制js获取下拉框的高度
|
||||||
this.bodyView.style.display = 'block';
|
this.bodyView.style.display = 'block';
|
||||||
@ -566,7 +609,7 @@ class Framework extends Component{
|
|||||||
|
|
||||||
//获取下拉元素的高度
|
//获取下拉元素的高度
|
||||||
let bodyViewRect = this.bodyView.getBoundingClientRect();
|
let bodyViewRect = this.bodyView.getBoundingClientRect();
|
||||||
let bodyViewHeight = bodyViewRect.height;
|
bodyViewHeight = bodyViewRect.height;
|
||||||
|
|
||||||
//还原控制效果
|
//还原控制效果
|
||||||
this.bodyView.style.display = '';
|
this.bodyView.style.display = '';
|
||||||
@ -578,13 +621,12 @@ class Framework extends Component{
|
|||||||
let diff = clientHeight - y - rect.height - 20;
|
let diff = clientHeight - y - rect.height - 20;
|
||||||
direction = diff > bodyViewHeight || y < diff ? 'down' : 'up';
|
direction = diff > bodyViewHeight || y < diff ? 'down' : 'up';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(direction == 'down'){
|
if(direction == 'down'){
|
||||||
this.bodyView.style.top = rect.height + 4 + 'px';
|
this.bodyView.style.top = (rect.height + 4 + rect.y )+ 'px';
|
||||||
this.bodyView.style.bottom = 'auto';
|
this.bodyView.style.bottom = 'auto';
|
||||||
}else{
|
}else{
|
||||||
this.bodyView.style.top = 'auto';
|
this.bodyView.style.top = (4 +rect.y - bodyViewHeight )+ 'px';
|
||||||
this.bodyView.style.bottom = rect.height + 4 + 'px';
|
this.bodyView.style.bottom = 'auto';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,65 @@ class Label extends Component{
|
|||||||
input && input.blur();
|
input && input.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labelDrag(item, e){
|
||||||
|
let type = e.type;
|
||||||
|
let node = e.target;
|
||||||
|
while(true){
|
||||||
|
if(!node || node.tagName === 'I'){
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
if(node.tagName === 'DIV' && node.style.position !== 'fixed'){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(e)
|
||||||
|
|
||||||
|
if(type === 'mousedown'){
|
||||||
|
let dragNode = node.cloneNode(true);
|
||||||
|
let { pageX, pageY, offsetX, offsetY } = e;//鼠标当前位置
|
||||||
|
|
||||||
|
console.log(pageX, pageY, offsetX, offsetY)
|
||||||
|
|
||||||
|
dragNode.style.position = 'fixed';
|
||||||
|
dragNode.style.left = (pageX - offsetX) + 'px';
|
||||||
|
dragNode.style.top = (pageY - offsetY) + 'px';
|
||||||
|
node.appendChild(dragNode);
|
||||||
|
|
||||||
|
console.log(dragNode)
|
||||||
|
|
||||||
|
|
||||||
|
dragNode.onmousemove = (ev) => {
|
||||||
|
dragNode.style.left = (ev.pageX - offsetX) + 'px';
|
||||||
|
dragNode.style.top = (ev.pageY - offsetY) + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
dragNode.mouseup = () => {
|
||||||
|
dragNode.parentNode.removeChild(dragNode);
|
||||||
|
dragNode.onmousemove = null;
|
||||||
|
dragNode.mouseup = null;
|
||||||
|
dragNode.mouseleave = null;
|
||||||
|
}
|
||||||
|
dragNode.mouseleave = () => {
|
||||||
|
console.log('mouseleave')
|
||||||
|
}
|
||||||
|
}else if(type === 'mouseup'){
|
||||||
|
let childs = node.childNodes;
|
||||||
|
for(let i = 0; i < childs.length; i++) {
|
||||||
|
let f = childs[i];
|
||||||
|
if(f.tagName === 'DIV'){
|
||||||
|
node.removeChild(f);
|
||||||
|
f.onmousemove = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount(){
|
componentDidMount(){
|
||||||
if (this.labelRef.addEventListener) {
|
if (this.labelRef.addEventListener) {
|
||||||
this.labelRef.addEventListener('DOMMouseScroll', this.scrollFunc.bind(this), false);
|
this.labelRef.addEventListener('DOMMouseScroll', this.scrollFunc.bind(this), false);
|
||||||
@ -82,8 +141,12 @@ class Label extends Component{
|
|||||||
html = arr.splice(0, count).map(sel => {
|
html = arr.splice(0, count).map(sel => {
|
||||||
const styleProps = { width: conf.showIcon ? 'calc(100% - 20px)' : '100%', }
|
const styleProps = { width: conf.showIcon ? 'calc(100% - 20px)' : '100%', }
|
||||||
const className = ['xm-label-block', sel[disabled] ? 'disabled':''].join(' ');
|
const className = ['xm-label-block', sel[disabled] ? 'disabled':''].join(' ');
|
||||||
|
// onMouseDown = { this.labelDrag.bind(this, sel) }
|
||||||
|
// onMouseUp = { this.labelDrag.bind(this, sel) }
|
||||||
return (
|
return (
|
||||||
<div class={className} style={ style }>
|
<div class={className} style={ style }
|
||||||
|
|
||||||
|
>
|
||||||
{ conf.template && isFunction(conf.template) ? (
|
{ conf.template && isFunction(conf.template) ? (
|
||||||
<span style={ styleProps } dangerouslySetInnerHTML={{ __html: conf.template(sel, arr) }}></span>
|
<span style={ styleProps } dangerouslySetInnerHTML={{ __html: conf.template(sel, arr) }}></span>
|
||||||
) : (
|
) : (
|
||||||
@ -104,7 +167,10 @@ class Label extends Component{
|
|||||||
}
|
}
|
||||||
}else if(type == 'search'){
|
}else if(type == 'search'){
|
||||||
innerHTML = false;
|
innerHTML = false;
|
||||||
let one = list[0][name];
|
let one = '';
|
||||||
|
if(list.length){
|
||||||
|
one = list[0][name]
|
||||||
|
}
|
||||||
|
|
||||||
html = (
|
html = (
|
||||||
<input class="label-search-input" type="text" placeholder={ config.searchTips } style={{ width: '100%', border: 'none' }} value={
|
<input class="label-search-input" type="text" placeholder={ config.searchTips } style={{ width: '100%', border: 'none' }} value={
|
||||||
|
@ -18,7 +18,7 @@ class Cascader extends Component{
|
|||||||
|
|
||||||
optionClick(item, selected, disabled, type, index, e){
|
optionClick(item, selected, disabled, type, index, e){
|
||||||
if(type === 'line'){
|
if(type === 'line'){
|
||||||
if(disabled){
|
if(!item.optgroup && disabled){
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
//加载中的不需要进行处理
|
//加载中的不需要进行处理
|
||||||
@ -57,7 +57,7 @@ class Cascader extends Component{
|
|||||||
render(config, state) {
|
render(config, state) {
|
||||||
|
|
||||||
const { prop, empty, sels, theme, radio, template, data, cascader } = config;
|
const { prop, empty, sels, theme, radio, template, data, cascader } = config;
|
||||||
let { name, value, disabled, children } = prop;
|
let { name, value, disabled, children, optgroup } = prop;
|
||||||
const showIcon = config.model.icon != 'hidden';
|
const showIcon = config.model.icon != 'hidden';
|
||||||
|
|
||||||
const renderItem = (item, indent, index, checked) => {
|
const renderItem = (item, indent, index, checked) => {
|
||||||
@ -108,6 +108,12 @@ class Cascader extends Component{
|
|||||||
itemStyle.backgroundColor = theme.hover
|
itemStyle.backgroundColor = theme.hover
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//隐藏图标的处理
|
||||||
|
if(!showIcon && selected){
|
||||||
|
itemStyle.backgroundColor = theme.color;
|
||||||
|
dis && (itemStyle.backgroundColor = '#C2C2C2');
|
||||||
|
}
|
||||||
|
|
||||||
const contentStyle = {}, checkedStyle = {};
|
const contentStyle = {}, checkedStyle = {};
|
||||||
if(checked){
|
if(checked){
|
||||||
contentStyle.color = theme.color
|
contentStyle.color = theme.color
|
||||||
@ -133,7 +139,7 @@ class Cascader extends Component{
|
|||||||
} onMouseEnter={ hoverChange } onMouseLeave={ hoverChange }>
|
} onMouseEnter={ hoverChange } onMouseLeave={ hoverChange }>
|
||||||
{ showIcon && <i class={ iconClass } style={ iconStyle } onClick={ this.optionClick.bind(this, item, selected, dis, 'checkbox', index) }></i> }
|
{ showIcon && <i class={ iconClass } style={ iconStyle } onClick={ this.optionClick.bind(this, item, selected, dis, 'checkbox', index) }></i> }
|
||||||
<div class='xm-option-content' style={ contentStyle } dangerouslySetInnerHTML={{ __html: template({ data, item, arr: sels, name: item[name], value: item[value] }) }}></div>
|
<div class='xm-option-content' style={ contentStyle } dangerouslySetInnerHTML={{ __html: template({ data, item, arr: sels, name: item[name], value: item[value] }) }}></div>
|
||||||
{ item[children] && <div class={ checkedClass } style={ checkedStyle }></div> }
|
{ item[optgroup] && <div class={ checkedClass } style={ checkedStyle }></div> }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -146,7 +152,7 @@ class Cascader extends Component{
|
|||||||
const checked = child && this.state.expand[index] === item[value];
|
const checked = child && this.state.expand[index] === item[value];
|
||||||
checked && boxArr.push(
|
checked && boxArr.push(
|
||||||
<div class="xm-cascader-box" index={ index % 4 } style={{ left: indent + 'px', width: cascader.indent + 'px'}}>
|
<div class="xm-cascader-box" index={ index % 4 } style={{ left: indent + 'px', width: cascader.indent + 'px'}}>
|
||||||
<div class="xm-cascader-scroll">{ child.map(c => renderGroup(c, indent, index + 1)) }</div>
|
<div class="xm-cascader-scroll scroll-body">{ child.map(c => renderGroup(c, indent, index + 1)) }</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
return renderItem(item, indent, index, checked)
|
return renderItem(item, indent, index, checked)
|
||||||
@ -161,7 +167,7 @@ class Cascader extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={ this.blockClick } class="xm-body-cascader" style={{ width: cascader.indent + 'px', maxHeight: config.height }}>
|
<div onClick={ this.blockClick } class="xm-body-cascader scroll-body" style={{ width: cascader.indent + 'px', maxHeight: config.height }}>
|
||||||
{ arr }
|
{ arr }
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -183,9 +183,7 @@ class General extends Component{
|
|||||||
}
|
}
|
||||||
let val = data[index][value];
|
let val = data[index][value];
|
||||||
this.setState({ val })
|
this.setState({ val })
|
||||||
//键盘选中时滚动到可视范围内
|
this.viewTo(val);
|
||||||
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
|
|
||||||
opt && opt.scrollIntoView(false)
|
|
||||||
}else
|
}else
|
||||||
//Down 键
|
//Down 键
|
||||||
if(keyCode === 40){
|
if(keyCode === 40){
|
||||||
@ -196,19 +194,24 @@ class General extends Component{
|
|||||||
}
|
}
|
||||||
let val = data[index][value];
|
let val = data[index][value];
|
||||||
this.setState({ val })
|
this.setState({ val })
|
||||||
//键盘选中时滚动到可视范围内
|
this.viewTo(val);
|
||||||
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
|
|
||||||
opt && opt.scrollIntoView(false)
|
|
||||||
}else
|
}else
|
||||||
//Enter 键
|
//Enter 键
|
||||||
if(keyCode === 13){
|
if(keyCode === this.props.selectedKeyCode){
|
||||||
if(this.state.val != emptyVal){
|
if(this.state.val != emptyVal){
|
||||||
let option = data[index];
|
let option = data[index];
|
||||||
this.optionClick(option, this.props.sels.findIndex(item => item[value] === this.state.val) != -1, option[disabled], e)
|
this.optionClick(option, this.props.sels.findIndex(item => item[value] === this.state.val) != -1, option[disabled], e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewTo(val){
|
||||||
|
//键盘选中时滚动到可视范围内
|
||||||
|
if(void 0 != this.base){
|
||||||
|
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
|
||||||
|
opt && opt.scrollIntoView(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//组件将要接收新属性
|
//组件将要接收新属性
|
||||||
@ -239,7 +242,7 @@ 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, max, enableKeyboard } = config
|
let { data, flatData, prop, template, theme, radio, sels, empty, filterable, filterMethod, remoteSearch, remoteMethod, delay, searchTips, create, pageRemote, max, enableKeyboard, enableHoverFirst } = config
|
||||||
|
|
||||||
const { name, value, disabled, children, optgroup } = prop;
|
const { name, value, disabled, children, optgroup } = prop;
|
||||||
|
|
||||||
@ -286,8 +289,9 @@ class General extends Component{
|
|||||||
|
|
||||||
//如果是分组模式, 要分页先去除分组, 然后在计算分页, 最后再加上分组
|
//如果是分组模式, 要分页先去除分组, 然后在计算分页, 最后再加上分组
|
||||||
let groupInfo = {};
|
let groupInfo = {};
|
||||||
arr.filter(item => item[optgroup]).forEach(g => {
|
arr.filter(item => item[optgroup]).forEach((g, groupIndex) => {
|
||||||
g[children].forEach(item => groupInfo[item[value]] = g);
|
groupInfo[groupIndex] = g;
|
||||||
|
g[children].forEach(item => item.__group__index = groupIndex);
|
||||||
});
|
});
|
||||||
arr = arr.filter(item => !item[optgroup]);
|
arr = arr.filter(item => !item[optgroup]);
|
||||||
|
|
||||||
@ -342,7 +346,8 @@ class General extends Component{
|
|||||||
let newArr = [], group, tmpGroup = { __tmp: true };
|
let newArr = [], group, tmpGroup = { __tmp: true };
|
||||||
tmpGroup[optgroup] = true;
|
tmpGroup[optgroup] = true;
|
||||||
arr.forEach(item => {
|
arr.forEach(item => {
|
||||||
let g = groupInfo[item[value]];
|
let g = groupInfo[item.__group__index];
|
||||||
|
delete item.__group__index;
|
||||||
if(group && !g){
|
if(group && !g){
|
||||||
g = tmpGroup
|
g = tmpGroup
|
||||||
}
|
}
|
||||||
@ -357,7 +362,9 @@ class General extends Component{
|
|||||||
//查看是否创建了条目
|
//查看是否创建了条目
|
||||||
if(creator){
|
if(creator){
|
||||||
creator = create(this.state.filterValue, deepMerge([], arr));
|
creator = create(this.state.filterValue, deepMerge([], arr));
|
||||||
creator && arr.splice(0, 0, {...creator, __node: {}});
|
if(void 0 != creator){
|
||||||
|
arr.splice(0, 0, ...(isArray(creator) ? creator : [creator]).map(i => ({ ...i, __node: {} })));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let safetyArr = deepMerge([], arr);
|
let safetyArr = deepMerge([], arr);
|
||||||
@ -519,7 +526,11 @@ class General extends Component{
|
|||||||
|
|
||||||
arr = arr.map(renderGroup);
|
arr = arr.map(renderGroup);
|
||||||
|
|
||||||
if(!arr.length){
|
if(arr.length){
|
||||||
|
if(enableHoverFirst && this.state.val == emptyVal){
|
||||||
|
this.keydown('div', { keyCode: 40 })
|
||||||
|
}
|
||||||
|
}else{
|
||||||
//查看无数据情况下是否显示分页
|
//查看无数据情况下是否显示分页
|
||||||
!config.pageEmptyShow && (paging = '');
|
!config.pageEmptyShow && (paging = '');
|
||||||
arr.push(<div class="xm-select-empty">{ empty }</div>)
|
arr.push(<div class="xm-select-empty">{ empty }</div>)
|
||||||
|
@ -20,6 +20,7 @@ class Tree extends Component{
|
|||||||
this.inputOver = true;
|
this.inputOver = true;
|
||||||
this.__value = '';
|
this.__value = '';
|
||||||
this.tempData = [];
|
this.tempData = [];
|
||||||
|
this.__skipAutoExpand = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
init(props){
|
init(props){
|
||||||
@ -180,11 +181,14 @@ class Tree extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!hiddenStatus){//如果是显示状态
|
if(!hiddenStatus){//如果是显示状态
|
||||||
|
if(this.__skipAutoExpand != val){//第一次搜索默认展开过滤后的数据
|
||||||
let keys = this.state.expandedKeys;
|
let keys = this.state.expandedKeys;
|
||||||
if(val && keys.findIndex(key => key === item[value]) === -1){
|
if(val && keys.findIndex(key => key === item[value]) === -1){
|
||||||
keys.push(item[value]);
|
keys.push(item[value]);
|
||||||
this.setState({ expandedKeys: keys })
|
this.setState({ expandedKeys: keys })
|
||||||
}
|
}
|
||||||
|
this.__skipAutoExpand = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.__node.hidn = hiddenStatus;
|
item.__node.hidn = hiddenStatus;
|
||||||
@ -216,6 +220,7 @@ class Tree extends Component{
|
|||||||
//清空输入框的值
|
//清空输入框的值
|
||||||
this.setState({ filterValue: '', val: emptyVal });
|
this.setState({ filterValue: '', val: emptyVal });
|
||||||
this.__value = '';
|
this.__value = '';
|
||||||
|
this.__skipAutoExpand = '';
|
||||||
this.searchInputRef && (this.searchInputRef.value = '');
|
this.searchInputRef && (this.searchInputRef.value = '');
|
||||||
}else{
|
}else{
|
||||||
//聚焦输入框
|
//聚焦输入框
|
||||||
|
@ -26,6 +26,7 @@ class xmOptions {
|
|||||||
|
|
||||||
//记录最新的配置项
|
//记录最新的配置项
|
||||||
this.options = deepMerge(this.options, options);
|
this.options = deepMerge(this.options, options);
|
||||||
|
this.options.__render_success = false;
|
||||||
|
|
||||||
//如果dom不存在, 则不进行渲染事项
|
//如果dom不存在, 则不进行渲染事项
|
||||||
let { dom } = this.options;
|
let { dom } = this.options;
|
||||||
@ -46,6 +47,8 @@ class xmOptions {
|
|||||||
|
|
||||||
render(<Framework { ...this.options } __update={ Date.now() } updateData={ updateData } />, dom);
|
render(<Framework { ...this.options } __update={ Date.now() } updateData={ updateData } />, dom);
|
||||||
|
|
||||||
|
this.options.__render_success = true;
|
||||||
|
|
||||||
//返回多选对象
|
//返回多选对象
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -252,6 +255,23 @@ class xmOptions {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 滚动到某个选项
|
||||||
|
*/
|
||||||
|
scroll(val){
|
||||||
|
let opt = this.options.dom.querySelector(`.xm-option[value="${ val }"]`);
|
||||||
|
opt && opt.scrollIntoView(false)
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新计算下拉的位置
|
||||||
|
*/
|
||||||
|
calcPosition(){
|
||||||
|
childData[this.options.el].calcPosition()
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default xmOptions;
|
export default xmOptions;
|
||||||
|
@ -81,6 +81,10 @@ export default function (lan = 'zn') {
|
|||||||
showCount: 0,
|
showCount: 0,
|
||||||
//是否开启键盘操作
|
//是否开启键盘操作
|
||||||
enableKeyboard: true,
|
enableKeyboard: true,
|
||||||
|
//开启键盘操作后是否默认选中第一条
|
||||||
|
enableHoverFirst: true,
|
||||||
|
//键盘选中的KeyCode, 默认是Enter
|
||||||
|
selectedKeyCode: 13,
|
||||||
//工具条
|
//工具条
|
||||||
toolbar: {
|
toolbar: {
|
||||||
show: false,
|
show: false,
|
||||||
@ -160,7 +164,7 @@ export default function (lan = 'zn') {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
icon: 'show',
|
icon: 'show',
|
||||||
type: 'absolute',
|
type: 'absolute',//可选值, relative, fixed
|
||||||
},
|
},
|
||||||
//自定义选中的图标
|
//自定义选中的图标
|
||||||
iconfont: {
|
iconfont: {
|
||||||
@ -184,6 +188,12 @@ export default function (lan = 'zn') {
|
|||||||
//监听选中事件
|
//监听选中事件
|
||||||
on({ arr, item, selected }){
|
on({ arr, item, selected }){
|
||||||
|
|
||||||
|
},
|
||||||
|
submitConversion(sels, prop){
|
||||||
|
return sels.map(item => item[prop.value]).join(',')
|
||||||
|
},
|
||||||
|
done(){
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
src/index.js
34
src/index.js
@ -1,4 +1,4 @@
|
|||||||
import { name, version } from '../package.json'
|
import { name, version, website } from '../package.json'
|
||||||
import { selector, warn } from '@/common/util'
|
import { selector, warn } from '@/common/util'
|
||||||
import Select from '@/components/xm-select'
|
import Select from '@/components/xm-select'
|
||||||
|
|
||||||
@ -9,6 +9,11 @@ export const childData = {};
|
|||||||
export default {
|
export default {
|
||||||
name,
|
name,
|
||||||
version,
|
version,
|
||||||
|
doc: website,
|
||||||
|
KeyCode: {
|
||||||
|
Enter: 13,
|
||||||
|
Space: 32,
|
||||||
|
},
|
||||||
render(options) {
|
render(options) {
|
||||||
let { el } = options;
|
let { el } = options;
|
||||||
options.dom = selector(el);
|
options.dom = selector(el);
|
||||||
@ -23,7 +28,7 @@ export default {
|
|||||||
|
|
||||||
let instance = new Select(options);
|
let instance = new Select(options);
|
||||||
//已经渲染
|
//已经渲染
|
||||||
if (instance) {
|
if (instance && instance.options.__render_success) {
|
||||||
datas[el] = instance;
|
datas[el] = instance;
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
@ -54,18 +59,25 @@ export default {
|
|||||||
args.splice(0, 2);
|
args.splice(0, 2);
|
||||||
return this.get(filter).map(instance => instance[method](...args));
|
return this.get(filter).map(instance => instance[method](...args));
|
||||||
},
|
},
|
||||||
arr2tree(arr, pid, id, children, topParentId){
|
arr2tree(arr=[], pid='pid', id='id', children='children', topParentId=0){
|
||||||
arr.forEach(item => {
|
const datas = {} // 数据缓存
|
||||||
if(item[pid] != topParentId){
|
return safety(arr).filter(item =>{
|
||||||
let parent = arr.find(i => i[id] === item[pid])
|
const _id = item[id] // 提取自身id 必须唯一
|
||||||
if(parent){
|
const _pid = item[pid] // 提取父级id
|
||||||
if(!parent[children]){
|
const self = datas[_id] // 读取数据缓存中的数据
|
||||||
parent[children] = [];
|
let parent = datas[_pid] // 读取父级数据
|
||||||
|
if(self){ // 如果缓存中有数据
|
||||||
|
item[children] = self[children] // 把缓存的数据保存到自身
|
||||||
}
|
}
|
||||||
parent[children].push(item)
|
datas[_id] = item // 替换缓存中的数据
|
||||||
|
if(!parent){ // 如果没有找到父级数据
|
||||||
|
parent = { // 创建一个全新的父级数据
|
||||||
|
[children]:[]
|
||||||
}
|
}
|
||||||
|
datas[_pid] = parent // 记录到缓存
|
||||||
}
|
}
|
||||||
|
parent.push(item) // 推送自身到父级
|
||||||
|
return id == topParentId // 这里用 == 防止出现 字符串不等于数字类型的情况
|
||||||
})
|
})
|
||||||
return arr.filter(i => i[pid] == topParentId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
src/main.js
13
src/main.js
@ -1,7 +1,8 @@
|
|||||||
import '@/common/expand'
|
import '@/common/expand'
|
||||||
import '@/style/index.less'
|
|
||||||
import '@/style/iconfont.less'
|
import '@/style/iconfont.less'
|
||||||
|
import '@/style/index.less'
|
||||||
import { default as xmSelect, datas } from './index.js';
|
import { default as xmSelect, datas } from './index.js';
|
||||||
|
import {throttle} from '@/common/util'
|
||||||
|
|
||||||
const moduleName = 'xmSelect';
|
const moduleName = 'xmSelect';
|
||||||
|
|
||||||
@ -15,6 +16,16 @@ window.addEventListener('click', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听页面滚动事件
|
||||||
|
*/
|
||||||
|
window.addEventListener('scroll', throttle(() => {
|
||||||
|
Object.keys(datas).forEach(key => {
|
||||||
|
let item = datas[key]
|
||||||
|
item && item.calcPosition && item.calcPosition()
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object') {
|
if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object') {
|
||||||
module.exports = xmSelect;
|
module.exports = xmSelect;
|
||||||
|
@ -61,7 +61,7 @@ xm-select{
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
&:hover{
|
&:hover,&:focus{
|
||||||
border-color: #C0C4CC;
|
border-color: #C0C4CC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,11 +109,13 @@ xm-select{
|
|||||||
}
|
}
|
||||||
.label-content{
|
.label-content{
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.auto-row{
|
&.auto-row{
|
||||||
.label-content{
|
.label-content{
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
padding-right: 30px !important;
|
||||||
}
|
}
|
||||||
.xm-label-block > span{
|
.xm-label-block > span{
|
||||||
white-space: unset;
|
white-space: unset;
|
||||||
@ -124,7 +126,7 @@ xm-select{
|
|||||||
.scroll{
|
.scroll{
|
||||||
.label-content{
|
.label-content{
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 3px 30px 3px 10px;
|
padding: 3px 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,6 +488,9 @@ xm-select{
|
|||||||
.disabled .xm-right-arrow{
|
.disabled .xm-right-arrow{
|
||||||
color: #C2C2C2 !important;
|
color: #C2C2C2 !important;
|
||||||
}
|
}
|
||||||
|
.hide-icon.disabled .xm-right-arrow{
|
||||||
|
color: #999 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user