@hzab/group-user-selector 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,31 +1,35 @@
1
- # @hzab/group-user-selector@0.0.8
2
-
3
- feat: 搜索条件提示词改为传入
4
-
5
- # @hzab/group-user-selector@0.0.7
6
-
7
- fix:部门点击下一级时,pageNum 初始化
8
-
9
- # @hzab/group-user-selector@0.0.6
10
-
11
- fix:部门分页切换带上部门 id
12
-
13
- # @hzab/group-user-selector@0.0.5
14
-
15
- 完善请求类型
16
-
17
- # @hzab/group-user-selector@0.0.4
18
-
19
- 修复分页 bug
20
-
21
- # @hzab/group-user-selector@0.0.3
22
-
23
- 增加列表自定义分页
24
-
25
- # @hzab/group-user-selector@0.0.2
26
-
27
- 增加搜索列表自定义分页
28
-
29
- # @hzab/group-user-selector@0.0.1
30
-
31
- 组件初始化
1
+ # @hzab/group-user-selector@0.0.9
2
+
3
+ feat: 增加一键清除 API
4
+
5
+ # @hzab/group-user-selector@0.0.8
6
+
7
+ feat: 搜索条件提示词改为传入
8
+
9
+ # @hzab/group-user-selector@0.0.7
10
+
11
+ fix:部门点击下一级时,pageNum 初始化
12
+
13
+ # @hzab/group-user-selector@0.0.6
14
+
15
+ fix:部门分页切换带上部门 id
16
+
17
+ # @hzab/group-user-selector@0.0.5
18
+
19
+ 完善请求类型
20
+
21
+ # @hzab/group-user-selector@0.0.4
22
+
23
+ 修复分页 bug
24
+
25
+ # @hzab/group-user-selector@0.0.3
26
+
27
+ 增加列表自定义分页
28
+
29
+ # @hzab/group-user-selector@0.0.2
30
+
31
+ 增加搜索列表自定义分页
32
+
33
+ # @hzab/group-user-selector@0.0.1
34
+
35
+ 组件初始化
package/README.md CHANGED
@@ -58,17 +58,27 @@ import Demo from "@hzab/group-user-selector";
58
58
 
59
59
  - 生成文档:npm run docs
60
60
  - 本地运行:npm run dev
61
- - 打包编译:npm run build
62
61
 
63
62
  ## 发布
64
63
 
65
- - 注意:示例代码生效,但发布之后未生效。确认是否执行了编译!!!
64
+ - npm 源和云效源都需要发布
66
65
 
67
- - 编译组件:npm run build
68
66
  - 命令:npm publish --access public
69
67
  - 发布目录:
70
68
  - src
71
69
 
70
+ ### nrm
71
+
72
+ - 安装
73
+ npm install -g nrm
74
+ - 增加源
75
+ nrm add aliyun https://packages.aliyun.com/62046985b3ead41b374a17f7/npm/npm-registry/
76
+ - 切换源
77
+ nrm use aliyun
78
+ nrm use npm
79
+ - 登录(账号密码在 https://packages.aliyun.com/npm/npm-registry/guide 查看)
80
+ npm login --registry=https://packages.aliyun.com/62046985b3ead41b374a17f7/npm/npm-registry/
81
+
72
82
  ## 配置
73
83
 
74
84
  ### 配置文件
package/package.json CHANGED
@@ -1,52 +1,52 @@
1
- {
2
- "name": "@hzab/group-user-selector",
3
- "version": "0.0.8",
4
- "description": "",
5
- "main": "src",
6
- "scripts": {
7
- "dev": "webpack serve -c ./config/webpack.config.js --env local",
8
- "build": "webpack -c ./config/webpack.config.js --env production",
9
- "publish-patch": "npm version patch && npm publish --access public",
10
- "publish-minor": "npm version minor && npm publish --access public",
11
- "publish-major": "npm version major && npm publish --access public",
12
- "prepare": "husky install",
13
- "mac-chmod": "chmod +x .husky && chmod +x .husky/pre-commit",
14
- "docs": "typedoc"
15
- },
16
- "files": [
17
- "src",
18
- "CHANGELOG.md"
19
- ],
20
- "keywords": [],
21
- "author": "CaiYansong",
22
- "license": "ISC",
23
- "devDependencies": {
24
- "@hzab/permissions": "^1.0.0",
25
- "@hzab/webpack-config": "^1.0.0",
26
- "@types/react": "^17.0.62",
27
- "@types/react-dom": "^17.0.20",
28
- "antd": "^4.14.0",
29
- "axios": "^1.4.0",
30
- "eslint": "^8.30.0",
31
- "less": "^4.1.3",
32
- "mobx": "^6.7.0",
33
- "mobx-react": "^7.6.0",
34
- "react": "^17.0.2",
35
- "react-dom": "^17.0.2",
36
- "react-router-dom": "^6.14.1",
37
- "typedoc": "^0.24.8",
38
- "typescript": "^4.9.4"
39
- },
40
- "directories": {
41
- "src": "src"
42
- },
43
- "lint-staged": {
44
- "**.{js,jsx,ts,tsx,css,scss,less,json,html}": [
45
- "prettier --write"
46
- ]
47
- },
48
- "dependencies": {
49
- "@hzab/data-model": "^1.7.0",
50
- "lodash": "^4.17.21"
51
- }
52
- }
1
+ {
2
+ "name": "@hzab/group-user-selector",
3
+ "version": "0.0.9",
4
+ "description": "",
5
+ "main": "src",
6
+ "scripts": {
7
+ "dev": "webpack serve -c ./config/webpack.config.js --env local",
8
+ "build": "webpack -c ./config/webpack.config.js --env production",
9
+ "publish-patch": "npm version patch && npm publish --access public",
10
+ "publish-minor": "npm version minor && npm publish --access public",
11
+ "publish-major": "npm version major && npm publish --access public",
12
+ "prepare": "husky install",
13
+ "mac-chmod": "chmod +x .husky && chmod +x .husky/pre-commit",
14
+ "docs": "typedoc"
15
+ },
16
+ "files": [
17
+ "src",
18
+ "CHANGELOG.md"
19
+ ],
20
+ "keywords": [],
21
+ "author": "CaiYansong",
22
+ "license": "ISC",
23
+ "devDependencies": {
24
+ "@hzab/permissions": "^1.0.0",
25
+ "@hzab/webpack-config": "^1.0.0",
26
+ "@types/react": "^17.0.62",
27
+ "@types/react-dom": "^17.0.20",
28
+ "antd": "^4.14.0",
29
+ "axios": "^1.4.0",
30
+ "eslint": "^8.30.0",
31
+ "less": "^4.1.3",
32
+ "mobx": "^6.7.0",
33
+ "mobx-react": "^7.6.0",
34
+ "react": "^17.0.2",
35
+ "react-dom": "^17.0.2",
36
+ "react-router-dom": "^6.14.1",
37
+ "typedoc": "^0.24.8",
38
+ "typescript": "^4.9.4"
39
+ },
40
+ "directories": {
41
+ "src": "src"
42
+ },
43
+ "lint-staged": {
44
+ "**.{js,jsx,ts,tsx,css,scss,less,json,html}": [
45
+ "prettier --write"
46
+ ]
47
+ },
48
+ "dependencies": {
49
+ "@hzab/data-model": "^1.7.0",
50
+ "lodash": "^4.17.21"
51
+ }
52
+ }
package/src/index.less CHANGED
@@ -1,163 +1,168 @@
1
- .group-selection {
2
- position: relative;
3
- width: 100%;
4
- height: 100%;
5
- overflow: hidden;
6
- display: grid;
7
- grid-template-columns: 1fr 1fr;
8
-
9
- .group-selection-loading {
10
- position: absolute;
11
- top: 0;
12
- left: 0;
13
- width: 100%;
14
- height: 100%;
15
- display: flex;
16
- align-items: center;
17
- justify-content: center;
18
- background: rgba(255, 255, 255, 0.5);
19
- z-index: 100;
20
- }
21
-
22
- .group-selection-list {
23
- box-sizing: border-box;
24
- display: flex;
25
- flex-direction: column;
26
- position: relative;
27
- height: 100%;
28
- overflow: hidden;
29
- padding: 24px;
30
- border-right: 1px solid #e8e8e8;
31
-
32
- // .group-selection-search-view {
33
- // display: flex;
34
- // flex-direction: column;
35
- // box-sizing: border-box;
36
- // position: absolute;
37
- // top: 56px;
38
- // left: 24px;
39
- // width: calc(100% - 48px);
40
- // height: calc(100% - 80px);
41
- // overflow-y: hidden;
42
- // z-index: 10;
43
- // background-color: #fff;
44
-
45
- // .group-selection-search-list {
46
- // box-sizing: border-box;
47
- // flex: 1;
48
- // overflow-y: auto;
49
- // padding: 24px ;
50
-
51
- // &::-webkit-scrollbar {
52
- // display: none;
53
- // }
54
- // }
55
-
56
- // .group-selection-search-pagination {
57
- // position: absolute;
58
- // left: 0;
59
- // bottom: 0;
60
- // width: 100%;
61
- // height: 40px;
62
- // display: flex;
63
- // align-items: center;
64
- // justify-content: center;
65
- // background-color: #fff;
66
- // }
67
- // }
68
-
69
- .group-selection-breadcrumb {
70
- margin-top: 12px;
71
- .group-selection-breadcrumb-item {
72
- cursor: pointer;
73
- }
74
- }
75
-
76
- .group-selection-list-check {
77
- margin: 12px 0;
78
- }
79
-
80
- .group-selection-list-view {
81
- flex: 1;
82
- overflow: auto;
83
-
84
- &::-webkit-scrollbar {
85
- display: none;
86
- }
87
- }
88
-
89
- // .group-selection-list-item {
90
- // display: flex;
91
- // align-items: center;
92
- // justify-content: space-between;
93
- // margin-bottom: 24px;
94
-
95
- // .avatar {
96
- // display: flex;
97
- // align-items: center;
98
- // cursor: pointer;
99
-
100
- // img {
101
- // width: 32px;
102
- // height: auto;
103
- // margin: 0 12px;
104
- // }
105
-
106
- // .label {
107
- // font-size: 16px;
108
- // color: #141414;
109
- // }
110
- // }
111
-
112
- // .next {
113
- // color: rgb(64, 62, 245);
114
- // cursor: pointer;
115
- // flex-shrink: 0;
116
- // }
117
-
118
- // .noNext {
119
- // opacity: 0.5;
120
- // }
121
- // }
122
- }
123
-
124
- .group-selection-detail {
125
- position: relative;
126
- height: 100%;
127
- overflow: auto;
128
-
129
- &::-webkit-scrollbar {
130
- display: none;
131
- }
132
-
133
- .group-selection-detail-list {
134
- display: flex;
135
- align-items: center;
136
- flex-wrap: wrap;
137
- padding: 24px;
138
-
139
- .group-selection-detail-item {
140
- flex-shrink: 0;
141
- display: flex;
142
- align-items: center;
143
- padding: 4px 8px;
144
- margin: 0 12px 12px 0;
145
- color: #e8e8e8;
146
- border-radius: 8px;
147
- background-color: aliceblue;
148
-
149
- img {
150
- width: 32px;
151
- height: auto;
152
- margin-right: 12px;
153
- }
154
-
155
- .label {
156
- font-size: 16px;
157
- color: #141414;
158
- margin-right: 12px;
159
- }
160
- }
161
- }
162
- }
163
- }
1
+ .group-selection {
2
+ position: relative;
3
+ width: 100%;
4
+ height: 100%;
5
+ overflow: hidden;
6
+ display: grid;
7
+ grid-template-columns: 1fr 1fr;
8
+
9
+ .group-selection-loading {
10
+ position: absolute;
11
+ top: 0;
12
+ left: 0;
13
+ width: 100%;
14
+ height: 100%;
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ background: rgba(255, 255, 255, 0.5);
19
+ z-index: 100;
20
+ }
21
+
22
+ .group-selection-list {
23
+ box-sizing: border-box;
24
+ display: flex;
25
+ flex-direction: column;
26
+ position: relative;
27
+ height: 100%;
28
+ overflow: hidden;
29
+ padding: 24px;
30
+ border-right: 1px solid #e8e8e8;
31
+
32
+ // .group-selection-search-view {
33
+ // display: flex;
34
+ // flex-direction: column;
35
+ // box-sizing: border-box;
36
+ // position: absolute;
37
+ // top: 56px;
38
+ // left: 24px;
39
+ // width: calc(100% - 48px);
40
+ // height: calc(100% - 80px);
41
+ // overflow-y: hidden;
42
+ // z-index: 10;
43
+ // background-color: #fff;
44
+
45
+ // .group-selection-search-list {
46
+ // box-sizing: border-box;
47
+ // flex: 1;
48
+ // overflow-y: auto;
49
+ // padding: 24px ;
50
+
51
+ // &::-webkit-scrollbar {
52
+ // display: none;
53
+ // }
54
+ // }
55
+
56
+ // .group-selection-search-pagination {
57
+ // position: absolute;
58
+ // left: 0;
59
+ // bottom: 0;
60
+ // width: 100%;
61
+ // height: 40px;
62
+ // display: flex;
63
+ // align-items: center;
64
+ // justify-content: center;
65
+ // background-color: #fff;
66
+ // }
67
+ // }
68
+
69
+ .group-selection-breadcrumb {
70
+ margin-top: 12px;
71
+ .group-selection-breadcrumb-item {
72
+ cursor: pointer;
73
+ }
74
+ }
75
+
76
+ .group-selection-list-check {
77
+ margin: 12px 0;
78
+ }
79
+
80
+ .group-selection-list-view {
81
+ flex: 1;
82
+ overflow: auto;
83
+
84
+ &::-webkit-scrollbar {
85
+ display: none;
86
+ }
87
+ }
88
+
89
+ // .group-selection-list-item {
90
+ // display: flex;
91
+ // align-items: center;
92
+ // justify-content: space-between;
93
+ // margin-bottom: 24px;
94
+
95
+ // .avatar {
96
+ // display: flex;
97
+ // align-items: center;
98
+ // cursor: pointer;
99
+
100
+ // img {
101
+ // width: 32px;
102
+ // height: auto;
103
+ // margin: 0 12px;
104
+ // }
105
+
106
+ // .label {
107
+ // font-size: 16px;
108
+ // color: #141414;
109
+ // }
110
+ // }
111
+
112
+ // .next {
113
+ // color: rgb(64, 62, 245);
114
+ // cursor: pointer;
115
+ // flex-shrink: 0;
116
+ // }
117
+
118
+ // .noNext {
119
+ // opacity: 0.5;
120
+ // }
121
+ // }
122
+ }
123
+
124
+ .group-selection-detail {
125
+ position: relative;
126
+ height: 100%;
127
+ overflow: auto;
128
+
129
+ &::-webkit-scrollbar {
130
+ display: none;
131
+ }
132
+ .group-selection-detail-title {
133
+ text-align: end;
134
+ color: var(--ant-primary-color);
135
+ cursor: pointer;
136
+ }
137
+
138
+ .group-selection-detail-list {
139
+ display: flex;
140
+ align-items: center;
141
+ flex-wrap: wrap;
142
+ padding: 24px;
143
+
144
+ .group-selection-detail-item {
145
+ flex-shrink: 0;
146
+ display: flex;
147
+ align-items: center;
148
+ padding: 4px 8px;
149
+ margin: 0 12px 12px 0;
150
+ color: #e8e8e8;
151
+ border-radius: 8px;
152
+ background-color: aliceblue;
153
+
154
+ img {
155
+ width: 32px;
156
+ height: auto;
157
+ margin-right: 12px;
158
+ }
159
+
160
+ .label {
161
+ font-size: 16px;
162
+ color: #141414;
163
+ margin-right: 12px;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
package/src/index.tsx CHANGED
@@ -1,221 +1,233 @@
1
- import { useEffect, useState, useImperativeHandle, forwardRef, useReducer, useRef } from "react";
2
- import { Input, Breadcrumb, Checkbox, message, Spin, Pagination, ConfigProvider } from "antd";
3
- import zhCN from "antd/es/locale/zh_CN";
4
- import { CloseCircleFilled, SearchOutlined } from "@ant-design/icons";
5
- import "moment/locale/zh-cn";
6
- import _ from "lodash";
7
-
8
- import defaultAvatar from "./assets/imgs/defaultAvatar.png";
9
- import department from "./assets/imgs/department.png";
10
- import SearchRenter from "./components/SearchRenter";
11
- import ItemRenter from "./components/ItemRenter";
12
-
13
- import "./index.less";
14
-
15
- function GroupSelection(props, ref) {
16
- const {
17
- model,
18
- queryKey = "parentId",
19
- value = [],
20
- labelKey = "label",
21
- valueKey = "value",
22
- isUserKey = "isUser",
23
- imgKey = "img",
24
- hasPagination = true,
25
- modelGetType = "getList",
26
- } = props;
27
- const [breadcrumb, setBreadcrumb] = useState([
28
- {
29
- [labelKey]: "首页",
30
- [valueKey]: undefined,
31
- },
32
- ]);
33
- const [loading, setLoading] = useState(false);
34
- const [checkAll, setCheckAll] = useState(false);
35
- const [selectList, setSelectList] = useState([]);
36
- const [selectedList, setSelectedList] = useState([...value]);
37
- const [queryValue, setQueryValue] = useState(undefined);
38
- const [total, setTotal] = useState(0);
39
-
40
- const pageData = useRef(
41
- hasPagination
42
- ? {
43
- pageSize: 10,
44
- pageNum: 1,
45
- }
46
- : {},
47
- );
48
-
49
- // 通过useImperativeHandle定义要抛出的值或方法
50
- useImperativeHandle(ref, () => ({
51
- selectedList,
52
- }));
53
-
54
- useEffect(() => {
55
- const selectedValues = new Set(selectedList.map((item) => item[valueKey]));
56
- const isAllSelected = selectList.every((item) => selectedValues.has(item[valueKey]));
57
- setCheckAll(isAllSelected);
58
- }, [selectList, selectedList]);
59
-
60
- const getTreeData = (value?: string | number) => {
61
- setLoading(true);
62
- model &&
63
- model[modelGetType]({ [queryKey]: value, ...pageData.current })
64
- .then((res) => {
65
- setSelectList(res?.list || res || []);
66
- setTotal(res?.pagination?.total || res?.total || 0);
67
- })
68
- .catch((err) => {
69
- message.error(err._message || "未知错误");
70
- })
71
- .finally(() => {
72
- setLoading(false);
73
- });
74
- };
75
-
76
- useEffect(() => {
77
- getTreeData();
78
- }, []);
79
-
80
- const handleChangePage = (pageNum, pageSize) => {
81
- const pages = {
82
- pageNum: pageSize !== pageData.current?.pageSize ? 1 : pageNum,
83
- pageSize,
84
- };
85
- pageData.current = pages;
86
-
87
- getTreeData(queryValue);
88
- };
89
-
90
- const handleBreadcrumb = (e) => {
91
- const index = breadcrumb.findIndex((item) => item[valueKey] === e[valueKey]);
92
- const arr = [...breadcrumb];
93
- arr.splice(index + 1);
94
- setBreadcrumb(arr);
95
- setQueryValue(e[valueKey]);
96
- getTreeData(e[valueKey]);
97
- };
98
-
99
- const handleClickItem = (item) => {
100
- const arr = [...breadcrumb];
101
- arr.push(item);
102
- setBreadcrumb(arr);
103
- setQueryValue(item[valueKey]);
104
- if (hasPagination) {
105
- pageData.current.pageNum = 1;
106
- }
107
- getTreeData(item[valueKey]);
108
- };
109
-
110
- const handleCheckDelete = (item) => {
111
- setSelectedList(selectedList.filter((i) => i[valueKey] !== item[valueKey]));
112
- };
113
-
114
- const handleCheckItem = (item) => {
115
- const is = selectedList.some((i) => i[valueKey] === item[valueKey]);
116
- if (is) {
117
- handleCheckDelete(item);
118
- } else {
119
- setSelectedList([...selectedList, item]);
120
- }
121
- };
122
-
123
- const handleCheckAll = (e) => {
124
- const isChecked = e.target.checked;
125
- if (isChecked) {
126
- // 全选:合并 selectList 并去重
127
- setSelectedList((prev) => {
128
- const selectedValues = new Set(prev.map((item) => item[valueKey])); // 已选值的 Set
129
- const newItems = selectList.filter((item) => !selectedValues.has(item[valueKey]));
130
- return [...prev, ...newItems]; // 合并新项
131
- });
132
- } else {
133
- // 取消全选:仅移除 selectList 中的项
134
- setSelectedList((prev) => {
135
- const selectValues = new Set(selectList.map((item) => item[valueKey])); // 需移除值的 Set
136
- return prev.filter((item) => !selectValues.has(item[valueKey])); // 过滤保留其他项
137
- });
138
- }
139
- };
140
-
141
- const handleCheck = (item) => {
142
- const is = selectedList.some((i) => i[valueKey] === item[valueKey]);
143
- return is;
144
- };
145
-
146
- const Renter = (item) => {
147
- const isCheck = handleCheck(item);
148
- return (
149
- <ItemRenter
150
- {...props}
151
- item={item}
152
- isCheck={isCheck}
153
- handleCheckItem={handleCheckItem}
154
- handleCheckDelete={handleCheckDelete}
155
- handleClickItem={handleClickItem}
156
- />
157
- );
158
- };
159
-
160
- return (
161
- <ConfigProvider locale={zhCN}>
162
- <div className="group-selection">
163
- {loading ? (
164
- <div className="group-selection-loading">
165
- <Spin tip="Loading..." spinning={loading} />
166
- </div>
167
- ) : null}
168
- <div className="group-selection-list">
169
- <SearchRenter {...props} setLoading={setLoading} ItemRenter={Renter} />
170
- <Breadcrumb className="group-selection-breadcrumb">
171
- {breadcrumb.map((item) => {
172
- return (
173
- <Breadcrumb.Item
174
- className="group-selection-breadcrumb-item"
175
- onClick={() => handleBreadcrumb(item)}
176
- key={item[valueKey]}
177
- >
178
- {item[labelKey]}
179
- </Breadcrumb.Item>
180
- );
181
- })}
182
- </Breadcrumb>
183
- <div className="group-selection-list-check">
184
- <Checkbox onChange={handleCheckAll} checked={checkAll && !!selectList.length} /> 全选
185
- </div>
186
- <div className="group-selection-list-view">
187
- {selectList.map((item, index) => {
188
- return Renter(item);
189
- })}
190
- </div>
191
- {hasPagination ? (
192
- <div>
193
- <Pagination
194
- current={pageData.current?.pageNum}
195
- total={total}
196
- pageSize={pageData.current?.pageSize}
197
- onChange={handleChangePage}
198
- size="small"
199
- />
200
- </div>
201
- ) : null}
202
- </div>
203
- <div className="group-selection-detail">
204
- <div className="group-selection-detail-list">
205
- {selectedList.map((item, index) => {
206
- return (
207
- <div className="group-selection-detail-item" key={item[valueKey]}>
208
- <img src={item[imgKey] ? item[imgKey] : item[isUserKey] ? defaultAvatar : department} />
209
- <div className="label">{item[labelKey]}</div>
210
- <CloseCircleFilled style={{ cursor: "pointer" }} onClick={() => handleCheckDelete(item)} />
211
- </div>
212
- );
213
- })}
214
- </div>
215
- </div>
216
- </div>
217
- </ConfigProvider>
218
- );
219
- }
220
-
221
- export default forwardRef(GroupSelection);
1
+ import { useEffect, useState, useImperativeHandle, forwardRef, useReducer, useRef } from "react";
2
+ import { Input, Breadcrumb, Checkbox, message, Spin, Pagination, ConfigProvider } from "antd";
3
+ import zhCN from "antd/es/locale/zh_CN";
4
+ import { CloseCircleFilled, SearchOutlined } from "@ant-design/icons";
5
+ import "moment/locale/zh-cn";
6
+ import _ from "lodash";
7
+
8
+ import defaultAvatar from "./assets/imgs/defaultAvatar.png";
9
+ import department from "./assets/imgs/department.png";
10
+ import SearchRenter from "./components/SearchRenter";
11
+ import ItemRenter from "./components/ItemRenter";
12
+
13
+ import "./index.less";
14
+
15
+ function GroupSelection(props, ref) {
16
+ const {
17
+ model,
18
+ queryKey = "parentId",
19
+ value = [],
20
+ labelKey = "label",
21
+ valueKey = "value",
22
+ isUserKey = "isUser",
23
+ imgKey = "img",
24
+ hasPagination = true,
25
+ modelGetType = "getList",
26
+ allowClear = false,
27
+ } = props;
28
+ const [breadcrumb, setBreadcrumb] = useState([
29
+ {
30
+ [labelKey]: "首页",
31
+ [valueKey]: undefined,
32
+ },
33
+ ]);
34
+ const [loading, setLoading] = useState(false);
35
+ const [checkAll, setCheckAll] = useState(false);
36
+ const [selectList, setSelectList] = useState([]);
37
+ const [selectedList, setSelectedList] = useState([...value]);
38
+ const [queryValue, setQueryValue] = useState(undefined);
39
+ const [total, setTotal] = useState(0);
40
+
41
+ const pageData = useRef(
42
+ hasPagination
43
+ ? {
44
+ pageSize: 10,
45
+ pageNum: 1,
46
+ }
47
+ : {},
48
+ );
49
+
50
+ // 通过useImperativeHandle定义要抛出的值或方法
51
+ useImperativeHandle(ref, () => ({
52
+ selectedList,
53
+ handleAllowClear,
54
+ }));
55
+
56
+ const handleAllowClear = () => {
57
+ setSelectedList([]);
58
+ };
59
+
60
+ useEffect(() => {
61
+ const selectedValues = new Set(selectedList.map((item) => item[valueKey]));
62
+ const isAllSelected = selectList.every((item) => selectedValues.has(item[valueKey]));
63
+ setCheckAll(isAllSelected);
64
+ }, [selectList, selectedList]);
65
+
66
+ const getTreeData = (value?: string | number) => {
67
+ setLoading(true);
68
+ model &&
69
+ model[modelGetType]({ [queryKey]: value, ...pageData.current })
70
+ .then((res) => {
71
+ setSelectList(res?.list || res || []);
72
+ setTotal(res?.pagination?.total || res?.total || 0);
73
+ })
74
+ .catch((err) => {
75
+ message.error(err._message || "未知错误");
76
+ })
77
+ .finally(() => {
78
+ setLoading(false);
79
+ });
80
+ };
81
+
82
+ useEffect(() => {
83
+ getTreeData();
84
+ }, []);
85
+
86
+ const handleChangePage = (pageNum, pageSize) => {
87
+ const pages = {
88
+ pageNum: pageSize !== pageData.current?.pageSize ? 1 : pageNum,
89
+ pageSize,
90
+ };
91
+ pageData.current = pages;
92
+
93
+ getTreeData(queryValue);
94
+ };
95
+
96
+ const handleBreadcrumb = (e) => {
97
+ const index = breadcrumb.findIndex((item) => item[valueKey] === e[valueKey]);
98
+ const arr = [...breadcrumb];
99
+ arr.splice(index + 1);
100
+ setBreadcrumb(arr);
101
+ setQueryValue(e[valueKey]);
102
+ getTreeData(e[valueKey]);
103
+ };
104
+
105
+ const handleClickItem = (item) => {
106
+ const arr = [...breadcrumb];
107
+ arr.push(item);
108
+ setBreadcrumb(arr);
109
+ setQueryValue(item[valueKey]);
110
+ if (hasPagination) {
111
+ pageData.current.pageNum = 1;
112
+ }
113
+ getTreeData(item[valueKey]);
114
+ };
115
+
116
+ const handleCheckDelete = (item) => {
117
+ setSelectedList(selectedList.filter((i) => i[valueKey] !== item[valueKey]));
118
+ };
119
+
120
+ const handleCheckItem = (item) => {
121
+ const is = selectedList.some((i) => i[valueKey] === item[valueKey]);
122
+ if (is) {
123
+ handleCheckDelete(item);
124
+ } else {
125
+ setSelectedList([...selectedList, item]);
126
+ }
127
+ };
128
+
129
+ const handleCheckAll = (e) => {
130
+ const isChecked = e.target.checked;
131
+ if (isChecked) {
132
+ // 全选:合并 selectList 并去重
133
+ setSelectedList((prev) => {
134
+ const selectedValues = new Set(prev.map((item) => item[valueKey])); // 已选值的 Set
135
+ const newItems = selectList.filter((item) => !selectedValues.has(item[valueKey]));
136
+ return [...prev, ...newItems]; // 合并新项
137
+ });
138
+ } else {
139
+ // 取消全选:仅移除 selectList 中的项
140
+ setSelectedList((prev) => {
141
+ const selectValues = new Set(selectList.map((item) => item[valueKey])); // 需移除值的 Set
142
+ return prev.filter((item) => !selectValues.has(item[valueKey])); // 过滤保留其他项
143
+ });
144
+ }
145
+ };
146
+
147
+ const handleCheck = (item) => {
148
+ const is = selectedList.some((i) => i[valueKey] === item[valueKey]);
149
+ return is;
150
+ };
151
+
152
+ const Renter = (item) => {
153
+ const isCheck = handleCheck(item);
154
+ return (
155
+ <ItemRenter
156
+ {...props}
157
+ item={item}
158
+ isCheck={isCheck}
159
+ handleCheckItem={handleCheckItem}
160
+ handleCheckDelete={handleCheckDelete}
161
+ handleClickItem={handleClickItem}
162
+ />
163
+ );
164
+ };
165
+
166
+ return (
167
+ <ConfigProvider locale={zhCN}>
168
+ <div className="group-selection">
169
+ {loading ? (
170
+ <div className="group-selection-loading">
171
+ <Spin tip="Loading..." spinning={loading} />
172
+ </div>
173
+ ) : null}
174
+ <div className="group-selection-list">
175
+ <SearchRenter {...props} setLoading={setLoading} ItemRenter={Renter} />
176
+ <Breadcrumb className="group-selection-breadcrumb">
177
+ {breadcrumb.map((item) => {
178
+ return (
179
+ <Breadcrumb.Item
180
+ className="group-selection-breadcrumb-item"
181
+ onClick={() => handleBreadcrumb(item)}
182
+ key={item[valueKey]}
183
+ >
184
+ {item[labelKey]}
185
+ </Breadcrumb.Item>
186
+ );
187
+ })}
188
+ </Breadcrumb>
189
+ <div className="group-selection-list-check">
190
+ <Checkbox onChange={handleCheckAll} checked={checkAll && !!selectList.length} /> 全选
191
+ </div>
192
+ <div className="group-selection-list-view">
193
+ {selectList.map((item, index) => {
194
+ return Renter(item);
195
+ })}
196
+ </div>
197
+ {hasPagination ? (
198
+ <div>
199
+ <Pagination
200
+ current={pageData.current?.pageNum}
201
+ total={total}
202
+ pageSize={pageData.current?.pageSize}
203
+ onChange={handleChangePage}
204
+ size="small"
205
+ />
206
+ </div>
207
+ ) : null}
208
+ </div>
209
+ <div className="group-selection-detail">
210
+ {allowClear ? (
211
+ <div className="group-selection-detail-title" onClick={handleAllowClear}>
212
+ 清空
213
+ </div>
214
+ ) : null}
215
+
216
+ <div className="group-selection-detail-list">
217
+ {selectedList.map((item, index) => {
218
+ return (
219
+ <div className="group-selection-detail-item" key={item[valueKey]}>
220
+ <img src={item[imgKey] ? item[imgKey] : item[isUserKey] ? defaultAvatar : department} />
221
+ <div className="label">{item[labelKey]}</div>
222
+ <CloseCircleFilled style={{ cursor: "pointer" }} onClick={() => handleCheckDelete(item)} />
223
+ </div>
224
+ );
225
+ })}
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </ConfigProvider>
230
+ );
231
+ }
232
+
233
+ export default forwardRef(GroupSelection);