@nokecy/qc-ui 1.1.4 → 1.1.5

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,18 +1,13 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
- import _debounce from "lodash/debounce";
3
- var _excluded = ["record", "index", "moveRow", "children"];
2
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
3
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
5
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
4
6
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
7
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
8
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
7
9
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
8
10
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
9
- function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
10
- function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
11
- function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
12
- function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
13
- function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
14
- function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
15
- function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
16
11
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
17
12
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
18
13
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -20,316 +15,517 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
20
15
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
21
16
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
22
17
  /**
23
- * 列配置表格组件
24
- * 使用简单的 Ant Design Table 替代 Formily ArrayTable
25
- * 通过 React useState 直接管理数据状态,确保 type 字段不会丢失
18
+ * 列配置表格组件 - AG-Grid 版本
19
+ * 使用 AG-Grid 替代 Ant Design Table,性能更优
26
20
  */
27
21
 
28
- import React, { useCallback, useMemo, useState, useRef, useEffect, useLayoutEffect } from 'react';
29
- import { Table, Button, Input, InputNumber, Select, Switch, Space, Popconfirm, message, Tag, Tooltip } from 'antd';
30
- import { PlusOutlined, DeleteOutlined, FolderOutlined, FolderAddOutlined, FileOutlined, DownOutlined, RightOutlined, DragOutlined, HolderOutlined } from '@ant-design/icons';
31
- import { DndProvider, useDrag, useDrop } from 'react-dnd';
32
- import { HTML5Backend } from 'react-dnd-html5-backend';
33
- import { generateId, findLastDescendantIndex, reorderItems } from "./columnGroupUtils";
34
- import MoveNodeModal from "./MoveNodeModal";
35
- var DRAG_TYPE = 'COLUMN_ROW';
36
- /**
37
- * 受控的名称输入组件 - 解决光标跳出问题
38
- * 通过内部维护输入状态,只在失去焦点时同步到父组件
39
- */
40
- var NameInput = function NameInput(_ref) {
41
- var value = _ref.value,
42
- onChange = _ref.onChange,
43
- placeholder = _ref.placeholder,
44
- isNew = _ref.isNew;
45
- var _useState = useState(value),
46
- _useState2 = _slicedToArray(_useState, 2),
47
- localValue = _useState2[0],
48
- setLocalValue = _useState2[1];
49
-
50
- // 同步外部变化到本地状态
51
- useEffect(function () {
52
- setLocalValue(value);
53
- }, [value]);
54
-
55
- // 处理输入变化 - 只更新本地状态
56
- var handleChange = function handleChange(e) {
57
- setLocalValue(e.target.value);
58
- };
22
+ import React, { useCallback, useMemo, useState, useRef, useEffect } from 'react';
23
+ import { Button, Select, Switch, Space, Popconfirm, message, Tag, Input } from 'antd';
24
+ import { DeleteOutlined, FolderOutlined, FolderAddOutlined, FileOutlined, HolderOutlined } from '@ant-design/icons';
25
+ import { AgGridReact } from 'ag-grid-react';
26
+ import { generateId } from "./columnGroupUtils";
27
+ import { DebounceNumberInput } from "./DebounceInputs";
28
+ import { agGridTheme } from "../theme";
29
+ // ==================== Cell Renderers ====================
30
+
31
+ /** 类型标签 */
32
+ var TypeCellRenderer = function TypeCellRenderer(props) {
33
+ var item = props.data;
34
+ if (!item) return null;
35
+ if (item.type === 'group') {
36
+ return /*#__PURE__*/React.createElement(Tag, {
37
+ color: "blue",
38
+ icon: /*#__PURE__*/React.createElement(FolderOutlined, null)
39
+ }, "\u5206\u7EC4");
40
+ }
41
+ return /*#__PURE__*/React.createElement(Tag, {
42
+ color: "green",
43
+ icon: /*#__PURE__*/React.createElement(FileOutlined, null)
44
+ }, "\u5217");
45
+ };
59
46
 
60
- // 失去焦点时同步到父组件
61
- var handleBlur = function handleBlur() {
62
- if (localValue !== value) {
63
- onChange(localValue);
47
+ /** 名称列 (带拖拽图标和缩进) */
48
+ var NameCellRenderer = function NameCellRenderer(props) {
49
+ var item = props.data;
50
+ if (!item) return null;
51
+ var paddingLeft = (item.level || 0) * 20;
52
+
53
+ // 从 props 中获取自定义参数
54
+ var _ref = props,
55
+ expandedKeys = _ref.expandedKeys,
56
+ onToggleExpand = _ref.onToggleExpand,
57
+ allItems = _ref.allItems;
58
+
59
+ // 检查是否有子节点
60
+ var hasChildren = allItems === null || allItems === void 0 ? void 0 : allItems.some(function (i) {
61
+ return i.parentId === item.id;
62
+ });
63
+ var isExpanded = expandedKeys === null || expandedKeys === void 0 ? void 0 : expandedKeys.has(item.id);
64
+ return /*#__PURE__*/React.createElement("div", {
65
+ style: {
66
+ display: 'flex',
67
+ alignItems: 'center',
68
+ gap: 8,
69
+ paddingLeft: paddingLeft
64
70
  }
65
- };
66
-
67
- // 按回车键时也同步并失去焦点
68
- var handlePressEnter = function handlePressEnter() {
69
- if (localValue !== value) {
70
- onChange(localValue);
71
+ }, item.type === 'group' && hasChildren ? /*#__PURE__*/React.createElement("span", {
72
+ onClick: function onClick() {
73
+ return onToggleExpand === null || onToggleExpand === void 0 ? void 0 : onToggleExpand(item.id);
74
+ },
75
+ style: {
76
+ cursor: 'pointer',
77
+ width: 16,
78
+ display: 'inline-block'
71
79
  }
72
- };
73
- return /*#__PURE__*/React.createElement(Input, {
74
- value: localValue,
75
- onChange: handleChange,
76
- onBlur: handleBlur,
77
- onPressEnter: handlePressEnter,
80
+ }, isExpanded ? '▼' : '▶') : /*#__PURE__*/React.createElement("span", {
81
+ style: {
82
+ width: 16,
83
+ display: 'inline-block'
84
+ }
85
+ }), /*#__PURE__*/React.createElement(HolderOutlined, {
86
+ style: {
87
+ color: '#999',
88
+ cursor: 'grab'
89
+ }
90
+ }), /*#__PURE__*/React.createElement(Input, {
91
+ value: item.headerName || '',
92
+ onChange: function onChange(e) {
93
+ if (props.node) {
94
+ props.node.setDataValue('headerName', e.target.value);
95
+ }
96
+ },
78
97
  bordered: false,
79
- placeholder: placeholder,
98
+ placeholder: "\u8F93\u5165\u540D\u79F0",
80
99
  style: {
81
- flex: 1,
82
- background: isNew ? '#fff7e6' : 'transparent'
100
+ flex: 1
101
+ }
102
+ }));
103
+ };
104
+
105
+ /** 宽度输入 */
106
+ var WidthCellRenderer = function WidthCellRenderer(props) {
107
+ var item = props.data;
108
+ if (!item || item.type !== 'column') return /*#__PURE__*/React.createElement("span", null, "-");
109
+ return /*#__PURE__*/React.createElement(DebounceNumberInput, {
110
+ value: item.width || 150,
111
+ onChange: function onChange(val) {
112
+ if (props.node) {
113
+ props.node.setDataValue('width', val);
114
+ }
115
+ },
116
+ min: 50,
117
+ max: 1000,
118
+ style: {
119
+ width: '100%'
83
120
  }
84
121
  });
85
122
  };
86
123
 
87
- /**
88
- * 可拖拽的表格行组件
89
- */
90
- var DraggableRow = function DraggableRow(_ref2) {
91
- var record = _ref2.record,
92
- index = _ref2.index,
93
- moveRow = _ref2.moveRow,
94
- children = _ref2.children,
95
- restProps = _objectWithoutProperties(_ref2, _excluded);
96
- var ref = useRef(null);
97
- var _useDrag = useDrag({
98
- type: DRAG_TYPE,
99
- item: {
100
- id: record.id,
101
- index: index,
102
- parentId: record.parentId,
103
- level: record.level
124
+ /** Switch 渲染器工厂 */
125
+ var createSwitchRenderer = function createSwitchRenderer(field) {
126
+ return function (props) {
127
+ var item = props.data;
128
+ if (!item || item.type !== 'column') return /*#__PURE__*/React.createElement("span", null, "-");
129
+
130
+ // 获取字段值
131
+ var fieldValue = item[field];
132
+
133
+ // 只检查布尔值
134
+ var isChecked = fieldValue === true;
135
+ return /*#__PURE__*/React.createElement(Switch, {
136
+ checked: isChecked,
137
+ onChange: function onChange(checked) {
138
+ if (props.node) {
139
+ // 保存时统一为布尔值
140
+ props.node.setDataValue(field, checked);
141
+ }
104
142
  },
105
- collect: function collect(monitor) {
106
- return {
107
- isDragging: monitor.isDragging()
108
- };
143
+ size: "small"
144
+ });
145
+ };
146
+ };
147
+
148
+ /** 列过滤按钮渲染器 (注意:suppressHeaderFilterButton 字段为 true 时表示"禁用"过滤按钮) */
149
+ var SuppressFilterButtonRenderer = function SuppressFilterButtonRenderer(props) {
150
+ var item = props.data;
151
+ if (!item || item.type !== 'column') return /*#__PURE__*/React.createElement("span", null, "-");
152
+
153
+ // 注意:suppressHeaderFilterButton = true 表示"禁用"过滤按钮
154
+ // 所以 Switch 显示需要取反:checked 表示"启用"过滤
155
+ var isFilterEnabled = !item.suppressHeaderFilterButton;
156
+ return /*#__PURE__*/React.createElement(Switch, {
157
+ checked: isFilterEnabled,
158
+ onChange: function onChange(checked) {
159
+ if (props.node) {
160
+ // 取反保存:checked=true 表示启用过滤,保存为 false
161
+ props.node.setDataValue('suppressHeaderFilterButton', !checked);
109
162
  }
110
- }),
111
- _useDrag2 = _slicedToArray(_useDrag, 2),
112
- isDragging = _useDrag2[0].isDragging,
113
- drag = _useDrag2[1];
114
- var _useDrop = useDrop({
115
- accept: DRAG_TYPE,
116
- hover: function hover(item, monitor) {
117
- if (!ref.current) return;
118
- var dragIndex = item.index;
119
- var hoverIndex = index;
120
- if (dragIndex === hoverIndex) return;
121
-
122
- // 只允许同级别、同父节点的节点之间拖拽
123
- if (item.parentId !== record.parentId || item.level !== record.level) {
124
- return;
125
- }
126
- var hoverBoundingRect = ref.current.getBoundingClientRect();
127
- var hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
128
- var clientOffset = monitor.getClientOffset();
129
- if (!clientOffset) return;
130
- var hoverClientY = clientOffset.y - hoverBoundingRect.top;
131
-
132
- // 向下拖拽时,只有当鼠标超过中线才触发
133
- if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
134
- // 向上拖拽时,只有当鼠标超过中线才触发
135
- if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;
136
- moveRow(dragIndex, hoverIndex, item, {
137
- id: record.id,
138
- index: hoverIndex,
139
- parentId: record.parentId,
140
- level: record.level
141
- });
142
- item.index = hoverIndex;
163
+ },
164
+ size: "small"
165
+ });
166
+ };
167
+
168
+ /** Select 下拉框渲染器 */
169
+ var FlexCellRenderer = function FlexCellRenderer(props) {
170
+ var item = props.data;
171
+ if (!item || item.type !== 'column') return /*#__PURE__*/React.createElement("span", null, "-");
172
+ return /*#__PURE__*/React.createElement(Select, {
173
+ value: item.flex || 0,
174
+ onChange: function onChange(val) {
175
+ if (props.node) {
176
+ props.node.setDataValue('flex', val);
177
+ }
178
+ },
179
+ style: {
180
+ width: '100%'
181
+ },
182
+ options: [{
183
+ value: 1,
184
+ label: '占满'
185
+ }, {
186
+ value: 0,
187
+ label: '不占满'
188
+ }],
189
+ size: "small"
190
+ });
191
+ };
192
+ var PinnedCellRenderer = function PinnedCellRenderer(props) {
193
+ var item = props.data;
194
+ if (!item || item.type !== 'column') return /*#__PURE__*/React.createElement("span", null, "-");
195
+
196
+ // 标准化 pinned 值 - 处理旧版本的 notPinned
197
+ var normalizedValue = !item.pinned || item.pinned === 'notPinned' ? 'none' : item.pinned;
198
+ return /*#__PURE__*/React.createElement(Select, {
199
+ value: normalizedValue,
200
+ onChange: function onChange(val) {
201
+ if (props.node) {
202
+ // 保存时将 none 转换为 undefined 或 null
203
+ props.node.setDataValue('pinned', val === 'none' ? null : val);
143
204
  }
144
- }),
145
- _useDrop2 = _slicedToArray(_useDrop, 2),
146
- drop = _useDrop2[1];
147
- drag(drop(ref));
148
- return /*#__PURE__*/React.createElement("tr", _extends({
149
- ref: ref
150
- }, restProps, {
205
+ },
151
206
  style: {
152
- opacity: isDragging ? 0.5 : 1,
153
- cursor: 'move'
207
+ width: '100%'
208
+ },
209
+ options: [{
210
+ value: 'none',
211
+ label: '不固定'
212
+ }, {
213
+ value: 'left',
214
+ label: '左侧'
215
+ }, {
216
+ value: 'right',
217
+ label: '右侧'
218
+ }],
219
+ size: "small"
220
+ });
221
+ };
222
+
223
+ /** 操作列 */
224
+ var ActionCellRenderer = function ActionCellRenderer(props) {
225
+ var item = props.data;
226
+ if (!item) return null;
227
+
228
+ // 从 props 中获取自定义参数
229
+ var _ref2 = props,
230
+ onDelete = _ref2.onDelete,
231
+ onAddChildGroup = _ref2.onAddChildGroup,
232
+ simple = _ref2.simple;
233
+ return /*#__PURE__*/React.createElement(Space, {
234
+ size: "small"
235
+ }, !simple && item.type === 'group' && /*#__PURE__*/React.createElement(Button, {
236
+ type: "link",
237
+ size: "small",
238
+ icon: /*#__PURE__*/React.createElement(FolderAddOutlined, null),
239
+ onClick: function onClick() {
240
+ return onAddChildGroup === null || onAddChildGroup === void 0 ? void 0 : onAddChildGroup(item.id);
241
+ }
242
+ }, "\u6DFB\u52A0\u5B50\u5206\u7EC4"), item.type === 'group' && /*#__PURE__*/React.createElement(Popconfirm, {
243
+ title: "\u5220\u9664\u5206\u7EC4\u540E,\u5176\u5B50\u9879\u5C06\u79FB\u81F3\u6839\u7EA7\u522B,\u786E\u5B9A\u5220\u9664\u5417?",
244
+ onConfirm: function onConfirm() {
245
+ return onDelete === null || onDelete === void 0 ? void 0 : onDelete(item.id);
154
246
  }
155
- }), children);
247
+ }, /*#__PURE__*/React.createElement(Button, {
248
+ type: "link",
249
+ danger: true,
250
+ size: "small",
251
+ icon: /*#__PURE__*/React.createElement(DeleteOutlined, null)
252
+ }, "\u5220\u9664")));
156
253
  };
157
254
 
158
- /**
159
- * 列配置表格组件
160
- */
255
+ // ==================== 主组件 ====================
256
+
161
257
  var ColumnConfigTable = function ColumnConfigTable(_ref3) {
162
258
  var items = _ref3.items,
163
259
  onChange = _ref3.onChange,
164
260
  _ref3$simple = _ref3.simple,
165
261
  simple = _ref3$simple === void 0 ? false : _ref3$simple,
166
- scrollY = _ref3.scrollY;
167
- // 本地状态管理(用于优化性能)
168
- var _useState3 = useState(items),
262
+ _ref3$scrollY = _ref3.scrollY,
263
+ scrollY = _ref3$scrollY === void 0 ? 480 : _ref3$scrollY;
264
+ var _useState = useState(items),
265
+ _useState2 = _slicedToArray(_useState, 2),
266
+ localItems = _useState2[0],
267
+ setLocalItems = _useState2[1];
268
+ var _useState3 = useState(new Set()),
169
269
  _useState4 = _slicedToArray(_useState3, 2),
170
- localItems = _useState4[0],
171
- setLocalItems = _useState4[1];
172
-
173
- // 移动节点弹窗状态
174
- var _useState5 = useState(false),
175
- _useState6 = _slicedToArray(_useState5, 2),
176
- moveModalVisible = _useState6[0],
177
- setMoveModalVisible = _useState6[1];
178
- var _useState7 = useState(null),
179
- _useState8 = _slicedToArray(_useState7, 2),
180
- currentMoveNode = _useState8[0],
181
- setCurrentMoveNode = _useState8[1];
182
-
183
- // 表格容器引用和动态高度
184
- var containerRef = useRef(null);
185
- var _useState9 = useState(scrollY || 480),
186
- _useState10 = _slicedToArray(_useState9, 2),
187
- tableHeight = _useState10[0],
188
- setTableHeight = _useState10[1];
189
-
190
- /**
191
- * 同步更新本地状态和通知父组件
192
- */
270
+ expandedKeys = _useState4[0],
271
+ setExpandedKeys = _useState4[1];
272
+ var gridApiRef = useRef();
273
+
274
+ // 同步外部数据
275
+ React.useEffect(function () {
276
+ setLocalItems(items);
277
+ // 默认展开所有分组
278
+ var allGroupIds = items.filter(function (item) {
279
+ return item.type === 'group';
280
+ }).map(function (item) {
281
+ return item.id;
282
+ });
283
+ setExpandedKeys(new Set(allGroupIds));
284
+ }, [items]);
285
+
286
+ // 更新数据并同步到父组件
193
287
  var updateItems = useCallback(function (newItems) {
194
- // 重新计算 order
195
- reorderItems(newItems);
196
- setLocalItems(_toConsumableArray(newItems));
197
- onChange(_toConsumableArray(newItems));
288
+ setLocalItems(newItems);
289
+ onChange(newItems);
198
290
  }, [onChange]);
199
291
 
200
- /**
201
- * 防抖的更新函数 - 用于输入框等高频更新场景
202
- * 延迟 300ms 后才通知父组件,减少不必要的重渲染
203
- */
204
- var debouncedUpdateItems = useMemo(function () {
205
- return _debounce(function (newItems) {
206
- reorderItems(newItems);
207
- onChange(_toConsumableArray(newItems));
208
- }, 300);
209
- }, [onChange]);
292
+ // Grid 准备完成
293
+ var onGridReady = useCallback(function (params) {
294
+ gridApiRef.current = params.api;
295
+ }, []);
210
296
 
211
- // 组件卸载时清理防抖函数
212
- useEffect(function () {
213
- return function () {
214
- debouncedUpdateItems.cancel();
297
+ // 递归获取节点的所有子孙节点ID
298
+ var getAllDescendantIds = useCallback(function (nodeId) {
299
+ var descendants = new Set();
300
+ var findDescendants = function findDescendants(id) {
301
+ localItems.forEach(function (item) {
302
+ if (item.parentId === id) {
303
+ descendants.add(item.id);
304
+ findDescendants(item.id); // 递归查找子孙
305
+ }
306
+ });
215
307
  };
216
- }, [debouncedUpdateItems]);
217
-
218
- // 动态计算表格容器高度
219
- useLayoutEffect(function () {
220
- var _containerRef$current3, _containerRef$current4;
221
- // 如果外部传入了 scrollY,使用外部值
222
- if (scrollY) {
223
- setTableHeight(scrollY);
224
- return;
225
- }
226
- var calculateHeight = function calculateHeight() {
227
- if (containerRef.current) {
228
- var _containerRef$current, _containerRef$current2;
229
- // 查找 Modal 相关元素
230
- var _modalContent = containerRef.current.closest('.ant-modal-content');
231
- var modalHeader = (_containerRef$current = containerRef.current.closest('.ant-modal')) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.querySelector('.ant-modal-header');
232
- var modalFooter = (_containerRef$current2 = containerRef.current.closest('.ant-modal')) === null || _containerRef$current2 === void 0 ? void 0 : _containerRef$current2.querySelector('.ant-modal-footer');
233
- if (_modalContent) {
234
- // 获取各部分的实际高度
235
- var modalContentRect = _modalContent.getBoundingClientRect();
236
- var containerRect = containerRef.current.getBoundingClientRect();
237
- var headerHeight = modalHeader ? modalHeader.getBoundingClientRect().height : 0;
238
- var footerHeight = modalFooter ? modalFooter.getBoundingClientRect().height : 0;
239
-
240
- // 计算表格可用高度 = Modal Content 高度 - Header - Footer - 容器之前的内容高度 - 底部留白
241
- var contentBeforeTable = containerRect.top - modalContentRect.top - headerHeight;
242
- var availableHeight = modalContentRect.height - headerHeight - footerHeight - contentBeforeTable - 55;
243
-
244
- // 设置最小高度为300px
245
- var finalHeight = Math.max(600, availableHeight);
246
- setTableHeight(finalHeight);
308
+ findDescendants(nodeId);
309
+ return descendants;
310
+ }, [localItems]);
311
+
312
+ // 递归更新节点及其所有子孙节点的 level
313
+ var updateNodeLevels = useCallback(function (items, nodeId, newLevel) {
314
+ return items.map(function (item) {
315
+ if (item.id === nodeId) {
316
+ return _objectSpread(_objectSpread({}, item), {}, {
317
+ level: newLevel
318
+ });
319
+ }
320
+ // 如果是子节点,递归更新
321
+ if (item.parentId === nodeId) {
322
+ return updateNodeLevels([_objectSpread(_objectSpread({}, item), {}, {
323
+ level: newLevel + 1
324
+ })], item.id, newLevel + 1)[0];
325
+ }
326
+ return item;
327
+ });
328
+ }, []);
329
+
330
+ // 行拖拽结束
331
+ var onRowDragEnd = useCallback(function (event) {
332
+ var _event$overNode;
333
+ var movingNode = event.node.data;
334
+ var overNode = (_event$overNode = event.overNode) === null || _event$overNode === void 0 ? void 0 : _event$overNode.data;
335
+ if (!movingNode || !overNode || movingNode.id === overNode.id) return;
336
+
337
+ // 检查是否拖动到分组上(移入分组)
338
+ if (overNode.type === 'group') {
339
+ // 防止将分组拖入其子孙节点
340
+ if (movingNode.type === 'group') {
341
+ var descendantIds = getAllDescendantIds(movingNode.id);
342
+ if (descendantIds.has(overNode.id)) {
343
+ message.error('不能将分组移入其子孙节点');
344
+ return;
247
345
  }
248
346
  }
249
- };
250
347
 
251
- // 延迟执行确保DOM渲染完成
252
- setTimeout(calculateHeight, 0);
253
- window.addEventListener('resize', calculateHeight);
348
+ // 收集需要移动的所有节点(包括子孙节点)
349
+ var movingIds = new Set([movingNode.id]);
350
+ if (movingNode.type === 'group') {
351
+ var descendants = getAllDescendantIds(movingNode.id);
352
+ descendants.forEach(function (id) {
353
+ return movingIds.add(id);
354
+ });
355
+ }
254
356
 
255
- // 监听 Modal 大小变化
256
- var observer = new MutationObserver(function () {
257
- // 使用 requestAnimationFrame 确保 flex 布局更新完成
258
- requestAnimationFrame(calculateHeight);
259
- });
260
- var modal = (_containerRef$current3 = containerRef.current) === null || _containerRef$current3 === void 0 ? void 0 : _containerRef$current3.closest('.ant-modal');
261
- var modalContent = (_containerRef$current4 = containerRef.current) === null || _containerRef$current4 === void 0 ? void 0 : _containerRef$current4.closest('.ant-modal-content');
262
-
263
- // 同时监听 modal 和 modalContent 的 style 变化
264
- if (modal) {
265
- observer.observe(modal, {
266
- attributes: true,
267
- attributeFilter: ['style']
357
+ // 1. 分离出不移动的节点和要移动的节点
358
+ var nonMovingItems = [];
359
+ var movingItems = [];
360
+ localItems.forEach(function (item) {
361
+ if (movingIds.has(item.id)) {
362
+ movingItems.push(item);
363
+ } else {
364
+ nonMovingItems.push(item);
365
+ }
268
366
  });
367
+
368
+ // 2. 在目标分组的子节点末尾找到插入位置
369
+ var targetGroupIndex = nonMovingItems.findIndex(function (item) {
370
+ return item.id === overNode.id;
371
+ });
372
+ var insertIndex = targetGroupIndex + 1;
373
+
374
+ // 找到目标分组的最后一个子节点位置
375
+ while (insertIndex < nonMovingItems.length && nonMovingItems[insertIndex].parentId === overNode.id) {
376
+ insertIndex++;
377
+ }
378
+
379
+ // 3. 更新移动节点的父节点和层级
380
+ var newLevel = (overNode.level || 0) + 1;
381
+ var levelDiff = newLevel - (movingNode.level || 0);
382
+ var updatedMovingItems = movingItems.map(function (item) {
383
+ if (item.id === movingNode.id) {
384
+ // 主节点:更新父节点和层级
385
+ return _objectSpread(_objectSpread({}, item), {}, {
386
+ parentId: overNode.id,
387
+ level: newLevel
388
+ });
389
+ } else {
390
+ // 子孙节点:只更新层级(保持原有的 parentId)
391
+ return _objectSpread(_objectSpread({}, item), {}, {
392
+ level: (item.level || 0) + levelDiff
393
+ });
394
+ }
395
+ });
396
+
397
+ // 4. 合并数组并重新计算 order
398
+ var result = [].concat(_toConsumableArray(nonMovingItems.slice(0, insertIndex)), _toConsumableArray(updatedMovingItems), _toConsumableArray(nonMovingItems.slice(insertIndex)));
399
+ var finalItems = result.map(function (item, index) {
400
+ return _objectSpread(_objectSpread({}, item), {}, {
401
+ order: index
402
+ });
403
+ });
404
+
405
+ // 自动展开目标分组
406
+ setExpandedKeys(function (prev) {
407
+ return new Set(prev).add(overNode.id);
408
+ });
409
+ updateItems(finalItems);
410
+ message.success("\u5DF2\u79FB\u52A8\u5230 ".concat(overNode.headerName));
411
+ return;
269
412
  }
270
- if (modalContent) {
271
- observer.observe(modalContent, {
272
- attributes: true,
273
- attributeFilter: ['style']
413
+
414
+ // 同级拖拽排序
415
+ if (movingNode.parentId === overNode.parentId) {
416
+ // 收集需要移动的所有节点(包括子孙节点)
417
+ var _movingIds = new Set([movingNode.id]);
418
+ if (movingNode.type === 'group') {
419
+ var _descendants = getAllDescendantIds(movingNode.id);
420
+ _descendants.forEach(function (id) {
421
+ return _movingIds.add(id);
422
+ });
423
+ }
424
+
425
+ // 分离出不移动的节点和要移动的节点
426
+ var _nonMovingItems = [];
427
+ var _movingItems = [];
428
+ localItems.forEach(function (item) {
429
+ if (_movingIds.has(item.id)) {
430
+ _movingItems.push(item);
431
+ } else {
432
+ _nonMovingItems.push(item);
433
+ }
434
+ });
435
+
436
+ // 找到目标节点的位置
437
+ var overIndex = _nonMovingItems.findIndex(function (item) {
438
+ return item.id === overNode.id;
439
+ });
440
+ if (overIndex === -1) return;
441
+
442
+ // 找到移动节点原本的位置
443
+ var originalMovingIndex = localItems.findIndex(function (item) {
444
+ return item.id === movingNode.id;
445
+ });
446
+ var originalOverIndex = localItems.findIndex(function (item) {
447
+ return item.id === overNode.id;
274
448
  });
449
+
450
+ // 根据原始位置判断插入位置
451
+ var _insertIndex = originalMovingIndex < originalOverIndex ? overIndex + 1 : overIndex;
452
+
453
+ // 插入到新位置
454
+ var _result = [].concat(_toConsumableArray(_nonMovingItems.slice(0, _insertIndex)), _movingItems, _toConsumableArray(_nonMovingItems.slice(_insertIndex)));
455
+
456
+ // 更新 order 字段
457
+ var updatedItems = _result.map(function (item, index) {
458
+ return _objectSpread(_objectSpread({}, item), {}, {
459
+ order: index
460
+ });
461
+ });
462
+ updateItems(updatedItems);
463
+ message.success('排序已更新');
464
+ return;
275
465
  }
276
- return function () {
277
- window.removeEventListener('resize', calculateHeight);
278
- observer.disconnect();
279
- };
280
- }, [scrollY]);
281
-
282
- /**
283
- * 切换分组的折叠/展开状态
284
- */
285
- var handleToggleCollapse = useCallback(function (nodeId) {
286
- var newItems = localItems.map(function (item) {
287
- if (item.id === nodeId && item.type === 'group') {
466
+
467
+ // 跨级拖拽 - 将节点移到不同的父节点下
468
+ // 例如:将分组中的列拖到根级别
469
+ if (movingNode.parentId !== overNode.parentId) {
470
+ // 收集需要移动的节点(只移动单个节点,不包括子孙)
471
+ var _movingIds2 = new Set([movingNode.id]);
472
+
473
+ // 分离出不移动的节点和要移动的节点
474
+ var _nonMovingItems2 = [];
475
+ var _movingItems2 = [];
476
+ localItems.forEach(function (item) {
477
+ if (_movingIds2.has(item.id)) {
478
+ _movingItems2.push(item);
479
+ } else {
480
+ _nonMovingItems2.push(item);
481
+ }
482
+ });
483
+
484
+ // 找到目标节点的位置
485
+ var _overIndex = _nonMovingItems2.findIndex(function (item) {
486
+ return item.id === overNode.id;
487
+ });
488
+ if (_overIndex === -1) return;
489
+
490
+ // 找到移动节点原本的位置
491
+ var _originalMovingIndex = localItems.findIndex(function (item) {
492
+ return item.id === movingNode.id;
493
+ });
494
+ var _originalOverIndex = localItems.findIndex(function (item) {
495
+ return item.id === overNode.id;
496
+ });
497
+
498
+ // 根据原始位置判断插入位置
499
+ var _insertIndex2 = _originalMovingIndex < _originalOverIndex ? _overIndex + 1 : _overIndex;
500
+
501
+ // 更新移动节点的父节点和层级
502
+ var _updatedMovingItems = _movingItems2.map(function (item) {
288
503
  return _objectSpread(_objectSpread({}, item), {}, {
289
- _collapsed: !item._collapsed
504
+ parentId: overNode.parentId,
505
+ // 使用目标节点的父节点
506
+ level: overNode.level // 使用目标节点的层级
290
507
  });
291
- }
292
- return item;
293
- });
294
- setLocalItems(newItems);
295
- // 注意: _collapsed 是临时UI状态,不需要通知父组件持久化
296
- }, [localItems]);
508
+ });
297
509
 
298
- /**
299
- * 检查节点是否应该被隐藏(因为祖先节点折叠)
300
- */
301
- var isNodeHidden = useCallback(function (item) {
302
- var currentId = item.parentId;
303
- while (currentId) {
304
- var parent = localItems.find(function (i) {
305
- return i.id === currentId;
510
+ // 插入到新位置
511
+ var _result2 = [].concat(_toConsumableArray(_nonMovingItems2.slice(0, _insertIndex2)), _toConsumableArray(_updatedMovingItems), _toConsumableArray(_nonMovingItems2.slice(_insertIndex2)));
512
+
513
+ // 更新 order 字段
514
+ var _updatedItems = _result2.map(function (item, index) {
515
+ return _objectSpread(_objectSpread({}, item), {}, {
516
+ order: index
517
+ });
306
518
  });
307
- if (!parent) break;
308
- if (parent.type === 'group' && parent._collapsed) {
309
- return true;
310
- }
311
- currentId = parent.parentId;
519
+ updateItems(_updatedItems);
520
+ message.success("\u5DF2\u79FB\u52A8\u5230".concat(overNode.parentId ? '分组内' : '根级别'));
312
521
  }
313
- return false;
314
- }, [localItems]);
522
+ }, [localItems, updateItems, getAllDescendantIds]);
315
523
 
316
- /**
317
- * 获取可见的节点列表(过滤掉被折叠隐藏的节点)
318
- */
319
- var visibleItems = useMemo(function () {
320
- return (localItems === null || localItems === void 0 ? void 0 : localItems.filter(function (item) {
321
- return !isNodeHidden(item);
322
- })) || [];
323
- }, [localItems, isNodeHidden]);
324
-
325
- /**
326
- * 添加根级别分组
327
- */
524
+ // 添加根级别分组
328
525
  var handleAddRootGroup = useCallback(function () {
329
526
  var newGroup = {
330
527
  id: generateId(),
331
528
  type: 'group',
332
- // ⚠️ 关键:明确设置 type
333
529
  level: 0,
334
530
  order: localItems.length,
335
531
  headerName: '新分组',
@@ -340,650 +536,193 @@ var ColumnConfigTable = function ColumnConfigTable(_ref3) {
340
536
  message.success('已添加根级别分组');
341
537
  }, [localItems, updateItems]);
342
538
 
343
- /**
344
- * 为指定分组添加子分组
345
- */
539
+ // 添加子分组
346
540
  var handleAddChildGroup = useCallback(function (parentId) {
347
- var parentIndex = localItems.findIndex(function (item) {
541
+ var parent = localItems.find(function (item) {
348
542
  return item.id === parentId;
349
543
  });
350
- if (parentIndex === -1) {
351
- message.error('未找到父节点');
352
- return;
353
- }
354
- var parent = localItems[parentIndex];
355
- if (parent.type !== 'group') {
544
+ if (!parent || parent.type !== 'group') {
356
545
  message.error('只能为分组添加子分组');
357
546
  return;
358
547
  }
359
-
360
- // 找到插入位置(父节点的所有后代节点之后)
361
- var insertIndex = findLastDescendantIndex(localItems, parentId) + 1;
362
548
  var newGroup = {
363
549
  id: generateId(),
364
550
  type: 'group',
365
- // ⚠️ 关键:明确设置 type
366
551
  parentId: parentId,
367
- level: parent.level + 1,
368
- order: insertIndex,
552
+ level: (parent.level || 0) + 1,
553
+ order: localItems.length,
369
554
  headerName: '新子分组',
370
555
  groupId: generateId(),
371
556
  _isNew: true
372
557
  };
373
- var newItems = _toConsumableArray(localItems);
374
- newItems.splice(insertIndex, 0, newGroup);
375
- updateItems(newItems);
376
- message.success("\u5DF2\u4E3A\u5206\u7EC4\"".concat(parent.headerName, "\"\u6DFB\u52A0\u5B50\u5206\u7EC4"));
558
+ updateItems([].concat(_toConsumableArray(localItems), [newGroup]));
559
+ message.success('已添加子分组');
377
560
  }, [localItems, updateItems]);
378
561
 
379
- /**
380
- * 打开移动节点弹窗
381
- */
382
- var handleOpenMoveModal = useCallback(function (node) {
383
- setCurrentMoveNode(node);
384
- setMoveModalVisible(true);
385
- }, [localItems]);
386
-
387
- /**
388
- * 关闭移动节点弹窗
389
- */
390
- var handleCloseMoveModal = useCallback(function () {
391
- setMoveModalVisible(false);
392
- setCurrentMoveNode(null);
393
- }, []);
394
-
395
- /**
396
- * 确认移动节点
397
- */
398
- var handleConfirmMove = useCallback(function (targetParentId) {
399
- if (!currentMoveNode) return;
400
- var nodeId = currentMoveNode.id;
401
-
402
- // 1. 验证:不能移动到自己的后代节点下
403
- if (targetParentId) {
404
- var checkId = targetParentId;
405
- while (checkId) {
406
- if (checkId === nodeId) {
407
- message.error('不能将节点移动到自己的后代节点下');
408
- return;
409
- }
410
- var parent = localItems.find(function (item) {
411
- return item.id === checkId;
412
- });
413
- checkId = parent === null || parent === void 0 ? void 0 : parent.parentId;
414
- }
415
- }
416
-
417
- // 2. 验证:不能移动到同一个父节点下(无意义操作)
418
- if (currentMoveNode.parentId === targetParentId) {
419
- message.warning('节点已经在该父节点下,无需移动');
420
- handleCloseMoveModal();
421
- return;
422
- }
423
-
424
- // 3. 执行移动
425
- var newItems = _toConsumableArray(localItems);
426
-
427
- // 3.1 收集要移动的节点(包括所有后代)
428
- var nodesToMove = [];
429
- var collectNodes = function collectNodes(id) {
430
- var node = newItems.find(function (item) {
431
- return item.id === id;
432
- });
433
- if (!node) return;
434
- nodesToMove.push(node);
435
-
436
- // 递归收集子节点
437
- newItems.forEach(function (item) {
438
- if (item.parentId === id) {
439
- collectNodes(item.id);
440
- }
441
- });
442
- };
443
- collectNodes(nodeId);
444
-
445
- // 3.2 从原位置移除这些节点
446
- var remainingItems = newItems.filter(function (item) {
447
- return !nodesToMove.some(function (n) {
448
- return n.id === item.id;
449
- });
450
- });
451
-
452
- // 3.3 计算新的层级
453
- var targetParent = targetParentId ? remainingItems.find(function (item) {
454
- return item.id === targetParentId;
455
- }) : undefined;
456
- var newLevel = targetParent ? targetParent.level + 1 : 0;
457
- var levelDelta = newLevel - currentMoveNode.level;
458
-
459
- // 3.4 更新节点的 parentId 和 level
460
- var updatedNodesToMove = nodesToMove.map(function (node) {
461
- return _objectSpread(_objectSpread({}, node), {}, {
462
- parentId: node.id === nodeId ? targetParentId : node.parentId,
463
- level: node.level + levelDelta
464
- });
562
+ // 删除节点
563
+ var handleDelete = useCallback(function (id) {
564
+ var item = localItems.find(function (i) {
565
+ return i.id === id;
465
566
  });
466
-
467
- // 3.5 找到插入位置
468
- var insertIndex;
469
- if (targetParentId) {
470
- // 插入到目标父节点的所有后代之后
471
- insertIndex = findLastDescendantIndex(remainingItems, targetParentId) + 1;
472
- } else {
473
- // 插入到根级别的末尾
474
- insertIndex = remainingItems.length;
475
- }
476
-
477
- // 3.6 插入节点
478
- remainingItems.splice.apply(remainingItems, [insertIndex, 0].concat(_toConsumableArray(updatedNodesToMove)));
479
-
480
- // 3.7 重新计算 order
481
- reorderItems(remainingItems);
482
- updateItems(remainingItems);
483
- message.success("\u5DF2\u5C06\"".concat(currentMoveNode.headerName, "\"\u79FB\u52A8\u5230\u65B0\u4F4D\u7F6E"));
484
- handleCloseMoveModal();
485
- }, [currentMoveNode, localItems, updateItems, handleCloseMoveModal]);
486
-
487
- /**
488
- * 删除节点(及其所有子节点)
489
- */
490
- var handleDeleteNode = useCallback(function (nodeId) {
491
- var nodeIndex = localItems.findIndex(function (item) {
492
- return item.id === nodeId;
493
- });
494
- if (nodeIndex === -1) {
495
- message.error('未找到要删除的节点');
496
- return;
497
- }
498
- var node = localItems[nodeIndex];
499
-
500
- // 查找所有需要删除的节点(包括所有后代)
501
- var toDelete = [nodeId];
502
- var findDescendants = function findDescendants(parentId) {
503
- localItems.forEach(function (item) {
504
- if (item.parentId === parentId) {
505
- toDelete.push(item.id);
506
- if (item.type === 'group') {
507
- findDescendants(item.id);
508
- }
567
+ if ((item === null || item === void 0 ? void 0 : item.type) === 'group') {
568
+ // 删除分组时,将其直接子项移至根级别
569
+ var updatedItems = localItems.filter(function (i) {
570
+ return i.id !== id;
571
+ }) // 移除分组本身
572
+ .map(function (i) {
573
+ // 如果是该分组的直接子项
574
+ if (i.parentId === id) {
575
+ return _objectSpread(_objectSpread({}, i), {}, {
576
+ parentId: undefined,
577
+ // 移至根级别
578
+ level: 0 // 设置为根级别
579
+ });
509
580
  }
581
+ return i;
510
582
  });
511
- };
512
- if (node.type === 'group') {
513
- findDescendants(nodeId);
583
+ updateItems(updatedItems);
584
+ message.success('分组已删除,子项已移至根级别');
514
585
  }
515
- var newItems = localItems.filter(function (item) {
516
- return !toDelete.includes(item.id);
517
- });
518
- updateItems(newItems);
519
- message.success(node.type === 'group' ? "\u5DF2\u5220\u9664\u5206\u7EC4\"".concat(node.headerName, "\"\u53CA\u5176 ").concat(toDelete.length - 1, " \u4E2A\u5B50\u8282\u70B9") : "\u5DF2\u5220\u9664\u5217\"".concat(node.headerName, "\""));
520
586
  }, [localItems, updateItems]);
521
587
 
522
- /**
523
- * 更新节点属性
524
- * @param immediate 是否立即更新父组件(非输入框类更新需要立即生效)
525
- */
526
- var handleUpdateNode = useCallback(function (nodeId, updates) {
527
- var immediate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
528
- var newItems = localItems.map(function (item) {
529
- if (item.id === nodeId) {
530
- // ⚠️ 关键:确保 type 字段不会被覆盖
531
- return _objectSpread(_objectSpread(_objectSpread({}, item), updates), {}, {
532
- type: item.type,
533
- // 强制保留原始 type
534
- _isNew: false // 清除新建标记
535
- });
588
+ // 切换展开/折叠
589
+ var toggleExpand = useCallback(function (id) {
590
+ setExpandedKeys(function (prev) {
591
+ var next = new Set(prev);
592
+ if (next.has(id)) {
593
+ next.delete(id);
594
+ } else {
595
+ next.add(id);
536
596
  }
537
- return item;
538
- });
539
-
540
- // 立即更新本地状态(保证输入框显示最新内容)
541
- setLocalItems(_toConsumableArray(newItems));
542
-
543
- // 根据场景选择立即更新或防抖更新
544
- if (immediate) {
545
- // 非文本输入的更新(如开关、选择器等)需要立即生效
546
- reorderItems(newItems);
547
- onChange(_toConsumableArray(newItems));
548
- } else {
549
- // 文本输入使用防抖,避免频繁触发父组件更新
550
- debouncedUpdateItems(newItems);
551
- }
552
- }, [localItems, onChange, debouncedUpdateItems]);
553
-
554
- /**
555
- * 处理拖拽排序
556
- */
557
- var handleDragSort = useCallback(function (dragIndex, hoverIndex, dragItem, hoverItem) {
558
- // 只有同级别、同父节点的节点才能拖拽排序
559
- if (dragItem.parentId !== hoverItem.parentId || dragItem.level !== hoverItem.level) {
560
- return;
561
- }
562
-
563
- // ✅ 关键修复: 通过 ID 找到在 localItems 中的真实索引
564
- // dragIndex 和 hoverIndex 是基于 visibleItems 的索引,不能直接用于 localItems
565
- var realDragIndex = localItems.findIndex(function (item) {
566
- return item.id === dragItem.id;
567
- });
568
- var realHoverIndex = localItems.findIndex(function (item) {
569
- return item.id === hoverItem.id;
597
+ return next;
570
598
  });
571
- if (realDragIndex === -1 || realHoverIndex === -1) {
572
- console.error('[ColumnConfigTable] 找不到节点索引:', {
573
- dragItem: dragItem,
574
- hoverItem: hoverItem
575
- });
576
- return;
577
- }
578
- var newItems = _toConsumableArray(localItems);
579
- var draggedItem = newItems[realDragIndex];
580
-
581
- // 如果是分组节点,需要收集所有后代节点一起移动
582
- var nodesToMove = [draggedItem];
583
- if (draggedItem.type === 'group') {
584
- // 递归收集所有后代节点
585
- var collectDescendants = function collectDescendants(parentId) {
586
- newItems.forEach(function (item) {
587
- if (item.parentId === parentId) {
588
- nodesToMove.push(item);
589
- if (item.type === 'group') {
590
- collectDescendants(item.id);
591
- }
592
- }
593
- });
594
- };
595
- collectDescendants(draggedItem.id);
596
- }
597
-
598
- // 从原数组中移除所有要移动的节点
599
- var remainingItems = newItems.filter(function (item) {
600
- return !nodesToMove.some(function (n) {
601
- return n.id === item.id;
602
- });
603
- });
604
-
605
- // 计算新的插入位置
606
- // 如果拖动到下方,需要调整插入索引(因为移除了节点)
607
- var adjustedHoverIndex = realHoverIndex;
608
- if (realDragIndex < realHoverIndex) {
609
- // 向下拖动:需要减去被移除的节点数量
610
- adjustedHoverIndex = realHoverIndex - nodesToMove.length;
611
- }
612
-
613
- // 在新位置插入所有节点
614
- remainingItems.splice.apply(remainingItems, [adjustedHoverIndex, 0].concat(nodesToMove));
615
-
616
- // 使用 updateItems 来同时更新本地状态和通知父组件
617
- updateItems(remainingItems);
618
- }, [localItems, updateItems]);
619
-
620
- /**
621
- * 获取缩进符号
622
- */
623
- var getIndentSymbol = useCallback(function (item) {
624
- var _siblings;
625
- if (item.level === 0) return '';
626
- var siblings = localItems.filter(function (i) {
627
- return i.parentId === item.parentId;
628
- });
629
- var isLast = ((_siblings = siblings[siblings.length - 1]) === null || _siblings === void 0 ? void 0 : _siblings.id) === item.id;
630
- return isLast ? '└─ ' : '├─ ';
631
- }, [localItems]);
599
+ }, []);
632
600
 
633
- /**
634
- * 渲染名称列
635
- */
636
- var renderNameCell = useCallback(function (text, record) {
637
- var indent = record.level * 20;
638
- var indentSymbol = getIndentSymbol(record);
639
- var NodeIcon = record.type === 'group' ? /*#__PURE__*/React.createElement(FolderOutlined, {
640
- style: {
641
- color: '#1890ff',
642
- fontSize: 14
643
- }
644
- }) : /*#__PURE__*/React.createElement(FileOutlined, {
645
- style: {
646
- color: '#52c41a',
647
- fontSize: 14
648
- }
649
- });
650
- return /*#__PURE__*/React.createElement("div", {
651
- style: {
652
- paddingLeft: indent,
653
- display: 'flex',
654
- alignItems: 'center',
655
- gap: 8
656
- }
657
- }, record.level > 0 && /*#__PURE__*/React.createElement("span", {
658
- style: {
659
- color: '#999',
660
- fontFamily: 'monospace',
661
- fontSize: 12,
662
- flexShrink: 0
663
- }
664
- }, indentSymbol), record.type === 'group' ? /*#__PURE__*/React.createElement("span", {
665
- onClick: function onClick() {
666
- return handleToggleCollapse(record.id);
667
- },
668
- style: {
669
- cursor: 'pointer',
670
- fontSize: 12,
671
- color: '#999',
672
- flexShrink: 0,
673
- width: 16,
674
- textAlign: 'center'
675
- }
676
- }, record._collapsed ? /*#__PURE__*/React.createElement(RightOutlined, null) : /*#__PURE__*/React.createElement(DownOutlined, null)) : /*#__PURE__*/React.createElement("span", {
677
- style: {
678
- width: 16,
679
- flexShrink: 0
680
- }
681
- }), /*#__PURE__*/React.createElement("span", {
682
- style: {
683
- flexShrink: 0
684
- }
685
- }, NodeIcon), /*#__PURE__*/React.createElement(Tag, {
686
- color: record.type === 'group' ? 'blue' : 'green',
687
- style: {
688
- margin: 0,
689
- flexShrink: 0
690
- }
691
- }, record.type === 'group' ? '分组' : '列'), /*#__PURE__*/React.createElement(NameInput, {
692
- value: text,
693
- onChange: function onChange(newValue) {
694
- return handleUpdateNode(record.id, {
695
- headerName: newValue
696
- }, true);
697
- },
698
- placeholder: record.type === 'group' ? '请输入分组名称' : '请输入列名称',
699
- isNew: record._isNew
700
- }));
701
- }, [getIndentSymbol, handleUpdateNode, handleToggleCollapse]);
702
-
703
- /**
704
- * 渲染操作列
705
- */
706
- var renderActionsCell = useCallback(function (_, record) {
707
- if (record.type === 'group') {
708
- return /*#__PURE__*/React.createElement(Space, {
709
- size: "small"
710
- }, !simple && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Tooltip, {
711
- title: "\u6DFB\u52A0\u5B50\u5206\u7EC4"
712
- }, /*#__PURE__*/React.createElement(Button, {
713
- type: "link",
714
- size: "small",
715
- icon: /*#__PURE__*/React.createElement(FolderAddOutlined, null),
716
- onClick: function onClick() {
717
- return handleAddChildGroup(record.id);
718
- }
719
- }, "\u6DFB\u52A0\u5B50\u5206\u7EC4")), /*#__PURE__*/React.createElement(Tooltip, {
720
- title: "\u79FB\u52A8\u8282\u70B9"
721
- }, /*#__PURE__*/React.createElement(Button, {
722
- type: "link",
723
- size: "small",
724
- icon: /*#__PURE__*/React.createElement(DragOutlined, null),
725
- onClick: function onClick() {
726
- return handleOpenMoveModal(record);
727
- }
728
- }, "\u79FB\u52A8")), record._isNew && /*#__PURE__*/React.createElement(Popconfirm, {
729
- title: "\u786E\u5B9A\u8981\u5220\u9664\u5206\u7EC4\"".concat(record.headerName, "\"\u53CA\u5176\u6240\u6709\u5B50\u8282\u70B9\u5417\uFF1F"),
730
- onConfirm: function onConfirm() {
731
- return handleDeleteNode(record.id);
732
- },
733
- okText: "\u786E\u5B9A",
734
- cancelText: "\u53D6\u6D88"
735
- }, /*#__PURE__*/React.createElement(Tooltip, {
736
- title: "\u5220\u9664\u5206\u7EC4"
737
- }, /*#__PURE__*/React.createElement(Button, {
738
- type: "link",
739
- size: "small",
740
- danger: true,
741
- icon: /*#__PURE__*/React.createElement(DeleteOutlined, null)
742
- })))));
743
- } else {
744
- return /*#__PURE__*/React.createElement(Space, {
745
- size: "small"
746
- }, !simple && /*#__PURE__*/React.createElement(Tooltip, {
747
- title: "\u79FB\u52A8\u8282\u70B9"
748
- }, /*#__PURE__*/React.createElement(Button, {
749
- type: "link",
750
- size: "small",
751
- icon: /*#__PURE__*/React.createElement(DragOutlined, null),
752
- onClick: function onClick() {
753
- return handleOpenMoveModal(record);
754
- }
755
- }, "\u79FB\u52A8")));
756
- }
757
- }, [simple, handleAddChildGroup, handleDeleteNode, handleOpenMoveModal]);
758
-
759
- /**
760
- * 表格列配置
761
- */
762
- var columns = useMemo(function () {
763
- return [{
764
- title: '排序',
765
- key: 'sort',
766
- width: 40,
767
- align: 'center',
768
- render: function render() {
769
- return /*#__PURE__*/React.createElement(Tooltip, {
770
- title: "\u62D6\u52A8\u6392\u5E8F\uFF08\u4EC5\u652F\u6301\u540C\u7EA7\uFF09"
771
- }, /*#__PURE__*/React.createElement(HolderOutlined, {
772
- style: {
773
- fontSize: 16,
774
- color: '#999',
775
- cursor: 'move'
776
- }
777
- }));
778
- }
779
- }, {
780
- title: '操作',
781
- key: 'actions',
601
+ // 列定义
602
+ var columnDefs = useMemo(function () {
603
+ var cols = [{
604
+ field: 'type',
605
+ headerName: '类型',
782
606
  width: 120,
783
- align: 'center',
784
- render: renderActionsCell
785
- }, {
786
- title: '名称',
787
- dataIndex: 'headerName',
788
- key: 'headerName',
789
- width: 100,
790
- render: renderNameCell
607
+ minWidth: 100,
608
+ maxWidth: 150,
609
+ cellRenderer: TypeCellRenderer,
610
+ rowDrag: true,
611
+ suppressSizeToFit: true
791
612
  }, {
792
- title: '宽度',
793
- dataIndex: 'width',
794
- key: 'width',
795
- width: 80,
796
- align: 'center',
797
- render: function render(value, record) {
798
- if (record.type !== 'column') return '-';
799
- return /*#__PURE__*/React.createElement(InputNumber, {
800
- value: value,
801
- onChange: function onChange(newValue) {
802
- return handleUpdateNode(record.id, {
803
- width: newValue || undefined
804
- }, true);
805
- },
806
- min: 50,
807
- max: 1000,
808
- style: {
809
- width: '100%'
810
- }
811
- });
613
+ field: 'headerName',
614
+ headerName: '名称',
615
+ flex: 1,
616
+ minWidth: 200,
617
+ cellRenderer: NameCellRenderer,
618
+ cellRendererParams: {
619
+ expandedKeys: expandedKeys,
620
+ onToggleExpand: toggleExpand,
621
+ allItems: localItems
812
622
  }
813
623
  }, {
814
- title: '占满',
815
- dataIndex: 'flex',
816
- key: 'flex',
817
- width: 50,
818
- align: 'center',
819
- render: function render(value, record) {
820
- if (record.type !== 'column') return '-';
821
- return /*#__PURE__*/React.createElement(Select, {
822
- value: value || 0,
823
- onChange: function onChange(newValue) {
824
- return handleUpdateNode(record.id, {
825
- flex: newValue
826
- }, true);
827
- },
828
- style: {
829
- width: '100%'
830
- },
831
- options: [{
832
- value: 1,
833
- label: '占满'
834
- }, {
835
- value: 0,
836
- label: '不占满'
837
- }]
838
- });
839
- }
624
+ field: 'width',
625
+ headerName: '宽度',
626
+ width: 100,
627
+ cellRenderer: WidthCellRenderer
840
628
  }, {
841
- title: '固定',
842
- dataIndex: 'pinned',
843
- key: 'pinned',
844
- width: 50,
845
- align: 'center',
846
- render: function render(value, record) {
847
- if (record.type !== 'column') return '-';
848
- return /*#__PURE__*/React.createElement(Select, {
849
- value: value || 'notPinned',
850
- onChange: function onChange(newValue) {
851
- return handleUpdateNode(record.id, {
852
- pinned: newValue
853
- }, true);
854
- },
855
- style: {
856
- width: '100%'
857
- },
858
- options: [{
859
- value: 'left',
860
- label: '左固定'
861
- }, {
862
- value: 'right',
863
- label: '右固定'
864
- }, {
865
- value: 'notPinned',
866
- label: '不固定'
867
- }]
868
- });
869
- }
629
+ field: 'flex',
630
+ headerName: '占满',
631
+ width: 120,
632
+ cellRenderer: FlexCellRenderer
870
633
  }, {
871
- title: '隐藏',
872
- dataIndex: 'hide',
873
- key: 'hide',
874
- width: 50,
875
- align: 'center',
876
- render: function render(value, record) {
877
- if (record.type !== 'column') return '-';
878
- return /*#__PURE__*/React.createElement(Switch, {
879
- checked: value || false,
880
- onChange: function onChange(checked) {
881
- return handleUpdateNode(record.id, {
882
- hide: checked
883
- }, true);
884
- }
885
- });
886
- }
634
+ field: 'pinned',
635
+ headerName: '固定',
636
+ width: 120,
637
+ cellRenderer: PinnedCellRenderer
887
638
  }, {
888
- title: '布局',
889
- dataIndex: 'cellClass',
890
- key: 'cellClass',
891
- width: 50,
892
- align: 'center',
893
- render: function render(value, record) {
894
- if (record.type !== 'column') return '-';
895
- return /*#__PURE__*/React.createElement(Select, {
896
- value: value || 'custom-cell-left',
897
- onChange: function onChange(newValue) {
898
- return handleUpdateNode(record.id, {
899
- cellClass: newValue
900
- }, true);
901
- },
902
- style: {
903
- width: '100%'
904
- },
905
- options: [{
906
- value: 'custom-cell-left',
907
- label: '左对齐'
908
- }, {
909
- value: 'custom-cell-right',
910
- label: '右对齐'
911
- }, {
912
- value: 'custom-cell-centered',
913
- label: '居中'
914
- }]
915
- });
916
- }
639
+ field: 'hide',
640
+ headerName: '隐藏',
641
+ width: 80,
642
+ cellRenderer: createSwitchRenderer('hide')
917
643
  }, {
918
- title: '列过滤',
919
- dataIndex: 'suppressHeaderFilterButton',
920
- key: 'suppressHeaderFilterButton',
921
- width: 50,
922
- align: 'center',
923
- render: function render(value, record) {
924
- if (record.type !== 'column') return '-';
925
- return /*#__PURE__*/React.createElement(Switch, {
926
- checked: !value,
927
- onChange: function onChange(checked) {
928
- return handleUpdateNode(record.id, {
929
- suppressHeaderFilterButton: !checked
930
- }, true);
931
- }
932
- });
644
+ field: 'suppressHeaderFilterButton',
645
+ headerName: '列过滤',
646
+ width: 80,
647
+ cellRenderer: SuppressFilterButtonRenderer
648
+ }, {
649
+ headerName: '操作',
650
+ width: 200,
651
+ suppressSizeToFit: true,
652
+ cellRenderer: ActionCellRenderer,
653
+ cellRendererParams: {
654
+ onDelete: handleDelete,
655
+ onAddChildGroup: handleAddChildGroup,
656
+ simple: simple
933
657
  }
934
658
  }];
935
- }, [renderNameCell, renderActionsCell, handleUpdateNode]);
936
- return /*#__PURE__*/React.createElement(DndProvider, {
937
- backend: HTML5Backend
938
- }, /*#__PURE__*/React.createElement("div", {
939
- ref: containerRef
659
+ return cols;
660
+ }, [handleDelete, handleAddChildGroup, simple, expandedKeys, toggleExpand, localItems]);
661
+
662
+ // 根据展开状态过滤数据
663
+ var visibleItems = useMemo(function () {
664
+ if (simple) return localItems;
665
+
666
+ // 递归检查节点的所有祖先是否都展开
667
+ var isNodeVisible = function isNodeVisible(item) {
668
+ // 根级别节点始终显示
669
+ if (!item.parentId) return true;
670
+
671
+ // 找到父节点
672
+ var parent = localItems.find(function (i) {
673
+ return i.id === item.parentId;
674
+ });
675
+ if (!parent) return false;
676
+
677
+ // 父节点必须展开
678
+ if (!expandedKeys.has(parent.id)) return false;
679
+
680
+ // 递归检查父节点的可见性
681
+ return isNodeVisible(parent);
682
+ };
683
+ return localItems.filter(isNodeVisible);
684
+ }, [localItems, expandedKeys, simple]);
685
+
686
+ // 添加拖拽样式
687
+ useEffect(function () {
688
+ var style = document.createElement('style');
689
+ style.textContent = "\n /* \u62D6\u62FD\u884C\u6837\u5F0F */\n .ag-row-dragging {\n opacity: 0.5;\n transition: opacity 0.2s ease;\n }\n\n /* \u62D6\u62FD\u5E7D\u7075\u6837\u5F0F */\n .ag-dnd-ghost {\n background: #fff !important;\n border: 2px dashed #1890ff !important;\n border-radius: 4px !important;\n padding: 8px !important;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;\n opacity: 0.9 !important;\n font-size: 14px !important;\n }\n\n /* \u53EF\u653E\u7F6E\u76EE\u6807\u9AD8\u4EAE */\n .ag-row-hover.ag-row {\n background-color: #e6f7ff !important;\n transition: background-color 0.2s ease;\n }\n\n /* \u5206\u7EC4\u884C\u7279\u6B8A\u9AD8\u4EAE */\n .ag-row-hover.ag-row[row-id*=\"group\"] {\n background-color: #bae7ff !important;\n border: 2px solid #1890ff;\n transition: all 0.2s ease;\n }\n\n /* \u884C\u52A8\u753B */\n .ag-row {\n transition: transform 0.3s ease, opacity 0.3s ease;\n }\n\n /* \u62D6\u62FD\u624B\u67C4\u6837\u5F0F */\n .ag-row .ag-cell[col-id=\"type\"] {\n cursor: grab !important;\n }\n\n .ag-row-dragging .ag-cell[col-id=\"type\"] {\n cursor: grabbing !important;\n }\n ";
690
+ document.head.appendChild(style);
691
+ return function () {
692
+ document.head.removeChild(style);
693
+ };
694
+ }, []);
695
+ return /*#__PURE__*/React.createElement("div", {
696
+ style: {
697
+ width: '100%'
698
+ }
940
699
  }, !simple && /*#__PURE__*/React.createElement("div", {
941
700
  style: {
942
- marginBottom: 16
701
+ marginBottom: 8
943
702
  }
944
703
  }, /*#__PURE__*/React.createElement(Button, {
945
- type: "primary",
946
- icon: /*#__PURE__*/React.createElement(PlusOutlined, null),
704
+ type: "dashed",
705
+ icon: /*#__PURE__*/React.createElement(FolderAddOutlined, null),
947
706
  onClick: handleAddRootGroup
948
707
  }, "\u6DFB\u52A0\u6839\u7EA7\u522B\u5206\u7EC4")), /*#__PURE__*/React.createElement("div", {
949
708
  style: {
950
- maxHeight: tableHeight,
951
- overflow: 'auto'
709
+ height: scrollY
952
710
  }
953
- }, /*#__PURE__*/React.createElement(Table, {
954
- columns: columns,
955
- dataSource: visibleItems,
956
- rowKey: "id",
957
- pagination: false,
958
- bordered: true,
959
- size: "small",
960
- scroll: {
961
- x: 1000
711
+ }, /*#__PURE__*/React.createElement(AgGridReact, {
712
+ theme: agGridTheme,
713
+ rowData: visibleItems,
714
+ columnDefs: columnDefs,
715
+ getRowId: function getRowId(params) {
716
+ return params.data.id;
962
717
  },
963
- components: {
964
- body: {
965
- row: function row(props) {
966
- var record = visibleItems.find(function (item) {
967
- return item.id === props['data-row-key'];
968
- });
969
- var index = visibleItems.findIndex(function (item) {
970
- return item.id === props['data-row-key'];
971
- });
972
- if (!record) return /*#__PURE__*/React.createElement("tr", props);
973
- return /*#__PURE__*/React.createElement(DraggableRow, _extends({
974
- record: record,
975
- index: index,
976
- moveRow: handleDragSort
977
- }, props));
978
- }
979
- }
980
- }
981
- })), /*#__PURE__*/React.createElement(MoveNodeModal, {
982
- visible: moveModalVisible,
983
- currentNode: currentMoveNode,
984
- allItems: localItems,
985
- onCancel: handleCloseMoveModal,
986
- onConfirm: handleConfirmMove
718
+ rowDragManaged: false,
719
+ onRowDragEnd: onRowDragEnd,
720
+ onGridReady: onGridReady,
721
+ animateRows: true,
722
+ rowDragEntireRow: true,
723
+ suppressMoveWhenRowDragging: true,
724
+ suppressMovableColumns: true,
725
+ domLayout: "normal"
987
726
  })));
988
727
  };
989
728
  export default ColumnConfigTable;