@jzt-packages/components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +68 -0
- package/src/JztBackTop/index.vue +255 -0
- package/src/JztButtonList/index.vue +88 -0
- package/src/JztChart/index.vue +95 -0
- package/src/JztCharts/index.vue +317 -0
- package/src/JztClassTabs/index.vue +156 -0
- package/src/JztDateSelect/dateSelect.vue +186 -0
- package/src/JztDateSelect/dateType.vue +54 -0
- package/src/JztDateSelect/index.ts +135 -0
- package/src/JztDateSelect/interface/index.ts +13 -0
- package/src/JztDialog/index.vue +249 -0
- package/src/JztEllipsisTooltip/index.vue +61 -0
- package/src/JztEmpty/index.vue +45 -0
- package/src/JztErrorPage/403.vue +30 -0
- package/src/JztErrorPage/404.vue +19 -0
- package/src/JztErrorPage/500.vue +18 -0
- package/src/JztErrorPage/assets/401.png +0 -0
- package/src/JztErrorPage/assets/403.png +0 -0
- package/src/JztErrorPage/assets/404.png +0 -0
- package/src/JztErrorPage/assets/500.png +0 -0
- package/src/JztErrorPage/index.scss +35 -0
- package/src/JztErrorPage/index.vue +35 -0
- package/src/JztFilePreview/components/pdfViewer.vue +221 -0
- package/src/JztFilePreview/hooks/useImageMethod.ts +256 -0
- package/src/JztFilePreview/index.scss +171 -0
- package/src/JztFilePreview/index.vue +68 -0
- package/src/JztFilePreview/interface/index.ts +18 -0
- package/src/JztFilePreview/previewFile.vue +371 -0
- package/src/JztFormGrid/README.md +520 -0
- package/src/JztFormGrid/components/formItem.vue +209 -0
- package/src/JztFormGrid/components/formItemValue.vue +384 -0
- package/src/JztFormGrid/components/showDetailForm.vue +172 -0
- package/src/JztFormGrid/index.scss +60 -0
- package/src/JztFormGrid/index.vue +513 -0
- package/src/JztFormGrid/interface/index.ts +106 -0
- package/src/JztGrid/components/GridItem.vue +68 -0
- package/src/JztGrid/index.vue +179 -0
- package/src/JztGrid/interface/index.ts +6 -0
- package/src/JztImportExcel/assets/delete.png +0 -0
- package/src/JztImportExcel/index.scss +46 -0
- package/src/JztImportExcel/index.vue +430 -0
- package/src/JztImportExcel/interface/index.ts +25 -0
- package/src/JztLabelTitle/index.vue +65 -0
- package/src/JztLeftRightMode/components/CollapseButton.vue +80 -0
- package/src/JztLeftRightMode/components/LeftCard.vue +203 -0
- package/src/JztLeftRightMode/components/LeftLayout.vue +173 -0
- package/src/JztLeftRightMode/components/RightHeader.vue +186 -0
- package/src/JztLeftRightMode/components/RightLayout.vue +235 -0
- package/src/JztLeftRightMode/components/RightTableHeader.vue +43 -0
- package/src/JztLeftRightMode/hooks/useCollapse.ts +17 -0
- package/src/JztLeftRightMode/hooks/useDefaultProps.ts +19 -0
- package/src/JztLeftRightMode/hooks/useLeftLayout.ts +201 -0
- package/src/JztLeftRightMode/hooks/useMode.ts +20 -0
- package/src/JztLeftRightMode/hooks/usePrevNext.ts +60 -0
- package/src/JztLeftRightMode/hooks/useRightLayout.ts +215 -0
- package/src/JztLeftRightMode/hooks/useSlots.ts +15 -0
- package/src/JztLeftRightMode/index.ts +3 -0
- package/src/JztLeftRightMode/index.vue +494 -0
- package/src/JztLeftRightMode/types/index.ts +457 -0
- package/src/JztLoading/fullScreen.ts +45 -0
- package/src/JztLoading/index.scss +67 -0
- package/src/JztLoading/index.vue +18 -0
- package/src/JztLogin/components/LoginFooter.vue +17 -0
- package/src/JztLogin/components/LoginForm.vue +99 -0
- package/src/JztLogin/hooks/useLogin.ts +186 -0
- package/src/JztLogin/index.scss +142 -0
- package/src/JztLogin/index.vue +31 -0
- package/src/JztLogin/interface/index.ts +47 -0
- package/src/JztNumericalRange/index.vue +81 -0
- package/src/JztPageCard/comm/datePicker.vue +151 -0
- package/src/JztPageCard/comm/details.vue +60 -0
- package/src/JztPageCard/comm/export.vue +24 -0
- package/src/JztPageCard/comm/tabs.vue +94 -0
- package/src/JztPageCard/comm/tooltip.vue +31 -0
- package/src/JztPageCard/index.vue +287 -0
- package/src/JztPagination/index.vue +70 -0
- package/src/JztProductInfo/components/imagePreview.vue +275 -0
- package/src/JztProductInfo/components/qxUnique.vue +101 -0
- package/src/JztProductInfo/components/records.vue +265 -0
- package/src/JztProductInfo/hooks/useParams.ts +143 -0
- package/src/JztProductInfo/hooks/useQxUnique.tsx +466 -0
- package/src/JztProductInfo/images/defaultProduct.png +0 -0
- package/src/JztProductInfo/index.ts +116 -0
- package/src/JztProductInfo/index.vue +108 -0
- package/src/JztProductInfo/interface/index.ts +15 -0
- package/src/JztQueryDetailTable/index.scss +100 -0
- package/src/JztQueryDetailTable/index.vue +400 -0
- package/src/JztQueryDetailTable/interface/index.ts +10 -0
- package/src/JztQueryTable/QueryTable /345/212/237/350/203/275.md" +1580 -0
- package/src/JztQueryTable/README.md +567 -0
- package/src/JztQueryTable/components/ColSetting.vue +67 -0
- package/src/JztQueryTable/components/ColumnsSetting.vue +404 -0
- package/src/JztQueryTable/components/ColumnsSetting1.vue +220 -0
- package/src/JztQueryTable/components/DeployToAccountLevelSetting.vue +351 -0
- package/src/JztQueryTable/components/Pagination.vue +54 -0
- package/src/JztQueryTable/components/TableColumn.vue +109 -0
- package/src/JztQueryTable/const.ts +1 -0
- package/src/JztQueryTable/hooks/useQueryTable.ts +194 -0
- package/src/JztQueryTable/hooks/useSelection.ts +47 -0
- package/src/JztQueryTable/hooks/useTableSetting.ts +197 -0
- package/src/JztQueryTable/hooks/useTemplate.ts +127 -0
- package/src/JztQueryTable/index.scss +91 -0
- package/src/JztQueryTable/index.vue +1445 -0
- package/src/JztQueryTable/interface/index.ts +185 -0
- package/src/JztRegionSelect/index.vue +134 -0
- package/src/JztSearchForm/components/SearchFormItem.vue +473 -0
- package/src/JztSearchForm/index.vue +530 -0
- package/src/JztSearchForm/interface/index.ts +100 -0
- package/src/JztSelectFilter/index.scss +63 -0
- package/src/JztSelectFilter/index.vue +110 -0
- package/src/JztSelectTable/index.vue +257 -0
- package/src/JztTable/index.scss +72 -0
- package/src/JztTable/index.vue +353 -0
- package/src/JztTable/interface/index.ts +1 -0
- package/src/JztTime/comm/agencySelect.vue +112 -0
- package/src/JztTime/comm/collapseRow.vue +132 -0
- package/src/JztTime/comm/dateSelect.vue +292 -0
- package/src/JztTime/comm/deptSelect.vue +193 -0
- package/src/JztTime/comm/typeSelect.vue +97 -0
- package/src/JztTime/index.ts +216 -0
- package/src/JztTime/index.vue +303 -0
- package/src/JztTime/interface/index.ts +23 -0
- package/src/JztTreeFilter/index.scss +44 -0
- package/src/JztTreeFilter/index.vue +177 -0
- package/src/JztUploadFile/interface/index.ts +21 -0
- package/src/JztUploadFile/multiple.scss +215 -0
- package/src/JztUploadFile/multiple.vue +318 -0
- package/src/JztUploadFile/single.scss +226 -0
- package/src/JztUploadFile/single.vue +274 -0
- package/src/JztUploadImg/Img.vue +294 -0
- package/src/JztUploadImg/Imgs.vue +411 -0
- package/src/JztUploadImg/index.scss +138 -0
- package/src/JztUploadImg/interface/index.ts +22 -0
- package/src/SelectIcon/index.scss +39 -0
- package/src/SelectIcon/index.vue +106 -0
- package/src/SvgIcon/index.vue +22 -0
- package/src/hooks/useAuthButtons.ts +58 -0
- package/src/hooks/useFormByUserType.ts +90 -0
- package/src/hooks/useTableEvents.ts +30 -0
- package/src/hooks/useUploadFileHook.ts +262 -0
- package/src/index.ts +91 -0
- package/src/typings/global.d.ts +101 -0
- package/src/utils/index.ts +107 -0
- package/src/utils/tree.ts +57 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,1445 @@
|
|
|
1
|
+
<!-- 📚📚📚 Pro-Table 文档: https://juejin.cn/post/7166068828202336263 -->
|
|
2
|
+
|
|
3
|
+
<template>
|
|
4
|
+
<div class="jzt-query-page">
|
|
5
|
+
<!-- 查询表单 -->
|
|
6
|
+
<JztSearchForm
|
|
7
|
+
ref="searchFormRef"
|
|
8
|
+
v-show="isShowSearch"
|
|
9
|
+
:search="_search"
|
|
10
|
+
@setFirstValue="setFirstValue"
|
|
11
|
+
:reset="_reset"
|
|
12
|
+
:customForm="customForm"
|
|
13
|
+
:allow-collapsed="allowCollapsed"
|
|
14
|
+
:collapsed="collapsed"
|
|
15
|
+
:query-items="searchColumns"
|
|
16
|
+
:search-param="searchParam"
|
|
17
|
+
:show-param="initParam"
|
|
18
|
+
:search-init-param="searchInitParam"
|
|
19
|
+
:search-col="searchCol"
|
|
20
|
+
:show-query-btn="showQueryBtn"
|
|
21
|
+
:show-reset-btn="showResetBtn"
|
|
22
|
+
:suffix="btnSuffix"
|
|
23
|
+
:is-card="searchCard"
|
|
24
|
+
:valid-query="validQuery"
|
|
25
|
+
>
|
|
26
|
+
<template #rightBtn>
|
|
27
|
+
<div v-if="!!slots.rightBtn" class="mr5 flx-left-center" style="height: 100%">
|
|
28
|
+
<slot name="rightBtn">
|
|
29
|
+
<!-- <el-button v-if="showQueryBtn" type="primary" @click="_search">搜索</el-button>
|
|
30
|
+
<el-button v-if="showQueryBtn" plain @click="reset">重置</el-button> -->
|
|
31
|
+
</slot>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
<template #formTopSlot>
|
|
35
|
+
<slot name="formTopSlot"></slot>
|
|
36
|
+
<el-tabs
|
|
37
|
+
v-if="tabIsTop && tabsList && tabsList.length"
|
|
38
|
+
v-model="tabActiveValue"
|
|
39
|
+
class="table-tabs mb10"
|
|
40
|
+
style="margin-top: -8px"
|
|
41
|
+
:class="tabType ? tabsClass?.[tabType] : 'spd-line-card'"
|
|
42
|
+
:type="tabType"
|
|
43
|
+
@tab-change="handleTabChange"
|
|
44
|
+
>
|
|
45
|
+
<el-tab-pane
|
|
46
|
+
v-for="tab in tabsList"
|
|
47
|
+
:label="tab[tabsProps?.label || 'label']"
|
|
48
|
+
:name="tab[tabsProps?.value || 'value']"
|
|
49
|
+
></el-tab-pane>
|
|
50
|
+
</el-tabs>
|
|
51
|
+
</template>
|
|
52
|
+
</JztSearchForm>
|
|
53
|
+
<slot>
|
|
54
|
+
<!-- 表格主体 -->
|
|
55
|
+
<div
|
|
56
|
+
v-loading="displayLoading"
|
|
57
|
+
:class="['table-main', isFullscreen ? 'fullscreen-page' : '', tableCard ? 'spd-card' : 'noCardGap']"
|
|
58
|
+
>
|
|
59
|
+
<slot name="tableTops" />
|
|
60
|
+
<slot name="queryTableMain">
|
|
61
|
+
<el-tabs
|
|
62
|
+
v-if="!tabIsTop && tabsList && tabsList.length"
|
|
63
|
+
v-model="tabActiveValue"
|
|
64
|
+
class="table-tabs"
|
|
65
|
+
:class="tabType ? tabsClass?.[tabType] : 'spd-line-card'"
|
|
66
|
+
:type="tabType"
|
|
67
|
+
@tab-change="handleTabChange"
|
|
68
|
+
>
|
|
69
|
+
<el-tab-pane
|
|
70
|
+
v-for="tab in tabsList"
|
|
71
|
+
:label="tab[tabsProps?.label || 'label']"
|
|
72
|
+
:name="tab[tabsProps?.value || 'value']"
|
|
73
|
+
>
|
|
74
|
+
<template #label>
|
|
75
|
+
<span class="custom-tabs-label flx-left-center">
|
|
76
|
+
<component v-if="tab?.leftRender" :is="tab?.leftRender" />
|
|
77
|
+
<span>{{ tab[tabsProps?.label || 'label'] }}</span>
|
|
78
|
+
<span v-if="!isEmpty(tab?.tabCount)">
|
|
79
|
+
({{ tabsProps?.isFold && Number(tab?.tabCount) > 99 ? '99+' : tab.tabCount }})
|
|
80
|
+
</span>
|
|
81
|
+
<component v-if="tab?.rightRender" :is="tab?.rightRender" />
|
|
82
|
+
</span>
|
|
83
|
+
</template>
|
|
84
|
+
</el-tab-pane>
|
|
85
|
+
</el-tabs>
|
|
86
|
+
<!-- <slot name="btnTop" /> -->
|
|
87
|
+
<!-- 表格头部 操作按钮 -->
|
|
88
|
+
<div class="table-header flx-between-top" v-if="(topList && topList.length) || hasSlotContent || toolButton">
|
|
89
|
+
<slot name="headerTitle" />
|
|
90
|
+
<div class="header-button-lf">
|
|
91
|
+
<div class="flx-left-center flx-wrap p2">
|
|
92
|
+
<slot
|
|
93
|
+
name="toolBtnLeft"
|
|
94
|
+
:selected-list="selectedList"
|
|
95
|
+
:selected-list-ids="selectedListIds"
|
|
96
|
+
:is-selected="isSelected"
|
|
97
|
+
/>
|
|
98
|
+
<JztButtonList
|
|
99
|
+
v-if="topList && topList.length"
|
|
100
|
+
:buttonList="topList"
|
|
101
|
+
@onClick="handleButtonClick"
|
|
102
|
+
></JztButtonList>
|
|
103
|
+
<slot
|
|
104
|
+
name="toolBtnRight"
|
|
105
|
+
:selected-list="selectedList"
|
|
106
|
+
:selected-list-ids="selectedListIds"
|
|
107
|
+
:is-selected="isSelected"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
<div v-if="toolButton" class="header-button-ri">
|
|
112
|
+
<slot
|
|
113
|
+
name="settingBtnLeft"
|
|
114
|
+
:selected-list="selectedList"
|
|
115
|
+
:selected-list-ids="selectedListIds"
|
|
116
|
+
:is-selected="isSelected"
|
|
117
|
+
/>
|
|
118
|
+
<slot
|
|
119
|
+
name="toolButton"
|
|
120
|
+
:selected-list="selectedList"
|
|
121
|
+
:selected-list-ids="selectedListIds"
|
|
122
|
+
:is-selected="isSelected"
|
|
123
|
+
>
|
|
124
|
+
<slot
|
|
125
|
+
name="toolLeft"
|
|
126
|
+
:selected-list="selectedList"
|
|
127
|
+
:selected-list-ids="selectedListIds"
|
|
128
|
+
:is-selected="isSelected"
|
|
129
|
+
/>
|
|
130
|
+
<el-button
|
|
131
|
+
v-if="showToolButton('refresh') && requestApi"
|
|
132
|
+
:icon="Refresh"
|
|
133
|
+
:icon-size="16"
|
|
134
|
+
title="刷新"
|
|
135
|
+
class="tool_btn grey-button"
|
|
136
|
+
plain
|
|
137
|
+
@click="getTableList"
|
|
138
|
+
/>
|
|
139
|
+
<el-button
|
|
140
|
+
v-if="showToolButton('fullScreen')"
|
|
141
|
+
:title="isFullscreen ? '退出全屏' : '全屏'"
|
|
142
|
+
class="tool_btn grey-button"
|
|
143
|
+
plain
|
|
144
|
+
@click="isFullscreen = !isFullscreen"
|
|
145
|
+
>
|
|
146
|
+
<i :class="['iconfont', isFullscreen ? 'icon-shiyingchuangkou' : 'icon-manping']"></i>
|
|
147
|
+
</el-button>
|
|
148
|
+
<el-button
|
|
149
|
+
v-if="showToolButton('setting') && tableColumns.length"
|
|
150
|
+
title="列配置"
|
|
151
|
+
:icon="Setting"
|
|
152
|
+
class="tool_btn grey-button"
|
|
153
|
+
plain
|
|
154
|
+
@click.native.stop="changeDialog"
|
|
155
|
+
/>
|
|
156
|
+
<!-- <ColumnsSetting
|
|
157
|
+
v-if="showToolButton('setting') && tableColumns.length"
|
|
158
|
+
v-model:columns="tableColumns"
|
|
159
|
+
:column-types="columnTypes"
|
|
160
|
+
v-model:loading="loading1"
|
|
161
|
+
@refreshSetting="getSettingConfig"
|
|
162
|
+
>
|
|
163
|
+
<template #reference>
|
|
164
|
+
<el-button title="列设置" :icon="Setting" class="tool_btn" />
|
|
165
|
+
</template>
|
|
166
|
+
</ColumnsSetting> -->
|
|
167
|
+
<!-- <el-button
|
|
168
|
+
v-if="showToolButton('search') && searchColumns?.length"
|
|
169
|
+
title="显示/隐藏查询"
|
|
170
|
+
class="tool_btn"
|
|
171
|
+
:icon="Search"
|
|
172
|
+
@click="isShowSearch = !isShowSearch"
|
|
173
|
+
/> -->
|
|
174
|
+
</slot>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<!-- 表格上方插槽 -->
|
|
178
|
+
<slot
|
|
179
|
+
name="tableHeader"
|
|
180
|
+
:selected-list="selectedList"
|
|
181
|
+
:selected-list-ids="selectedListIds"
|
|
182
|
+
:is-selected="isSelected"
|
|
183
|
+
/>
|
|
184
|
+
<!-- <div style="padding: 0 10px; height: 100%"> -->
|
|
185
|
+
<!-- 表格主体 -->
|
|
186
|
+
|
|
187
|
+
<el-table
|
|
188
|
+
v-if="isShow"
|
|
189
|
+
ref="tableRef"
|
|
190
|
+
v-bind="$attrs"
|
|
191
|
+
:id="uuid"
|
|
192
|
+
:row-class-name="
|
|
193
|
+
({ row, rowIndex }) => {
|
|
194
|
+
// 处理 rowClassName 为函数的情况
|
|
195
|
+
if (typeof rowClassName === 'function') {
|
|
196
|
+
return rowClassName({ row, rowIndex })
|
|
197
|
+
}
|
|
198
|
+
const isSelected = rowKey && radio && radio === row[rowKey]
|
|
199
|
+
// 处理 rowClassName 为字符串的情况
|
|
200
|
+
return isSelected ? rowClassName || 'highlight-current-row' : ''
|
|
201
|
+
}
|
|
202
|
+
"
|
|
203
|
+
:data="processTableData"
|
|
204
|
+
:default-expand-all="isExpandAll"
|
|
205
|
+
:border="border"
|
|
206
|
+
:row-key="rowKey"
|
|
207
|
+
:class="[
|
|
208
|
+
addTableNoEmpty && (!processTableData || !processTableData.length) ? 'noDataEmpty' : '',
|
|
209
|
+
addTableNoEmpty ? 'tableNoHeight' : ''
|
|
210
|
+
]"
|
|
211
|
+
@cell-click="handleCellClick"
|
|
212
|
+
@selection-change="tableSelectionChange"
|
|
213
|
+
@header-dragend="headerDragend"
|
|
214
|
+
@row-click="handleRowClick"
|
|
215
|
+
>
|
|
216
|
+
<!-- 默认插槽 -->
|
|
217
|
+
<slot />
|
|
218
|
+
|
|
219
|
+
<template
|
|
220
|
+
v-for="item in typeof columnConfig?.layout === 'string'
|
|
221
|
+
? columnConfig?.layout?.split?.(',')
|
|
222
|
+
: columnConfig?.layout"
|
|
223
|
+
:key="item"
|
|
224
|
+
>
|
|
225
|
+
<el-table-column
|
|
226
|
+
v-if="columnTypes.includes(item?.trim?.() as TypeProps)"
|
|
227
|
+
v-bind="{ type: item, ...(columnConfig?.[`${item}Props`] || {}) }"
|
|
228
|
+
:width="columnConfig?.[`${item}Props`]?.width ?? (['expand', 'sort'].includes(item) ? 100 : 80)"
|
|
229
|
+
:align="columnConfig?.[`${item}Props`]?.align ?? 'center'"
|
|
230
|
+
:reserve-selection="
|
|
231
|
+
columnConfig?.[`${item}Props`].reserveSelection ?? columnConfig?.[`${item}Props`]?.type == 'selection'
|
|
232
|
+
"
|
|
233
|
+
>
|
|
234
|
+
<template #default="scope">
|
|
235
|
+
<template v-if="item.includes('expand')">
|
|
236
|
+
<component
|
|
237
|
+
:is="columnConfig?.expandProps?.render"
|
|
238
|
+
v-bind="scope"
|
|
239
|
+
v-if="columnConfig?.expandProps?.render"
|
|
240
|
+
/>
|
|
241
|
+
<slot v-else :name="columnConfig?.expandProps?.type" v-bind="scope" />
|
|
242
|
+
</template>
|
|
243
|
+
<el-radio v-if="item.includes('radio')" v-model="radio" :label="scope.row[rowKey]">
|
|
244
|
+
<i></i>
|
|
245
|
+
</el-radio>
|
|
246
|
+
<el-tag v-if="item.includes('sort')" class="move">
|
|
247
|
+
<el-icon><DCaret /></el-icon>
|
|
248
|
+
</el-tag>
|
|
249
|
+
</template>
|
|
250
|
+
</el-table-column>
|
|
251
|
+
</template>
|
|
252
|
+
<template v-for="(item, index) in selectColumns" :key="item">
|
|
253
|
+
<el-table-column
|
|
254
|
+
v-if="item.type && columnTypes.includes(item.type)"
|
|
255
|
+
v-bind="item"
|
|
256
|
+
:align="item.align ?? 'center'"
|
|
257
|
+
:reserve-selection="item.reserveSelection ?? item.type == 'selection'"
|
|
258
|
+
:selectable="item.type === 'selection' && item?.selectable ? item?.selectable : () => true"
|
|
259
|
+
>
|
|
260
|
+
<template #default="scope">
|
|
261
|
+
<!-- <div style="width: 100%; height: 100%"> -->
|
|
262
|
+
<template v-if="item.type == 'expand'">
|
|
263
|
+
<component :is="item.render" v-bind="scope" v-if="item.render" />
|
|
264
|
+
<slot v-else :name="item.type" v-bind="scope" />
|
|
265
|
+
</template>
|
|
266
|
+
<el-radio
|
|
267
|
+
v-if="item.type == 'radio'"
|
|
268
|
+
v-model="radio"
|
|
269
|
+
:name="'radio' + index"
|
|
270
|
+
:label="scope.row[rowKey]"
|
|
271
|
+
@change="setRadioItem(scope.row[rowKey], scope.row, item)"
|
|
272
|
+
>
|
|
273
|
+
<i></i>
|
|
274
|
+
</el-radio>
|
|
275
|
+
<el-tag v-if="item.type == 'sort'" type="primary" class="move" size="small">
|
|
276
|
+
<i class="iconfont icon-tuozhuai fontS-20"></i>
|
|
277
|
+
</el-tag>
|
|
278
|
+
<!-- </div> -->
|
|
279
|
+
</template>
|
|
280
|
+
</el-table-column>
|
|
281
|
+
</template>
|
|
282
|
+
<template v-for="item in tableColumns" :key="item">
|
|
283
|
+
<!-- other -->
|
|
284
|
+
<TableColumn v-if="item.prop !== 'operation' && item.isShow && !item.hidden" :column="item">
|
|
285
|
+
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
|
|
286
|
+
<slot :name="slot" v-bind="scope" />
|
|
287
|
+
</template>
|
|
288
|
+
</TableColumn>
|
|
289
|
+
<!-- 自定义表格操作列 -->
|
|
290
|
+
</template>
|
|
291
|
+
<!-- 自定义表格操作列 -->
|
|
292
|
+
<template v-for="(item, index) in operationColumns">
|
|
293
|
+
<el-table-column v-if="item.isShow !== false" v-bind="item" :align="item.align ?? 'left'">
|
|
294
|
+
<template #default="{ row }">
|
|
295
|
+
<template v-for="(btnItem, index) in item.btnLList" :key="index">
|
|
296
|
+
<span v-if="hasPermissionFn(btnItem, row)">
|
|
297
|
+
<component v-if="btnItem.render" :is="btnItem.render" v-bind="row" style="margin: 0 12px" />
|
|
298
|
+
<span v-else-if="btnItem.slotName">
|
|
299
|
+
<slot :name="btnItem.slotName" v-bind="row" />
|
|
300
|
+
</span>
|
|
301
|
+
<el-button
|
|
302
|
+
v-else
|
|
303
|
+
@click="btnItem.fun && tableOperationFn(btnItem, row)"
|
|
304
|
+
:disabled="btnItem.disabledFn && btnItem.disabledFn(row)"
|
|
305
|
+
:link="btnItem.link === false ? false : true"
|
|
306
|
+
:type="btnItem.type || 'primary'"
|
|
307
|
+
:icon="btnItem.icon || ''"
|
|
308
|
+
v-bind="(btnItem as any).$attr"
|
|
309
|
+
style="margin: 0 6px"
|
|
310
|
+
>
|
|
311
|
+
<span>{{ btnItem.text }}</span>
|
|
312
|
+
</el-button>
|
|
313
|
+
</span>
|
|
314
|
+
</template>
|
|
315
|
+
</template>
|
|
316
|
+
</el-table-column>
|
|
317
|
+
</template>
|
|
318
|
+
<!-- 插入表格最后一行之后的插槽 -->
|
|
319
|
+
<template #append>
|
|
320
|
+
<slot name="append" />
|
|
321
|
+
</template>
|
|
322
|
+
<!-- 无数据 -->
|
|
323
|
+
<template #empty>
|
|
324
|
+
<div class="table-empty">
|
|
325
|
+
<slot name="empty">
|
|
326
|
+
<JztEmpty :emptyText="emptyText" />
|
|
327
|
+
</slot>
|
|
328
|
+
<slot name="empty-desc"></slot>
|
|
329
|
+
</div>
|
|
330
|
+
</template>
|
|
331
|
+
</el-table>
|
|
332
|
+
<!-- 新增按钮放在这个tableBottom插槽,class使用jzt-table-add-btn 就可以 -->
|
|
333
|
+
<slot name="tableBottom" />
|
|
334
|
+
<!-- 分页组件 -->
|
|
335
|
+
<slot name="pagination">
|
|
336
|
+
<Pagination
|
|
337
|
+
v-if="pagination"
|
|
338
|
+
:pageable="pageable"
|
|
339
|
+
:show-check-total="showCheckTotal"
|
|
340
|
+
:checkTotal="selectedListIds.length || 0"
|
|
341
|
+
:handle-size-change="handleSizeChange"
|
|
342
|
+
:handle-current-change="handleCurrentChange"
|
|
343
|
+
/>
|
|
344
|
+
</slot>
|
|
345
|
+
</slot>
|
|
346
|
+
<slot name="pageFooter" :loading="displayLoading" :pageable="pageable" />
|
|
347
|
+
</div>
|
|
348
|
+
</slot>
|
|
349
|
+
</div>
|
|
350
|
+
<teleport to="body">
|
|
351
|
+
<ColumnsSetting
|
|
352
|
+
ref="columnsSettingRef"
|
|
353
|
+
v-if="showToolButton('setting') && tableColumns.length && gridParams1?.url"
|
|
354
|
+
v-model:columns="tableColumns"
|
|
355
|
+
:column-types="columnTypes"
|
|
356
|
+
:grid-params="gridParams1"
|
|
357
|
+
v-model:loading="loading1"
|
|
358
|
+
@refresh-column-table="refreshColumnTable"
|
|
359
|
+
@changeColumns="changeColumns"
|
|
360
|
+
></ColumnsSetting>
|
|
361
|
+
</teleport>
|
|
362
|
+
</template>
|
|
363
|
+
|
|
364
|
+
<script setup lang="ts" name="ProTable">
|
|
365
|
+
import { Refresh, Setting } from '@element-plus/icons-vue'
|
|
366
|
+
import { isEmpty, removeEmpty, hasPermission } from '@jzt-spd/utils'
|
|
367
|
+
import { GUIDNoneValue } from '@jzt-spd/utils/consts'
|
|
368
|
+
import { ElTable, ElTableColumn } from 'element-plus'
|
|
369
|
+
import * as _ from 'lodash-es'
|
|
370
|
+
import { cloneDeep, debounce } from 'lodash-es'
|
|
371
|
+
import Sortable from 'sortablejs'
|
|
372
|
+
import { computed, onBeforeUnmount, onMounted, provide, ref, unref, useAttrs, useSlots, watch } from 'vue'
|
|
373
|
+
// import { hasPermission } from '../hooks/useAuthButtons'
|
|
374
|
+
// import { localGet, isEmpty } from '@jzt-spd/utils'
|
|
375
|
+
import { useFormSetByUserType } from '../hooks/useFormByUserType'
|
|
376
|
+
import { useTableEvents } from '../hooks/useTableEvents'
|
|
377
|
+
import JztButtonList from '../JztButtonList/index.vue'
|
|
378
|
+
import JztEmpty from '../JztEmpty/index.vue'
|
|
379
|
+
import { BreakPoint } from '../JztGrid/interface'
|
|
380
|
+
import JztSearchForm from '../JztSearchForm/index.vue'
|
|
381
|
+
import { generateUUID, handleProp } from '../utils'
|
|
382
|
+
import ColumnsSetting from './components/ColumnsSetting.vue'
|
|
383
|
+
import Pagination from './components/Pagination.vue'
|
|
384
|
+
import TableColumn from './components/TableColumn.vue'
|
|
385
|
+
import { useOperation, useTable } from './hooks/useQueryTable'
|
|
386
|
+
import { useSelection } from './hooks/useSelection'
|
|
387
|
+
import {
|
|
388
|
+
ColumnConfigProps,
|
|
389
|
+
ColumnProps,
|
|
390
|
+
GridParams,
|
|
391
|
+
TableConfigProps,
|
|
392
|
+
tabsListProps,
|
|
393
|
+
TabsProps,
|
|
394
|
+
TopButtonProps,
|
|
395
|
+
TypeProps
|
|
396
|
+
} from './interface'
|
|
397
|
+
// const tabActive = ref(0)
|
|
398
|
+
export interface ProTableProps {
|
|
399
|
+
tableLoading?: boolean // 表格加载中
|
|
400
|
+
tabActiveName?: string | number // tabs绑定值的key
|
|
401
|
+
tabActive?: string | number // tabs绑定值
|
|
402
|
+
tabsList?: tabsListProps[] //tablist list数据
|
|
403
|
+
tabsProps?: TabsProps // tablist 配置项
|
|
404
|
+
tableConfig: TableConfigProps // 列配置项 ==> 必传
|
|
405
|
+
columnConfig?: ColumnConfigProps // 列多选,显示序号,单选等配置项 非必传
|
|
406
|
+
// columns: ColumnProps[]; // 列配置项 ==> 必传
|
|
407
|
+
data?: any[] // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
|
|
408
|
+
requestApi?: (params: any) => Promise<any> // 请求表格数据的 api ==> 非必传
|
|
409
|
+
requestAuto?: boolean // 是否自动执行请求 api ==> 非必传(默认为true)
|
|
410
|
+
requestError?: (params: any) => void // 表格 api 请求错误监听 ==> 非必传
|
|
411
|
+
dataCallback?: (data: any) => any // 返回数据的回调函数,可以对数据进行处理 ==> 非必传
|
|
412
|
+
title?: string // 表格标题 ==> 非必传
|
|
413
|
+
pagination?: boolean // 是否需要分页组件 ==> 非必传(默认为true)
|
|
414
|
+
initParam?: any // 初始化请求参数 ==> 非必传(默认为{})
|
|
415
|
+
border?: boolean // 是否带有纵向边框 ==> 非必传(默认为true)
|
|
416
|
+
toolButton?: ('refresh' | 'setting' | 'search' | 'fullScreen')[] | boolean // 是否显示表格功能按钮 ==> 非必传(默认为true)
|
|
417
|
+
rowKey?: string // 行数据的 Key,用来优化 Table 的渲染,当表格数据多选时,所指定的 id ==> 非必传(默认为 id)
|
|
418
|
+
searchCol?: number | Record<BreakPoint, number> // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 3, lg: 4, xl: 5,xxl:6 }
|
|
419
|
+
searchFilter?: (data: any) => Record<string, any> // 查询时处理数据的函数 可对查询前的数据进行编辑后再查询 ==> 非必传
|
|
420
|
+
allowCollapsed?: boolean // 查询表单是否折叠
|
|
421
|
+
collapsed?: boolean // 查询表单默认是否折叠
|
|
422
|
+
tableLoaded?: (data: any) => void // 表格加载完成的回调函数 ==> 非必传
|
|
423
|
+
showQueryBtn?: boolean // 是否展示查询重构按钮
|
|
424
|
+
showResetBtn?: boolean // 是否展示重置按钮
|
|
425
|
+
customForm?: boolean // 是否是默认表单(可设置labelWidth、search.width),并且不会随着屏幕变化
|
|
426
|
+
btnSuffix?: boolean // 按钮是否总是在最后(不跟随)
|
|
427
|
+
gridParams?: GridParams // 列配置 gridId url toolButton 开启列配置必须传递
|
|
428
|
+
tabType?: '' | 'card' | 'border-card' // el-tabs 的type
|
|
429
|
+
tableCard?: boolean // 是需要card样式,某下弹框不需要的话 设置false
|
|
430
|
+
searchCard?: boolean // 是需要card样式,某下弹框不需要的话 设置false
|
|
431
|
+
addTableNoEmpty?: boolean // 添加表格时,是否需要置空图
|
|
432
|
+
showCheckTotal?: boolean // 是否显示表格合计
|
|
433
|
+
validQuery?: boolean // 是否校验查询条件
|
|
434
|
+
tabIsTop?: boolean // tab是否在顶部
|
|
435
|
+
rowClassName?: Function | string // 行样式ClassName
|
|
436
|
+
emptyText?: string // 置空图文案
|
|
437
|
+
searchClear?: boolean // 是否显示查询清空选中
|
|
438
|
+
minHeight?: string | boolean // 表格最小高度
|
|
439
|
+
tabChangeSearch?: boolean // tab change 自动请求
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// 自定义 el-tabs 样式 类名
|
|
443
|
+
const tabsClass = {
|
|
444
|
+
card: 'spd-card-tabs',
|
|
445
|
+
'border-card': 'spd-border-card-tabs'
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// 接受父组件参数,配置默认值
|
|
449
|
+
const props = withDefaults(defineProps<ProTableProps>(), {
|
|
450
|
+
tableLoading: false, // 表格加载中
|
|
451
|
+
tabsProps: () => ({
|
|
452
|
+
label: 'label',
|
|
453
|
+
value: 'value',
|
|
454
|
+
isFold: false // 超过99 是否显示99+
|
|
455
|
+
}),
|
|
456
|
+
columns: () => [],
|
|
457
|
+
requestAuto: true,
|
|
458
|
+
pagination: true,
|
|
459
|
+
initParam: () => ({}),
|
|
460
|
+
border: true,
|
|
461
|
+
toolButton: true,
|
|
462
|
+
rowKey: 'id',
|
|
463
|
+
searchCol: () => ({ xs: 1, sm: 2, md: 3, lg: 4, xl: 5, xxl: 6 }),
|
|
464
|
+
allowCollapsed: true,
|
|
465
|
+
collapsed: true,
|
|
466
|
+
showQueryBtn: true,
|
|
467
|
+
showResetBtn: true,
|
|
468
|
+
customForm: false,
|
|
469
|
+
btnSuffix: false,
|
|
470
|
+
tabType: '',
|
|
471
|
+
tableCard: true,
|
|
472
|
+
searchCard: true,
|
|
473
|
+
addTableNoEmpty: false,
|
|
474
|
+
validQuery: false,
|
|
475
|
+
tabIsTop: false,
|
|
476
|
+
emptyText: '暂无内容',
|
|
477
|
+
searchClear: true,
|
|
478
|
+
minHeight: '170px',
|
|
479
|
+
tabChangeSearch: false
|
|
480
|
+
})
|
|
481
|
+
const { useButtonClickFn } = useTableEvents()
|
|
482
|
+
const slots = useSlots()
|
|
483
|
+
const hasSlotContent = computed(() => {
|
|
484
|
+
return !!(slots.toolBtnLeft || slots.toolBtnRight)
|
|
485
|
+
})
|
|
486
|
+
// const tabActiveValue = computed(() => {
|
|
487
|
+
// if (!isEmpty(props.tabActive)) {
|
|
488
|
+
// return props.tabActive
|
|
489
|
+
// } else if (!isEmpty(props.tabActiveName)) {
|
|
490
|
+
// return props.initParam[props.tabActiveName]
|
|
491
|
+
// }
|
|
492
|
+
// })
|
|
493
|
+
const gridParams1 = ref<GridParams>({
|
|
494
|
+
gridId: props.gridParams?.gridId || '',
|
|
495
|
+
url: props.gridParams?.url || ''
|
|
496
|
+
})
|
|
497
|
+
const tabActiveValue = computed({
|
|
498
|
+
get() {
|
|
499
|
+
if (!isEmpty(props.tabActive)) {
|
|
500
|
+
changeTabsSetting(props.tabActive)
|
|
501
|
+
return props.tabActive
|
|
502
|
+
} else if (props.tabActiveName) {
|
|
503
|
+
changeTabsSetting(props.initParam[props.tabActiveName])
|
|
504
|
+
return props.initParam[props.tabActiveName]
|
|
505
|
+
}
|
|
506
|
+
// 修复: 当两个条件都不满足时,返回空字符串避免 undefined
|
|
507
|
+
return ''
|
|
508
|
+
},
|
|
509
|
+
set(val: string) {
|
|
510
|
+
if (!isEmpty(props.tabActiveName) && props.tabActiveName) {
|
|
511
|
+
const params = {
|
|
512
|
+
...props.initParam,
|
|
513
|
+
[props.tabActiveName]: val
|
|
514
|
+
}
|
|
515
|
+
emit('update:initParam', params)
|
|
516
|
+
changeInitParams(params)
|
|
517
|
+
}
|
|
518
|
+
if (!isEmpty(props.tabActive)) {
|
|
519
|
+
emit('update:tabActive', val)
|
|
520
|
+
}
|
|
521
|
+
if (!showToolButton('setting')) {
|
|
522
|
+
// 有列配置的时候,需要先请求数据 在获取表格数据,因为这里涉及到pageSize,所以需要等待
|
|
523
|
+
getTableList()
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
})
|
|
527
|
+
// const isShowSetting = computed(() => {
|
|
528
|
+
// return showToolButton('setting') ? 500 : 0
|
|
529
|
+
// })
|
|
530
|
+
|
|
531
|
+
// 表格全屏
|
|
532
|
+
const isFullscreen = ref(false)
|
|
533
|
+
const searchFormRef = ref()
|
|
534
|
+
// table 实例
|
|
535
|
+
const tableRef = ref<InstanceType<typeof ElTable>>()
|
|
536
|
+
|
|
537
|
+
// 生成组件唯一id
|
|
538
|
+
const uuid = ref('id-' + generateUUID())
|
|
539
|
+
|
|
540
|
+
// column 列类型
|
|
541
|
+
const columnTypes: TypeProps[] = ['selection', 'radio', 'index', 'expand', 'sort']
|
|
542
|
+
|
|
543
|
+
// 是否显示搜索模块
|
|
544
|
+
const isShowSearch = ref(true)
|
|
545
|
+
|
|
546
|
+
// 控制 ToolButton 显示
|
|
547
|
+
const showToolButton = (key: 'refresh' | 'setting' | 'search' | 'fullScreen') => {
|
|
548
|
+
return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// // 判断单选 多选类型
|
|
552
|
+
// const hasCheckType = (key: 'radio' | 'selection' | 'expand') => {
|
|
553
|
+
// if (!key) return false
|
|
554
|
+
// return selectColumns.value.some(item => item.type === key)
|
|
555
|
+
// }
|
|
556
|
+
|
|
557
|
+
// 是否需要防抖
|
|
558
|
+
const debounceTime = computed(() => (showToolButton('setting') && props.gridParams?.url ? 500 : 0))
|
|
559
|
+
|
|
560
|
+
const changeTabsSetting = debounce((tabActive?: string | number) => {
|
|
561
|
+
changeTable(props.tableConfig.columns)
|
|
562
|
+
if (!props?.tabsList || !props?.tabsList?.length) return
|
|
563
|
+
// 所有 tab 的 gridId 一致(或都没有)时,不切换 gridId,共享同一份列配置
|
|
564
|
+
if (isTabColl.value) return
|
|
565
|
+
const index = props.tabsList.findIndex(item => item.value === tabActive)
|
|
566
|
+
if (index !== -1) {
|
|
567
|
+
// 如果有则取 tabsList,无则不取(适用于所有的tab字段不一致,每个 tab 单独配置)
|
|
568
|
+
gridParams1.value.gridId = props.tabsList[index]?.gridId || gridParams1.value.gridId || ''
|
|
569
|
+
}
|
|
570
|
+
}, debounceTime.value)
|
|
571
|
+
|
|
572
|
+
// 单选值
|
|
573
|
+
const radio = ref('')
|
|
574
|
+
// 单选值- 选中row
|
|
575
|
+
const radioItem = ref()
|
|
576
|
+
|
|
577
|
+
// 设置单选值
|
|
578
|
+
const setRadioItem = (key: string, row: any, item?: ColumnProps) => {
|
|
579
|
+
tableRef.value?.setCurrentRow(row)
|
|
580
|
+
radio.value = key
|
|
581
|
+
radioItem.value = row
|
|
582
|
+
emit('radioChange', key, row)
|
|
583
|
+
}
|
|
584
|
+
// 设置默认选中行
|
|
585
|
+
const setExpandRow = debounce(() => {
|
|
586
|
+
if (!processTableData.value || !processTableData.value.length) {
|
|
587
|
+
expandedRows.value = []
|
|
588
|
+
return
|
|
589
|
+
}
|
|
590
|
+
if (isExpandAll.value) {
|
|
591
|
+
const rowKey = props.rowKey
|
|
592
|
+
expandedRows.value = processTableData.value.map(item => {
|
|
593
|
+
return item[rowKey]
|
|
594
|
+
})
|
|
595
|
+
} else {
|
|
596
|
+
expandedRows.value = []
|
|
597
|
+
}
|
|
598
|
+
}, 200)
|
|
599
|
+
|
|
600
|
+
// 当前展开的行(如果你想支持只展开一个)
|
|
601
|
+
const expandedRows = ref<any[]>([])
|
|
602
|
+
// 行点击事件
|
|
603
|
+
// const handleRowClick = (row, column, event) => {
|
|
604
|
+
// const rowKey = props.rowKey
|
|
605
|
+
// if (column.type === 'radio') {
|
|
606
|
+
// setRadioItem(row[rowKey], row)
|
|
607
|
+
// } else if (column.type === 'selection') {
|
|
608
|
+
// // 最后一个参数必须是 false
|
|
609
|
+
// tableRef.value!.toggleRowSelection(row, undefined, false)
|
|
610
|
+
// } else if (column.type === 'expand') {
|
|
611
|
+
// const isExpanded = expandedRows.value.includes(row[rowKey])
|
|
612
|
+
// if (isExpanded) {
|
|
613
|
+
// // 折叠
|
|
614
|
+
// tableRef.value!.toggleRowExpansion(row, false)
|
|
615
|
+
// expandedRows.value = expandedRows.value.filter(item => item !== row[rowKey])
|
|
616
|
+
// } else {
|
|
617
|
+
// // 展开
|
|
618
|
+
// tableRef.value!.toggleRowExpansion(row, true)
|
|
619
|
+
// expandedRows.value.push(row[rowKey])
|
|
620
|
+
// }
|
|
621
|
+
// }
|
|
622
|
+
// }
|
|
623
|
+
// 单元格点击事件
|
|
624
|
+
const handleCellClick = (row, column, cell) => {
|
|
625
|
+
emit('cellClick', row, column)
|
|
626
|
+
const rowKey = props.rowKey
|
|
627
|
+
if (column.type === 'radio') {
|
|
628
|
+
setRadioItem(row[rowKey], row)
|
|
629
|
+
} else if (column.type === 'selection') {
|
|
630
|
+
// 最后一个参数必须是 false
|
|
631
|
+
tableRef.value!.toggleRowSelection(row, undefined, false)
|
|
632
|
+
} else if (column.type === 'expand') {
|
|
633
|
+
const isExpanded = expandedRows.value.includes(row[rowKey])
|
|
634
|
+
if (isExpanded) {
|
|
635
|
+
// 折叠
|
|
636
|
+
tableRef.value!.toggleRowExpansion(row, false)
|
|
637
|
+
expandedRows.value = expandedRows.value.filter(item => item !== row[rowKey])
|
|
638
|
+
} else {
|
|
639
|
+
// 展开
|
|
640
|
+
tableRef.value!.toggleRowExpansion(row, true)
|
|
641
|
+
expandedRows.value.push(row[rowKey])
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// 行点击事件
|
|
646
|
+
const handleRowClick = (row: any, column: any) => {
|
|
647
|
+
const rowKey = props.rowKey
|
|
648
|
+
setRadioItem(row[rowKey], row)
|
|
649
|
+
emit('rowClick', row, column)
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// 效验查询条件必填
|
|
653
|
+
const validateSearchForm = async () => {
|
|
654
|
+
if (!props.validQuery || !searchFormRef.value) {
|
|
655
|
+
// 需要初始化数据
|
|
656
|
+
return true
|
|
657
|
+
} else {
|
|
658
|
+
const validate = await searchFormRef.value.validateForm()
|
|
659
|
+
return validate
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// 所有 tab 的 gridId 是否一致(不存在或全部一致),一致时共享同一份列配置,切换 tab 不改变 gridId
|
|
663
|
+
// true: isTabColl.value说明所有tab 用同一个列配置
|
|
664
|
+
const isTabColl = computed(() => {
|
|
665
|
+
// 收集所有存在的 gridId(过滤掉 undefined / null / 空值)
|
|
666
|
+
const gridIds = props.tabsList?.map(item => item.gridId).filter(val => !isEmpty(val))
|
|
667
|
+
// 没有 gridId 字段,或所有 gridId 完全一致,都返回 true
|
|
668
|
+
if (gridIds?.length === 0) return true
|
|
669
|
+
return gridIds?.every(val => val === gridIds[0])
|
|
670
|
+
})
|
|
671
|
+
// tab切换
|
|
672
|
+
const handleTabChange = (val: string | number) => {
|
|
673
|
+
// isTabColl.value说明所有tab 用同一个列配置
|
|
674
|
+
if (isTabColl.value && props.tabChangeSearch) {
|
|
675
|
+
searchAndClear()
|
|
676
|
+
}
|
|
677
|
+
emit('tabChange', val)
|
|
678
|
+
}
|
|
679
|
+
// 表格多选 Hooks
|
|
680
|
+
const { selectionChange, selectedList, selectedListIds, isSelected, headerDragend } = useSelection(props.rowKey)
|
|
681
|
+
const loading = ref<boolean>(false)
|
|
682
|
+
const loading1 = ref<boolean>(false)
|
|
683
|
+
// 合并后的展示用 loading:开启即时、关闭延迟 200ms。
|
|
684
|
+
// 用于衔接「列配置 loading1」与「数据 loading」两个先后周期,避免出现 loading→结束→loading 的断层。
|
|
685
|
+
const displayLoading = ref(false)
|
|
686
|
+
let loadingOffTimer: ReturnType<typeof setTimeout> | null = null
|
|
687
|
+
watch(
|
|
688
|
+
() => loading.value || loading1.value || props.tableLoading,
|
|
689
|
+
val => {
|
|
690
|
+
if (val) {
|
|
691
|
+
// 任一 loading 开启:立即显示,并清掉待关闭的定时器
|
|
692
|
+
if (loadingOffTimer) {
|
|
693
|
+
clearTimeout(loadingOffTimer)
|
|
694
|
+
loadingOffTimer = null
|
|
695
|
+
}
|
|
696
|
+
displayLoading.value = true
|
|
697
|
+
} else {
|
|
698
|
+
// 全部关闭:延迟 200ms 再隐藏,若期间又有 loading 开启则会被上面的 clearTimeout 取消
|
|
699
|
+
loadingOffTimer = setTimeout(() => {
|
|
700
|
+
displayLoading.value = false
|
|
701
|
+
loadingOffTimer = null
|
|
702
|
+
}, 200)
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
{ immediate: true }
|
|
706
|
+
)
|
|
707
|
+
// 组件卸载时清理待执行的关闭定时器,避免卸载后仍触发 ref 赋值
|
|
708
|
+
onBeforeUnmount(() => {
|
|
709
|
+
if (loadingOffTimer) {
|
|
710
|
+
clearTimeout(loadingOffTimer)
|
|
711
|
+
loadingOffTimer = null
|
|
712
|
+
}
|
|
713
|
+
})
|
|
714
|
+
|
|
715
|
+
// 表格 tableSelectionChange多选事件
|
|
716
|
+
const tableSelectionChange = e => {
|
|
717
|
+
selectionChange(e)
|
|
718
|
+
emit('selectionChange', {
|
|
719
|
+
isSelected: isSelected.value,
|
|
720
|
+
selectedList: selectedList.value,
|
|
721
|
+
selectedListIds: selectedListIds.value
|
|
722
|
+
})
|
|
723
|
+
}
|
|
724
|
+
const formatterForm = form => {
|
|
725
|
+
let searchForm = cloneDeep(form)
|
|
726
|
+
searchColumns.value.forEach(item => {
|
|
727
|
+
// isSetEmptyData:空值默认值&& 未选择或输入数据 -> 设置默认值进行查询
|
|
728
|
+
if (item?.search && item.search?.isSetEmptyData && isEmpty(searchForm[item.prop])) {
|
|
729
|
+
searchForm[item.prop] = item.search.emptyNullValue || GUIDNoneValue
|
|
730
|
+
}
|
|
731
|
+
// 如果是数组 切设置了切割字段 -> 切割数组到开始与结束字段 并删除原字段
|
|
732
|
+
if (item.search && item.search?.startField && item.search?.endField) {
|
|
733
|
+
const { startField, endField } = item.search
|
|
734
|
+
const timeList = searchForm[item.prop] || []
|
|
735
|
+
if (item?.search?.el !== 'JztNumericalRange' && item?.search?.el !== 'JztDateSelect') {
|
|
736
|
+
// 只有时间才需要删除字段
|
|
737
|
+
searchForm[startField] = timeList[0] ?? ''
|
|
738
|
+
searchForm[endField] = timeList[1] ?? ''
|
|
739
|
+
}
|
|
740
|
+
delete searchForm[item.prop]
|
|
741
|
+
}
|
|
742
|
+
})
|
|
743
|
+
const endFrom = removeEmpty(searchForm)
|
|
744
|
+
return endFrom
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const { isExpandAll, isShow, toggleExpandAll } = useOperation()
|
|
748
|
+
// 表格操作 Hooks
|
|
749
|
+
const {
|
|
750
|
+
tableData,
|
|
751
|
+
pageable,
|
|
752
|
+
searchParam,
|
|
753
|
+
searchInitParam,
|
|
754
|
+
getTableList,
|
|
755
|
+
search,
|
|
756
|
+
reset,
|
|
757
|
+
handleSizeChange,
|
|
758
|
+
handleCurrentChange,
|
|
759
|
+
changeInitParams
|
|
760
|
+
} = useTable(
|
|
761
|
+
validateSearchForm,
|
|
762
|
+
props.requestApi,
|
|
763
|
+
props.initParam,
|
|
764
|
+
props.pagination,
|
|
765
|
+
props.dataCallback,
|
|
766
|
+
props.requestError,
|
|
767
|
+
props.searchFilter,
|
|
768
|
+
props.tableLoaded,
|
|
769
|
+
loading,
|
|
770
|
+
formatterForm
|
|
771
|
+
)
|
|
772
|
+
// 清空选中数据列表
|
|
773
|
+
const clearSelection = () => tableRef.value?.clearSelection()
|
|
774
|
+
|
|
775
|
+
// 是否可以发起请求(等待所有 isSetFirstValue 的异步 enum 加载完成)
|
|
776
|
+
const isReadyToRequest = ref(true)
|
|
777
|
+
|
|
778
|
+
// 监听 waitFirstValueCount 改变,为了等待所有isSetFirstValue=true的获取完成后再执行请求,需要携带参数
|
|
779
|
+
const setFirstValue = count => {
|
|
780
|
+
if (count == waitFirstValueCount.value) {
|
|
781
|
+
isReadyToRequest.value = true
|
|
782
|
+
props.requestAuto && _search()
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// 初始化表格数据 && 拖拽排序
|
|
787
|
+
onMounted(async () => {
|
|
788
|
+
dragSort()
|
|
789
|
+
// 未设置过 isSetFirstValue=true,直接请求
|
|
790
|
+
// 判断列设置是因为,必须要判断pageSize
|
|
791
|
+
if (!waitFirstValueCount.value && !showToolButton('setting')) {
|
|
792
|
+
props.requestAuto && _search()
|
|
793
|
+
}
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
// 处理表格数据
|
|
797
|
+
// let processTableData = computed(() => {
|
|
798
|
+
// if (!props.data) return tableData.value
|
|
799
|
+
// if (!props.pagination) return props.data
|
|
800
|
+
// return props.data.slice(
|
|
801
|
+
// (pageable.value.pageIndex - 1) * pageable.value.pageSize,
|
|
802
|
+
// pageable.value.pageSize * pageable.value.pageIndex
|
|
803
|
+
// )
|
|
804
|
+
// })
|
|
805
|
+
// 处理表格数据
|
|
806
|
+
const processTableData = computed({
|
|
807
|
+
get() {
|
|
808
|
+
// radio.value = ''
|
|
809
|
+
// radioItem.value = {}
|
|
810
|
+
if (!props.data) return tableData.value
|
|
811
|
+
if (!props.pagination) return props.data
|
|
812
|
+
return props.data.slice(
|
|
813
|
+
(pageable.value.pageIndex - 1) * pageable.value.pageSize,
|
|
814
|
+
pageable.value.pageSize * pageable.value.pageIndex
|
|
815
|
+
)
|
|
816
|
+
},
|
|
817
|
+
set(val) {
|
|
818
|
+
if (!Array.isArray(val)) return
|
|
819
|
+
tableData.value = val
|
|
820
|
+
}
|
|
821
|
+
})
|
|
822
|
+
// 监听表格数据,处理默认展开行的数据
|
|
823
|
+
watch(
|
|
824
|
+
() => processTableData.value,
|
|
825
|
+
list => {
|
|
826
|
+
radio.value = ''
|
|
827
|
+
radioItem.value = {}
|
|
828
|
+
setExpandRow()
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
deep: true,
|
|
832
|
+
immediate: true
|
|
833
|
+
}
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
// 监听页面 initParam 改化,重新获取表格数据
|
|
837
|
+
// !删除该功能 在tab切换时已经处理掉接口了
|
|
838
|
+
// watch(
|
|
839
|
+
// () => props.initParam[props.tabActiveName],
|
|
840
|
+
// () => {
|
|
841
|
+
// if (props.initParam && !isEmpty(props.tabActiveName)) {
|
|
842
|
+
// getTableList
|
|
843
|
+
// }
|
|
844
|
+
// },
|
|
845
|
+
// { deep: true }
|
|
846
|
+
// )
|
|
847
|
+
|
|
848
|
+
const tableColumns = ref<ColumnProps[]>([])
|
|
849
|
+
|
|
850
|
+
// props.tableConfig.columns 动态更新后及时更新tableColumns
|
|
851
|
+
// watch(
|
|
852
|
+
// () => props.tableConfig.columns,
|
|
853
|
+
// () => {
|
|
854
|
+
// tableColumns.value = props.tableConfig.columns.filter(item => item.prop && !item.type)
|
|
855
|
+
// tableColumns.value = props.tableConfig.columns.filter(item => item.prop && !item.type)
|
|
856
|
+
// }
|
|
857
|
+
// )
|
|
858
|
+
// 列表操作列 'index', 'selection', 'radio', 'expand', 'sort'
|
|
859
|
+
const selectColumns = computed<ColumnProps[]>(() => {
|
|
860
|
+
const selectCol = props.tableConfig.columns.filter(item => {
|
|
861
|
+
if (item.type && columnTypes.includes(item.type)) {
|
|
862
|
+
return getColIsShow(item)
|
|
863
|
+
}
|
|
864
|
+
})
|
|
865
|
+
return selectCol || []
|
|
866
|
+
})
|
|
867
|
+
// 获取当前的列是否展示
|
|
868
|
+
const getColIsShow = item => {
|
|
869
|
+
let isShow = item.isShow
|
|
870
|
+
const type = typeof item.isShow
|
|
871
|
+
if (type === 'object') {
|
|
872
|
+
isShow = item.isShow?.value
|
|
873
|
+
} else if (type === 'function') {
|
|
874
|
+
isShow = item.isShow?.()
|
|
875
|
+
}
|
|
876
|
+
return isShow !== false
|
|
877
|
+
}
|
|
878
|
+
const operationColumns = computed<ColumnProps[]>(() => {
|
|
879
|
+
const operationCol = props.tableConfig.columns.filter(item => {
|
|
880
|
+
if (item.prop === 'operation') {
|
|
881
|
+
return getColIsShow(item)
|
|
882
|
+
}
|
|
883
|
+
})
|
|
884
|
+
return operationCol || []
|
|
885
|
+
})
|
|
886
|
+
|
|
887
|
+
watch(
|
|
888
|
+
() => props.tableConfig.columns,
|
|
889
|
+
columns => {
|
|
890
|
+
changeTable(columns)
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
deep: true
|
|
894
|
+
}
|
|
895
|
+
)
|
|
896
|
+
watch(
|
|
897
|
+
() => gridParams1.value.gridId,
|
|
898
|
+
gridId => {
|
|
899
|
+
setTimeout(() => {
|
|
900
|
+
columnsSettingRef.value?.changeTableColumns(props.tableConfig.columns, gridParams1.value)
|
|
901
|
+
}, 0)
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
deep: true,
|
|
905
|
+
immediate: true
|
|
906
|
+
}
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
const changeTable = debounce((columns: ColumnProps[]) => {
|
|
910
|
+
const settingColumns = columnsSettingRef.value?.getCurrentTableColumns() || []
|
|
911
|
+
if (settingColumns && settingColumns.length) {
|
|
912
|
+
buildTableColumns(settingColumns)
|
|
913
|
+
} else {
|
|
914
|
+
flatColumnsFunc(columns)
|
|
915
|
+
}
|
|
916
|
+
}, debounceTime.value)
|
|
917
|
+
|
|
918
|
+
// 接收 queryItems 并设置为响应式
|
|
919
|
+
const queryItems = computed(() => props.tableConfig.queryItems || [])
|
|
920
|
+
//reactive<QueryItemsProps[]>(props.tableConfig.queryItems || []);
|
|
921
|
+
|
|
922
|
+
// 接收 topList 并设置为响应式 TopButtonProps
|
|
923
|
+
const topList = computed<TopButtonProps[]>(() => props.tableConfig.topList || [])
|
|
924
|
+
|
|
925
|
+
// 定义 enumMap 存储 enum 值(避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择)
|
|
926
|
+
const enumMap = ref(new Map<string, { [key: string]: any }[]>())
|
|
927
|
+
const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => {
|
|
928
|
+
if (!enumValue) return
|
|
929
|
+
|
|
930
|
+
// 如果当前 enumMap 存在相同的值 return
|
|
931
|
+
if (enumMap.value.has(prop!) && (typeof enumValue === 'function' || enumMap.value.get(prop!) === enumValue)) return
|
|
932
|
+
|
|
933
|
+
// 当前 enum 为静态数据,则直接存储到 enumMap
|
|
934
|
+
if (typeof enumValue !== 'function') return enumMap.value.set(prop!, unref(enumValue!))
|
|
935
|
+
|
|
936
|
+
// 为了防止接口执行慢,而存储慢,导致重复请求,所以预先存储为[],接口返回后再二次存储
|
|
937
|
+
// enumMap.value.set(prop!, [])
|
|
938
|
+
|
|
939
|
+
// // 当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap
|
|
940
|
+
// const { data, result } = await enumValue()
|
|
941
|
+
// enumMap.value.set(prop!, data || result)
|
|
942
|
+
}
|
|
943
|
+
const originalColumn = ref<any[]>([])
|
|
944
|
+
|
|
945
|
+
// 处理数据,递归处理
|
|
946
|
+
const flatColumnsFunc = async (columns: ColumnProps[]) => {
|
|
947
|
+
tableColumns.value = await processColumns(columns)
|
|
948
|
+
originalColumn.value = _.cloneDeep(tableColumns.value)
|
|
949
|
+
}
|
|
950
|
+
// 初始化 处理数据
|
|
951
|
+
const processColumns = async (columns: ColumnProps[]): Promise<ColumnProps[]> => {
|
|
952
|
+
const result: ColumnProps[] = []
|
|
953
|
+
for (const col of columns) {
|
|
954
|
+
if (col.prop && !col.type && col.isShow !== false && col.prop !== 'operation') {
|
|
955
|
+
const newCol: ColumnProps = {
|
|
956
|
+
...col,
|
|
957
|
+
isShow: col.isShow ?? true,
|
|
958
|
+
isSetting: unref(col.isSetting) ?? true,
|
|
959
|
+
isFilterEnum: col.isFilterEnum ?? true
|
|
960
|
+
}
|
|
961
|
+
if (col._children?.length) {
|
|
962
|
+
newCol._children = await processColumns(col._children)
|
|
963
|
+
}
|
|
964
|
+
await setEnumMap(newCol)
|
|
965
|
+
result.push(newCol)
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return result
|
|
969
|
+
}
|
|
970
|
+
const buildTableColumns = (asyncColumns: ColumnProps[]) => {
|
|
971
|
+
const result: ColumnProps[] = []
|
|
972
|
+
for (const col of asyncColumns) {
|
|
973
|
+
if (!col.prop || col.type || col.isShow === false || col.prop === 'operation' || col.hidden) {
|
|
974
|
+
continue
|
|
975
|
+
} else {
|
|
976
|
+
result.push({
|
|
977
|
+
...col,
|
|
978
|
+
isShow: unref(col.isShow) ?? true,
|
|
979
|
+
isSetting: unref(col.isSetting) ?? true,
|
|
980
|
+
isFilterEnum: col.isFilterEnum ?? true,
|
|
981
|
+
// hidden: col.hidden === true,
|
|
982
|
+
isSetShow: col.hidden !== false
|
|
983
|
+
})
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
tableColumns.value = result
|
|
988
|
+
originalColumn.value = _.cloneDeep(result)
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
flatColumnsFunc(props.tableConfig.columns)
|
|
992
|
+
// 注入 enumColumnMap
|
|
993
|
+
provide('enumColumnMap', enumMap)
|
|
994
|
+
// 获取类配置数据
|
|
995
|
+
|
|
996
|
+
const changeColumns = (columns: ColumnProps[], pageSize: number, id: string) => {
|
|
997
|
+
// 1️⃣ 构建 tableColumns(一次性)
|
|
998
|
+
const nextTableColumns = cloneDeep(columns)
|
|
999
|
+
|
|
1000
|
+
// 2️⃣ 构建 emit 所需数据
|
|
1001
|
+
const allColumns: any[] = []
|
|
1002
|
+
const checkColumns: any[] = []
|
|
1003
|
+
for (const item of columns) {
|
|
1004
|
+
if (!item.prop) continue
|
|
1005
|
+
const colInfo = {
|
|
1006
|
+
fieldName: item.prop,
|
|
1007
|
+
titleName: item.label
|
|
1008
|
+
}
|
|
1009
|
+
allColumns.push(colInfo)
|
|
1010
|
+
if (item.isShow && !item.hidden) {
|
|
1011
|
+
checkColumns.push(colInfo)
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// 3️⃣ 一次性更新响应式状态
|
|
1016
|
+
tableColumns.value = nextTableColumns
|
|
1017
|
+
pageable.value.pageSize = pageSize
|
|
1018
|
+
|
|
1019
|
+
// 4️⃣ emit(数据已稳定)
|
|
1020
|
+
emit('getEndColumns', {
|
|
1021
|
+
allColumns,
|
|
1022
|
+
checkColumns,
|
|
1023
|
+
pageSize,
|
|
1024
|
+
id
|
|
1025
|
+
})
|
|
1026
|
+
|
|
1027
|
+
// 5️⃣ 最后决定是否刷新
|
|
1028
|
+
if (props.requestAuto && !waitFirstValueCount.value && isReadyToRequest.value) {
|
|
1029
|
+
handleSizeChange(pageSize)
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// const changeColumns = (columns: ColumnProps[], pageSize: number, id: string) => {
|
|
1034
|
+
// // console.log('changeColumns', !waitFirstValueCount.value, !isAllMounted.value)
|
|
1035
|
+
// tableColumns.value = cloneDeep(columns)
|
|
1036
|
+
// if (props.requestAuto && !waitFirstValueCount.value && isAllMounted.value) {
|
|
1037
|
+
// // 刷新数据
|
|
1038
|
+
// handleSizeChange(pageSize)
|
|
1039
|
+
// }
|
|
1040
|
+
// const endCol = [] as any
|
|
1041
|
+
// const allCol = [] as any
|
|
1042
|
+
|
|
1043
|
+
// columns.forEach(item => {
|
|
1044
|
+
// allCol.push({
|
|
1045
|
+
// fieldName: item.prop,
|
|
1046
|
+
// titleName: item.label
|
|
1047
|
+
// })
|
|
1048
|
+
// if (item.isShow && !item.hidden) {
|
|
1049
|
+
// endCol.push({
|
|
1050
|
+
// fieldName: item.prop,
|
|
1051
|
+
// titleName: item.label
|
|
1052
|
+
// })
|
|
1053
|
+
// }
|
|
1054
|
+
// })
|
|
1055
|
+
// emit('getEndColumns', {
|
|
1056
|
+
// allColumns: allCol,
|
|
1057
|
+
// checkColumns: endCol,
|
|
1058
|
+
// pageSize: pageSize,
|
|
1059
|
+
// id: id
|
|
1060
|
+
// })
|
|
1061
|
+
// pageable.value.pageSize = pageSize
|
|
1062
|
+
// }
|
|
1063
|
+
// TODO 暂时不走配置
|
|
1064
|
+
// const getSettingConfig = () => {
|
|
1065
|
+
// getTableSetting(showToolButton('setting'), tableColumns.value, loading1).then(res => {
|
|
1066
|
+
// tableColumns.value = res
|
|
1067
|
+
// // tableColumns.value = tableColumns.value
|
|
1068
|
+
// })
|
|
1069
|
+
// }
|
|
1070
|
+
// getSettingConfig()
|
|
1071
|
+
|
|
1072
|
+
// 过滤需要搜索的配置项 && 排序
|
|
1073
|
+
const searchColumns = computed(() => {
|
|
1074
|
+
return queryItems.value
|
|
1075
|
+
?.filter(item => item.search?.el || item.search?.render)
|
|
1076
|
+
.sort((a, b) => a.search!.order! - b.search!.order!)
|
|
1077
|
+
})
|
|
1078
|
+
const waitFirstValueCount = ref<number>(0) // 需要等待的次数
|
|
1079
|
+
|
|
1080
|
+
const initSearchParam = () => {
|
|
1081
|
+
// 设置 搜索表单默认排序 && 搜索表单项的默认值
|
|
1082
|
+
searchColumns.value?.forEach((column, index) => {
|
|
1083
|
+
// column.search!.order = column.search?.order ?? index + 2
|
|
1084
|
+
if (!column.search) return
|
|
1085
|
+
column.search.order ??= index + 2
|
|
1086
|
+
if (useFormSetByUserType(column?.isSetFirstValue, column.prop) && typeof column.enum === 'function') {
|
|
1087
|
+
// 设置isSetFirstValue && enum设置的是函数时,需要等待函数返回数据,才能设置默认值
|
|
1088
|
+
waitFirstValueCount.value++
|
|
1089
|
+
isReadyToRequest.value = false
|
|
1090
|
+
}
|
|
1091
|
+
const key = column.search?.key ?? handleProp(column.prop!)
|
|
1092
|
+
// 计算默认值(优先级明确)
|
|
1093
|
+
let defaultValue
|
|
1094
|
+
if (!isEmpty(column.search?.defaultValue)) {
|
|
1095
|
+
defaultValue = column.search?.defaultValue
|
|
1096
|
+
} else if (!isEmpty(props.initParam[column.prop])) {
|
|
1097
|
+
defaultValue = props.initParam[column.prop]
|
|
1098
|
+
}
|
|
1099
|
+
// 设置默认值
|
|
1100
|
+
if (!isEmpty(defaultValue)) {
|
|
1101
|
+
searchParam.value[key] = defaultValue
|
|
1102
|
+
searchInitParam.value[key] = defaultValue
|
|
1103
|
+
}
|
|
1104
|
+
})
|
|
1105
|
+
}
|
|
1106
|
+
initSearchParam()
|
|
1107
|
+
|
|
1108
|
+
const columnsSettingRef = ref()
|
|
1109
|
+
const changeDialog = event => {
|
|
1110
|
+
event.stopPropagation()
|
|
1111
|
+
columnsSettingRef.value?.changeDialog(gridParams1.value)
|
|
1112
|
+
}
|
|
1113
|
+
// 外部手动保存列配置
|
|
1114
|
+
const saveColumns = async () => {
|
|
1115
|
+
return new Promise(async (resolve, reject) => {
|
|
1116
|
+
const { id, columns } = await columnsSettingRef.value?._saveColumn(false)
|
|
1117
|
+
const endCol = [] as any
|
|
1118
|
+
columns.forEach(item => {
|
|
1119
|
+
if (item.isShow && !item.hidden) {
|
|
1120
|
+
endCol.push({
|
|
1121
|
+
fieldName: item.prop,
|
|
1122
|
+
titleName: item.label
|
|
1123
|
+
})
|
|
1124
|
+
}
|
|
1125
|
+
})
|
|
1126
|
+
resolve({
|
|
1127
|
+
columns: endCol,
|
|
1128
|
+
id
|
|
1129
|
+
})
|
|
1130
|
+
})
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// setTimeout(async () => {
|
|
1134
|
+
// const id = await saveColumns()
|
|
1135
|
+
// }, 2000)
|
|
1136
|
+
// 定义 emit 事件
|
|
1137
|
+
const emit = defineEmits<{
|
|
1138
|
+
search: [any]
|
|
1139
|
+
reset: []
|
|
1140
|
+
dragSort: [{ newIndex?: number; oldIndex?: number }]
|
|
1141
|
+
radioChange: [any, any]
|
|
1142
|
+
tabChange: [any]
|
|
1143
|
+
selectionChange: [
|
|
1144
|
+
{
|
|
1145
|
+
isSelected: boolean
|
|
1146
|
+
selectedList: any[]
|
|
1147
|
+
selectedListIds: string[]
|
|
1148
|
+
}
|
|
1149
|
+
]
|
|
1150
|
+
'update:initParam': [any]
|
|
1151
|
+
'update:tabActive': [any]
|
|
1152
|
+
getEndColumns: [any]
|
|
1153
|
+
refreshColumnTable: [any]
|
|
1154
|
+
rowClick: any
|
|
1155
|
+
cellClick: any
|
|
1156
|
+
}>()
|
|
1157
|
+
// 格式化后的搜索参数
|
|
1158
|
+
const formattedSearchForm = ref({})
|
|
1159
|
+
// 搜索清空
|
|
1160
|
+
const _search = () => {
|
|
1161
|
+
search()
|
|
1162
|
+
if (props.searchClear) {
|
|
1163
|
+
clearSelection()
|
|
1164
|
+
setRadioItem('', {})
|
|
1165
|
+
selectionChange([])
|
|
1166
|
+
tableRef.value?.setCurrentRow({})
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// 处理格式化数据
|
|
1170
|
+
const searchForm = formatterForm({
|
|
1171
|
+
...searchParam.value,
|
|
1172
|
+
...props.initParam
|
|
1173
|
+
})
|
|
1174
|
+
formattedSearchForm.value = searchForm
|
|
1175
|
+
emit('search', searchForm)
|
|
1176
|
+
}
|
|
1177
|
+
// 重新搜索并清空表格的勾选
|
|
1178
|
+
const searchAndClear = () => {
|
|
1179
|
+
clearSelection()
|
|
1180
|
+
setRadioItem('', {})
|
|
1181
|
+
selectionChange([])
|
|
1182
|
+
_search()
|
|
1183
|
+
tableRef.value?.setCurrentRow({})
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const _reset = () => {
|
|
1187
|
+
reset()
|
|
1188
|
+
clearSelection()
|
|
1189
|
+
emit('reset')
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// 表格拖拽排序
|
|
1193
|
+
let sortableInstance: Sortable | null = null
|
|
1194
|
+
const dragSort = () => {
|
|
1195
|
+
const tbody = document.querySelector(`#${uuid.value} tbody`) as HTMLElement
|
|
1196
|
+
if (!tbody) return
|
|
1197
|
+
sortableInstance = Sortable.create(tbody, {
|
|
1198
|
+
handle: '.move',
|
|
1199
|
+
animation: 300,
|
|
1200
|
+
onEnd({ newIndex, oldIndex }) {
|
|
1201
|
+
// 修复: 边界检查,防止 undefined 或越界
|
|
1202
|
+
if (newIndex === undefined || oldIndex === undefined) return
|
|
1203
|
+
if (newIndex < 0 || oldIndex < 0) return
|
|
1204
|
+
if (newIndex >= processTableData.value.length || oldIndex >= processTableData.value.length) return
|
|
1205
|
+
|
|
1206
|
+
const [removedItem] = processTableData.value.splice(oldIndex, 1)
|
|
1207
|
+
processTableData.value.splice(newIndex, 0, removedItem)
|
|
1208
|
+
emit('dragSort', { newIndex, oldIndex })
|
|
1209
|
+
}
|
|
1210
|
+
})
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// 组件卸载时清理 Sortable 实例
|
|
1214
|
+
onBeforeUnmount(() => {
|
|
1215
|
+
if (sortableInstance) {
|
|
1216
|
+
sortableInstance.destroy()
|
|
1217
|
+
sortableInstance = null
|
|
1218
|
+
}
|
|
1219
|
+
})
|
|
1220
|
+
|
|
1221
|
+
// // 是否显示表格操作按钮
|
|
1222
|
+
const hasPermissionFn = (btnItem: TopButtonProps, row: any) => {
|
|
1223
|
+
// console.log(btnItem.text, btnItem.hideFun ? !btnItem.hideFun(row) : true, hasPermission(btnItem.hasPerm))
|
|
1224
|
+
|
|
1225
|
+
return (btnItem.hideFun ? !btnItem.hideFun(row) : true) && hasPermission(btnItem.hasPerm)
|
|
1226
|
+
}
|
|
1227
|
+
// 按钮点击
|
|
1228
|
+
const handleButtonClick = (fun: Function) => {
|
|
1229
|
+
const searchForm = formatterForm({
|
|
1230
|
+
...searchParam.value,
|
|
1231
|
+
...props.initParam
|
|
1232
|
+
})
|
|
1233
|
+
fun &&
|
|
1234
|
+
fun({
|
|
1235
|
+
radio: radio.value,
|
|
1236
|
+
radioItem: radioItem.value,
|
|
1237
|
+
isSelected: isSelected.value,
|
|
1238
|
+
selectedList: selectedList.value,
|
|
1239
|
+
selectedListIds: selectedListIds.value,
|
|
1240
|
+
searchParam: searchParam.value,
|
|
1241
|
+
searchForm // 经过格式化的数据 ,未删除searchParam, 兼容上面被使用的情况
|
|
1242
|
+
})
|
|
1243
|
+
}
|
|
1244
|
+
// 操作列点击操作
|
|
1245
|
+
const tableOperationFn = (item: any, row: any) => {
|
|
1246
|
+
useButtonClickFn(item, row)
|
|
1247
|
+
// let secondConfirm = ''
|
|
1248
|
+
// if (item.secondConfirm) {
|
|
1249
|
+
// secondConfirm = item.secondConfirm
|
|
1250
|
+
// } else if (item.id === 'delete') {
|
|
1251
|
+
// secondConfirm = `确定${item.text || '删除'}该条数据吗?`
|
|
1252
|
+
// }
|
|
1253
|
+
// if (secondConfirm) {
|
|
1254
|
+
// ElMessageBox.confirm(secondConfirm, '警告', {
|
|
1255
|
+
// confirmButtonText: '确定',
|
|
1256
|
+
// cancelButtonText: '取消',
|
|
1257
|
+
// type: 'warning'
|
|
1258
|
+
// })
|
|
1259
|
+
// .then(() => {
|
|
1260
|
+
// item.fun(row)
|
|
1261
|
+
// })
|
|
1262
|
+
// .catch(error => {
|
|
1263
|
+
// })
|
|
1264
|
+
// } else {
|
|
1265
|
+
// item.fun(row)
|
|
1266
|
+
// }
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/*
|
|
1270
|
+
动态更改表格数据,只需要是内部请求时才需要调用
|
|
1271
|
+
外部使用方法 传入一个回调 处理数组后返回最终结果即可
|
|
1272
|
+
外部使用方式:
|
|
1273
|
+
jztQueryTableRef?.value?.changeTableData(data => {
|
|
1274
|
+
return newData
|
|
1275
|
+
})
|
|
1276
|
+
*/
|
|
1277
|
+
const changeTableData = (dataCallback: any) => {
|
|
1278
|
+
const endData = dataCallback(processTableData.value)
|
|
1279
|
+
processTableData.value = endData || []
|
|
1280
|
+
}
|
|
1281
|
+
// 获取表格的选中值
|
|
1282
|
+
const getTableCheckedValue = () => {
|
|
1283
|
+
return {
|
|
1284
|
+
radio: radio.value,
|
|
1285
|
+
radioItem: radioItem.value,
|
|
1286
|
+
isSelected: isSelected.value,
|
|
1287
|
+
selectedList: selectedList.value,
|
|
1288
|
+
selectedListIds: selectedListIds.value
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
//列配置 确定 重置 刷新列表
|
|
1292
|
+
const refreshColumnTable = (type: string) => {
|
|
1293
|
+
emit('refreshColumnTable', type)
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// 暴露给父组件的参数和方法 (外部需要什么,都可以从这里暴露出去)
|
|
1297
|
+
defineExpose({
|
|
1298
|
+
element: tableRef,
|
|
1299
|
+
tableData: processTableData,
|
|
1300
|
+
// radio, // radio 如果有人后面用到这个,联调自测的时候请改用 getTableCheckedValue去获取(谢谢)
|
|
1301
|
+
// radioItem,
|
|
1302
|
+
pageable,
|
|
1303
|
+
searchParam,
|
|
1304
|
+
searchInitParam,
|
|
1305
|
+
formattedSearchForm, // 格式化后的搜索参数
|
|
1306
|
+
isSelected,
|
|
1307
|
+
selectedList,
|
|
1308
|
+
selectedListIds,
|
|
1309
|
+
|
|
1310
|
+
// 下面为 function
|
|
1311
|
+
getTableList, // 如果纯查询,在当前页面查询的话,就掉用这个接口
|
|
1312
|
+
search, // 查询,会重置页码
|
|
1313
|
+
reset,
|
|
1314
|
+
handleSizeChange,
|
|
1315
|
+
handleCurrentChange,
|
|
1316
|
+
clearSelection,
|
|
1317
|
+
enumMap,
|
|
1318
|
+
toggleExpandAll,
|
|
1319
|
+
changeTableData,
|
|
1320
|
+
setRadioItem,
|
|
1321
|
+
searchAndClear, // 查询并清空表格单选与多选数据 会触发emit
|
|
1322
|
+
getTableCheckedValue,
|
|
1323
|
+
saveColumns
|
|
1324
|
+
})
|
|
1325
|
+
|
|
1326
|
+
// 获取 attrs
|
|
1327
|
+
const attrs = useAttrs()
|
|
1328
|
+
// const maxHeight = computed(() => {
|
|
1329
|
+
// const maxHeight = props.maxHeight //attrs['max-height'] || attrs['maxHeight']
|
|
1330
|
+
// if (!maxHeight) return '100%'
|
|
1331
|
+
// if (typeof maxHeight === 'number') {
|
|
1332
|
+
// return maxHeight + 'px'
|
|
1333
|
+
// } else {
|
|
1334
|
+
// return maxHeight
|
|
1335
|
+
// }
|
|
1336
|
+
// })
|
|
1337
|
+
|
|
1338
|
+
// 表格固定高度
|
|
1339
|
+
const height = computed(() => {
|
|
1340
|
+
if (attrs['height']) {
|
|
1341
|
+
if (typeof attrs['height'] === 'number') {
|
|
1342
|
+
return attrs['height'] + 'px'
|
|
1343
|
+
} else {
|
|
1344
|
+
return attrs['height']
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return '100%'
|
|
1348
|
+
})
|
|
1349
|
+
// 页面固定高度
|
|
1350
|
+
const pageHeight = computed(() => {
|
|
1351
|
+
const height = attrs['page-height'] || attrs['pageHeight']
|
|
1352
|
+
if (!height) return '100%'
|
|
1353
|
+
if (typeof height === 'number') {
|
|
1354
|
+
return height + 'px'
|
|
1355
|
+
} else {
|
|
1356
|
+
return height
|
|
1357
|
+
}
|
|
1358
|
+
})
|
|
1359
|
+
// 表格最小高度
|
|
1360
|
+
const minHeight = computed(() => {
|
|
1361
|
+
const minHeight = props.minHeight // attrs['min-height'] || attrs['minHeight']
|
|
1362
|
+
if (minHeight === false) {
|
|
1363
|
+
return '100%'
|
|
1364
|
+
} else if (typeof minHeight === 'string') {
|
|
1365
|
+
return minHeight || '170px'
|
|
1366
|
+
} else {
|
|
1367
|
+
return '170px'
|
|
1368
|
+
}
|
|
1369
|
+
})
|
|
1370
|
+
</script>
|
|
1371
|
+
<style lang="scss" scoped>
|
|
1372
|
+
@use './index';
|
|
1373
|
+
|
|
1374
|
+
.jzt-query-page {
|
|
1375
|
+
display: flex;
|
|
1376
|
+
flex-direction: column;
|
|
1377
|
+
justify-content: flex-start;
|
|
1378
|
+
height: v-bind(pageHeight);
|
|
1379
|
+
:deep(.el-tabs .el-tabs__header.is-top) {
|
|
1380
|
+
margin-bottom: 2px !important;
|
|
1381
|
+
}
|
|
1382
|
+
:deep(.jzt-table-add-btn) {
|
|
1383
|
+
height: 42px;
|
|
1384
|
+
padding: 8px;
|
|
1385
|
+
font-size: 14px;
|
|
1386
|
+
border-left: 1px solid var(--el-border-color);
|
|
1387
|
+
border-right: 1px solid var(--el-border-color);
|
|
1388
|
+
border-bottom: 1px solid var(--el-border-color);
|
|
1389
|
+
border-bottom-left-radius: 2px;
|
|
1390
|
+
border-bottom-right-radius: 2px;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
:deep(.header-button-lf) {
|
|
1394
|
+
.el-button {
|
|
1395
|
+
margin: 4px 8px 4px 0;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
:deep(.header-button-ri) {
|
|
1399
|
+
.el-button {
|
|
1400
|
+
margin: 4px 4px 4px 4px;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
.table-main {
|
|
1404
|
+
flex: 1;
|
|
1405
|
+
overflow: hidden;
|
|
1406
|
+
:deep(.el-scrollbar__wrap) {
|
|
1407
|
+
// height: 0;
|
|
1408
|
+
min-height: 0;
|
|
1409
|
+
}
|
|
1410
|
+
:deep(.el-table .el-scrollbar) {
|
|
1411
|
+
min-height: v-bind(minHeight);
|
|
1412
|
+
height: v-bind(height);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
.header-button-ri {
|
|
1416
|
+
flex-wrap: nowrap !important;
|
|
1417
|
+
}
|
|
1418
|
+
.noCardGap {
|
|
1419
|
+
padding: 0;
|
|
1420
|
+
}
|
|
1421
|
+
.el-table {
|
|
1422
|
+
flex: 1;
|
|
1423
|
+
// margin-top: 2px;
|
|
1424
|
+
.el-scrollbar {
|
|
1425
|
+
min-height: 150px;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
:deep(.el-table) {
|
|
1430
|
+
.highlight-current-row {
|
|
1431
|
+
background-color: var(--el-color-primary-light-9) !important;
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
.el-table.noDataEmpty {
|
|
1436
|
+
:deep(.el-scrollbar) {
|
|
1437
|
+
display: none;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
.el-table.tableNoHeight {
|
|
1441
|
+
:deep(.el-scrollbar) {
|
|
1442
|
+
min-height: 0;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
</style>
|