fast-crud-ui3 1.5.16 → 1.5.18-beta

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 (168) hide show
  1. package/lib/assets/fonts/iconfont.d.ts +0 -0
  2. package/lib/components/checkbox-group/index.d.ts +2 -0
  3. package/lib/components/checkbox-group/src/fast-checkbox-group.d.ts +72 -0
  4. package/lib/components/content-dialog/index.d.ts +2 -0
  5. package/lib/components/content-dialog/src/fast-cell-content.d.ts +83 -0
  6. package/lib/components/json-viewer/index.d.ts +2 -0
  7. package/lib/components/json-viewer/src/fast-json-viewer.d.ts +48 -0
  8. package/lib/components/mapping.d.ts +5 -0
  9. package/lib/components/object-picker/index.d.ts +2 -0
  10. package/lib/components/object-picker/src/fast-object-picker.d.ts +132 -0
  11. package/lib/components/select/index.d.ts +2 -0
  12. package/lib/components/select/src/fast-select.d.ts +83 -0
  13. package/lib/components/table/index.d.ts +2 -0
  14. package/lib/components/table/src/RowConfirm.d.ts +39 -0
  15. package/lib/components/table/src/dynamic-filter-form.d.ts +118 -0
  16. package/lib/components/table/src/dynamic-filter-list.d.ts +57 -0
  17. package/lib/components/table/src/easy-filter.d.ts +118 -0
  18. package/lib/components/table/src/export-confirm.d.ts +12 -0
  19. package/lib/components/table/src/quick-filter-form.d.ts +42 -0
  20. package/lib/components/table/src/row-form.d.ts +33 -0
  21. package/lib/components/table/src/stored-filter-manager.d.ts +55 -0
  22. package/lib/components/table/src/stored-filter.d.ts +37 -0
  23. package/lib/components/table/src/table-head-cell.d.ts +9 -0
  24. package/lib/components/table/src/table.d.ts +507 -0
  25. package/lib/components/table/src/util.d.ts +77 -0
  26. package/lib/components/table-column/config.d.ts +5 -0
  27. package/lib/components/table-column/index.d.ts +2 -0
  28. package/lib/components/table-column/src/table-column.d.ts +256 -0
  29. package/lib/components/table-column-date-picker/config.d.ts +5 -0
  30. package/lib/components/table-column-date-picker/index.d.ts +2 -0
  31. package/lib/components/table-column-date-picker/src/table-column-date-picker.d.ts +173 -0
  32. package/lib/components/table-column-file/config.d.ts +5 -0
  33. package/lib/components/table-column-file/index.d.ts +2 -0
  34. package/lib/components/table-column-file/src/table-column-file.d.ts +285 -0
  35. package/lib/components/table-column-img/config.d.ts +5 -0
  36. package/lib/components/table-column-img/index.d.ts +2 -0
  37. package/lib/components/table-column-img/src/table-column-img.d.ts +285 -0
  38. package/lib/components/table-column-input/config.d.ts +5 -0
  39. package/lib/components/table-column-input/index.d.ts +2 -0
  40. package/lib/components/table-column-input/src/table-column-input.d.ts +173 -0
  41. package/lib/components/table-column-number/config.d.ts +5 -0
  42. package/lib/components/table-column-number/index.d.ts +2 -0
  43. package/lib/components/table-column-number/src/table-column-number.d.ts +173 -0
  44. package/lib/components/table-column-object/config.d.ts +5 -0
  45. package/lib/components/table-column-object/index.d.ts +2 -0
  46. package/lib/components/table-column-object/src/table-column-object.d.ts +315 -0
  47. package/lib/components/table-column-select/config.d.ts +5 -0
  48. package/lib/components/table-column-select/index.d.ts +2 -0
  49. package/lib/components/table-column-select/src/table-column-select.d.ts +276 -0
  50. package/lib/components/table-column-switch/config.d.ts +5 -0
  51. package/lib/components/table-column-switch/index.d.ts +2 -0
  52. package/lib/components/table-column-switch/src/table-column-switch.d.ts +175 -0
  53. package/lib/components/table-column-textarea/config.d.ts +5 -0
  54. package/lib/components/table-column-textarea/index.d.ts +2 -0
  55. package/lib/components/table-column-textarea/src/table-column-textarea.d.ts +173 -0
  56. package/lib/components/table-column-time-picker/config.d.ts +5 -0
  57. package/lib/components/table-column-time-picker/index.d.ts +2 -0
  58. package/lib/components/table-column-time-picker/src/table-column-time-picker.d.ts +173 -0
  59. package/lib/components/upload/index.d.ts +2 -0
  60. package/lib/components/upload/src/fast-upload.d.ts +120 -0
  61. package/lib/fast-crud-ui3.cjs.js +44 -17
  62. package/lib/fast-crud-ui3.es.js +9991 -5946
  63. package/lib/fast-crud-ui3.umd.js +44 -17
  64. package/lib/global.d.ts +42 -0
  65. package/lib/index.d.ts +56 -0
  66. package/lib/mixins/table-column.d.ts +104 -0
  67. package/lib/mixins/upload.d.ts +15 -0
  68. package/lib/model/cond.d.ts +48 -0
  69. package/lib/model/editComponentConfig.d.ts +25 -0
  70. package/lib/model/fastTableOption.d.ts +513 -0
  71. package/lib/model/filterComponentConfig.d.ts +55 -0
  72. package/lib/model/opt.d.ts +20 -0
  73. package/lib/model/order.d.ts +28 -0
  74. package/lib/model/pageQuery.d.ts +43 -0
  75. package/lib/model/query.d.ts +101 -0
  76. package/lib/model/rel.d.ts +5 -0
  77. package/lib/style.css +1 -1
  78. package/lib/util/cache.d.ts +17 -0
  79. package/lib/util/dialog.d.ts +49 -0
  80. package/lib/util/escape.d.ts +7 -0
  81. package/lib/util/http.d.ts +8 -0
  82. package/lib/util/pick.d.ts +9 -0
  83. package/lib/util/util.d.ts +311 -0
  84. package/package.json +19 -5
  85. package/packages/assets/fonts/iconfont.css +163 -0
  86. package/packages/assets/fonts/iconfont.js +1 -0
  87. package/packages/assets/fonts/iconfont.ttf +0 -0
  88. package/packages/assets/fonts/iconfont.woff +0 -0
  89. package/packages/assets/fonts/iconfont.woff2 +0 -0
  90. package/packages/components/checkbox-group/index.js +7 -0
  91. package/packages/components/checkbox-group/src/fast-checkbox-group.vue +83 -0
  92. package/packages/components/content-dialog/index.js +7 -0
  93. package/packages/components/content-dialog/src/fast-cell-content.vue +115 -0
  94. package/packages/components/json-viewer/index.js +7 -0
  95. package/packages/components/json-viewer/src/fast-json-viewer.vue +54 -0
  96. package/packages/components/mapping.js +95 -0
  97. package/packages/components/object-picker/index.js +7 -0
  98. package/packages/components/object-picker/src/fast-object-picker.vue +170 -0
  99. package/packages/components/select/index.js +7 -0
  100. package/packages/components/select/src/fast-select.vue +90 -0
  101. package/packages/components/table/index.js +7 -0
  102. package/packages/components/table/src/RowConfirm.vue +87 -0
  103. package/packages/components/table/src/dynamic-filter-form.vue +253 -0
  104. package/packages/components/table/src/dynamic-filter-list.vue +172 -0
  105. package/packages/components/table/src/easy-filter.vue +129 -0
  106. package/packages/components/table/src/export-confirm.vue +55 -0
  107. package/packages/components/table/src/quick-filter-form.vue +140 -0
  108. package/packages/components/table/src/row-form.vue +137 -0
  109. package/packages/components/table/src/stored-filter-manager.vue +240 -0
  110. package/packages/components/table/src/stored-filter.vue +180 -0
  111. package/packages/components/table/src/table-head-cell.vue +41 -0
  112. package/packages/components/table/src/table.vue +1309 -0
  113. package/packages/components/table/src/util.js +496 -0
  114. package/packages/components/table-column/config.js +64 -0
  115. package/packages/components/table-column/index.js +7 -0
  116. package/packages/components/table-column/src/table-column.vue +44 -0
  117. package/packages/components/table-column-date-picker/config.js +139 -0
  118. package/packages/components/table-column-date-picker/index.js +7 -0
  119. package/packages/components/table-column-date-picker/src/table-column-date-picker.vue +54 -0
  120. package/packages/components/table-column-file/config.js +83 -0
  121. package/packages/components/table-column-file/index.js +7 -0
  122. package/packages/components/table-column-file/src/table-column-file.vue +79 -0
  123. package/packages/components/table-column-img/config.js +83 -0
  124. package/packages/components/table-column-img/index.js +7 -0
  125. package/packages/components/table-column-img/src/table-column-img.vue +82 -0
  126. package/packages/components/table-column-input/config.js +77 -0
  127. package/packages/components/table-column-input/index.js +7 -0
  128. package/packages/components/table-column-input/src/table-column-input.vue +60 -0
  129. package/packages/components/table-column-number/config.js +89 -0
  130. package/packages/components/table-column-number/index.js +7 -0
  131. package/packages/components/table-column-number/src/table-column-number.vue +54 -0
  132. package/packages/components/table-column-object/config.js +66 -0
  133. package/packages/components/table-column-object/index.js +7 -0
  134. package/packages/components/table-column-object/src/table-column-object.vue +75 -0
  135. package/packages/components/table-column-select/config.js +67 -0
  136. package/packages/components/table-column-select/index.js +7 -0
  137. package/packages/components/table-column-select/src/table-column-select.vue +101 -0
  138. package/packages/components/table-column-switch/config.js +55 -0
  139. package/packages/components/table-column-switch/index.js +7 -0
  140. package/packages/components/table-column-switch/src/table-column-switch.vue +82 -0
  141. package/packages/components/table-column-textarea/config.js +77 -0
  142. package/packages/components/table-column-textarea/index.js +7 -0
  143. package/packages/components/table-column-textarea/src/table-column-textarea.vue +56 -0
  144. package/packages/components/table-column-time-picker/config.js +62 -0
  145. package/packages/components/table-column-time-picker/index.js +7 -0
  146. package/packages/components/table-column-time-picker/src/table-column-time-picker.vue +53 -0
  147. package/packages/components/upload/index.js +7 -0
  148. package/packages/components/upload/src/fast-upload.vue +272 -0
  149. package/packages/global.d.ts +42 -0
  150. package/packages/index.js +145 -0
  151. package/packages/mixins/table-column.js +133 -0
  152. package/packages/mixins/upload.js +14 -0
  153. package/packages/model/cond.js +74 -0
  154. package/packages/model/editComponentConfig.js +72 -0
  155. package/packages/model/fastTableOption.js +769 -0
  156. package/packages/model/filterComponentConfig.js +191 -0
  157. package/packages/model/opt.js +21 -0
  158. package/packages/model/order.js +37 -0
  159. package/packages/model/pageQuery.js +52 -0
  160. package/packages/model/query.js +161 -0
  161. package/packages/model/rel.js +5 -0
  162. package/packages/style.scss +5 -0
  163. package/packages/util/cache.js +92 -0
  164. package/packages/util/dialog.js +133 -0
  165. package/packages/util/escape.js +34 -0
  166. package/packages/util/http.js +18 -0
  167. package/packages/util/pick.js +92 -0
  168. package/packages/util/util.js +892 -0
@@ -0,0 +1,1309 @@
1
+ <template>
2
+ <div class="fc-fast-table">
3
+ <div ref="title" class="fc-fast-table-title" v-if="showTitle && option.title">{{ option.title }}</div>
4
+ <div ref="quick" class="fc-quick-filter-wrapper" :style="quickFilterWrapperStyle" v-if="queryable">
5
+ <!-- 快筛 -->
6
+ <quick-filter-form ref="quickForm" :filters="quickFilters" :option="option">
7
+ <slot name="quickFilter" v-bind="scopeParam"></slot>
8
+ </quick-filter-form>
9
+ </div>
10
+ <div ref="operation" class="fc-fast-table-operation-bar">
11
+ <div class="fc-operation-filter" v-if="queryable">
12
+ <!-- 简筛区 -->
13
+ <easy-filter :filters="easyFilters" :size="option.style.size" @search="pageLoad"/>
14
+ <el-button type="primary" class="fc-easy-filter-btn" :size="option.style.size" :icon="Search"
15
+ @click="pageLoad"/>
16
+ <el-button type="info" plain :size="option.style.size" :icon="RefreshLeft" @click="resetFilter"/>
17
+ <!-- 存筛区 -->
18
+ <stored-filter class="fc-stored-btn-wrapper"
19
+ ref="storedFilter"
20
+ :group-labels="storedLabels"
21
+ :table-option="option"
22
+ :column-config="columnConfig"
23
+ :size="option.style.size"
24
+ @change="pageLoad"/>
25
+ </div>
26
+ <!-- 按钮功能区 -->
27
+ <div class="fc-fast-table-operation-btn">
28
+ <slot name="button" v-bind="scopeParam"></slot>
29
+ <template v-if="status === 'normal'">
30
+ <el-button :size="option.style.size" @click="toInsert"
31
+ v-if="insertable">新建
32
+ </el-button>
33
+ <el-button type="danger" plain :size="option.style.size" @click="deleteRow"
34
+ v-if="deletable">删除
35
+ </el-button>
36
+ </template>
37
+ <template v-if="(updatable && status === 'update') || (insertable && status === 'insert')">
38
+ <el-button type="danger" plain @click="removeNewRows" v-if="status === 'insert' && editRows.length > 0">移除
39
+ </el-button>
40
+ <el-button type="primary" :size="option.style.size" @click="saveEditRows">保存</el-button>
41
+ <el-button :size="option.style.size" @click="toInsert" v-if="status === 'insert' && insertable">继续新建
42
+ </el-button>
43
+ <el-button :size="option.style.size" @click="cancelEditStatus">取消</el-button>
44
+ </template>
45
+ <!-- 下拉按钮-更多 -->
46
+ <el-dropdown class="fc-fast-table-operation-more" :size="option.style.size" v-if="showMoreBtn">
47
+ <el-button type="primary" plain :size="option.style.size">
48
+ <span>更多</span>
49
+ <el-icon class="el-icon--right">
50
+ <ArrowDown/>
51
+ </el-icon>
52
+ </el-button>
53
+ <template #dropdown>
54
+ <el-dropdown-menu>
55
+ <el-dropdown-item @click="activeBatchEdit" v-if="updatable">
56
+ <el-icon>
57
+ <Edit/>
58
+ </el-icon>
59
+ <span>批量编辑</span>
60
+ </el-dropdown-item>
61
+ <!-- TODO 1.6 批量修改: 指定一些记录,批量将某些字段修改为指定值 -->
62
+ <!-- <el-dropdown-item @click="activeBatchUpdate" >批量修改</el-dropdown-item>-->
63
+ <el-dropdown-item @click="exportData" v-if="exportable">
64
+ <el-icon>
65
+ <Download/>
66
+ </el-icon>
67
+ <span>导出</span>
68
+ </el-dropdown-item>
69
+ <template v-for="button in moreButtons">
70
+ <el-dropdown-item :disabled="getBoolVal(button.disable, false)"
71
+ @click="() => executeInContext(button.click)"
72
+ v-if="getBoolVal(button.showable, true)">
73
+ <el-icon v-if="button.icon">
74
+ <component :is="button.icon"/>
75
+ </el-icon>
76
+ <span>{{ button.label }}</span>
77
+ </el-dropdown-item>
78
+ </template>
79
+ </el-dropdown-menu>
80
+ </template>
81
+ </el-dropdown>
82
+ </div>
83
+ </div>
84
+ <div ref="dynamic" class="fc-dynamic-filter-wrapper">
85
+ <!-- 动筛列表 -->
86
+ <dynamic-filter-list :filters="dynamicFilters" :size="option.style.size" @search="pageLoad"></dynamic-filter-list>
87
+ </div>
88
+ <div class="fc-fast-table-wrapper">
89
+ <el-table v-bind="$attrs"
90
+ :data="list"
91
+ ref="table"
92
+ :row-style="rowStyle"
93
+ highlight-current-row
94
+ @current-change="handleCurrentChange"
95
+ @row-click="handleRowClick"
96
+ @row-dblclick="handleRowDblclick"
97
+ @select="handleSelect"
98
+ @selection-change="handleSelectionChange"
99
+ @select-all="handleSelectAll"
100
+ v-loading="loading"
101
+ :key="tableKey"
102
+ :height="heightTable"
103
+ :size="option.style.size"
104
+ border>
105
+ <el-table-column type="selection" width="55" v-if="enableMulti"/>
106
+ <el-table-column label="序号" :min-width="indexWith" v-if="enableIndex">
107
+ <template #default="{ $index }">
108
+ {{ $index + 1 + pageQuery.size * (pageQuery.current - 1) }}
109
+ </template>
110
+ </el-table-column>
111
+ <slot></slot>
112
+ </el-table>
113
+ </div>
114
+ <div ref="pagination" class="fc-pagination-wrapper">
115
+ <div class="fc-footer-wrapper">
116
+ <div class="fc-check-tip" v-if="queryable && checkedRows.length > 0">
117
+ <el-link underline="always" @click="clearCheckedRows">清除</el-link>
118
+ <el-text>已勾选的</el-text>
119
+ <el-link underline="always" @click="viewCheckedRows">{{ checkedRows.length }}</el-link>
120
+ <el-text>条记录</el-text>
121
+ </div>
122
+ <slot name="foot" v-bind="scopeParam"></slot>
123
+ </div>
124
+ <el-pagination v-model:page-size="pageQuery.size"
125
+ v-model:current-page="pageQuery.current"
126
+ :page-sizes="option.pagination['page-sizes']"
127
+ :total="total"
128
+ @current-change="pageLoad"
129
+ @size-change="() => pageLoad()"
130
+ :layout="option.pagination.layout"
131
+ v-if="queryable"></el-pagination>
132
+ </div>
133
+ </div>
134
+ </template>
135
+
136
+ <script>
137
+ import {nextTick} from "vue"
138
+ import {ElMessage, ElMessageBox} from 'element-plus'
139
+ import {remove} from 'lodash-es'
140
+ import QuickFilterForm from "./quick-filter-form.vue"
141
+ import EasyFilter from "./easy-filter.vue"
142
+ import StoredFilter from "./stored-filter.vue"
143
+ import DynamicFilterForm from "./dynamic-filter-form.vue"
144
+ import DynamicFilterList from "./dynamic-filter-list.vue"
145
+ import RowForm from "./row-form.vue"
146
+ import RowConfirm from "./RowConfirm.vue"
147
+ import PageQuery from '../../../model/pageQuery.js'
148
+ import FastTableOption from "../../../model/fastTableOption.js"
149
+ import {
150
+ calLength,
151
+ getFullHeight, getInnerHeight,
152
+ ifBlank,
153
+ isBoolean,
154
+ isEmpty,
155
+ isFunction,
156
+ isNull,
157
+ isNumber,
158
+ noRepeatAdd
159
+ } from "../../../util/util"
160
+ import * as cache from '../../../util/cache.js'
161
+ import {getEditConfig, iterBuildComponentConfig, rowValid, toTableRow, buildParamForExport, isFatRow} from "./util"
162
+ import {openDialog} from "../../../util/dialog"
163
+ import {buildFinalQueryComponentConfig} from "../../mapping"
164
+ import {ArrowDown, Download, Edit, RefreshLeft, Search} from "@element-plus/icons-vue";
165
+ import {post} from "../../../util/http.js";
166
+ import * as util from "../../../util/util.js";
167
+
168
+ export default {
169
+ name: "FastTable",
170
+ components: {ArrowDown, Download, Edit, QuickFilterForm, EasyFilter, StoredFilter, DynamicFilterList},
171
+ emits: ['currentChange', 'select', 'selectionChange', 'selectAll', 'rowClick', 'rowDblclick'],
172
+ props: {
173
+ option: {
174
+ type: FastTableOption,
175
+ required: true
176
+ },
177
+ data: { // 初始化数据(必须是fatRow)
178
+ type: Array,
179
+ default: () => [],
180
+ validator: (value) => util.isArray(value) && value.every(row => isFatRow(row))
181
+ }
182
+ },
183
+ computed: {
184
+ RefreshLeft() {
185
+ return RefreshLeft
186
+ },
187
+ Search() {
188
+ return Search
189
+ },
190
+ showTitle() {
191
+ return this.getBoolVal(this.option.showTitle, true)
192
+ },
193
+ queryable() {
194
+ return this.getBoolVal(this.option.queryable, true)
195
+ },
196
+ insertable() {
197
+ return this.getBoolVal(this.option.insertable, true)
198
+ },
199
+ updatable() {
200
+ return this.getBoolVal(this.option.updatable, true)
201
+ },
202
+ deletable() {
203
+ return this.getBoolVal(this.option.deletable, true)
204
+ },
205
+ exportable() {
206
+ return this.getBoolVal(this.option.exportable, true)
207
+ },
208
+ enableDblClickEdit() {
209
+ return this.getBoolVal(this.option.enableDblClickEdit, true)
210
+ },
211
+ enableMulti() {
212
+ return this.getBoolVal(this.option.enableMulti, true)
213
+ },
214
+ enableIndex() {
215
+ return this.getBoolVal(this.option.enableIndex, false)
216
+ },
217
+ enableColumnFilter() {
218
+ return this.getBoolVal(this.option.enableColumnFilter, true)
219
+ },
220
+ enableFilterCache() {
221
+ return this.getBoolVal(this.option.enableFilterCache, true)
222
+ },
223
+ lazyLoad() {
224
+ return this.getBoolVal(this.option.lazyLoad, false)
225
+ },
226
+ // 状态: normal-常规状态; insert-新增状态; update-编辑状态
227
+ status() {
228
+ const {editRows} = this;
229
+ if (editRows.length === 0) {
230
+ return 'normal';
231
+ }
232
+ if (editRows.every(r => r.status === 'update')) {
233
+ return 'update';
234
+ } else if (editRows.every(r => r.status === 'insert')) {
235
+ return 'insert';
236
+ } else {
237
+ return 'normal';
238
+ }
239
+ },
240
+ // 快筛区域样式: 控制当快筛区无内容时,css隐藏快筛区
241
+ quickFilterWrapperStyle() {
242
+ const filtersEmpty = !this.quickFilters || this.quickFilters.length === 0
243
+ let slotEmpty = true
244
+ if (this.$slots.quickFilter) {
245
+ const slotContent = this.$slots.quickFilter(this.scopeParam)
246
+ slotEmpty = !slotContent || slotContent.length === 0
247
+ }
248
+ return {
249
+ display: filtersEmpty && slotEmpty ? 'none' : 'block'
250
+ }
251
+ },
252
+ indexWith() {
253
+ const currentPageMaxIndex = this.pageQuery.current * this.pageQuery.size
254
+ const width = calLength(currentPageMaxIndex) + 40
255
+ return width <= 60 ? 60 : width
256
+ },
257
+ // 行样式
258
+ rowStyle() {
259
+ return {
260
+ height: this.option.style.bodyRowHeight
261
+ }
262
+ },
263
+ // table表格高度
264
+ heightTable() {
265
+ if (this.$attrs.hasOwnProperty('height')) { // 自定义最高优先级
266
+ return this.$attrs.height;
267
+ }
268
+ return this.tableFlexHeight;
269
+ },
270
+ // 表格级别的slot向上透传的统一参数
271
+ scopeParam() {
272
+ const {choseRow, checkedRows, editRows, pageQuery} = this
273
+ return {
274
+ query: pageQuery,
275
+ size: this.option.style.size,
276
+ choseRow: choseRow,
277
+ checkedRows: checkedRows,
278
+ editRows: editRows
279
+ }
280
+ },
281
+ // 是否展示“更多”下拉菜单按钮
282
+ showMoreBtn() {
283
+ return this.updatable || this.exportable || !util.isEmpty(this.moreButtons)
284
+ },
285
+ // “更多”按钮扩展
286
+ moreButtons() {
287
+ return this.option.moreButtons
288
+ },
289
+ // 处于编辑激活状态的行,包括insert和update
290
+ editRows() {
291
+ return this.list.filter(r => r.status !== 'normal')
292
+ }
293
+ },
294
+ data() {
295
+ const size = this.option.pagination.size;
296
+ const pageQuery = new PageQuery(1, size);
297
+ pageQuery.extra = this.option.condExtra
298
+ if (!ifBlank(this.option.sortField)) {
299
+ pageQuery.addOrder(this.option.sortField, !this.option.sortDesc);
300
+ }
301
+ return {
302
+ heightObserver: new ResizeObserver(() => this.calTableHeight()), // 表格高度重算监听器(dom级别)和窗口resize不冲突,互为弥补
303
+ tableKey: 0, // 用于前端刷新表格
304
+ loading: false, // 表格数据是否正加载中
305
+ choseRow: null, // 当前选中的行记录
306
+ checkedRows: [], // 代表多选时勾选的行记录
307
+ pageQuery: pageQuery, // 分页查询构造参数
308
+ columnConfig: {}, // 列对应的配置。key: column prop属性名, value为通过fast-table-column*定义的属性(外加tableColumnComponentName属性)
309
+ quickFilters: [], // 快筛配置
310
+ easyFilters: [], // 简筛配置
311
+ dynamicFilters: [], // 动筛配置
312
+ storedLabels: [], // 勾选的存筛组标签名
313
+ list: this.data, // 表格当前页的数据, 不单纯有业务数据, 还有配置数据(用于实现行内、弹窗表单)
314
+ total: 0, // 表格总数
315
+ tableFlexHeight: null, //表格的弹性高度(动态计算值), 初始值是null非常重要, 如果内部计算出现问题外部又没自定义高度,相当于没有设置height值, 默认展示效果
316
+ }
317
+ },
318
+ provide() {
319
+ return {
320
+ openDynamicFilterForm: this.openDynamicFilterForm, // 提供给fast-table-column* 触发创建动筛的能力
321
+ tableStyle: this.option.style, // 提供给fast-table-column显示高度、尺寸等配置
322
+ context: this.option.context // 提供给fast-table-column* 获取上下文信息
323
+ }
324
+ },
325
+ created() {
326
+ this.option.ref = this // important: 后续很多逻辑需要用到, 借助option即可获取组件中的一些数据
327
+ },
328
+ async mounted() {
329
+ this.buildComponentConfig() // 构建组件数据(筛选组件元数据等) very important!
330
+ if (this.enableFilterCache) {
331
+ await this.popStashFilter() // 加载分页筛选条件
332
+ }
333
+ if (!this.lazyLoad) {
334
+ this.pageLoad()
335
+ }
336
+ if (this.option.style.flexHeight) {
337
+ await nextTick(() => {
338
+ this.heightObserver.observe(this.$refs.quick) // FIX: 快筛项如果利用grid-area调整位置会导致第一次表格高度计算有误, 通过这个解决此问题
339
+ this.heightObserver.observe(this.$refs.dynamic)
340
+ this.calTableHeight()
341
+ window.addEventListener('resize', this.calTableHeight)
342
+ })
343
+ this.$watch('dynamicFilters.length', () => { // 动态过滤器变化时可能高度改变, 重新计算高度
344
+ nextTick(() => {
345
+ this.calTableHeight()
346
+ })
347
+ })
348
+ }
349
+ },
350
+ methods: {
351
+ /**
352
+ * 添加到编辑行: 编辑行改为computed自动计算, 无需添加, 保留此方法便于触发针对新增的编辑行的校验
353
+ * @param fatRows
354
+ */
355
+ addToEditRows(fatRows) {
356
+ // 立即校验一下以便标识出必填等字段
357
+ rowValid(fatRows, this.option).catch((errors) => {
358
+ // do nothing: 不提示错误, 会显示红框
359
+ });
360
+ },
361
+ /**
362
+ * 重新渲染table,提供给外部是用
363
+ */
364
+ reRender() {
365
+ this.tableKey++;
366
+ },
367
+ /**
368
+ * 解析FastTable下的vnodes, 得到列配置和列中组件配置。核心方法(important!)
369
+ */
370
+ buildComponentConfig() {
371
+ const tableColumnVNodes = this.$slots.default ? this.$slots.default() : [];
372
+ iterBuildComponentConfig(tableColumnVNodes, this.option, ({
373
+ tableColumnComponentName,
374
+ col,
375
+ customConfig,
376
+ quickFilter,
377
+ easyFilter,
378
+ formItemConfig,
379
+ inlineItemConfig
380
+ }) => {
381
+ if (quickFilter) {
382
+ const {props = {}} = quickFilter;
383
+ noRepeatAdd(this.quickFilters, quickFilter,
384
+ (ele, item) => ele.col === item.col,
385
+ props.firstFilter !== false) // deprecated: 1.6.0
386
+ }
387
+ if (easyFilter) {
388
+ const {props = {}} = easyFilter;
389
+ noRepeatAdd(this.easyFilters, easyFilter,
390
+ (ele, item) => ele.col === item.col,
391
+ props.firstFilter !== false) // deprecated: 1.6.0
392
+ }
393
+ this.columnConfig[col] = {
394
+ tableColumnComponentName: tableColumnComponentName,
395
+ customConfig: customConfig,
396
+ formItemConfig: formItemConfig,
397
+ inlineItemConfig: inlineItemConfig
398
+ };
399
+ })
400
+ // 排序
401
+ this.quickFilters.sort((f1, f2) => {
402
+ const d = f1.index - f2.index
403
+ return d === 0 ? -1 : d
404
+ })
405
+ this.easyFilters.sort((f1, f2) => {
406
+ const d = f1.index - f2.index
407
+ return d === 0 ? -1 : d
408
+ })
409
+ },
410
+ /**
411
+ * 暂只支持单列排序, 原因: 1.通过option指定的默认排序不好回显在表头; 2.多字段排序会导致操作比较繁琐
412
+ * @param col
413
+ * @param asc
414
+ */
415
+ buildOrder(col, asc) {
416
+ if (isEmpty(col)) {
417
+ return;
418
+ }
419
+ if (isBoolean(asc)) {
420
+ // 用户指定排序前, 当只有默认排序时, 移除默认排序
421
+ if (!isEmpty(this.option.sortField) && this.pageQuery.orders.length === 1 && this.pageQuery.orders[0].col === this.option.sortField) {
422
+ this.pageQuery.removeOrder(this.option.sortField);
423
+ }
424
+ this.pageQuery.addOrder(col, asc);
425
+ return;
426
+ }
427
+
428
+ this.pageQuery.removeOrder(col);
429
+ if (this.pageQuery.orders.length === 0) {
430
+ this.pageQuery.addOrder(this.option.sortField, !this.option.sortDesc)
431
+ }
432
+ },
433
+ /**
434
+ * 分页加载请求
435
+ * @param page 第几页, 可不传(默认为当前页码)
436
+ */
437
+ pageLoad(page) {
438
+ if (!this.queryable) {
439
+ return
440
+ }
441
+ const confirmPromise = (this.status !== 'normal')
442
+ ? ElMessageBox.confirm('当前处于编辑状态, 点击【确定】将丢失已编辑内容?', '提示', {
443
+ confirmButtonText: '确定',
444
+ cancelButtonText: '取消'
445
+ })
446
+ : Promise.resolve()
447
+ confirmPromise.then(() => {
448
+ const conds = []
449
+ // 添加快筛条件
450
+ const quickConds = this.quickFilters.filter(f => !f.disabled && f.isEffective()).map(f => f.getConds()).flat()
451
+ conds.push(...quickConds)
452
+ // 添加简筛条件
453
+ const easyConds = this.easyFilters.filter(f => !f.disabled && f.isEffective()).map(f => f.getConds()).flat()
454
+ conds.push(...easyConds)
455
+ // 添加动筛条件
456
+ const dynamicConds = this.dynamicFilters.filter(f => !f.disabled && f.isEffective()).map(f => f.getConds()).flat()
457
+ conds.push(...dynamicConds)
458
+ // 添加存筛条件
459
+ if (this.storedLabels.length > 0) {
460
+ const storeFilters = this.$refs.storedFilter.getStoreFilters();
461
+ const storedConds = storeFilters.filter(f => !f.disabled && f.isEffective()).map(f => f.getConds()).flat()
462
+ conds.push(...storedConds)
463
+ }
464
+ // 添加固定的预置条件
465
+ conds.push(...this.option.conds);
466
+ this.pageQuery.setConds(conds);
467
+ const context = this.option.context;
468
+ const beforeLoad = this.option.beforeLoad;
469
+ return new Promise((resolve, reject) => {
470
+ if (!isEmpty(page) && isNumber(page)) {
471
+ this.pageQuery.current = page
472
+ }
473
+ beforeLoad.call(context, {query: this.pageQuery}).then(() => {
474
+ this.loading = true
475
+ if (this.enableFilterCache) {
476
+ this.stashFilter() // 缓存分页筛选条件
477
+ }
478
+ post(this.option.pageUrl, this.pageQuery.toJson()).then(res => {
479
+ this.exitEditStatus();
480
+ const loadSuccess = this.option.loadSuccess;
481
+ loadSuccess.call(context, {query: this.pageQuery, res: res}).then(({records = [], total = 0}) => {
482
+ this.list = records.map(r => toTableRow(r, this.columnConfig, 'normal', 'inline'));
483
+ this.total = total;
484
+ nextTick(() => {
485
+ this.setChoseRow(0); // 默认选中第一行
486
+ this.syncRowSelection(); // 同步可能得选中状态
487
+ })
488
+ }).finally(() => {
489
+ resolve()
490
+ })
491
+ }).catch(err => {
492
+ const loadFail = this.option.loadFail;
493
+ loadFail.call(context, {query: this.pageQuery, error: err}).then(() => {
494
+ ElMessage.error('加载失败:' + JSON.stringify(err));
495
+ })
496
+ reject(err);
497
+ }).finally(() => {
498
+ this.loading = false;
499
+ })
500
+ }).catch(err => {
501
+ reject(err);
502
+ })
503
+ })
504
+ })
505
+ },
506
+ /**
507
+ * 重置筛选条件值
508
+ */
509
+ resetFilter() {
510
+ const context = this.option.context
511
+ const beforeReset = this.option.beforeReset
512
+ beforeReset.call(context, {query: this.pageQuery}).then(() => {
513
+ this.quickFilters.forEach((f) => {
514
+ if (f.reset()) {
515
+ this.$refs.quickForm.handleChange(f) // 为了触发快筛值onChange
516
+ }
517
+ })
518
+ this.easyFilters.forEach((f) => f.reset())
519
+ this.dynamicFilters.length = 0
520
+ this.storedLabels.length = 0
521
+ // 清空自定义扩展筛选参数
522
+ Object.keys(this.pageQuery.extra).forEach(key => {
523
+ this.pageQuery.extra[key] = null
524
+ })
525
+ this.pageLoad()
526
+ }).catch(() => {
527
+ console.debug('你取消了重置操.')
528
+ })
529
+ },
530
+ /**
531
+ * insert前校验
532
+ */
533
+ toInsert() {
534
+ const {editType} = this.option
535
+ if (this.status !== 'normal' && this.status !== 'insert') {
536
+ console.warn(`当前FastTable处于${this.status}状态, 不允许新增`)
537
+ return
538
+ }
539
+ if (editType === 'form') {
540
+ this.addForm()
541
+ } else {
542
+ this.addRow()
543
+ }
544
+ },
545
+ /**
546
+ * 弹窗表单新增
547
+ */
548
+ addForm(row = {}) {
549
+ if (!this.insertable) {
550
+ return;
551
+ }
552
+ const {context, beforeToInsert} = this.option;
553
+ const rows = [row]
554
+ beforeToInsert.call(context, rows).then(() => {
555
+ const fatRow = toTableRow(rows[0], this.columnConfig, 'insert', 'form');
556
+ openDialog({
557
+ component: RowForm,
558
+ props: {
559
+ option: this.option,
560
+ config: fatRow.config,
561
+ row: fatRow,
562
+ type: 'insert',
563
+ layout: this.option.style.formLayout
564
+ },
565
+ dialogProps: {
566
+ width: '50%',
567
+ title: '新增',
568
+ 'close-on-click-modal': false
569
+ }
570
+ }).then(() => {
571
+ this.pageLoad();
572
+ })
573
+ }).catch(() => {
574
+ console.debug('你已取消新建')
575
+ })
576
+ },
577
+ /**
578
+ * 增加一个新增编辑状态的行
579
+ */
580
+ addRow(row = {}) {
581
+ this.addRows([row]);
582
+ },
583
+ /**
584
+ * 增加多个新增状态的行
585
+ * @param rows
586
+ */
587
+ addRows(rows = []) {
588
+ if (!this.insertable) {
589
+ return
590
+ }
591
+ if (this.status !== 'normal' && this.status !== 'insert') {
592
+ ElMessage.warning(`当前表格处于${this.status}状态, 不允许新增`);
593
+ return
594
+ }
595
+ const {context, beforeToInsert} = this.option;
596
+ beforeToInsert.call(context, rows).then(() => {
597
+ const newRows = rows.map(r => toTableRow(r, this.columnConfig, 'insert', 'inline'));
598
+ this.list.unshift(...newRows);
599
+ this.addToEditRows(newRows);
600
+ this.setChoseRow(0);
601
+ }).catch(() => {
602
+ console.debug('你已取消新建')
603
+ })
604
+ },
605
+ /**
606
+ * 删除行(多选模式下,删除checkedRows; 非多选模式下, 删除choseRow)
607
+ */
608
+ deleteRow() {
609
+ if (!this.deletable) {
610
+ return
611
+ }
612
+ const enableMulti = this.enableMulti
613
+ let beDeleteRows = (enableMulti ? this.checkedRows : (util.isEmpty(this.choseRow) ? [] : [this.choseRow]))
614
+ if (util.isEmpty(beDeleteRows)) {
615
+ ElMessage.warning(`请先${enableMulti ? '勾' : '点'}选要删除的行`)
616
+ return
617
+ }
618
+
619
+ const rows = beDeleteRows.map(r => r.row);
620
+ const {context, beforeDeleteTip, beforeDelete} = this.option;
621
+ let param = {
622
+ fatRows: beDeleteRows,
623
+ rows: rows
624
+ }
625
+ beforeDeleteTip.call(context, param).then(() => {
626
+ const columnConfigs = Object.entries(this.columnConfig).map(([col, config]) => config)
627
+ openDialog({
628
+ component: RowConfirm,
629
+ props: {
630
+ rows: beDeleteRows,
631
+ columnConfigs: columnConfigs,
632
+ action: 'delete'
633
+ },
634
+ dialogProps: {
635
+ title: `确认删除以下记录?`,
636
+ width: '90%',
637
+ buttons: [
638
+ {
639
+ text: '确定删除',
640
+ type: 'danger',
641
+ onClick: (component) => {
642
+ return Promise.resolve(component.getRows())
643
+ }
644
+ },
645
+ {
646
+ text: '取消',
647
+ onClick: () => {
648
+ return Promise.reject() // 走catch逻辑
649
+ }
650
+ }
651
+ ]
652
+ }
653
+ }).then((confirmedRows) => {
654
+ this.syncRowSelection()
655
+ param = {fatRows: confirmedRows, rows: confirmedRows.map(r => r.row)}
656
+ beforeDelete.call(context, param).then((postData) => {
657
+ if (postData.length === 0) {
658
+ ElMessage.warning('无可删除数据')
659
+ return
660
+ }
661
+ const {deleteUrl, batchDeleteUrl, deleteSuccess, deleteFail} = this.option;
662
+ const postPromise = (postData.length === 1 ? post(deleteUrl, postData[0]) : post(batchDeleteUrl, postData))
663
+ postPromise.then(res => {
664
+ this.checkedRows.length = 0 // 删除成功则清除已勾选数据
665
+ this.pageLoad() // 刷新分页数据
666
+ deleteSuccess.call(context, {
667
+ ...param,
668
+ res: res
669
+ }).then(() => {
670
+ ElMessage.success('删除成功')
671
+ })
672
+ }).catch(err => {
673
+ deleteFail.call(context, {...param, error: err}).then(() => {
674
+ ElMessage.error('删除失败:' + JSON.stringify(err));
675
+ })
676
+ })
677
+ }).catch(() => {
678
+ console.log('[beforeDelete]取消删除..')
679
+ })
680
+ }).catch(() => {
681
+ // 已取消
682
+ this.syncRowSelection()
683
+ })
684
+ }).catch(() => {
685
+ console.log('[beforeDeleteTip]取消删除..')
686
+ })
687
+ // this.option._deleteRows(beDeleteRows).then(() => this.pageLoad());
688
+ },
689
+ /**
690
+ * 打开动筛面板: 构造动筛组件配置, 动态创建面板并弹出。由于动筛是动态的,不能在mounted阶段构造好。
691
+ * @param column
692
+ */
693
+ openDynamicFilterForm(column) {
694
+ if (!this.enableColumnFilter || !this.queryable) {
695
+ return;
696
+ }
697
+ const {prop, label, order} = column
698
+ const {tableColumnComponentName, customConfig} = this.columnConfig[prop]
699
+ const dynamicFilter = buildFinalQueryComponentConfig(customConfig, tableColumnComponentName, 'dynamic', this.option)
700
+ openDialog({
701
+ component: DynamicFilterForm,
702
+ props: {
703
+ option: this.option,
704
+ filter: dynamicFilter,
705
+ order: order,
706
+ conds: this.pageQuery.conds,
707
+ },
708
+ dialogProps: {
709
+ width: '480px',
710
+ title: `数据筛选及排序: ${label}`,
711
+ }
712
+ }).then(async ({filter: dynamicFilter, order}) => {
713
+ if (dynamicFilter.isEffective()) {
714
+ await dynamicFilter.updateCondMsg()
715
+ this.dynamicFilters.push(dynamicFilter);
716
+ }
717
+ if (isBoolean(order.asc)) {
718
+ this.buildOrder(prop, order.asc)
719
+ column.order = order.asc ? 'asc' : 'desc'
720
+ } else {
721
+ this.buildOrder(prop, order.asc)
722
+ column.order = '';
723
+ }
724
+ this.pageLoad();
725
+ }).catch(msg => {
726
+ console.log(msg)
727
+ })
728
+ },
729
+ // 选中行发生变更
730
+ handleCurrentChange(row) {
731
+ this.choseRow = row;
732
+ this.$emit('currentChange', {fatRow: row, row: isNull(row) ? null : row.row, scope: this.scopeParam});
733
+ },
734
+ /**
735
+ * 选中指定行
736
+ * @param index 不传默认是0
737
+ */
738
+ setChoseRow(index = 0) {
739
+ if (this.list.length === 0) {
740
+ this.choseRow = null;
741
+ this.$refs.table.setCurrentRow(); // 清除选中高亮
742
+ return;
743
+ }
744
+ this.choseRow = this.list[index];
745
+ this.$refs.table.setCurrentRow(this.choseRow);
746
+ },
747
+ getChoseRow() {
748
+ return this.choseRow;
749
+ },
750
+ getCheckedRows() {
751
+ return this.checkedRows;
752
+ },
753
+ handleSelect(rows, row) {
754
+ this.$emit('select', {
755
+ fatRows: rows,
756
+ rows: rows.map(r => r.row),
757
+ fatRow: row,
758
+ row: row.row,
759
+ scope: this.scopeParam
760
+ });
761
+ const idField = this.option.idField
762
+ const isChecked = (rows.indexOf(row) > -1)
763
+ if (isChecked) { // 勾选
764
+ this.checkedRows.push(row)
765
+ } else { // 取消勾选
766
+ const eqCallback = ((r1, r2) => r1.row[idField] === r2.row[idField])
767
+ const idx = this.checkedRows.findIndex(r1 => eqCallback(r1, row))
768
+ if (idx > -1) {
769
+ this.checkedRows.splice(idx, 1)
770
+ }
771
+ }
772
+ },
773
+ handleSelectionChange(newRows) {
774
+ this.$emit('selectionChange', {fatRows: newRows, rows: newRows.map(r => r.row), scope: this.scopeParam})
775
+ },
776
+ handleSelectAll(rows) {
777
+ this.$emit('selectAll', {fatRows: rows, rows: rows.map(r => r.row), scope: this.scopeParam});
778
+ // rows中的全部塞进checkedRows
779
+ const idField = this.option.idField
780
+ const eqCallback = ((r1, r2) => r1.row[idField] === r2.row[idField])
781
+ rows.forEach(r1 => {
782
+ if (this.checkedRows.findIndex(r2 => eqCallback(r1, r2)) === -1) {
783
+ this.checkedRows.push(r1)
784
+ }
785
+ })
786
+ // 对于checkedRows中存在于list但不存在于rows的, 要从checkedRows里移除
787
+ for (let i = this.checkedRows.length - 1; i >= 0; i--) { // 倒序, important
788
+ const r1 = this.checkedRows[i]
789
+ // 若rows中无,且list有, 则表示是要取消的
790
+ if (rows.findIndex(r2 => eqCallback(r1, r2)) === -1 && this.list.findIndex(r2 => eqCallback(r1, r2)) > -1) {
791
+ this.checkedRows.splice(i, 1)
792
+ }
793
+ }
794
+ },
795
+ handleRowClick(row, column, event) {
796
+ this.$emit('rowClick', {fatRow: row, column, event, row: row.row, scope: this.scopeParam});
797
+ },
798
+ handleRowDblclick(row, column, event) {
799
+ this.$emit('rowDblclick', {fatRow: row, column, event, row: row.row, scope: this.scopeParam});
800
+ if (!this.enableDblClickEdit) {
801
+ return;
802
+ }
803
+ // 若当前编辑行已经处于编辑状态, 则直接emit并返回;
804
+ if (row.status === 'update' || row.status === 'insert') {
805
+ return;
806
+ }
807
+ // 当前存在编辑行时,不允许再新增编辑行
808
+ if (this.status !== 'normal') {
809
+ return;
810
+ }
811
+ if (this.option.editType === 'form') {
812
+ this.updateForm(row);
813
+ } else {
814
+ this.updateRow(row);
815
+ }
816
+ },
817
+ /**
818
+ * 表单更新一行
819
+ * @param fatRow
820
+ */
821
+ updateForm(fatRow) {
822
+ if (!this.updatable) {
823
+ return;
824
+ }
825
+ const {context, beforeToUpdate} = this.option;
826
+ beforeToUpdate.call(context, {fatRows: [fatRow], rows: [fatRow.row]}).then(() => {
827
+ openDialog({
828
+ component: RowForm,
829
+ props: {
830
+ option: this.option,
831
+ config: getEditConfig(this.columnConfig, 'form'),
832
+ row: fatRow,
833
+ type: 'update',
834
+ layout: this.option.style.formLayout
835
+ },
836
+ dialogProps: {
837
+ width: '50%',
838
+ title: '编辑',
839
+ 'close-on-click-modal': false
840
+ }
841
+ }).then(() => {
842
+ this.pageLoad();
843
+ })
844
+ }).catch(() => {
845
+ console.debug('你已取消编辑')
846
+ })
847
+ },
848
+ updateRow(fatRow) {
849
+ if (!this.updatable) {
850
+ return
851
+ }
852
+ if (this.status !== 'normal' && this.status !== 'update') {
853
+ ElMessage.warning(`当前表格处于${this.status}状态, 不允许更新`);
854
+ return
855
+ }
856
+ const {context, beforeToUpdate} = this.option;
857
+ beforeToUpdate.call(context, {fatRows: [fatRow], rows: [fatRow.row]}).then(() => {
858
+ fatRow.status = 'update';
859
+ this.addToEditRows([fatRow]);
860
+ }).catch(() => {
861
+ console.debug('你已取消编辑')
862
+ })
863
+ },
864
+ /**
865
+ * 激活批量编辑
866
+ */
867
+ activeBatchEdit() {
868
+ if (this.status !== 'normal') {
869
+ ElMessage.warning('请先退出编辑状态')
870
+ return;
871
+ }
872
+ const {context, beforeToUpdate} = this.option
873
+ beforeToUpdate.call(context, {
874
+ fatRows: this.list,
875
+ rows: this.list.map(r => r.row),
876
+ editRows: this.list.map(r => r.editRow)
877
+ }).then(() => {
878
+ this.list.forEach(r => r.status = 'update')
879
+ this.addToEditRows(this.list);
880
+ }).catch(() => {
881
+ console.debug('你已取消编辑')
882
+ })
883
+ },
884
+ /**
885
+ * 取消编辑状态: 包括新增、更新状态。会将编辑状态的行状态重置为'normal', 并清空编辑行数组editRows, 同时将表格状态重置为'normal'
886
+ */
887
+ cancelEditStatus() {
888
+ const {context, beforeCancel} = this.option;
889
+ beforeCancel.call(context, {
890
+ fatRows: this.editRows,
891
+ rows: this.editRows.map(r => r.row),
892
+ status: this.status
893
+ }).then(() => {
894
+ this.exitEditStatus();
895
+ }).catch(() => {
896
+ // 不允许取消
897
+ })
898
+ },
899
+ exitEditStatus() {
900
+ // 移除列表中可能存在的insert状态记录
901
+ remove(this.list, item => item.status === 'insert');
902
+ // 将编辑的行状态改为normal, 并清空editRows,因为editRows是list中的引用,所以不能光清空数组
903
+ this.editRows.forEach(r => {
904
+ r.status = 'normal'
905
+ r.editRow = {...r.row} // 重置editRow
906
+ })
907
+ // this.editRows.length = 0
908
+ // if (isNormal === false) { // 编辑状态时(尤其新建状态), 控制表格重新渲染, 避免一些“残留”。升级vue3后没看到什么残留,而且退出编辑都刷新页面并不好
909
+ // this.reRender();
910
+ // this.pageLoad()
911
+ // }
912
+ },
913
+ /**
914
+ * 同步行的选中状态
915
+ */
916
+ syncRowSelection() {
917
+ const enableMulti = this.enableMulti
918
+ if (enableMulti === false) {
919
+ this.checkedRows.length = 0
920
+ return
921
+ }
922
+ const idField = this.option.idField
923
+ for (const r1 of this.list) {
924
+ const selected = this.checkedRows.some(r2 => r1.row[idField] === r2.row[idField])
925
+ this.$refs.table.toggleRowSelection(r1, selected)
926
+ }
927
+ },
928
+ /**
929
+ * 查看勾选的数据
930
+ */
931
+ viewCheckedRows() {
932
+ const columnConfigs = Object.entries(this.columnConfig).map(([col, config]) => config)
933
+ openDialog({
934
+ component: RowConfirm,
935
+ props: {
936
+ rows: this.checkedRows,
937
+ columnConfigs: columnConfigs
938
+ },
939
+ dialogProps: {
940
+ title: '所有勾选的行',
941
+ width: '90%',
942
+ handleCancel: () => {
943
+ this.$nextTick(() => {
944
+ this.syncRowSelection()
945
+ })
946
+ }
947
+ }
948
+ })
949
+ },
950
+ /**
951
+ * 清空勾选
952
+ */
953
+ clearCheckedRows() {
954
+ this.checkedRows.length = 0
955
+ this.$refs.table.clearSelection()
956
+ },
957
+ /**
958
+ * 移除新建的行
959
+ */
960
+ removeNewRows() {
961
+ if (this.status !== 'insert' || this.editRows.length === 0) {
962
+ return
963
+ }
964
+ const enableMulti = this.enableMulti
965
+ let beRemoveRows = (enableMulti ? this.checkedRows : (util.isEmpty(this.choseRow) ? [] : [this.choseRow]))
966
+ if (isEmpty(beRemoveRows)) {
967
+ ElMessage.warning(`请先${enableMulti ? '勾' : '点'}选要移除的新建行`)
968
+ return
969
+ }
970
+ if (beRemoveRows.some(r => r.status !== 'insert')) {
971
+ ElMessage.warning('只能移除新建的行')
972
+ return
973
+ }
974
+ ElMessageBox.confirm(`确定移除这${beRemoveRows.length}条记录吗?`, '移除确认', {}).then(() => {
975
+ remove(this.list, item => beRemoveRows.indexOf(item) > -1)
976
+ if (this.editRows.length === 0) {
977
+ this.exitEditStatus()
978
+ }
979
+ this.checkedRows.length = 0 // 清除勾选数据
980
+ this.setChoseRow(0)
981
+ })
982
+ },
983
+ /**
984
+ * 保存编辑的行: 包括新增或更新状态的行。内部会将保存成功的记录的行状态置为normal
985
+ * @return 返回Promise。不存在需要保存的行 或 保存成功则返回resolve, 否则返回reject。
986
+ */
987
+ saveEditRows() {
988
+ if (this.editRows.length === 0) {
989
+ return Promise.resolve();
990
+ }
991
+ if (this.status !== 'insert' && this.status !== 'update') {
992
+ throw new Error(`当前FastTable状态异常:${this.status}, 无法保存编辑记录`);
993
+ }
994
+ rowValid(this.editRows, this.option).then(() => {
995
+ // 保存编辑的行: 包括新增、更新状态的行
996
+ let promise;
997
+ if (this.status === 'insert') {
998
+ promise = this.option._insertRows(this.editRows);
999
+ } else {
1000
+ promise = this.option._updateRows(this.editRows);
1001
+ }
1002
+ promise.then(() => {
1003
+ this.exitEditStatus(); // 退出编辑状态
1004
+ this.pageLoad();
1005
+ }).catch(() => {
1006
+ });
1007
+ }).catch((errors) => {
1008
+ const firstError = errors[0];
1009
+ ElMessage.error(firstError.message);
1010
+ })
1011
+ },
1012
+ /**
1013
+ * 批量更新记录
1014
+ */
1015
+ activeBatchUpdate() {
1016
+ // TODO 1.6 激活勾选,针对勾选的记录弹出批量更新弹窗,可指定要更新的字段和值,点击确定应用于这些记录
1017
+ },
1018
+ /**
1019
+ * 导出数据
1020
+ */
1021
+ exportData() {
1022
+ if (!this.exportable) {
1023
+ ElMessage.warning('当前表格不允许导出')
1024
+ return
1025
+ }
1026
+ this.option._exportData(buildParamForExport(this.columnConfig), this.pageQuery);
1027
+ },
1028
+ /**
1029
+ * 自定义表格
1030
+ */
1031
+ customTable() {
1032
+ // TODO 1.6 自定义表格: 可自定义——表格标题、默认简筛字段、默认排序字段和排序方式、各列宽、冻结哪些列等
1033
+ },
1034
+ /**
1035
+ * 计算表格自适高度: tableFlexHeight
1036
+ */
1037
+ calTableHeight() {
1038
+ const totalHeight = getInnerHeight(this.$el);
1039
+ const titleHeight = getFullHeight(this.$refs.title);
1040
+ const quickHeight = getFullHeight(this.$refs.quick);
1041
+ const operationHeight = getFullHeight(this.$refs.operation);
1042
+ const dynamicHeight = getFullHeight(this.$refs.dynamic);
1043
+ const paginationHeight = getFullHeight(this.$refs.pagination);
1044
+ this.tableFlexHeight = totalHeight - titleHeight - quickHeight - operationHeight - dynamicHeight - paginationHeight - 2;
1045
+ // console.log(`totalHeight: ${totalHeight}`)
1046
+ // console.log(`titleHeight: ${titleHeight}`)
1047
+ // console.log(`quickHeight: ${quickHeight}`)
1048
+ // console.log(`operationHeight: ${operationHeight}`)
1049
+ // console.log(`dynamicHeight: ${dynamicHeight}`)
1050
+ // console.log(`paginationHeight: ${paginationHeight}`)
1051
+ // console.log(`tableFlexHeight: ${this.tableFlexHeight}`)
1052
+ },
1053
+ getBoolVal(boolValOrFun, defaultVal = false) {
1054
+ if (isFunction(boolValOrFun)) {
1055
+ const result = this.executeInContext(boolValOrFun)
1056
+ return isBoolean(result) ? result : defaultVal
1057
+ }
1058
+ if (isBoolean(boolValOrFun)) {
1059
+ return boolValOrFun
1060
+ }
1061
+ return defaultVal
1062
+ },
1063
+ // 使用透传的context作为this执行函数,并传递参数scopeParam
1064
+ executeInContext(fn) {
1065
+ if (!isFunction(fn)) {
1066
+ console.error(`fn is not function: ${fn}`)
1067
+ return
1068
+ }
1069
+ return fn.call(this.option.context, this.scopeParam)
1070
+ },
1071
+ /**
1072
+ * 从缓存中加载搜索数据, 更新quickFilters、easyFilters、dynamicFilters、storedFilters里,以便实现缓存生效
1073
+ */
1074
+ async popStashFilter() {
1075
+ try {
1076
+ const stashFilters = cache.getFromSessionStorage(`CACHE_FILTER:${this.option.id}`)
1077
+ if (util.isEmpty(stashFilters) || !util.isArray(stashFilters)) {
1078
+ return
1079
+ }
1080
+ const extra = this.pageQuery.extra
1081
+ for (const {type, ...config} of stashFilters) {
1082
+ if (type === 'stored') {
1083
+ const {value} = config
1084
+ this.storedLabels = value
1085
+ continue;
1086
+ }
1087
+ if (type === 'extra') {
1088
+ const {value: cachedExtra} = config
1089
+ Object.entries(extra).forEach(([key, value]) => {
1090
+ const cacheValue = cachedExtra[key]
1091
+ if (!util.isEmpty(cacheValue)) {
1092
+ extra[key] = cacheValue
1093
+ }
1094
+ })
1095
+ continue;
1096
+ }
1097
+ const {col, opt, val, disabled} = config
1098
+ if (type === 'quick') {
1099
+ this.quickFilters.filter(f => f.col === col && f.opt === opt).forEach(f => {
1100
+ f.val = val
1101
+ f.disabled = disabled
1102
+ })
1103
+ } else if (type === 'easy') {
1104
+ this.easyFilters.filter(f => f.col === col && f.opt === opt).forEach(f => {
1105
+ f.val = val
1106
+ f.disabled = disabled
1107
+ })
1108
+ } else if (type === 'dynamic') {
1109
+ const {tableColumnComponentName, customConfig} = this.columnConfig[col]
1110
+ const dynamicFilter = buildFinalQueryComponentConfig(customConfig, tableColumnComponentName, 'dynamic', this.option)
1111
+ dynamicFilter.val = val
1112
+ dynamicFilter.disabled = disabled
1113
+ await dynamicFilter.updateCondMsg()
1114
+ this.dynamicFilters.push(dynamicFilter)
1115
+ } else {
1116
+ console.log(`${col}type值不正确:${type}`)
1117
+ }
1118
+ }
1119
+ } catch (err) {
1120
+ console.error(`从缓存中还原筛选条件时出现错误: ${err}`)
1121
+ cache.deleteFromSessionStorage(`CACHE_FILTER:${this.option.id}`)
1122
+ }
1123
+ },
1124
+ /**
1125
+ * 将筛选条件缓存起来
1126
+ */
1127
+ stashFilter() {
1128
+ try {
1129
+ // 将筛选条件缓存: 只存type、col、opt、val、disabled即可
1130
+ const stashFilters = []
1131
+ const callbackFn = (f) => {
1132
+ stashFilters.push({
1133
+ type: f.type,
1134
+ col: f.col,
1135
+ opt: f.opt,
1136
+ val: f.val,
1137
+ disabled: f.disabled,
1138
+ options: f.props.options
1139
+ })
1140
+ }
1141
+ this.quickFilters.filter(f => f.isEffective() && !f.isDefaultVal()).forEach(callbackFn)
1142
+ this.easyFilters.filter(f => f.isEffective() && !f.isDefaultVal()).forEach(callbackFn)
1143
+ this.dynamicFilters.filter(f => f.isEffective()).forEach(callbackFn)
1144
+ // 存筛比较特殊, 数据结构不同
1145
+ if (this.storedLabels.length > 0) {
1146
+ stashFilters.push({type: 'stored', value: this.storedLabels})
1147
+ }
1148
+ // 扩展参数也比较特殊,单独处理
1149
+ const extra = this.pageQuery.extra;
1150
+ if (!util.isEmpty(extra) && Object.keys(extra).some(key => !util.isEmpty(extra[key]))) {
1151
+ // 只缓存有效的自定义参数
1152
+ const effectExtra = Object.fromEntries(
1153
+ Object.entries(extra).filter(([_, value]) => !util.isEmpty(value))
1154
+ );
1155
+ stashFilters.push({type: 'extra', value: effectExtra})
1156
+ }
1157
+
1158
+ if (stashFilters.length > 0) {
1159
+ cache.setToSessionStorage(`CACHE_FILTER:${this.option.id}`, stashFilters)
1160
+ } else {
1161
+ cache.deleteFromSessionStorage(`CACHE_FILTER:${this.option.id}`)
1162
+ }
1163
+ } catch (err) {
1164
+ console.error(`缓存筛选条件时出现错误: ${err}`)
1165
+ }
1166
+ }
1167
+ },
1168
+ beforeUnmount() {
1169
+ // 清理事件监听
1170
+ window.removeEventListener('resize', this.calTableHeight);
1171
+ this.heightObserver.disconnect() // 取消所有监听
1172
+ }
1173
+ }
1174
+ </script>
1175
+
1176
+ <style scoped lang="scss">
1177
+ .fc-fast-table {
1178
+ height: 100%;
1179
+ box-sizing: border-box;
1180
+ padding: 10px;
1181
+ overflow: auto;
1182
+
1183
+ .fc-fast-table-title {
1184
+ font-weight: bold;
1185
+ }
1186
+
1187
+ .fc-quick-filter-wrapper {
1188
+ padding-bottom: 10px;
1189
+ box-sizing: border-box;
1190
+ border-bottom: 1px solid #dfdfdf;
1191
+ margin-bottom: 10px;
1192
+ }
1193
+
1194
+ .fc-fast-table-divider {
1195
+ margin: 0 0 10px 0;
1196
+ }
1197
+
1198
+ .fc-fast-table-operation-bar {
1199
+ margin-bottom: 10px;
1200
+ display: flex;
1201
+ justify-content: space-between;
1202
+ align-items: center;
1203
+ position: relative;
1204
+
1205
+ :deep(.el-button--default) {
1206
+ padding-left: 10px;
1207
+ padding-right: 10px;
1208
+ }
1209
+
1210
+ .fc-operation-filter {
1211
+ display: flex;
1212
+
1213
+ & > :not(:first-child) {
1214
+ margin-left: 5px;
1215
+ }
1216
+
1217
+ .fc-stored-btn-wrapper {
1218
+ margin-left: 10px;
1219
+ }
1220
+ }
1221
+
1222
+ .fc-fast-table-operation-btn {
1223
+ }
1224
+
1225
+ .fc-fast-table-operation-more {
1226
+ margin-left: 10px;
1227
+ }
1228
+ }
1229
+
1230
+ :deep(.el-table__cell) {
1231
+ padding: 0;
1232
+ }
1233
+
1234
+ :deep(td.fc-table-column > .cell) {
1235
+ padding: 0 10px;
1236
+
1237
+ .fc-table-inline-edit-component {
1238
+ width: 100%;
1239
+
1240
+ .el-input__inner {
1241
+ padding: 0 4px;
1242
+ }
1243
+
1244
+ .el-input-number__decrease, .el-input-number__increase {
1245
+ width: 15px;
1246
+ }
1247
+
1248
+ .el-input__prefix {
1249
+ display: none;
1250
+ }
1251
+
1252
+ input {
1253
+ text-align: left;
1254
+ }
1255
+
1256
+ .el-upload-list--picture-card .el-upload-list__item, .el-upload--picture-card {
1257
+ width: auto;
1258
+ height: 100%;
1259
+ aspect-ratio: 1 / 1;
1260
+ line-height: 100%;
1261
+ margin: 0;
1262
+
1263
+ & .el-icon-plus {
1264
+ $uploadIconSize: 18px;
1265
+ font-size: $uploadIconSize;
1266
+ width: $uploadIconSize;
1267
+ height: $uploadIconSize;
1268
+ margin-top: calc(50% - $uploadIconSize / 2);
1269
+ }
1270
+ }
1271
+
1272
+ .el-upload-list--text {
1273
+ .el-upload-list__item {
1274
+ margin: 0;
1275
+ line-height: 1;
1276
+
1277
+ & > * {
1278
+ display: inline;
1279
+ }
1280
+ }
1281
+ }
1282
+ }
1283
+
1284
+ .fc-table-inline-edit-component.fc-valid-error {
1285
+ border: 1px dashed #F56C6C;
1286
+ }
1287
+
1288
+ .el-upload-list__item {
1289
+ transition: none !important; // 防止内部FastUpload因数据刷新而跳动
1290
+ }
1291
+ }
1292
+
1293
+ .fc-pagination-wrapper {
1294
+ display: flex;
1295
+ margin-top: 3px;
1296
+ justify-content: space-between;
1297
+
1298
+ .fc-footer-wrapper {
1299
+ display: flex;
1300
+ align-items: center;
1301
+
1302
+ .fc-check-tip {
1303
+ display: flex;
1304
+ margin-right: 10px;
1305
+ }
1306
+ }
1307
+ }
1308
+ }
1309
+ </style>