vue-super-crud 1.7.1

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.
Files changed (291) hide show
  1. package/.browserslistrc +3 -0
  2. package/.versionrc.json +36 -0
  3. package/CHANGELOG.md +232 -0
  4. package/LICENSE +201 -0
  5. package/README.md +46 -0
  6. package/babel.config.js +12 -0
  7. package/build/alias.js +10 -0
  8. package/build/build.js +52 -0
  9. package/build/config.js +70 -0
  10. package/deploy.bat +14 -0
  11. package/docs/.vuepress/components/button/base.vue +88 -0
  12. package/docs/.vuepress/components/common/code-format.vue +331 -0
  13. package/docs/.vuepress/components/commonConfig/presetCodeTemplate/base.vue +68 -0
  14. package/docs/.vuepress/components/commonConfig/presetCodeTemplate/customParams.vue +73 -0
  15. package/docs/.vuepress/components/commonConfig/renderType/component.vue +160 -0
  16. package/docs/.vuepress/components/commonConfig/renderType/formatter.vue +49 -0
  17. package/docs/.vuepress/components/commonConfig/renderType/render.vue +91 -0
  18. package/docs/.vuepress/components/commonConfig/renderType/slot.vue +63 -0
  19. package/docs/.vuepress/components/crud/baseUse/baseUse.vue +98 -0
  20. package/docs/.vuepress/components/crud/baseUse/columnAction.vue +72 -0
  21. package/docs/.vuepress/components/crud/baseUse/columnWidth.vue +107 -0
  22. package/docs/.vuepress/components/crud/baseUse/handleRow.vue +65 -0
  23. package/docs/.vuepress/components/crud/baseUse/height.vue +82 -0
  24. package/docs/.vuepress/components/crud/baseUse/index.vue +54 -0
  25. package/docs/.vuepress/components/crud/baseUse/loading.vue +70 -0
  26. package/docs/.vuepress/components/crud/baseUse/pagination.vue +108 -0
  27. package/docs/.vuepress/components/crud/baseUse/selection.vue +114 -0
  28. package/docs/.vuepress/components/crud/baseUse/summaryMethod.vue +118 -0
  29. package/docs/.vuepress/components/crud/baseUse/title.vue +54 -0
  30. package/docs/.vuepress/components/crud/baseUse/toolbar.vue +69 -0
  31. package/docs/.vuepress/components/crud/buttons/common.vue +115 -0
  32. package/docs/.vuepress/components/crud/buttons/fast.vue +82 -0
  33. package/docs/.vuepress/components/crud/contextMenu/base.vue +72 -0
  34. package/docs/.vuepress/components/crud/copy.vue +52 -0
  35. package/docs/.vuepress/components/crud/crudEvents/api.vue +157 -0
  36. package/docs/.vuepress/components/crud/crudEvents/deleteTip.vue +93 -0
  37. package/docs/.vuepress/components/crud/crudEvents/events.vue +188 -0
  38. package/docs/.vuepress/components/crud/dataSort/base.vue +142 -0
  39. package/docs/.vuepress/components/crud/genDynamicColumns/base.vue +53 -0
  40. package/docs/.vuepress/components/crud/genDynamicColumns/dynamicAndFixed.vue +111 -0
  41. package/docs/.vuepress/components/crud/genDynamicColumns/treeDynamic.vue +68 -0
  42. package/docs/.vuepress/components/crud/handleBar/handleRow.vue +65 -0
  43. package/docs/.vuepress/components/crud/handleBar/toolbar.vue +69 -0
  44. package/docs/.vuepress/components/crud/renderType/1.vue +57 -0
  45. package/docs/.vuepress/components/crud/renderType/2.vue +63 -0
  46. package/docs/.vuepress/components/crud/renderType/3.vue +105 -0
  47. package/docs/.vuepress/components/crud/renderType/5.vue +91 -0
  48. package/docs/.vuepress/components/crud/search/1.vue +90 -0
  49. package/docs/.vuepress/components/crud/search/2.vue +78 -0
  50. package/docs/.vuepress/components/crud/search/3.vue +107 -0
  51. package/docs/.vuepress/components/crud/search/base.vue +123 -0
  52. package/docs/.vuepress/components/crud/search/localSearch.vue +124 -0
  53. package/docs/.vuepress/components/crud/search/special.vue +148 -0
  54. package/docs/.vuepress/components/crud/selection/events.vue +47 -0
  55. package/docs/.vuepress/components/crud/selection/pagination.vue +94 -0
  56. package/docs/.vuepress/components/crud/selection/singleSelection.vue +64 -0
  57. package/docs/.vuepress/components/crud/span/base.vue +69 -0
  58. package/docs/.vuepress/components/crud/span/special.vue +75 -0
  59. package/docs/.vuepress/components/crud/summary/base.vue +99 -0
  60. package/docs/.vuepress/components/crud/tableEdit/addDeleteBtn.vue +174 -0
  61. package/docs/.vuepress/components/crud/tableEdit/cellEdit.vue +194 -0
  62. package/docs/.vuepress/components/crud/tableEdit/controlEdit.vue +219 -0
  63. package/docs/.vuepress/components/crud/tableEdit/dialog.vue +172 -0
  64. package/docs/.vuepress/components/crud/tableEdit/free.vue +88 -0
  65. package/docs/.vuepress/components/crud/tableEdit/freeColumn.vue +82 -0
  66. package/docs/.vuepress/components/crud/tableEdit/methods.vue +154 -0
  67. package/docs/.vuepress/components/crud/tableEdit/rowAction.vue +107 -0
  68. package/docs/.vuepress/components/crud/tableEdit/rowBatch.vue +116 -0
  69. package/docs/.vuepress/components/crud/tableEdit/rowClick.vue +98 -0
  70. package/docs/.vuepress/components/crud/validate/base.vue +122 -0
  71. package/docs/.vuepress/components/crud/validate/custom.vue +82 -0
  72. package/docs/.vuepress/components/crud/validate/regulars.vue +88 -0
  73. package/docs/.vuepress/components/crud/validate/relation.vue +91 -0
  74. package/docs/.vuepress/components/crud/validate/tree.vue +82 -0
  75. package/docs/.vuepress/components/dialog/baseUse/base.vue +92 -0
  76. package/docs/.vuepress/components/dialog/baseUse/beforeConfirm.vue +78 -0
  77. package/docs/.vuepress/components/dialog/baseUse/control.vue +79 -0
  78. package/docs/.vuepress/components/dialog/baseUse/drawer.vue +59 -0
  79. package/docs/.vuepress/components/dialog/baseUse/footer.vue +87 -0
  80. package/docs/.vuepress/components/dialog/baseUse/insertSlot.vue +79 -0
  81. package/docs/.vuepress/components/dict/DictLinkage.vue +91 -0
  82. package/docs/.vuepress/components/dict/baseUse.vue +72 -0
  83. package/docs/.vuepress/components/dict/component.vue +82 -0
  84. package/docs/.vuepress/components/dict/localDict.vue +68 -0
  85. package/docs/.vuepress/components/form/baseUse/base.vue +48 -0
  86. package/docs/.vuepress/components/form/baseUse/dataFormat.vue +92 -0
  87. package/docs/.vuepress/components/form/baseUse/deep.vue +57 -0
  88. package/docs/.vuepress/components/form/baseUse/gridLayout.vue +47 -0
  89. package/docs/.vuepress/components/form/baseUse/group.vue +66 -0
  90. package/docs/.vuepress/components/form/baseUse/hidden.vue +40 -0
  91. package/docs/.vuepress/components/form/baseUse/inlineLayout.vue +48 -0
  92. package/docs/.vuepress/components/form/baseUse/label.vue +51 -0
  93. package/docs/.vuepress/components/form/baseUse/tooltip.vue +40 -0
  94. package/docs/.vuepress/components/form/baseUse/validate.vue +52 -0
  95. package/docs/.vuepress/components/form/detail/base.vue +78 -0
  96. package/docs/.vuepress/components/form/detail/border.vue +90 -0
  97. package/docs/.vuepress/components/form/detail/singleDetail.vue +72 -0
  98. package/docs/.vuepress/components/formatData/baseUse.vue +131 -0
  99. package/docs/.vuepress/components/mock/index.js +347 -0
  100. package/docs/.vuepress/components/mockData/custom.vue +69 -0
  101. package/docs/.vuepress/components/mockData/example.vue +290 -0
  102. package/docs/.vuepress/components/positionSlot/base.vue +24 -0
  103. package/docs/.vuepress/components/positionSlot/form.vue +71 -0
  104. package/docs/.vuepress/components/positionSlot/table.vue +85 -0
  105. package/docs/.vuepress/components/tabs/base.vue +57 -0
  106. package/docs/.vuepress/components/temp.js +195 -0
  107. package/docs/.vuepress/config.js +146 -0
  108. package/docs/.vuepress/enhanceApp.js +142 -0
  109. package/docs/.vuepress/public/favicon.ico +0 -0
  110. package/docs/.vuepress/public/super.png +0 -0
  111. package/docs/.vuepress/styles/index.styl +25 -0
  112. package/docs/.vuepress/styles/palette.styl +6 -0
  113. package/docs/README.md +14 -0
  114. package/docs/guide/button/base.md +31 -0
  115. package/docs/guide/commonConfig/jsx.md +166 -0
  116. package/docs/guide/commonConfig/presetCodeTemplate.md +68 -0
  117. package/docs/guide/commonConfig/renderType.md +181 -0
  118. package/docs/guide/crud/baseUse.md +120 -0
  119. package/docs/guide/crud/buttons.md +18 -0
  120. package/docs/guide/crud/config.md +217 -0
  121. package/docs/guide/crud/contextMenu.md +18 -0
  122. package/docs/guide/crud/dataSort.md +66 -0
  123. package/docs/guide/crud/genDynamicColumns.md +145 -0
  124. package/docs/guide/crud/handleBar.md +26 -0
  125. package/docs/guide/crud/renderType.md +4 -0
  126. package/docs/guide/crud/search.md +150 -0
  127. package/docs/guide/crud/selection.md +73 -0
  128. package/docs/guide/crud/span.md +98 -0
  129. package/docs/guide/crud/summary.md +167 -0
  130. package/docs/guide/crud/tableEdit.md +377 -0
  131. package/docs/guide/crud/validate.md +158 -0
  132. package/docs/guide/dialog/baseUse.md +81 -0
  133. package/docs/guide/dict/baseUse.md +174 -0
  134. package/docs/guide/dict/component.md +88 -0
  135. package/docs/guide/dict/config.md +44 -0
  136. package/docs/guide/form/baseUse.md +142 -0
  137. package/docs/guide/form/detail.md +38 -0
  138. package/docs/guide/formatData/baseUse.md +98 -0
  139. package/docs/guide/formatData/config.md +142 -0
  140. package/docs/guide/mockData/base.md +26 -0
  141. package/docs/guide/positionSlot/base.md +41 -0
  142. package/docs/guide/question/base.md +44 -0
  143. package/docs/guide/start/base.md +30 -0
  144. package/docs/guide/tabs/base.md +63 -0
  145. package/examples/App.vue +52 -0
  146. package/examples/Layout/components/AppMain.vue +40 -0
  147. package/examples/Layout/components/Item.vue +29 -0
  148. package/examples/Layout/components/Link.vue +44 -0
  149. package/examples/Layout/components/SidebarItem.vue +93 -0
  150. package/examples/Layout/index.vue +69 -0
  151. package/examples/assets/logo.png +0 -0
  152. package/examples/favicon.ico +0 -0
  153. package/examples/index.html +18 -0
  154. package/examples/main.js +54 -0
  155. package/examples/router/index.js +140 -0
  156. package/examples/store/index.js +0 -0
  157. package/examples/styles/index.scss +63 -0
  158. package/examples/styles/sidebar.scss +226 -0
  159. package/examples/styles/transition.scss +48 -0
  160. package/examples/styles/variables.scss +25 -0
  161. package/examples/views/crud/base.vue +68 -0
  162. package/examples/views/crud/handleRow.vue +84 -0
  163. package/examples/views/crud/search.vue +116 -0
  164. package/examples/views/dashboard/index.vue +244 -0
  165. package/examples/views/dashboard/index1.vue +234 -0
  166. package/examples/views/dashboard/test.vue +9 -0
  167. package/examples/views/formTest/index.vue +168 -0
  168. package/examples/views/nested/menu1/index.vue +7 -0
  169. package/examples/views/nested/menu1/menu1-1/index.vue +7 -0
  170. package/examples/views/nested/menu1/menu1-2/index.vue +7 -0
  171. package/examples/views/nested/menu1/menu1-2/menu1-2-1/index.vue +5 -0
  172. package/examples/views/nested/menu1/menu1-2/menu1-2-2/index.vue +5 -0
  173. package/examples/views/nested/menu1/menu1-3/index.vue +5 -0
  174. package/examples/views/nested/menu2/index.vue +5 -0
  175. package/gulpfile.js +84 -0
  176. package/lib/index.css +1 -0
  177. package/lib/super-crud.min.js +15 -0
  178. package/package.json +66 -0
  179. package/packages/button/index.vue +189 -0
  180. package/packages/core/components/comp.vue +223 -0
  181. package/packages/core/components/position.vue +135 -0
  182. package/packages/core/components/render.vue +460 -0
  183. package/packages/core/configManager.js +302 -0
  184. package/packages/core/create.js +8 -0
  185. package/packages/core/defaultRender.js +64 -0
  186. package/packages/core/dict/global.js +10 -0
  187. package/packages/core/dict/index.js +432 -0
  188. package/packages/core/dict/mixin.js +94 -0
  189. package/packages/core/event.js +60 -0
  190. package/packages/core/index.js +6 -0
  191. package/packages/core/init.js +122 -0
  192. package/packages/core/mock/genConfig.js +228 -0
  193. package/packages/core/mock/genData.js +422 -0
  194. package/packages/core/mock/index.js +4 -0
  195. package/packages/core/rules.js +111 -0
  196. package/packages/crud/column.vue +205 -0
  197. package/packages/crud/columnAction.vue +207 -0
  198. package/packages/crud/columnCell.vue +146 -0
  199. package/packages/crud/defaultColumn.vue +130 -0
  200. package/packages/crud/drawerColumn.vue +225 -0
  201. package/packages/crud/form.vue +69 -0
  202. package/packages/crud/index.vue +564 -0
  203. package/packages/crud/menuBar.vue +298 -0
  204. package/packages/crud/mixins/cacheHandler.js +36 -0
  205. package/packages/crud/mixins/calcColumnWidth.js +79 -0
  206. package/packages/crud/mixins/calcHeight.js +105 -0
  207. package/packages/crud/mixins/columnHandler.js +128 -0
  208. package/packages/crud/mixins/contextMenu.js +98 -0
  209. package/packages/crud/mixins/dataProcessor.js +202 -0
  210. package/packages/crud/mixins/dialog.js +109 -0
  211. package/packages/crud/mixins/excelHandler.js +150 -0
  212. package/packages/crud/mixins/exposeMethods.js +107 -0
  213. package/packages/crud/mixins/generateDynamicColumns.js +250 -0
  214. package/packages/crud/mixins/props.js +38 -0
  215. package/packages/crud/mixins/searchHandler.js +151 -0
  216. package/packages/crud/mixins/select.js +359 -0
  217. package/packages/crud/mixins/spanMethod.js +288 -0
  218. package/packages/crud/mixins/summary.js +177 -0
  219. package/packages/crud/mixins/tableEdit.js +547 -0
  220. package/packages/crud/mixins/validate.js +219 -0
  221. package/packages/crud/pagination.vue +110 -0
  222. package/packages/crud/search.vue +119 -0
  223. package/packages/crud/searchHeader.vue +231 -0
  224. package/packages/crud/selectBanner.vue +138 -0
  225. package/packages/crud/utils/EditState.js +319 -0
  226. package/packages/crud/utils/excelExport.js +112 -0
  227. package/packages/crud/utils/excelImport.js +112 -0
  228. package/packages/crud/utils/index.js +98 -0
  229. package/packages/dialog/dialog.js +233 -0
  230. package/packages/dialog/dialog.vue +15 -0
  231. package/packages/dialog/index.js +22 -0
  232. package/packages/dict/cascadeFormat.vue +179 -0
  233. package/packages/dict/dateFormat.vue +40 -0
  234. package/packages/dict/form/cascade.vue +61 -0
  235. package/packages/dict/form/checkbox.vue +90 -0
  236. package/packages/dict/form/extendMethod.js +22 -0
  237. package/packages/dict/form/input-base.js +31 -0
  238. package/packages/dict/form/input.js +20 -0
  239. package/packages/dict/form/radio.vue +69 -0
  240. package/packages/dict/form/select.vue +118 -0
  241. package/packages/dict/form/switch.vue +75 -0
  242. package/packages/dict/valueFormat.vue +188 -0
  243. package/packages/directive/dialog/drag.js +86 -0
  244. package/packages/directive/dialog/dragSize.js +42 -0
  245. package/packages/directive/index.js +9 -0
  246. package/packages/directive/insertSlot.js +10 -0
  247. package/packages/form/contextMenu.js +192 -0
  248. package/packages/form/draftDrawer.vue +391 -0
  249. package/packages/form/formAction.vue +97 -0
  250. package/packages/form/formItem.vue +259 -0
  251. package/packages/form/index.vue +451 -0
  252. package/packages/form/props.js +15 -0
  253. package/packages/grid/cell.vue +65 -0
  254. package/packages/grid/index.vue +130 -0
  255. package/packages/group/index.vue +96 -0
  256. package/packages/tabs/index.vue +290 -0
  257. package/packages/tooltip/index.js +9 -0
  258. package/packages/tooltip/tooltip.vue +32 -0
  259. package/packages/tooltip/tooltipComponent.js +38 -0
  260. package/packages/verifyInput/index.vue +131 -0
  261. package/src/config/common.js +88 -0
  262. package/src/config/crud.js +567 -0
  263. package/src/config/dialog.js +87 -0
  264. package/src/config/form.js +215 -0
  265. package/src/config/index.js +9 -0
  266. package/src/constants/index.js +72 -0
  267. package/src/index.js +67 -0
  268. package/src/template/btn/crud.js +6 -0
  269. package/src/template/btn/dialog.js +1 -0
  270. package/src/template/btn/form.js +3 -0
  271. package/src/template/btn/index.js +9 -0
  272. package/src/template/dicts.js +1 -0
  273. package/src/template/formatData.js +507 -0
  274. package/src/template/index.js +19 -0
  275. package/src/template/render.js +124 -0
  276. package/src/template/rules.js +53 -0
  277. package/src/utils/bem.js +49 -0
  278. package/src/utils/cache.js +77 -0
  279. package/src/utils/getType.js +34 -0
  280. package/src/utils/index.js +212 -0
  281. package/src/utils/mergeTemp.js +124 -0
  282. package/styles/button.scss +3 -0
  283. package/styles/crud.scss +425 -0
  284. package/styles/dialog.scss +95 -0
  285. package/styles/form.scss +532 -0
  286. package/styles/group.scss +78 -0
  287. package/styles/index.scss +94 -0
  288. package/styles/tabs.scss +139 -0
  289. package/styles/verifyInput.scss +56 -0
  290. package/vue-jsx-sync.js +90 -0
  291. package/vue.config.js +54 -0
@@ -0,0 +1,177 @@
1
+ import { isFunction, get } from "lodash-es";
2
+
3
+ // 内置统计方法
4
+ export const builtInSummaryMethods = {
5
+ // 求和
6
+ sum(values, config = {}) {
7
+ const { ignoreZero, absolute } = config;
8
+ let filteredValues = [...values];
9
+ if (ignoreZero) {
10
+ filteredValues = filteredValues.filter((v) => v !== 0);
11
+ }
12
+ if (absolute) {
13
+ filteredValues = filteredValues.map((v) => Math.abs(v));
14
+ }
15
+ return filteredValues.reduce((acc, curr) => acc + curr, 0);
16
+ },
17
+
18
+ // 平均值
19
+ avg(values, config = {}) {
20
+ const { ignoreZero } = config;
21
+ let filteredValues = [...values];
22
+ if (ignoreZero) {
23
+ filteredValues = filteredValues.filter((v) => v !== 0);
24
+ }
25
+ return filteredValues.length
26
+ ? filteredValues.reduce((acc, curr) => acc + curr, 0) /
27
+ filteredValues.length
28
+ : 0;
29
+ },
30
+
31
+ // 计数
32
+ count(values, config = {}) {
33
+ const { ignoreZero, predicate } = config;
34
+
35
+ if (isFunction(predicate)) {
36
+ return values.filter(predicate).length;
37
+ }
38
+
39
+ if (ignoreZero) {
40
+ return values.filter((v) => v !== 0).length;
41
+ }
42
+
43
+ return values.length;
44
+ },
45
+
46
+ // 最大值
47
+ max(values, config = {}) {
48
+ const { absolute } = config;
49
+ if (!values.length) return 0;
50
+
51
+ let filteredValues = [...values];
52
+ if (absolute) {
53
+ filteredValues = filteredValues.map((v) => Math.abs(v));
54
+ }
55
+ return Math.max(...filteredValues);
56
+ },
57
+
58
+ // 最小值
59
+ min(values, config = {}) {
60
+ const { absolute } = config;
61
+ if (!values.length) return 0;
62
+
63
+ let filteredValues = [...values];
64
+ if (absolute) {
65
+ filteredValues = filteredValues.map((v) => Math.abs(v));
66
+ }
67
+ return Math.min(...filteredValues);
68
+ },
69
+ };
70
+
71
+ export default {
72
+ methods: {
73
+ summaryMethod(param) {
74
+ if (!this.showSummary) return;
75
+
76
+ const { columns } = param;
77
+ const data = this.crudOptions.summaryData || this.list;
78
+ let sumsList = {};
79
+ let sums = new Array(columns.length).fill("");
80
+
81
+ // 使用自定义统计方法
82
+ if (isFunction(this.crudOptions.summaryMethod)) {
83
+ try {
84
+ sums = this.crudOptions.summaryMethod(param);
85
+ } catch (error) {
86
+ console.error("自定义统计方法执行出错:", error);
87
+ this.sumsList = {};
88
+ return sums;
89
+ }
90
+ } else {
91
+ // 使用内置统计方法
92
+ columns.forEach((column, index) => {
93
+ const currItem = column.col;
94
+ if (!currItem?.summary) return;
95
+
96
+ try {
97
+ const summaryConfig = this.normalizeSummaryConfig(currItem.summary);
98
+ const result = this.calculateSummary(data, column, summaryConfig);
99
+ sums[index] = this.formatSummaryResult(result, summaryConfig);
100
+
101
+ if (column.property) {
102
+ sumsList[column.property] = sums[index];
103
+ }
104
+ } catch (error) {
105
+ console.error(`列 ${column.property} 统计计算出错:`, error);
106
+ sums[index] = "";
107
+ }
108
+ });
109
+ }
110
+
111
+ this.sumsList = sumsList;
112
+ return sums;
113
+ },
114
+
115
+ // 标准化summary配置
116
+ normalizeSummaryConfig(summary) {
117
+ if (typeof summary === "string") {
118
+ return { type: summary };
119
+ }
120
+ if (typeof summary === "function") {
121
+ return { type: "custom", method: summary };
122
+ }
123
+ return summary;
124
+ },
125
+
126
+ // 计算统计值
127
+ calculateSummary(data, column, config) {
128
+ const { type, method, path, ignoreZero, absolute, predicate } = config;
129
+
130
+ // 获取数值 - 根据统计类型区分处理
131
+ let values;
132
+ if (type === "count") {
133
+ // count类型直接获取原始值
134
+ values = data.map((item) => {
135
+ return path ? get(item, path) : item[column.property];
136
+ });
137
+ } else {
138
+ // 其他类型转换为数值
139
+ values = data
140
+ .map((item) => {
141
+ const value = path ? get(item, path) : item[column.property];
142
+ return Number(value);
143
+ })
144
+ .filter((value) => !isNaN(value));
145
+ }
146
+
147
+ if (type === "custom" && isFunction(method)) {
148
+ return method(values, data);
149
+ }
150
+
151
+ const summaryMethod = builtInSummaryMethods[type.toLowerCase()];
152
+ if (!summaryMethod) {
153
+ console.warn(`未知的统计类型: ${type}`);
154
+ return type;
155
+ }
156
+
157
+ return summaryMethod(values, {
158
+ ignoreZero,
159
+ absolute,
160
+ predicate,
161
+ });
162
+ },
163
+
164
+ // 格式化统计结果
165
+ formatSummaryResult(result, config) {
166
+ const { prefix = "", suffix = "", decimals = 0 } = config;
167
+
168
+ if (result === null || result === undefined) return "";
169
+
170
+ const formattedValue = Number.isFinite(result)
171
+ ? Number(result).toFixed(decimals)
172
+ : result;
173
+
174
+ return `${prefix}${formattedValue}${suffix}`;
175
+ },
176
+ },
177
+ };
@@ -0,0 +1,547 @@
1
+ import { isEqual, cloneDeep, isFunction, uniqueId } from "lodash-es";
2
+ import { executeFunction } from "utils";
3
+ import EditState from "../utils/EditState";
4
+ import { focusFormElement } from "../utils";
5
+
6
+ export default {
7
+ data() {
8
+ return {
9
+ editState: null,
10
+ _documentClickHandler: null,
11
+ };
12
+ },
13
+ created() {},
14
+ watch: {
15
+ editConfig: {
16
+ handler() {
17
+ this.initEditState();
18
+ this.updateEditState();
19
+ this.bindTriggerEvent();
20
+ },
21
+ immediate: true,
22
+ },
23
+ },
24
+ destroyed() {
25
+ if (this._documentClickHandler) {
26
+ document.removeEventListener("click", this._documentClickHandler);
27
+ }
28
+ },
29
+ computed: {
30
+ editConfig() {
31
+ return this.processEditConfig();
32
+ },
33
+ noSaveEditMap() {
34
+ if (this.noSaveEditList.length === 0) return;
35
+ return this.noSaveEditList.reduce((pre, cur) => {
36
+ pre[cur[this.valueKey]] = cur;
37
+ return pre;
38
+ }, {});
39
+ },
40
+ },
41
+ methods: {
42
+ // 初始化编辑状态
43
+ initEditState() {
44
+ if (this.editState) return;
45
+ const { mode, isRowEdit, exclusive, trigger } = this.editConfig;
46
+ // 初始化编辑状态管理器
47
+ this.editState = new EditState({
48
+ valueKey: this.valueKey,
49
+ columns: this.trueRenderColumns,
50
+ mode,
51
+ isRowEdit,
52
+ exclusive: exclusive || trigger === "click" || trigger === "dblclick", // 根据 trigger 设置互斥
53
+ });
54
+ this.editState.on(
55
+ "edit-status-change",
56
+ ({ mode, rowKey, row, prop, type }) => {
57
+ this.forceUpdate();
58
+ if (prop && this.editConfig.autofocus) {
59
+ prop =
60
+ typeof this.editConfig.autofocus === "string"
61
+ ? this.editConfig.autofocus
62
+ : prop;
63
+ setTimeout(() => {
64
+ this.focusInput(rowKey, prop);
65
+ }, 100);
66
+ }
67
+ this.$emit("editStatusChange", {
68
+ mode,
69
+ rowKey,
70
+ row,
71
+ prop,
72
+ type,
73
+ });
74
+ }
75
+ );
76
+ },
77
+
78
+ // 更新编辑状态
79
+ updateEditState() {
80
+ if (!this.editState) return;
81
+ const { mode, isRowEdit, exclusive, trigger } = this.editConfig;
82
+ this.editState.mode = mode;
83
+ this.editState.exclusive =
84
+ exclusive || trigger === "click" || trigger === "dblclick";
85
+ this.editState.isRowEdit = isRowEdit;
86
+ },
87
+
88
+ // 处理编辑配置
89
+ processEditConfig() {
90
+ const {
91
+ isRowEdit,
92
+ addBtn,
93
+ editBtn,
94
+ viewBtn,
95
+ rowAddBtn,
96
+ footerAddBtn,
97
+ batchDeleteBtn,
98
+ deleteBtn,
99
+ } = this.crudOptions;
100
+
101
+ const editConfig = { ...this.crudOptions.editConfig };
102
+ if (editConfig.mode !== "dialog") {
103
+ // 将dialog配置映射到row配置
104
+ editConfig.rowAdd = editConfig.add;
105
+ delete editConfig.add;
106
+ editConfig.rowEdit = editConfig.edit;
107
+ delete editConfig.edit;
108
+ }
109
+ const config = {
110
+ ...editConfig,
111
+ add: addBtn || editConfig.add,
112
+ edit: editBtn || editConfig.edit,
113
+ view: viewBtn || editConfig.view,
114
+ rowAdd: rowAddBtn || editConfig.rowAdd,
115
+ lastAdd: footerAddBtn || editConfig.lastAdd,
116
+ batchDelete: batchDeleteBtn || editConfig.batchDelete,
117
+ delete: deleteBtn || editConfig.delete,
118
+ isRowEdit: editConfig.isRowEdit || isRowEdit,
119
+ };
120
+ if (this.crudOptions.freeEdit) {
121
+ config.mode = "free";
122
+ }
123
+ if (this.crudOptions.cellEdit) {
124
+ config.mode = "cell";
125
+ }
126
+ if (this.crudOptions.rowEdit) {
127
+ config.mode = "row";
128
+ config.rowEdit = config.rowEdit || {};
129
+ }
130
+ if (this.crudOptions.batchEdit) {
131
+ config.mode = "row";
132
+ config.batch = config.batch || {};
133
+ }
134
+ if (this.crudOptions.batchRowEdit) {
135
+ config.mode = "row";
136
+ config.rowEdit = config.rowEdit || {};
137
+ config.batch = config.batch || {};
138
+ }
139
+ if (
140
+ config.mode === "cell" &&
141
+ (config.trigger === "manual" || !config.trigger)
142
+ ) {
143
+ config.trigger = "click";
144
+ }
145
+ return config;
146
+ },
147
+
148
+ // 行编辑禁用
149
+ disabledRowEdit(scope) {
150
+ const isRowEdit = this.editConfig.isRowEdit;
151
+ return (
152
+ isRowEdit !== undefined &&
153
+ isFunction(isRowEdit) &&
154
+ isRowEdit(scope) === false
155
+ );
156
+ },
157
+
158
+ // 校验单元格编辑状态
159
+ validateEdit(col = {}, scope = {}) {
160
+ // 评估列的编辑条件
161
+ const canEdit =
162
+ typeof col.isEdit === "function" ? col.isEdit(scope) : col.isEdit;
163
+ if (canEdit === false) return false;
164
+ return this._runWithoutDeps(() => {
165
+ return this.editState.validateEdit(col, scope, canEdit);
166
+ });
167
+ },
168
+
169
+ // 行添加事件
170
+ handleRowAdd(params, type) {
171
+ type = type || this.crudOptions.rowAddType || "last";
172
+ let newRow = {};
173
+ this.trueRenderColumns.forEach((col) => {
174
+ if (newRow[col.prop] === undefined) {
175
+ newRow[col.prop] = col.initValue ?? "";
176
+ }
177
+ });
178
+
179
+ this.runBefore(
180
+ ["add"],
181
+ (data) => {
182
+ newRow = Object.assign(newRow, data, params);
183
+
184
+ // 设置添加状态
185
+ this.editState.setRowEditStatus(
186
+ newRow,
187
+ true,
188
+ this.editConfig.mode === "row" ? "add" : "edit",
189
+ {
190
+ addType: type,
191
+ }
192
+ );
193
+
194
+ if (type === "first") {
195
+ this.list.unshift(newRow);
196
+ } else {
197
+ this.list.push(newRow);
198
+ }
199
+ setTimeout(() => {
200
+ this.$refs.tableFormRef.clearValidate();
201
+ }, 0);
202
+ },
203
+ { row: newRow }
204
+ );
205
+ },
206
+
207
+ // 行编辑事件
208
+ handleRowEdit(scope, prop) {
209
+ this.runBefore(
210
+ ["edit"],
211
+ (data) => {
212
+ if (data) {
213
+ this.$set(this.list, scope.$index, data);
214
+ }
215
+ this.editState.setRowEditStatus(scope.row, true, "edit", {
216
+ prop,
217
+ });
218
+ },
219
+ scope
220
+ );
221
+ },
222
+
223
+ // 行点击事件
224
+ handleRowClick(scope, prop) {
225
+ if (
226
+ this.editConfig.mode === "row" &&
227
+ this.editConfig.trigger === "click"
228
+ ) {
229
+ if (this.editState.isRowEditing(scope.row)) return;
230
+ const editRow = this.editState.getEditingRows()[0]?.row;
231
+
232
+ if (editRow) {
233
+ this.handleRowSave({ row: editRow, $index: editRow.$index }, () => {
234
+ this.handleRowEdit(scope, prop);
235
+ });
236
+ } else {
237
+ this.handleRowEdit(scope, prop);
238
+ }
239
+
240
+ this._isClick = true;
241
+ setTimeout(() => {
242
+ this._isClick = false;
243
+ }, 0);
244
+ }
245
+ },
246
+
247
+ // 批量行编辑事件
248
+ handleBatchRowEdit() {
249
+ this.runBefore(["batchEdit"], (rows) => {
250
+ const list = this.editConfig.batch?.isSelect
251
+ ? this.selectionRow
252
+ : rows || this.list;
253
+
254
+ list.forEach((row, index) => {
255
+ this.editState.setRowEditStatus(row, true, "edit");
256
+ });
257
+ });
258
+ },
259
+
260
+ // 行保存事件
261
+ handleRowSave(scope, callback) {
262
+ this.validateField(scope.row).then(() => {
263
+ this.changeLoading(true);
264
+ const callBack = (row) => {
265
+ if (row) {
266
+ this.$set(this.list, scope.$index, row);
267
+ }
268
+ this.editState.setRowEditStatus(scope.row, false);
269
+ this.changeLoading();
270
+ callback && callback();
271
+ };
272
+
273
+ this.runBefore(["save"], callBack, scope, this.changeLoading);
274
+ });
275
+ },
276
+
277
+ // 批量行保存事件
278
+ handleBatchRowSave(changeBatchButton, topRows) {
279
+ this.validate().then(() => {
280
+ const editRows = this.editState
281
+ .getEditingRows()
282
+ .map((item) => item.row);
283
+ this.changeLoading(true);
284
+ const callBack = (rows) => {
285
+ (topRows || rows || editRows).forEach((row) => {
286
+ this.editState.setRowEditStatus(row, false);
287
+ });
288
+ changeBatchButton();
289
+ this.changeLoading();
290
+
291
+ if (this.editConfig.batch?.isSelect) {
292
+ this.clearSelection();
293
+ }
294
+ };
295
+ this.runBefore(["batchSave"], callBack, editRows, this.changeLoading);
296
+ });
297
+ },
298
+
299
+ // 行取消事件
300
+ handleRowCancel(scope) {
301
+ this.runBefore(
302
+ ["cancel"],
303
+ () => {
304
+ const editInfo = this.editState.getRowEditInfo(scope.row);
305
+
306
+ if (editInfo?.type === "add") {
307
+ this.list.splice(scope.$index, 1);
308
+ } else if (editInfo?.data) {
309
+ // 恢复原始数据
310
+ Object.keys(editInfo.data).forEach((key) => {
311
+ this.$set(scope.row, key, editInfo.data[key]);
312
+ });
313
+ }
314
+ this.editState.setRowEditStatus(scope.row, false);
315
+ this.clearErrorMsg(scope.row, scope.column?.property);
316
+ },
317
+ scope
318
+ );
319
+ },
320
+
321
+ // 批量行取消事件
322
+ handleBatchRowCancel(topRows) {
323
+ this.runBefore(["batchCancel"], (rows) => {
324
+ (topRows || rows || this.list).forEach((row, index) => {
325
+ if (this.editState.isRowEditing(row)) {
326
+ const editInfo = this.editState.getRowEditInfo(row);
327
+ if (editInfo?.data) {
328
+ this.$set(this.list, index, editInfo.data);
329
+ }
330
+ this.editState.setRowEditStatus(row, false);
331
+ }
332
+ });
333
+
334
+ if (this.editConfig.batch?.isSelect) {
335
+ this.clearSelection();
336
+ }
337
+ });
338
+ },
339
+
340
+ // 批量行删除事件
341
+ handleBatchDelete() {
342
+ if (this.selectionRow.length === 0) {
343
+ return this.$message.warning("请选择要删除的数据");
344
+ }
345
+ const handleDelete = () => {
346
+ this.changeLoading(true);
347
+ const callBack = () => {
348
+ const deleteIndex = this.selectionRow.map((item) => item.$index);
349
+ deleteIndex
350
+ .sort((a, b) => b - a)
351
+ .forEach((index) => {
352
+ this.list.splice(index, 1);
353
+ });
354
+
355
+ this.changeLoading();
356
+ };
357
+ this.runBefore(
358
+ ["batchDelete"],
359
+ callBack,
360
+ this.selectionRow,
361
+ this.changeLoading
362
+ );
363
+ };
364
+ handleDelete();
365
+ },
366
+
367
+ // 行删除事件
368
+ handleDelete(scope) {
369
+ const handleDelete = () => {
370
+ this.changeLoading(true);
371
+ const callBack = () => {
372
+ this.list.splice(scope.$index, 1);
373
+
374
+ this.changeLoading();
375
+ };
376
+ this.runBefore(["delete"], callBack, scope, this.changeLoading);
377
+ };
378
+ handleDelete();
379
+ },
380
+
381
+ // 单元格编辑事件
382
+ handleCellEdit(scope, column) {
383
+ if (this.editConfig.mode !== "cell") return;
384
+ this.runBefore(
385
+ ["edit"],
386
+ (data) => {
387
+ this.editState.setCellEditStatus(scope.row, column.prop);
388
+ if (data) {
389
+ this.$set(this.list[scope.$index], column.prop, data);
390
+ }
391
+ },
392
+ scope,
393
+ column
394
+ );
395
+ },
396
+
397
+ // 单元格点击事件
398
+ handleCellClick(scope, col) {
399
+ if (
400
+ this.editConfig.mode === "cell" &&
401
+ this.editConfig.trigger === "click"
402
+ ) {
403
+ if (this.editState.isCellEditing(scope.row, col.prop)) return;
404
+ const editCell = this.editState.getEditingCell();
405
+ if (editCell) {
406
+ this.handleCellSave(
407
+ { row: editCell.row, $index: editCell.row.$index },
408
+ col,
409
+ () => {
410
+ this.handleCellEdit(scope, col);
411
+ }
412
+ );
413
+ } else {
414
+ this.handleCellEdit(scope, col);
415
+ }
416
+
417
+ this._isCellClick = true;
418
+ setTimeout(() => {
419
+ this._isCellClick = false;
420
+ }, 0);
421
+ }
422
+ },
423
+
424
+ handleCellSave(scope, col, callback) {
425
+ this.validateField(scope.row).then(() => {
426
+ this.changeLoading(true);
427
+ const callBack = (data) => {
428
+ if (data) {
429
+ this.$set(scope.row, col.prop, data);
430
+ }
431
+ this.editState.clearAllEditStatus();
432
+ this.changeLoading();
433
+ callback && callback();
434
+ };
435
+ this.runBefore(
436
+ ["save"],
437
+ callBack,
438
+ scope.row[col.prop],
439
+ scope,
440
+ col,
441
+ this.changeLoading
442
+ );
443
+ });
444
+ },
445
+
446
+ // 判断点击是否在表格单元格内
447
+ isClickInTableCell(element) {
448
+ while (element && element !== this.$el) {
449
+ // 1. 检查是否是表格单元格
450
+ if (
451
+ element.tagName.toLowerCase() === "td" &&
452
+ element.classList.contains("el-table__cell")
453
+ ) {
454
+ return true;
455
+ }
456
+ element = element.parentElement;
457
+ }
458
+ return false;
459
+ },
460
+
461
+ // 判断点击是否在输入框清除按钮上
462
+ isClickInputClear(element) {
463
+ return (
464
+ element.classList.contains("el-input__clear") ||
465
+ element.classList.contains("el-icon-circle-close") ||
466
+ (element.parentElement &&
467
+ (element.parentElement.classList.contains("el-input__clear") ||
468
+ element.parentElement.classList.contains("el-icon-circle-close")))
469
+ );
470
+ },
471
+
472
+ // 绑定点击事件
473
+ bindTriggerEvent() {
474
+ if (this._documentClickHandler) return;
475
+ this.$nextTick(() => {
476
+ if (
477
+ this.editConfig.trigger === "click" ||
478
+ this.editConfig.trigger === "dblclick"
479
+ ) {
480
+ this._documentClickHandler = (e) => {
481
+ const target = e.target;
482
+ const isClickInCell = this.isClickInTableCell(target);
483
+ const isClickInputClear = this.isClickInputClear(target);
484
+ if (!isClickInCell && !isClickInputClear && !this._isClick) {
485
+ if (this.editConfig.mode === "row") {
486
+ const editRow = this.editState.getEditingRows()[0]?.row;
487
+ if (editRow) {
488
+ const scope = {
489
+ row: editRow,
490
+ $index: editRow.$index,
491
+ };
492
+ this.handleRowSave(scope, () => {
493
+ this.editState.clearOtherEditingRows();
494
+ this.forceUpdate();
495
+ });
496
+ } else {
497
+ this.editState.clearOtherEditingRows();
498
+ this.forceUpdate();
499
+ }
500
+ } else {
501
+ const editCell = this.editState.getEditingCell();
502
+ if (editCell) {
503
+ this.handleCellSave(
504
+ { row: editCell.row, $index: editCell.row.$index },
505
+ editCell.prop,
506
+ () => {
507
+ this.editState.clearAllEditStatus();
508
+ this.forceUpdate();
509
+ }
510
+ );
511
+ } else {
512
+ this.editState.clearAllEditStatus();
513
+ this.forceUpdate();
514
+ }
515
+ }
516
+ }
517
+ };
518
+
519
+ document.addEventListener("click", this._documentClickHandler);
520
+ }
521
+ });
522
+ },
523
+
524
+ // 聚焦输入框
525
+ focusInput(rowKey, prop) {
526
+ // 通过 data 属性定位到当前单元格
527
+ const cell = this.$el
528
+ .querySelector(".el-table__body")
529
+ .querySelector(`[data-row-key="${rowKey}"][data-prop="${prop}"]`);
530
+
531
+ if (!cell) return;
532
+ focusFormElement(cell);
533
+ },
534
+
535
+ // 最后添加事件
536
+ handleLastAdd() {
537
+ if (this.editConfig.mode === "dialog") {
538
+ this.handleAdd((this.editConfig.lastAdd || {}).addType || "last");
539
+ } else {
540
+ this.handleRowAdd(
541
+ {},
542
+ (this.editConfig.lastAdd || {}).addType || "last"
543
+ );
544
+ }
545
+ },
546
+ },
547
+ };