@bit-sun/business-component 4.0.12-alpha.2 → 4.0.12-alpha.21

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.
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { Table as SulaTable, request } from 'bssula';
3
3
  import { Resizable } from 'react-resizable';
4
4
  import ColumnSetting from './columnSetting';
@@ -9,8 +9,10 @@ import {
9
9
  Typography,
10
10
  } from 'antd';
11
11
  import ENUM from '@/utils/enumConfig';
12
- import { handleBssulaColumnsSpecialParams, parseWidth } from '@/utils/utils';
12
+ import { handleBssulaColumnsSpecialParams, parseWidth, uuid } from '@/utils/utils';
13
+ import TableSumComponent from './components/TableSumComponent';
13
14
  const { Text } = Typography;
15
+
14
16
  export default class ColumnSettingSulaTable extends React.Component {
15
17
  sulaTableRef: React.RefObject<unknown>;
16
18
  state: any;
@@ -126,21 +128,123 @@ export default class ColumnSettingSulaTable extends React.Component {
126
128
 
127
129
  ResizeableTitle = (props: any) => {
128
130
  const { onResize, width, ...restProps } = props;
129
-
130
- if (!width) {
131
- return <th {...restProps} />;
132
- }
131
+
132
+ const [innerWidth, setInnerWidth] = useState(width);
133
+ const [isResizing, setIsResizing] = useState(false); // 标记是否正在拖拽
134
+ const [isDragging, setIsDragging] = useState(false); // 标记拖拽句柄是否被拖拽
135
+ const [startX, setStartX] = useState(0); // 初始X坐标
136
+ const markerPosition = useRef({ left: 0, top: 0 });
137
+ const currentStart = useRef(0);
138
+ const uuidref = useRef(uuid());
139
+
140
+ const prevWidthRef = useRef(width);
141
+
142
+ const handleMouseDown = (e: any) => {
143
+ currentStart.current = e.clientX;
144
+ markerPosition.current = { left: e.clientX, top: e.clientY }
145
+ setIsResizing(true);
146
+ document.addEventListener('mousemove', handleMouseMove);
147
+ document.addEventListener('mouseup', handleMouseUp);
148
+ };
149
+
150
+ const handleMouseMove = (e: any) => {
151
+ e.stopPropagation();
152
+ e.preventDefault();
153
+ // 更新标记位置
154
+ markerPosition.current = { left: e.clientX, top: e.clientY }
155
+ const dom: HTMLElement | null = document.getElementById('text1');
156
+
157
+ if (dom && dom.style) {
158
+ dom.style.left = `${e.clientX}px`;
159
+ dom.style.top = `${e.clientY - 20}px`;
160
+ }
161
+ };
162
+
163
+ const handleMouseUp = (e: any) => {
164
+ document.removeEventListener('mousemove', handleMouseMove);
165
+ document.removeEventListener('mouseup', handleMouseUp);
166
+ setIsResizing(false);
167
+ };
168
+
169
+ const handleresize = (e: any, data: any, title: string) => {
170
+ const newWidth = data?.size?.width || 0;
171
+ setInnerWidth(newWidth); // 更新内部分宽度
172
+ };
173
+
174
+ const handleResizeStart = () => {
175
+ setIsResizing(true);
176
+ };
177
+
178
+ const handleResizeStop = (e: any, data: any) => {
179
+ setIsResizing(false);
180
+ if (onResize) {
181
+ onResize(e, data);
182
+ }
183
+ };
184
+
185
+ useEffect(() => {
186
+ if (width !== prevWidthRef.current) {
187
+ prevWidthRef.current = width;
188
+ setInnerWidth(width);
189
+ }
190
+ }, [width]);
191
+
192
+ const thStyle = {
193
+ boxShadow: isResizing ? '2px 2px 10px rgba(0, 0, 0, 0.3)' : 'none',
194
+ };
195
+
133
196
  return (
134
197
  <Resizable
135
- width={width}
198
+ width={innerWidth}
136
199
  height={0}
137
- onResize={onResize}
138
- draggableOpts={{ enableUserSelectHack: false }}
200
+ handle={
201
+ <div>
202
+ <div
203
+ style={{
204
+ width: '10px',
205
+ height: '30px',
206
+ cursor: 'col-resize', // 拖动时改变鼠标样式
207
+ position: 'absolute',
208
+ zIndex: 10,
209
+ top: 0,
210
+ right: 0,
211
+ }}
212
+ onMouseDown={handleMouseDown}
213
+ // onDrag={handleDrag}
214
+ >
215
+ </div>
216
+ {isResizing && <div
217
+ id="text1"
218
+ style={{
219
+ position: 'fixed',
220
+ left: markerPosition.current.left, // 跟随鼠标偏移一点
221
+ top: markerPosition.current.top - 20,
222
+ backgroundColor: '#1890ff',
223
+ color: 'white',
224
+ borderRadius: '4px',
225
+ zIndex: 9999,
226
+ pointerEvents: 'none',
227
+ height: '40px',
228
+ width: 2
229
+ }}
230
+ >
231
+ </div>}
232
+ </div>
233
+ }
234
+ onResize={(e: any, data: any) => {handleresize(e, data, restProps.title)}}
235
+ onResizeStart={handleResizeStart}
236
+ onResizeStop={handleResizeStop}
237
+ draggableOpts={{
238
+ enableUserSelectHack: true,
239
+ grid: [20, 20],
240
+ axis: 'x',
241
+ bounds: 'parent',
242
+ }}
139
243
  >
140
244
  <th {...restProps} />
141
245
  </Resizable>
142
246
  );
143
- }
247
+ };
144
248
 
145
249
  getTableScrollXWidth = (cols: any[]) => {
146
250
  if (cols.every((item: any) => item.width)) {
@@ -156,7 +260,7 @@ export default class ColumnSettingSulaTable extends React.Component {
156
260
  ...restProps,
157
261
  };
158
262
  let showSummary = null;
159
- if (this.state.showColumns.length) {
263
+ if (this.state.showColumns.length && !Array.isArray(summary)) {
160
264
  if (summary && summary().diy) {
161
265
  showSummary = this.getTableSummaryInfo();
162
266
  } else {
@@ -197,7 +301,10 @@ export default class ColumnSettingSulaTable extends React.Component {
197
301
  ...scroll,
198
302
  x: restProps.overScrollX || this.getTableScrollXWidth(showCol)
199
303
  },
200
- summary: showSummary,
304
+ sticky: true,
305
+ ...(
306
+ showSummary ? { summary: showSummary } : {}
307
+ )
201
308
  }
202
309
  return (
203
310
  <div style={{ position: 'relative' }}>
@@ -223,6 +330,9 @@ export default class ColumnSettingSulaTable extends React.Component {
223
330
  ...otherTableInfo
224
331
  }
225
332
  />
333
+ {Array.isArray(summary) && (
334
+ <TableSumComponent summary={summary} />
335
+ )}
226
336
  </div >
227
337
  )
228
338
  }
@@ -2,13 +2,43 @@
2
2
  @primary-color: #005cff;
3
3
 
4
4
  .search_select {
5
+ // 选择器 隐藏滚动条 横向滑动
6
+ .ant-select-selector{
7
+ height: 24px;
8
+ overflow: hidden;
9
+ .ant-select-selection-overflow{
10
+ height: 40px;
11
+ flex-wrap: nowrap;
12
+ overflow-x: auto;
13
+ }
14
+ .ant-select-selection-overflow-item {
15
+ align-self: auto;
16
+ }
17
+ }
5
18
  &_show {
6
19
  display: flex;
7
20
 
21
+ .ant-select-dropdown {
22
+ top: 24px !important;
23
+ width: calc(150%) !important;
24
+ }
25
+
8
26
  // 下拉框清除图标位置调整
9
27
  .ant-select-clear {
10
28
  right: 33px;
11
29
  }
30
+
31
+ // 解决多选宽度不够 滑动会白一块的问题
32
+ .ant-select-multiple.ant-select-show-arrow .ant-select-selector, .ant-select-multiple.ant-select-allow-clear .ant-select-selector {
33
+ padding-right: 28px;
34
+ }
35
+ }
36
+
37
+ &_show.search_select_show_list {
38
+ .ant-select-dropdown {
39
+ top: 24px !important;
40
+ width: calc(141%) !important;
41
+ }
12
42
  }
13
43
 
14
44
  &_expand_button {
@@ -20,7 +50,6 @@
20
50
  cursor: pointer;
21
51
  font-size: 14px;
22
52
  font-weight: bolder;
23
- background: #fafafa;
24
53
 
25
54
  span {
26
55
  position: absolute;
@@ -32,6 +61,8 @@
32
61
  &_expand_button:hover {
33
62
  background-color: @primary-color;
34
63
  color: #fff;
64
+ border-top-right-radius: 2px;
65
+ border-bottom-right-radius: 2px;
35
66
  }
36
67
  &_expand_button_disabled:hover {
37
68
  background-color: transparent;
@@ -282,3 +313,11 @@
282
313
  height: 300px !important;
283
314
  }
284
315
  }
316
+
317
+ .searchSelectMaxTagToolTip {
318
+ .ant-tooltip-inner {
319
+ max-height: 72px;
320
+ overflow-x: auto;
321
+ padding: 0px;
322
+ }
323
+ }
@@ -8,7 +8,7 @@ import { stringify } from 'querystring';
8
8
  import _, { escapeRegExp, isNil, values } from "lodash"
9
9
  import './index.less';
10
10
  import { BusinessSearchSelect, QueryMutipleInput } from '@/index';
11
- import { handleSourceName, getFormRowInfo, hasMoreQueryFields, defaultVisibleFieldsCount, getRealStr, ColSpan, getTableHeigth } from './utils';
11
+ import { handleSourceName, getFormRowInfo, hasMoreQueryFields, defaultVisibleFieldsCount, getRealStr, ColSpan, getTableHeigth, getCurrentSRKs, getRenderSource } from './utils';
12
12
  import { judgeIsRequestError } from '@/utils/requestUtils';
13
13
  import zhankaitiaojian from '../../../assets/zhankaitiaojian-icon.svg';
14
14
 
@@ -69,8 +69,13 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
69
69
  listHeight: 160,
70
70
  optionLabelProp: "label",
71
71
  autoClearSearchValue: false,
72
- placement: 'bottomLeft'
72
+ placement: 'bottomRight'
73
73
  }
74
+
75
+ const pathname = window.location.href;
76
+ var pattern = /(action|create|edit|view)/
77
+ const isFormPage = pathname.match(pattern)?.length > 0;
78
+
74
79
  const initPagination = { showQuickJumper: true, showSizeChanger: false, showTotal: (total: any) => `共 ${total} 条`, pageSize: tableInitPageSize }
75
80
  const tableInitPagination = { ...initPagination, total: 0, current: 1 }
76
81
  const disabled = noOperate || selectProps?.disabled || props?.disabled;
@@ -87,6 +92,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
87
92
 
88
93
  const [items, setItems] = useState([]);
89
94
  const [selectOpen, setSelectOpen] = useState(false);
95
+ const [isMaxTagsOpen, setIsMaxTagsOpen] = useState(false);
90
96
  const [scrollPage, setScrollPage] = useState(1);
91
97
  const [itemsTotal, setItemsTotal] = useState(0);
92
98
  const [fetching, setFetching] = useState(false);
@@ -368,7 +374,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
368
374
  textShowText,
369
375
  textShowKey: item[mappingTextShowKeyField || mappingValueField],
370
376
  value: item[mappingValueField],
371
- keyIndex: index + 1,
377
+ keyIndex: type != 1 ? ((queryParams?.currentPage - 1) * queryParams?.pageSize + index + 1) : (index+1),
372
378
  };
373
379
  })
374
380
  : Array.isArray(res) &&
@@ -393,13 +399,20 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
393
399
  textShowText,
394
400
  textShowKey: item[mappingTextShowKeyField || mappingValueField],
395
401
  value: item[mappingValueField],
396
- keyIndex: index + 1,
402
+ keyIndex: type != 1 ? ((queryParams?.currentPage - 1) * queryParams?.pageSize + index + 1) : (index+1),
397
403
  };
398
404
  })
399
405
  : [];
400
406
  }
407
+ // 补充搜索项--选中的数据添加到数据源中去
408
+ const currentSRKs = getCurrentSRKs(selectMode,labelInValue,value)
409
+ if(type === 1 && currentSRKs?.length && currentSRKs?.some(s=> !source?.find(r=> r.value==s))) {
410
+ const selectedOption = items.filter(option => currentSRKs?.includes(option.value))||[];
411
+ source = (source||[]).concat(selectedOption)
412
+ }
401
413
  // 数据源 不可以有重复key
402
414
  source = Array.isArray(source) ? _.uniqBy(source, 'value') : [];
415
+
403
416
  if(callback) {
404
417
  callback(source)
405
418
  } else {
@@ -834,9 +847,9 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
834
847
 
835
848
  const renderShowTable = (tableList, type) => {
836
849
  const tableBoxHeighth = getTableHeigth(modalTableProps?.tableSearchForm);
837
- const oSY = `calc(100vh - 391px - 82px)`; // 分页 24+16*2+10 「高 + margin * 2 + paddingBottom 10 」
850
+ const oSY = `calc(100vh - ${tableBoxHeighth}px - 82px)`; // 分页 24+16*2+10 「高 + margin * 2 + paddingBottom 10 」
838
851
  return (
839
- <div style={{ height: `calc(100vh - 391px)` }}>
852
+ <div style={{ height: `calc(100vh - ${tableBoxHeighth}px)` }}>
840
853
  <Table
841
854
  bordered
842
855
  size="middle"
@@ -889,7 +902,12 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
889
902
  }
890
903
 
891
904
  const onClear = () => {
905
+ // 执行父组件 onClear 事件
906
+ props?.onClear?.();
907
+ // 清空下拉框 / 弹窗 选中数据
892
908
  formaData([], items);
909
+ onChangeSelectedKeys([], [])
910
+ // 重置下拉框数据源
893
911
  resetSelectDataSource();
894
912
  }
895
913
 
@@ -905,13 +923,22 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
905
923
  }
906
924
 
907
925
  const onDropdownVisibleChange = (visible) => {
926
+ // 阻止maxTagPlaceholder点击事件触发下拉框展示事件
927
+ if(isMaxTagsOpen && !selectOpen) return;
928
+
908
929
  setSelectOpen(visible);
909
930
  // 关闭下拉框 如果首次本身就不展示数据的 没有选中数据-需要清空查询数据源; 首次展示的默认展示
910
931
  if (!visible && !value?.length) {
911
- resetSelectDataSource()
932
+ setTimeout(() => {
933
+ // 延时 是为了避免 执行时候出现下拉框弹两次的问题-可以看到数据源从展示到显示空数据框的问题
934
+ resetSelectDataSource()
935
+ }, 200)
912
936
  }
913
937
  }
914
938
  const renderTable = (dataSource) => {
939
+ const currentSRKs = getCurrentSRKs(selectMode,labelInValue,value)
940
+ const renderSource = getRenderSource(currentSRKs,items)
941
+
915
942
  return (
916
943
  <div className={`search_select_dropdown_table ${!selectMode?'search_select_dropdown_table1':''}`}>
917
944
  <Table
@@ -921,7 +948,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
921
948
  rowSelection: {
922
949
  type: 'checkbox',
923
950
  columnWidth: '24px',
924
- selectedRowKeys: labelInValue ? value?.map(s=> (s?.value||s)) : value,
951
+ selectedRowKeys: currentSRKs,
925
952
  preserveSelectedRowKeys: true, // 避免搜索之后 没有了选中前的数据 保证sks的正确性
926
953
  onChange: (sks, srs) => {
927
954
  const oldSelect = value?.map(s => ({ value: s?.value||s }))||[];
@@ -945,7 +972,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
945
972
  rowSelection: {
946
973
  type: 'radio',
947
974
  columnWidth: 0,
948
- selectedRowKeys: labelInValue ? value?.value&&[value?.value]||[] : value&&[value]||[],
975
+ selectedRowKeys: currentSRKs,
949
976
  },
950
977
  onRow: (record, rowKey) => ({
951
978
  onClick: event => {
@@ -955,7 +982,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
955
982
  })
956
983
  })}
957
984
  columns={selectProps?.renderTableColumns||[]}
958
- dataSource={items}
985
+ dataSource={renderSource}
959
986
  size="middle"
960
987
  pagination={false}
961
988
  rowKey={mappingValueField}
@@ -972,15 +999,33 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
972
999
  onChange(newValue);
973
1000
  }
974
1001
  return (
975
- <Tooltip title={selectedValues.map((i: any) => (
976
- <Tag
977
- closable={true}
978
- onClose={(e) => onClose(e, i)}
979
- style={{ marginRight: 3, background: '#f5f5f5', height: '24px', border: '1px solid #f0f0f0' }}
980
- >
981
- {i.label}
982
- </Tag>
983
- ))}>
1002
+ <Tooltip
1003
+ overlayClassName='searchSelectMaxTagToolTip'
1004
+ destroyTooltipOnHide
1005
+ placement="topRight"
1006
+ autoAdjustOverflow={false}
1007
+ title={(
1008
+ <div
1009
+ style={{ margin: '6px 8px 0px' }}
1010
+ onMouseEnter={() => {
1011
+ setIsMaxTagsOpen(true)
1012
+ }}
1013
+ onMouseLeave={() => {
1014
+ setIsMaxTagsOpen(false)
1015
+ }}
1016
+ >
1017
+ {selectedValues.map((i: any) => (
1018
+ <Tag
1019
+ closable={true}
1020
+ onClose={(e) => onClose(e, i)}
1021
+ style={{ margin: '0px 3px 3px 0px', background: '#f5f5f5', height: '24px', border: '1px solid #f0f0f0' }}
1022
+ >
1023
+ {i.label}
1024
+ </Tag>
1025
+ ))}
1026
+ </div>
1027
+ )}
1028
+ >
984
1029
  {`+ ${selectedValues?.length}`}
985
1030
  </Tooltip>
986
1031
  )
@@ -1044,7 +1089,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
1044
1089
  {getShowStr()}
1045
1090
  </div>) :
1046
1091
  <div
1047
- className="search_select_show"
1092
+ className={`${isFormPage ? '' : 'search_select_show_list'} search_select_show`}
1048
1093
  id={`search_select_div_${uniqueValue}`}
1049
1094
  >
1050
1095
  <Select
@@ -1054,7 +1099,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
1054
1099
  onClear={onClear}
1055
1100
  onDeselect={onDeselect}
1056
1101
  disabled={sDisabled}
1057
- dropdownStyle={{ borderRadius: '2px', padding: '10px 2px' }}
1102
+ dropdownStyle={{ borderRadius: '2px', padding: '10px 2px'}}
1058
1103
  open={selectOpen}
1059
1104
  onDropdownVisibleChange={onDropdownVisibleChange}
1060
1105
  dropdownRender={(menu) => items?.length ? renderTable(items) : menu}
@@ -1076,7 +1121,7 @@ const SearchSelect = forwardRef((props: any, ref: any) => {
1076
1121
  suffixIcon: <div className={`search_select_expand_button ${(sDisabled)?'search_select_expand_button_disabled':''}`} onClick={showModal}><SearchOutlined /></div>
1077
1122
  } : {})}
1078
1123
  {...currentSelectProps}
1079
- getPopupContainer={(triggerNode) => (getPopupContainer && getPopupContainer(triggerNode)) || triggerNode.parentElement}
1124
+ getPopupContainer={(triggerNode) => triggerNode.parentElement || getPopupContainer && getPopupContainer(triggerNode)}
1080
1125
  >
1081
1126
  {items.map(item => (
1082
1127
  <Option key={item.value} label={item.text}>
@@ -4,6 +4,7 @@ export const handleSourceName = (sName: any) => {
4
4
  return sName
5
5
  }
6
6
 
7
+ // ------------------------------------------处理样式相关--开始----------------------------------------
7
8
  export const getFormRowInfo = (list: any) => {
8
9
  const totalRows = Math.ceil(list.length / columnsPerRow); // 计算总行数
9
10
  const lastRowColumns = (list.length+1) % columnsPerRow; // 计算最后一行的实际列数
@@ -33,6 +34,41 @@ export const getRealStr = (oldSelect: any,newSelect: any, record: any) => {
33
34
 
34
35
  export const getTableHeigth = (list: any) => {
35
36
  const totalRows = Math.ceil((list?.length+1) / 4);
36
- if(totalRows == 1) return 502; // modal弹窗760 调整为700 适应小屏
37
- return 488 - totalRows*10
38
- }
37
+ if(totalRows == 1) return 358; // modal弹窗760 调整为700 适应小屏
38
+ return 411 - totalRows*10
39
+ }
40
+
41
+ // ------------------------------------------处理样式相关--结束----------------------------------------
42
+
43
+
44
+ // ------------------------------------------处理数据相关--开始----------------------------------------
45
+ export const getCurrentSRKs = (selectMode: any,labelInValue:boolean,value: any) => {
46
+ return selectMode ? (labelInValue ? value?.map((s: any)=> (s?.value||s)) : value) : (labelInValue ? value?.value&&[value?.value]||[] : value&&[value]||[])
47
+ }
48
+
49
+ export const getRenderSource = (currentSRKs: any, items: any) => {
50
+ // 判空处理
51
+ if(!currentSRKs?.length) return items||[];
52
+
53
+ // 创建映射对象 用于记录原始选中顺序
54
+ const orderMap = new Map<number, number>();
55
+ currentSRKs?.forEach((value: any, index: number) => {
56
+ orderMap.set(value, index);
57
+ });
58
+
59
+ // 被选中数据集合,获取之后排序
60
+ const selectedOption = items?.filter((option: any) => currentSRKs?.includes(option.value))||[];
61
+ const selectedOptionSort = selectedOption?.sort((a: any, b: any) => {
62
+ return (orderMap.get(a.value) ?? Infinity) - (orderMap.get(b.value) ?? Infinity);
63
+ })||[];
64
+
65
+ // 未选中数据集合
66
+ const otherOptions = items?.filter((option: any) => !currentSRKs?.includes(option.value))||[];
67
+
68
+ return [
69
+ ...selectedOptionSort,
70
+ ...otherOptions
71
+ ]
72
+ }
73
+
74
+ // ------------------------------------------处理数据相关--结束----------------------------------------
@@ -278,7 +278,6 @@ body {
278
278
 
279
279
  .ant-input-affix-wrapper {
280
280
  padding: 0 11px;
281
- height: 24px;
282
281
  }
283
282
  }
284
283