Compare commits

...

10 Commits

Author SHA1 Message Date
e7e9ee2a65 修改定位方式为固定定位,修正定位位置 2023-08-18 16:54:10 +08:00
maplemei
05dc278d28
update README.md.
Signed-off-by: maplemei <hnzzmsf@126.com>
2023-04-08 08:25:17 +00:00
maplemei
170d8ab0d1 bug fixed 2021-07-22 13:16:36 +08:00
maplemei
8774b1f79c 修正版本号 2021-06-30 14:00:28 +08:00
maplemei
479b26fa7f v1.2.3 2021-06-30 13:56:07 +08:00
ruanlinxin
eb4e6fa66e <rlx>
1.添加节流方法
2.滚动监听添加节流
2021-05-31 22:22:34 +08:00
ruanlinxin
12b7f0ce0e <rlx>
1. 优化arr2tree
2021-05-31 22:04:11 +08:00
maplemei
f8fa44c94c u 2021-05-28 18:30:08 +08:00
maplemei
e928c6a038 新增fixed布局 2021-01-19 16:28:25 +08:00
maplemei
b47c8cfcb9 修复渲染失败页面监听错误的问题 2020-12-04 16:39:55 +08:00
27 changed files with 151308 additions and 370 deletions

View File

@ -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
*2020-11-27*

279
README.md
View File

@ -1,76 +1,79 @@
# xm-select
#### 介绍
始于Layui, 下拉选择框的多选解决方案
前身[前往formSelectes](https://github.com/hnzzmsf/layui-formSelects), 由于渲染速度慢, 代码冗余, 被放弃了
`xm-select`使用了新的开发方式, 利用preact进行渲染, 大幅度提高渲染速度, 并且可以灵活拓展
[xm-select演示站点](https://maplemei.gitee.io/xm-select/)
> 支持功能
- [x] 国际化 - 中文/英文
- [x] 多选
- [x] 单选
- [x] 重复选
- [x] 分组
- [x] 工具条
- [x] 创建条目
- [x] 显示模式
- [x] 搜索模式 (本地数据过滤, 远程搜索)
- [x] 分页模式
- [x] 下拉树
- [x] 下拉任意 - 可以自己写html
> 联系方式
- xm-select技术群①: `660408068` (500人)
- xm-select技术群②: `938624691` (500人)
- xm-select技术群③: `1145047250` (500人)
[issues 需求记录](https://gitee.com/maplemei/xm-select/issues/I1NSO7)
[更新日志](CHANGELOG.md)
#### 软件架构
1. 引入第三方[preact](https://preactjs.com/)库, 利用jsx渲染页面结构
2. 使用[webpack](https://www.webpackjs.com/)进行打包
#### 快读上手
> 直接使用
```
1. 引入 `dist/xm-select.js`
2. 写一个`<div id="demo1"></div>`
3. 渲染
var demo1 = xmSelect.render({
el: '#demo1',
data: [
{name: '水果', value: 1, selected: true, disabled: true},
{name: '蔬菜', value: 2, selected: true},
{name: '桌子', value: 3, disabled: true},
{name: '北京', value: 4},
],
})
```
> 二次开发
```
1. git clone https://gitee.com/maplemei/xm-select.git
2. cd xm-select
3. yarn 或者 npm install
```
[目录结构说明](https://gitee.com/maplemei/xm-select/wikis/pages?sort_id=2465940&doc_id=820743)
# xm-select
#### 介绍
始于Layui, 下拉选择框的多选解决方案
前身[前往formSelectes](https://github.com/hnzzmsf/layui-formSelects), 由于渲染速度慢, 代码冗余, 被放弃了
`xm-select`使用了新的开发方式, 利用preact进行渲染, 大幅度提高渲染速度, 并且可以灵活拓展
[xm-select演示站点](https://maplemei.gitee.io/xm-select/)
> 支持功能
- [x] 国际化 - 中文/英文
- [x] 多选
- [x] 单选
- [x] 重复选
- [x] 分组
- [x] 工具条
- [x] 创建条目
- [x] 显示模式
- [x] 搜索模式 (本地数据过滤, 远程搜索)
- [x] 分页模式
- [x] 下拉树
- [x] 下拉任意 - 可以自己写html
> 联系方式
![输入图片说明](add.png)
- xm-select技术群①: `660408068` (500人)
- xm-select技术群②: `938624691` (500人)
- xm-select技术群③: `1145047250` (500人)
[issues 需求记录](https://gitee.com/maplemei/xm-select/issues)
[更新日志](CHANGELOG.md)
#### 软件架构
1. 引入第三方[preact](https://preactjs.com/)库, 利用jsx渲染页面结构
2. 使用[webpack](https://www.webpackjs.com/)进行打包
#### 快读上手
> 直接使用
```
1. 引入 `dist/xm-select.js`
2. 写一个`<div id="demo1"></div>`
3. 渲染
var demo1 = xmSelect.render({
el: '#demo1',
data: [
{name: '水果', value: 1, selected: true, disabled: true},
{name: '蔬菜', value: 2, selected: true},
{name: '桌子', value: 3, disabled: true},
{name: '北京', value: 4},
],
})
```
> 二次开发
```
1. git clone https://gitee.com/maplemei/xm-select.git
2. cd xm-select
3. yarn 或者 npm install
```
[目录结构说明](https://gitee.com/maplemei/xm-select/wikis/pages?sort_id=2465940&doc_id=820743)
> 打赏
如果喜欢作者的插件, 可以请作者吃雪糕 ^_^
@ -80,68 +83,68 @@
<img src="docs/assets/wx.jpg" alt="打赏" width="300" style="border: 1px solid #EFEFEF">
</a>
</p>
#### 示例
[示例页面](https://maplemei.gitee.io/xm-select/)
> 一个小栗子
```
<!-- 占位 -->
<div id="demo1"></div>
<!-- 引入插件 -->
<script src="../dist/xm-select.js" type="text/javascript" charset="utf-8"></script>
<!-- 渲染页面 -->
<script type="text/javascript">
var demo1 = xmSelect.render({
// 这里绑定css选择器
el: '#demo1',
// 渲染的数据
data: [
{name: '水果', value: 1, selected: true, disabled: true},
{name: '蔬菜', value: 2, selected: true},
{name: '桌子', value: 3, disabled: true},
{name: '北京', value: 4},
],
})
// 变量, demo1 可以通过API操作
// 获取选中值, demo1.getValue();
// 设置选中值, demo1.setValue([{ name: '动态值', value: 999 }])
// ...
</script>
```
#### 相关
> 支持IE吗
简单适配IE10以上的版本, 如有其它兼容性问题, 请加群反馈
> 为什么没有css文件
已经内置到js代码中了, 直接引入`xm-select.js`即可使用
> 开源 != 无私
有问题请自己多动手尝试^_^
> 成长之路
```
maplemei, 一个90后, 热爱前端的程序猿
16年接触了 贤心大大 的 layui, 开始走向了前端的不归之路
17年尝试自己写了一个基于layui的省市区联动插件, 同年底开发了layui select多选第一版
18年6月发布了formSelects
19年6月发布了xm-select
其实每个版本的更新都是自己对前端的一个新的认知, 也是一个新的学习之路
目前作者几乎不怎么使用layui, 已经走向了vue, react的新途 , xm-select的维护是对layui的一种情怀 ^_^
```
#### 示例
[示例页面](https://maplemei.gitee.io/xm-select/)
> 一个小栗子
```
<!-- 占位 -->
<div id="demo1"></div>
<!-- 引入插件 -->
<script src="../dist/xm-select.js" type="text/javascript" charset="utf-8"></script>
<!-- 渲染页面 -->
<script type="text/javascript">
var demo1 = xmSelect.render({
// 这里绑定css选择器
el: '#demo1',
// 渲染的数据
data: [
{name: '水果', value: 1, selected: true, disabled: true},
{name: '蔬菜', value: 2, selected: true},
{name: '桌子', value: 3, disabled: true},
{name: '北京', value: 4},
],
})
// 变量, demo1 可以通过API操作
// 获取选中值, demo1.getValue();
// 设置选中值, demo1.setValue([{ name: '动态值', value: 999 }])
// ...
</script>
```
#### 相关
> 支持IE吗
简单适配IE10以上的版本, 如有其它兼容性问题, 请加群反馈
> 为什么没有css文件
已经内置到js代码中了, 直接引入`xm-select.js`即可使用
> 开源 != 无私
有问题请自己多动手尝试^_^
> 成长之路
```
maplemei, 一个90后, 热爱前端的程序猿
16年接触了 贤心大大 的 layui, 开始走向了前端的不归之路
17年尝试自己写了一个基于layui的省市区联动插件, 同年底开发了layui select多选第一版
18年6月发布了formSelects
19年6月发布了xm-select
其实每个版本的更新都是自己对前端的一个新的认知, 也是一个新的学习之路
目前作者几乎不怎么使用layui, 已经走向了vue, react的新途 , xm-select的维护是对layui的一种情怀 ^_^
```

BIN
add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

View File

@ -1,4 +1,4 @@
const pkg = require('../package.json');
const pkg = require('../package.json');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
@ -7,14 +7,14 @@ const {
CleanWebpackPlugin
} = require('clean-webpack-plugin');
const isProd = process.env.NODE_ENV === 'prod';
const banner =
`@Title: ${pkg.name}
@Version: ${pkg.version}
@Description基于layui的多选解决方案
@Site: https://gitee.com/maplemei/xm-select
@Author: maplemei
const isProd = process.env.NODE_ENV === 'prod';
const banner =
`@Title: ${pkg.name}
@Version: ${pkg.version}
@Description基于layui的多选解决方案
@Site: https://gitee.com/maplemei/xm-select
@Author: maplemei
@LicenseApache License 2.0`;
const webpackConfig = {
@ -90,13 +90,16 @@ const webpackConfig = {
minify: {
collapseWhitespace: true
}
}),
new webpack.BannerPlugin(banner),
}),
new webpack.BannerPlugin(banner),
new VueLoaderPlugin(),
],
optimization: {
minimize: false,//可以自行配置是否压缩
},
devServer: {
host: '0.0.0.0',
port: 9000,
port: 9001,
publicPath: '/',
hot: true
},
@ -109,4 +112,4 @@ if (isProd) {
}
module.exports = webpackConfig;
module.exports = webpackConfig;

13471
dist/static/2.js vendored

File diff suppressed because one or more lines are too long

1128
dist/static/3.js vendored

File diff suppressed because one or more lines are too long

120377
dist/static/docs.js vendored

File diff suppressed because one or more lines are too long

8
dist/xm-select.js vendored

File diff suppressed because one or more lines are too long

View File

@ -38,6 +38,8 @@
this.fixedControl = bottom > document.documentElement.clientHeight &&
top + 44 <= document.documentElement.clientHeight;
this.$refs.control.style.left = this.fixedControl ? `${ left }px` : '0';
xmSelect.get().forEach(xs => xs.calcPosition());
},
removeScrollHandler() {
this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler);

View File

@ -4,10 +4,7 @@
<div class="container">
<h1>
<router-link :to="`/`">
<!-- logo -->
<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
</slot>
</router-link>
@ -19,7 +16,7 @@
<router-link active-class="active" :to="`/`">使用手册</router-link>
</li>
<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 class="nav-item">

View File

@ -48,7 +48,7 @@ layui.use('table', function() {
//修改一些css样式, 这里虽然能够使用, 但是还是不太友好, 努力中...
var cells = document.querySelectorAll('div[lay-id="demo"] .layui-table-cell');
for(var i = 0 ; i < cells.length ; i++ ){
cells[i].style.overflow = 'unset';
//cells[i].style.overflow = 'unset';
cells[i].style.height = 'auto';
}
//渲染多选
@ -56,6 +56,7 @@ layui.use('table', function() {
var xm = xmSelect.render({
el: '#XM-' + item.id,
autoRow: true,
model: { type: 'fixed' },
data: [
{name: '张三', value: 1},
{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>
```

View File

@ -5,46 +5,55 @@
<div id="demo1" class="xm-select-demo"></div>
<script>
var demo1 = xmSelect.render({
xmSelect.render({
el: '#demo1',
autoRow: true,
filterable: true,
tree: {
show: true,
showFolderIcon: true,
showLine: true,
indent: 20,
expandedKeys: [ -3 ],
simple: true,
clickExpand: false,
clickCheck: false,
strict: false
},
toolbar: {
show: true,
list: ['ALL', 'REVERSE', 'CLEAR']
height: '200px',
maxMethod(a, item){
console.log(item)
},
submitConversion(sels, prop){
return sels.map(item => item[prop.name]).join(',')
},
filterable: true,
height: 'auto',
data(){
return [
{name: '销售员', value: -1, disabled: true, children: [
{name: '销售员', value: -1, disabled: false, children: [
{name: '张三1', value: 1, selected: true, children: []},
{name: '李四1', value: 2, selected: true},
{name: '王五1', value: 3, disabled: true},
{name: '王五1', value: 13, 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: 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: 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>

View File

@ -46,12 +46,16 @@
| toolbar | 工具条, 具体看下表 | object | - | - |
| showCount | 展示在下拉框中的最多选项数量 | int | - | 0 |
| 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 |
| size | 尺寸 | string | large / medium / small / mini | medium |
| disabled | 是否禁用多选 | boolean | true / false | false |
| create | 创建条目 | function(val, data), val: 搜索的数据, data: 当前下拉数据 | - | null |
| tree | 树形结构, 具体看下表 | object | - | - |
| cascader | 级联结构, 具体看下表 | object | - | - |
| submitConversion | 配置表单提交数据 | function(sels, prop), sels: 已选中数据, prop: 自定义的prop | - | - |
| done | 渲染完成回调 | function | - | - |
### prop
@ -134,7 +138,7 @@ model: {
}
},
},
//展示类型, 下拉框形式: absolute, 直接显示模式: relative
//展示类型, 下拉框形式: absolute, 直接显示模式: relative, 浮动布局: fixed
type: 'absolute',
},
```
@ -211,7 +215,7 @@ list: [ "ALL", "CLEAR",
| render | 渲染多选 | (options: 配置项) | 实例对象 |
| get | 获取页面中已经渲染的多选 | (filter: 过滤`el`, single: 是否返回单实例) | 符合条件的实例数组 |
| 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 使用方式
@ -258,3 +262,4 @@ xmSelect.render()后会返回一个xmSelect对象, 可以进行方法调用
| changeExpandedKeys | 树模式下更新节点展开状态, v1.2.0 新增 | (keys: true-全部展开, false-全部关闭, 数组-展开的节点值) |
| enable | 启用选项, disabled=false, v1.2.0 新增 | (array: 想要启用的选项数组) |
| disable | 禁用用选项, disabled=true, v1.2.0 新增 | (array: 想要禁用的选项数组) |
| calcPosition | fixed布局模式下重新计算位置, v1.2.2 新增 | - |

9070
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +1,46 @@
{
"name": "xm-select",
"version": "1.2.1",
"version": "1.2.4",
"description": "始于Layui的select多选解决方案",
"website": "https://maplemei.gitee.io/xm-select",
"main": "index.js",
"scripts": {
"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"
},
"author": "",
"author": "maplemei",
"license": "ISC",
"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/preset-env": "^7.4.2",
"babel-loader": "^8.0.5",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-polyfill": "^6.26.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^6.0.0",
"css-loader": "^3.0.0",
"element-ui": "^2.12.0",
"file-loader": "^4.2.0",
"highlight.js": "^9.15.10",
"html-webpack-plugin": "^3.2.0",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"markdown-it": "^10.0.0",
"markdown-it-anchor": "^5.2.4",
"markdown-it-chain": "^1.3.0",
"markdown-it-container": "^2.0.0",
"preact": "^10.4.6",
"style-loader": "^0.23.1",
"transliteration": "^2.1.7",
"url-loader": "^2.1.0",
"vue": "^2.6.10",
"vue-loader": "^15.7.1",
"vue-router": "^3.1.3",
"vue-template-compiler": "^2.6.10",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1"
},
"devDependencies": {
"html-webpack-plugin": "^3.2.0"
}
}

6638
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -163,31 +163,49 @@ export function exchangeOptionsData(arr, { prop }){
}
}
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;
})
}
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;
})
}
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)
}
}

View File

@ -31,11 +31,12 @@ class Framework extends Component{
show: false,
tmpColor: '',
bodyClass: '',
time: 0,
}
}
init(props, refresh){
let { data, prop, initValue, radio } = props, sels;
let { data, prop, initValue, radio, tree, cascader } = props, sels;
//如果新数据和旧数据不同 或者 强制刷新 才进行数据处理
if(refresh){
let dataObj = {};
@ -47,6 +48,9 @@ class Framework extends Component{
}), dataObj)
if(radio && sels.length > 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 });
}
@ -116,7 +120,7 @@ class Framework extends Component{
return cgList;
}
value(sels, show, listenOn, jsChangeData){
value(sels, show, listenOn, jsChangeData, isAdd = true){
if(show !== false && show !== true){
show = this.state.show;
}
@ -125,7 +129,7 @@ class Framework extends Component{
let changeData = this.exchangeValue(sels);
//检测是否超选了
if(this.checkMax(changeData, changeData)){
if(this.checkMax(changeData, changeData, true)){
return ;
}
@ -134,19 +138,28 @@ class Framework extends Component{
this.clearAndReset(data, changeData, false);
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 })
}
clearAndReset(data, changeData, parentCK){
const { selected, children, value } = this.props.prop;
const { selected, disabled, children, value } = this.props.prop;
data.forEach(item => {
item[selected] = changeData.findIndex(c => c[value] === item[value]) != -1 || parentCK;
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){
const { prop, tree, cascader } = this.props;
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
//查看是否设置了多选上限
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);
//查看是否需要回调
maxMethod && isFunction(maxMethod) && maxMethod(sels, item);
@ -252,14 +266,14 @@ class Framework extends Component{
//选项, 选中状态, 禁用状态, 是否强制删除:在label上点击删除
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 ]
const { value, selected, disabled, children, optgroup } = prop
//如果是禁用状态, 不能进行操作
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;
if(item.__node.selected){
handlerType = 'del';
@ -280,7 +294,7 @@ class Framework extends Component{
this.treeHandler(sels, item, change, handlerType);
}
if(this.checkMax(change, change)){
if(this.checkMax(change, sels)){//TODO 这里还是有问题, 如果是取消的
return ;
}
sels = [ ...this.state.sels ], change = [];
@ -308,6 +322,7 @@ class Framework extends Component{
}else{
sels = [...sels, item]
}
this.clearAndReset(data, sels, itemSelected)
this.resetSelectValue(sels, [item], !itemSelected);
}
}
@ -387,7 +402,7 @@ class Framework extends Component{
}else
//树状结构数据更新
if(type === 'treeData'){
this.value(data, null, true)
this.value(data, null, true, false, false)
}else
//树状结构数据更新
if(type === 'close'){
@ -399,7 +414,7 @@ class Framework extends Component{
}else
//聚焦label搜索框
if(type === 'labelSearchBlur'){
this.labelRef.blur(data);
this.labelRef && this.labelRef.blur(data);
}else
//聚焦label搜索框
if(type === 'labelSearch'){
@ -422,7 +437,7 @@ class Framework extends Component{
sels.splice(index, 1);
}
});
this.value(sels, this.props.show, true, changeData)
this.value(sels, this.props.show, true, changeData, false)
}
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){
this.init(props, props.updateData);
@ -450,7 +480,7 @@ class Framework extends Component{
}
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 };
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 bodyStyle = this.calcPosition();
return (
<xm-select { ...xmSelectProps } >
@ -490,12 +521,12 @@ class Framework extends Component{
lay-verType={ config.layVerType }
lay-reqText={ config.layReqText }
name={ config.name }
value={ sels.map(item => item[prop.value]).join(',') }
value={ submitConversion(sels, prop) }
></input>
<i class={ show ? 'xm-icon xm-icon-expand' : 'xm-icon' } />
{ sels.length === 0 && <div class="xm-tips">{ config.tips }</div> }
<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 }
</div>
{ disabled && <div class="xm-select-disabled"></div> }
@ -510,10 +541,15 @@ class Framework extends Component{
//监听键盘事件
this.base.addEventListener('keydown', e => {
let keyCode = e.keyCode;
//ENTER
if(keyCode === 13){
this.onClick()
this.onClick(e)
}
});
// focus 可以监听tab切换
// this.base.addEventListener('focus', e => {
// })
//表单验证
this.input = this.base.querySelector('.xm-select-default');
@ -528,7 +564,7 @@ class Framework extends Component{
this.input.className = 'xm-select-default';
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;
}
let { done } = this.props;
done && done();
}
//此时页面又被重新渲染了
@ -558,7 +596,12 @@ class Framework extends Component{
return ;
}
if(model.type === 'fixed'){
return ;
}
let rect = this.base.getBoundingClientRect();
let bodyViewHeight;
if(direction === 'auto'){
//用于控制js获取下拉框的高度
this.bodyView.style.display = 'block';
@ -566,7 +609,7 @@ class Framework extends Component{
//获取下拉元素的高度
let bodyViewRect = this.bodyView.getBoundingClientRect();
let bodyViewHeight = bodyViewRect.height;
bodyViewHeight = bodyViewRect.height;
//还原控制效果
this.bodyView.style.display = '';
@ -578,13 +621,12 @@ class Framework extends Component{
let diff = clientHeight - y - rect.height - 20;
direction = diff > bodyViewHeight || y < diff ? 'down' : 'up';
}
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';
}else{
this.bodyView.style.top = 'auto';
this.bodyView.style.bottom = rect.height + 4 + 'px';
this.bodyView.style.top = (4 +rect.y - bodyViewHeight )+ 'px';
this.bodyView.style.bottom = 'auto';
}
}

View File

@ -36,6 +36,65 @@ class Label extends Component{
let input = this.base.querySelector('.label-search-input');
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(){
if (this.labelRef.addEventListener) {
@ -81,9 +140,13 @@ class Label extends Component{
html = arr.splice(0, count).map(sel => {
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 (
<div class={className} style={ style }>
<div class={className} style={ style }
>
{ conf.template && isFunction(conf.template) ? (
<span style={ styleProps } dangerouslySetInnerHTML={{ __html: conf.template(sel, arr) }}></span>
) : (
@ -104,7 +167,10 @@ class Label extends Component{
}
}else if(type == 'search'){
innerHTML = false;
let one = list[0][name];
let one = '';
if(list.length){
one = list[0][name]
}
html = (
<input class="label-search-input" type="text" placeholder={ config.searchTips } style={{ width: '100%', border: 'none' }} value={

View File

@ -18,7 +18,7 @@ class Cascader extends Component{
optionClick(item, selected, disabled, type, index, e){
if(type === 'line'){
if(disabled){
if(!item.optgroup && disabled){
return ;
}
//加载中的不需要进行处理
@ -57,7 +57,7 @@ class Cascader extends Component{
render(config, state) {
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 renderItem = (item, indent, index, checked) => {
@ -108,6 +108,12 @@ class Cascader extends Component{
itemStyle.backgroundColor = theme.hover
}
//隐藏图标的处理
if(!showIcon && selected){
itemStyle.backgroundColor = theme.color;
dis && (itemStyle.backgroundColor = '#C2C2C2');
}
const contentStyle = {}, checkedStyle = {};
if(checked){
contentStyle.color = theme.color
@ -133,7 +139,7 @@ class Cascader extends Component{
} onMouseEnter={ hoverChange } onMouseLeave={ hoverChange }>
{ 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>
{ item[children] && <div class={ checkedClass } style={ checkedStyle }></div> }
{ item[optgroup] && <div class={ checkedClass } style={ checkedStyle }></div> }
</div>
)
}
@ -146,7 +152,7 @@ class Cascader extends Component{
const checked = child && this.state.expand[index] === item[value];
checked && boxArr.push(
<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>
)
return renderItem(item, indent, index, checked)
@ -161,7 +167,7 @@ class Cascader extends Component{
}
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 }
</div>
)

View File

@ -87,7 +87,7 @@ class General extends Component{
}
}
searchInput(e){
searchInput(e){
let v = e.target.value;
if(v === this.__value){
@ -115,7 +115,7 @@ class General extends Component{
this.searchInputRef && this.searchInputRef.blur();
}
handleComposition(e){
handleComposition(e){
let type = e.type;
if(type === 'compositionstart'){
@ -164,7 +164,7 @@ class General extends Component{
this.pageNextClick();
}
}
if(this.props.enableKeyboard){
const { value, optgroup, disabled } = this.props.prop;
let data = this.tempData.filter(item => !item[optgroup] && !item[disabled]);
@ -172,7 +172,7 @@ class General extends Component{
if(len === -1){
return ;
}
let index = data.findIndex(item => item[value] === this.state.val);
//Up 键
if(keyCode === 38){
@ -183,9 +183,7 @@ class General extends Component{
}
let val = data[index][value];
this.setState({ val })
//键盘选中时滚动到可视范围内
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
opt && opt.scrollIntoView(false)
this.viewTo(val);
}else
//Down 键
if(keyCode === 40){
@ -196,19 +194,24 @@ class General extends Component{
}
let val = data[index][value];
this.setState({ val })
//键盘选中时滚动到可视范围内
let opt = this.base.querySelector(`.xm-option[value="${ val }"]`);
opt && opt.scrollIntoView(false)
this.viewTo(val);
}else
//Enter 键
if(keyCode === 13){
if(keyCode === this.props.selectedKeyCode){
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)
}
}
}
}
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) {
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;
@ -286,8 +289,9 @@ class General extends Component{
//如果是分组模式, 要分页先去除分组, 然后在计算分页, 最后再加上分组
let groupInfo = {};
arr.filter(item => item[optgroup]).forEach(g => {
g[children].forEach(item => groupInfo[item[value]] = g);
arr.filter(item => item[optgroup]).forEach((g, groupIndex) => {
groupInfo[groupIndex] = g;
g[children].forEach(item => item.__group__index = groupIndex);
});
arr = arr.filter(item => !item[optgroup]);
@ -342,7 +346,8 @@ class General extends Component{
let newArr = [], group, tmpGroup = { __tmp: true };
tmpGroup[optgroup] = true;
arr.forEach(item => {
let g = groupInfo[item[value]];
let g = groupInfo[item.__group__index];
delete item.__group__index;
if(group && !g){
g = tmpGroup
}
@ -356,8 +361,10 @@ class General extends Component{
//查看是否创建了条目
if(creator){
creator = create(this.state.filterValue, deepMerge([], arr));
creator && arr.splice(0, 0, {...creator, __node: {}});
creator = create(this.state.filterValue, deepMerge([], arr));
if(void 0 != creator){
arr.splice(0, 0, ...(isArray(creator) ? creator : [creator]).map(i => ({ ...i, __node: {} })));
}
}
let safetyArr = deepMerge([], arr);
@ -519,10 +526,14 @@ class General extends Component{
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 = '');
arr.push(<div class="xm-select-empty">{ empty }</div>)
arr.push(<div class="xm-select-empty">{ empty }</div>)
}
return (

View File

@ -20,6 +20,7 @@ class Tree extends Component{
this.inputOver = true;
this.__value = '';
this.tempData = [];
this.__skipAutoExpand = '';
}
init(props){
@ -59,7 +60,7 @@ class Tree extends Component{
const { tree, prop, sels } = this.props;
const { clickExpand, clickCheck } = tree;
//检测点击的是不是三角箭头
let isExpand = e.target && isFunction(e.target.getAttribute) && e.target.getAttribute('type') === 'expand'
//如果点击即展开
@ -180,10 +181,13 @@ class Tree extends Component{
}
if(!hiddenStatus){//如果是显示状态
let keys = this.state.expandedKeys;
if(val && keys.findIndex(key => key === item[value]) === -1){
keys.push(item[value]);
this.setState({ expandedKeys: keys })
if(this.__skipAutoExpand != val){//第一次搜索默认展开过滤后的数据
let keys = this.state.expandedKeys;
if(val && keys.findIndex(key => key === item[value]) === -1){
keys.push(item[value]);
this.setState({ expandedKeys: keys })
}
this.__skipAutoExpand = val;
}
}
}
@ -216,6 +220,7 @@ class Tree extends Component{
//清空输入框的值
this.setState({ filterValue: '', val: emptyVal });
this.__value = '';
this.__skipAutoExpand = '';
this.searchInputRef && (this.searchInputRef.value = '');
}else{
//聚焦输入框

View File

@ -25,7 +25,8 @@ class xmOptions {
let updateData = !!options.data;
//记录最新的配置项
this.options = deepMerge(this.options, options);
this.options = deepMerge(this.options, options);
this.options.__render_success = false;
//如果dom不存在, 则不进行渲染事项
let { dom } = this.options;
@ -45,7 +46,9 @@ class xmOptions {
}
render(<Framework { ...this.options } __update={ Date.now() } updateData={ updateData } />, dom);
this.options.__render_success = true;
//返回多选对象
return this;
}
@ -213,7 +216,7 @@ class xmOptions {
return arr;
}
/**
* 动态操作树状结构的节点展开状态
*/
@ -250,6 +253,23 @@ class xmOptions {
}
childData[this.options.el].upDate(sels, false)
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;
}
}

View File

@ -80,7 +80,11 @@ export default function (lan = 'zn') {
//选项显示数量
showCount: 0,
//是否开启键盘操作
enableKeyboard: true,
enableKeyboard: true,
//开启键盘操作后是否默认选中第一条
enableHoverFirst: true,
//键盘选中的KeyCode, 默认是Enter
selectedKeyCode: 13,
//工具条
toolbar: {
show: false,
@ -160,7 +164,7 @@ export default function (lan = 'zn') {
},
},
icon: 'show',
type: 'absolute',
type: 'absolute',//可选值, relative fixed
},
//自定义选中的图标
iconfont: {
@ -184,6 +188,12 @@ export default function (lan = 'zn') {
//监听选中事件
on({ arr, item, selected }){
}
},
submitConversion(sels, prop){
return sels.map(item => item[prop.value]).join(',')
},
done(){
}
}
}

View File

@ -1,15 +1,20 @@
import { name, version } from '../package.json'
import { selector, warn } from '@/common/util'
import Select from '@/components/xm-select'
import { name, version, website } from '../package.json'
import { selector, warn } from '@/common/util'
import Select from '@/components/xm-select'
export const datas = {};
export const optionData = {};
export const childData = {};
export default {
name,
export default {
name,
version,
render(options) {
doc: website,
KeyCode: {
Enter: 13,
Space: 32,
},
render(options) {
let { el } = options;
options.dom = selector(el);
@ -20,52 +25,59 @@ export default {
options.el = el;
}
optionData[el] = options;
let instance = new Select(options);
//已经渲染
if (instance) {
//已经渲染
if (instance && instance.options.__render_success) {
datas[el] = instance;
}
return instance;
},
get(filter, single) {
let type = Object.prototype.toString.call(filter);
let method;
switch (type) {
case '[object String]':
filter && (method = item => item === filter);
break;
case '[object RegExp]':
method = item => filter.test(item);
break;
case '[object Function]':
method = filter;
break;
default:
break;
}
let keys = Object.keys(datas)
let list = (method ? keys.filter(method) : keys).map(key => datas[key]).filter(instance => selector(instance.options.el));
return single ? list[0] : list;
},
batch(filter, method) {
let args = [...arguments];
args.splice(0, 2);
return this.get(filter).map(instance => instance[method](...args));
}
return instance;
},
arr2tree(arr, pid, id, children, topParentId){
arr.forEach(item => {
if(item[pid] != topParentId){
let parent = arr.find(i => i[id] === item[pid])
if(parent){
if(!parent[children]){
parent[children] = [];
}
parent[children].push(item)
}
get(filter, single) {
let type = Object.prototype.toString.call(filter);
let method;
switch (type) {
case '[object String]':
filter && (method = item => item === filter);
break;
case '[object RegExp]':
method = item => filter.test(item);
break;
case '[object Function]':
method = filter;
break;
default:
break;
}
let keys = Object.keys(datas)
let list = (method ? keys.filter(method) : keys).map(key => datas[key]).filter(instance => selector(instance.options.el));
return single ? list[0] : list;
},
batch(filter, method) {
let args = [...arguments];
args.splice(0, 2);
return this.get(filter).map(instance => instance[method](...args));
},
arr2tree(arr=[], pid='pid', id='id', children='children', topParentId=0){
const datas = {} // 数据缓存
return safety(arr).filter(item =>{
const _id = item[id] // 提取自身id 必须唯一
const _pid = item[pid] // 提取父级id
const self = datas[_id] // 读取数据缓存中的数据
let parent = datas[_pid] // 读取父级数据
if(self){ // 如果缓存中有数据
item[children] = self[children] // 把缓存的数据保存到自身
}
datas[_id] = item // 替换缓存中的数据
if(!parent){ // 如果没有找到父级数据
parent = { // 创建一个全新的父级数据
[children]:[]
}
datas[_pid] = parent // 记录到缓存
}
parent.push(item) // 推送自身到父级
return id == topParentId // 这里用 == 防止出现 字符串不等于数字类型的情况
})
return arr.filter(i => i[pid] == topParentId)
}
}
}

View File

@ -1,8 +1,9 @@
import '@/common/expand'
import '@/style/index.less'
import '@/style/iconfont.less'
import { default as xmSelect, datas } from './index.js';
import '@/style/iconfont.less'
import '@/style/index.less'
import { default as xmSelect, datas } from './index.js';
import {throttle} from '@/common/util'
const moduleName = 'xmSelect';
/**
@ -14,16 +15,26 @@ window.addEventListener('click', () => {
item && item.closed && item.closed()
})
})
if ((typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object') {
module.exports = xmSelect;
} else if (typeof define === 'function' && define.amd) {
define(xmSelect);
} else if (window.layui && layui.define) {
layui.define(function(exports) {
exports(moduleName, xmSelect);
});
/**
* 监听页面滚动事件
*/
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') {
module.exports = xmSelect;
} else if (typeof define === 'function' && define.amd) {
define(xmSelect);
} else if (window.layui && layui.define) {
layui.define(function(exports) {
exports(moduleName, xmSelect);
});
}
window[moduleName] = xmSelect;

View File

@ -61,7 +61,7 @@ xm-select{
cursor: pointer;
outline: none;
&:hover{
&:hover,&:focus{
border-color: #C0C4CC;
}
@ -109,11 +109,13 @@ xm-select{
}
.label-content{
flex-wrap: nowrap;
white-space: nowrap;
}
}
&.auto-row{
.label-content{
flex-wrap: wrap;
padding-right: 30px !important;
}
.xm-label-block > span{
white-space: unset;
@ -124,7 +126,7 @@ xm-select{
.scroll{
.label-content{
display: flex;
padding: 3px 30px 3px 10px;
padding: 3px 10px;
}
}
@ -486,6 +488,9 @@ xm-select{
.disabled .xm-right-arrow{
color: #C2C2C2 !important;
}
.hide-icon.disabled .xm-right-arrow{
color: #999 !important;
}
}
}