@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.
Files changed (145) hide show
  1. package/package.json +68 -0
  2. package/src/JztBackTop/index.vue +255 -0
  3. package/src/JztButtonList/index.vue +88 -0
  4. package/src/JztChart/index.vue +95 -0
  5. package/src/JztCharts/index.vue +317 -0
  6. package/src/JztClassTabs/index.vue +156 -0
  7. package/src/JztDateSelect/dateSelect.vue +186 -0
  8. package/src/JztDateSelect/dateType.vue +54 -0
  9. package/src/JztDateSelect/index.ts +135 -0
  10. package/src/JztDateSelect/interface/index.ts +13 -0
  11. package/src/JztDialog/index.vue +249 -0
  12. package/src/JztEllipsisTooltip/index.vue +61 -0
  13. package/src/JztEmpty/index.vue +45 -0
  14. package/src/JztErrorPage/403.vue +30 -0
  15. package/src/JztErrorPage/404.vue +19 -0
  16. package/src/JztErrorPage/500.vue +18 -0
  17. package/src/JztErrorPage/assets/401.png +0 -0
  18. package/src/JztErrorPage/assets/403.png +0 -0
  19. package/src/JztErrorPage/assets/404.png +0 -0
  20. package/src/JztErrorPage/assets/500.png +0 -0
  21. package/src/JztErrorPage/index.scss +35 -0
  22. package/src/JztErrorPage/index.vue +35 -0
  23. package/src/JztFilePreview/components/pdfViewer.vue +221 -0
  24. package/src/JztFilePreview/hooks/useImageMethod.ts +256 -0
  25. package/src/JztFilePreview/index.scss +171 -0
  26. package/src/JztFilePreview/index.vue +68 -0
  27. package/src/JztFilePreview/interface/index.ts +18 -0
  28. package/src/JztFilePreview/previewFile.vue +371 -0
  29. package/src/JztFormGrid/README.md +520 -0
  30. package/src/JztFormGrid/components/formItem.vue +209 -0
  31. package/src/JztFormGrid/components/formItemValue.vue +384 -0
  32. package/src/JztFormGrid/components/showDetailForm.vue +172 -0
  33. package/src/JztFormGrid/index.scss +60 -0
  34. package/src/JztFormGrid/index.vue +513 -0
  35. package/src/JztFormGrid/interface/index.ts +106 -0
  36. package/src/JztGrid/components/GridItem.vue +68 -0
  37. package/src/JztGrid/index.vue +179 -0
  38. package/src/JztGrid/interface/index.ts +6 -0
  39. package/src/JztImportExcel/assets/delete.png +0 -0
  40. package/src/JztImportExcel/index.scss +46 -0
  41. package/src/JztImportExcel/index.vue +430 -0
  42. package/src/JztImportExcel/interface/index.ts +25 -0
  43. package/src/JztLabelTitle/index.vue +65 -0
  44. package/src/JztLeftRightMode/components/CollapseButton.vue +80 -0
  45. package/src/JztLeftRightMode/components/LeftCard.vue +203 -0
  46. package/src/JztLeftRightMode/components/LeftLayout.vue +173 -0
  47. package/src/JztLeftRightMode/components/RightHeader.vue +186 -0
  48. package/src/JztLeftRightMode/components/RightLayout.vue +235 -0
  49. package/src/JztLeftRightMode/components/RightTableHeader.vue +43 -0
  50. package/src/JztLeftRightMode/hooks/useCollapse.ts +17 -0
  51. package/src/JztLeftRightMode/hooks/useDefaultProps.ts +19 -0
  52. package/src/JztLeftRightMode/hooks/useLeftLayout.ts +201 -0
  53. package/src/JztLeftRightMode/hooks/useMode.ts +20 -0
  54. package/src/JztLeftRightMode/hooks/usePrevNext.ts +60 -0
  55. package/src/JztLeftRightMode/hooks/useRightLayout.ts +215 -0
  56. package/src/JztLeftRightMode/hooks/useSlots.ts +15 -0
  57. package/src/JztLeftRightMode/index.ts +3 -0
  58. package/src/JztLeftRightMode/index.vue +494 -0
  59. package/src/JztLeftRightMode/types/index.ts +457 -0
  60. package/src/JztLoading/fullScreen.ts +45 -0
  61. package/src/JztLoading/index.scss +67 -0
  62. package/src/JztLoading/index.vue +18 -0
  63. package/src/JztLogin/components/LoginFooter.vue +17 -0
  64. package/src/JztLogin/components/LoginForm.vue +99 -0
  65. package/src/JztLogin/hooks/useLogin.ts +186 -0
  66. package/src/JztLogin/index.scss +142 -0
  67. package/src/JztLogin/index.vue +31 -0
  68. package/src/JztLogin/interface/index.ts +47 -0
  69. package/src/JztNumericalRange/index.vue +81 -0
  70. package/src/JztPageCard/comm/datePicker.vue +151 -0
  71. package/src/JztPageCard/comm/details.vue +60 -0
  72. package/src/JztPageCard/comm/export.vue +24 -0
  73. package/src/JztPageCard/comm/tabs.vue +94 -0
  74. package/src/JztPageCard/comm/tooltip.vue +31 -0
  75. package/src/JztPageCard/index.vue +287 -0
  76. package/src/JztPagination/index.vue +70 -0
  77. package/src/JztProductInfo/components/imagePreview.vue +275 -0
  78. package/src/JztProductInfo/components/qxUnique.vue +101 -0
  79. package/src/JztProductInfo/components/records.vue +265 -0
  80. package/src/JztProductInfo/hooks/useParams.ts +143 -0
  81. package/src/JztProductInfo/hooks/useQxUnique.tsx +466 -0
  82. package/src/JztProductInfo/images/defaultProduct.png +0 -0
  83. package/src/JztProductInfo/index.ts +116 -0
  84. package/src/JztProductInfo/index.vue +108 -0
  85. package/src/JztProductInfo/interface/index.ts +15 -0
  86. package/src/JztQueryDetailTable/index.scss +100 -0
  87. package/src/JztQueryDetailTable/index.vue +400 -0
  88. package/src/JztQueryDetailTable/interface/index.ts +10 -0
  89. package/src/JztQueryTable/QueryTable /345/212/237/350/203/275.md" +1580 -0
  90. package/src/JztQueryTable/README.md +567 -0
  91. package/src/JztQueryTable/components/ColSetting.vue +67 -0
  92. package/src/JztQueryTable/components/ColumnsSetting.vue +404 -0
  93. package/src/JztQueryTable/components/ColumnsSetting1.vue +220 -0
  94. package/src/JztQueryTable/components/DeployToAccountLevelSetting.vue +351 -0
  95. package/src/JztQueryTable/components/Pagination.vue +54 -0
  96. package/src/JztQueryTable/components/TableColumn.vue +109 -0
  97. package/src/JztQueryTable/const.ts +1 -0
  98. package/src/JztQueryTable/hooks/useQueryTable.ts +194 -0
  99. package/src/JztQueryTable/hooks/useSelection.ts +47 -0
  100. package/src/JztQueryTable/hooks/useTableSetting.ts +197 -0
  101. package/src/JztQueryTable/hooks/useTemplate.ts +127 -0
  102. package/src/JztQueryTable/index.scss +91 -0
  103. package/src/JztQueryTable/index.vue +1445 -0
  104. package/src/JztQueryTable/interface/index.ts +185 -0
  105. package/src/JztRegionSelect/index.vue +134 -0
  106. package/src/JztSearchForm/components/SearchFormItem.vue +473 -0
  107. package/src/JztSearchForm/index.vue +530 -0
  108. package/src/JztSearchForm/interface/index.ts +100 -0
  109. package/src/JztSelectFilter/index.scss +63 -0
  110. package/src/JztSelectFilter/index.vue +110 -0
  111. package/src/JztSelectTable/index.vue +257 -0
  112. package/src/JztTable/index.scss +72 -0
  113. package/src/JztTable/index.vue +353 -0
  114. package/src/JztTable/interface/index.ts +1 -0
  115. package/src/JztTime/comm/agencySelect.vue +112 -0
  116. package/src/JztTime/comm/collapseRow.vue +132 -0
  117. package/src/JztTime/comm/dateSelect.vue +292 -0
  118. package/src/JztTime/comm/deptSelect.vue +193 -0
  119. package/src/JztTime/comm/typeSelect.vue +97 -0
  120. package/src/JztTime/index.ts +216 -0
  121. package/src/JztTime/index.vue +303 -0
  122. package/src/JztTime/interface/index.ts +23 -0
  123. package/src/JztTreeFilter/index.scss +44 -0
  124. package/src/JztTreeFilter/index.vue +177 -0
  125. package/src/JztUploadFile/interface/index.ts +21 -0
  126. package/src/JztUploadFile/multiple.scss +215 -0
  127. package/src/JztUploadFile/multiple.vue +318 -0
  128. package/src/JztUploadFile/single.scss +226 -0
  129. package/src/JztUploadFile/single.vue +274 -0
  130. package/src/JztUploadImg/Img.vue +294 -0
  131. package/src/JztUploadImg/Imgs.vue +411 -0
  132. package/src/JztUploadImg/index.scss +138 -0
  133. package/src/JztUploadImg/interface/index.ts +22 -0
  134. package/src/SelectIcon/index.scss +39 -0
  135. package/src/SelectIcon/index.vue +106 -0
  136. package/src/SvgIcon/index.vue +22 -0
  137. package/src/hooks/useAuthButtons.ts +58 -0
  138. package/src/hooks/useFormByUserType.ts +90 -0
  139. package/src/hooks/useTableEvents.ts +30 -0
  140. package/src/hooks/useUploadFileHook.ts +262 -0
  141. package/src/index.ts +91 -0
  142. package/src/typings/global.d.ts +101 -0
  143. package/src/utils/index.ts +107 -0
  144. package/src/utils/tree.ts +57 -0
  145. package/tsconfig.json +45 -0
@@ -0,0 +1,567 @@
1
+ # JztQueryTable 查询表格组件
2
+
3
+ > 一个基于 **配置驱动(Config-Driven)** 的查询表格组件,在 `el-table` 基础上做了二次封装。把一个表格页常见的「搜索区 + 顶部操作按钮 + 表格主体 + 操作列 + 分页」全部封装成配置项,页面里几乎不写搜索 / 重置 / 分页逻辑。
4
+
5
+ 适用场景:列表查询页、带 Tab 的数据页、主从选择弹窗等。
6
+
7
+ > ⚠️ **关于同目录下的 `QueryTable 功能.md`**:那是早期从掘金搬来的**旧版本文档**,多处已与当前代码不符(如旧的 `columns` 顶级 prop、搜索 `el` 写 `'input'` 等),**请以本 README 为准**。
8
+
9
+ ---
10
+
11
+ ## 目录
12
+
13
+ - [一、核心特性](#一核心特性)
14
+ - [二、快速上手](#二快速上手)
15
+ - [三、组件结构与数据流](#三组件结构与数据流)
16
+ - [四、组件 Props(ProTableProps)](#四组件-propsprotableprops)
17
+ - [五、数据请求与分页约定](#五数据请求与分页约定)
18
+ - [六、tableConfig.columns 列配置](#六tableconfigcolumns-列配置)
19
+ - [七、tableConfig.queryItems 搜索配置](#七tableconfigqueryitems-搜索配置)
20
+ - [八、enum 字典](#八enum-字典)
21
+ - [九、顶部按钮 topList 与操作列 operation](#九顶部按钮-toplist-与操作列-operation)
22
+ - [十、特殊列 columnConfig(选择/序号/展开/排序)](#十特殊列-columnconfig选择序号展开排序)
23
+ - [十一、多选与单选](#十一多选与单选)
24
+ - [十二、Tabs 页签切换](#十二tabs-页签切换)
25
+ - [十三、自定义渲染(render / 插槽 / 表头)](#十三自定义渲染render--插槽--表头)
26
+ - [十四、暴露的属性和方法(ref)](#十四暴露的属性和方法ref)
27
+ - [十五、事件](#十五事件)
28
+ - [十六、插槽](#十六插槽)
29
+ - [十七、列配置功能](#十七列配置功能)
30
+ - [十八、常见踩坑点](#十八常见踩坑点)
31
+
32
+ ---
33
+
34
+ ## 一、核心特性
35
+
36
+ 1. **属性透传**:组件通过 `v-bind="$attrs"` 把所有属性 / 事件透传给 `el-table`,**支持 el-table 的全部 Props、事件、方法**,无额外心智负担。
37
+ 2. **搜索 / 重置 / 分页全自动**:传入 `requestApi` 即可,组件内部 hooks(`useTable`)托管搜索、重置、分页查询。
38
+ 3. **配置化搜索区**:在列配置上写 `search` 字段即可让该项变成搜索条件,复用 `JztSearchForm`(Grid 响应式布局)。
39
+ 4. **字典 enum**:列 / 搜索项共用一份 `enum`,自动格式化单元格内容 & 填充搜索下拉。
40
+ 5. **内置操作按钮**:顶部 `topList`、行内 `operation` 操作列,支持权限、二次确认、动态显隐、自定义渲染。
41
+ 6. **多选 / 单选 / 展开 / 拖拽排序**:开箱即用,跨页多选也支持。
42
+ 7. **Tabs 页签**:内置 `tabs-list`,切换 Tab 自动切换列配置 / 重新请求。
43
+ 8. **列配置持久化**:配合 `gridParams`,支持用户自定义列显隐、顺序、每页条数并保存到后端。
44
+
45
+ ---
46
+
47
+ ## 二、快速上手
48
+
49
+ 最小可用示例(接口请求 + 搜索 + 分页 + 操作列):
50
+
51
+ ```vue
52
+ <template>
53
+ <JztQueryTable
54
+ ref="proTable"
55
+ :table-config="tableConfig"
56
+ :request-api="getUserList"
57
+ row-key="id"
58
+ >
59
+ <!-- 行内操作按钮的另一种写法:用插槽 -->
60
+ <template #operation="scope">
61
+ <el-button type="primary" link @click="onEdit(scope.row)">编辑</el-button>
62
+ </template>
63
+ </JztQueryTable>
64
+ </template>
65
+
66
+ <script setup lang="tsx">
67
+ import { JztQueryTable } from '@jzt-spd/components'
68
+ import type { ProTableInstance, TableConfigProps } from '@jzt-spd/components/JztQueryTable'
69
+ import { getUserList, getUserStatus } from '@/api/modules/user'
70
+
71
+ const proTable = ref<ProTableInstance>()
72
+
73
+ const tableConfig = reactive<TableConfigProps>({
74
+ // 1. 搜索条件(数组里每项既描述列,又可带 search 描述搜索框)
75
+ queryItems: [
76
+ { prop: 'username', label: '用户姓名', search: { el: 'el-input' } },
77
+ {
78
+ prop: 'status', label: '用户状态',
79
+ enum: getUserStatus, // 异步字典
80
+ fieldNames: { label: 'userLabel', value: 'userStatus' },
81
+ search: { el: 'el-select', props: { filterable: true } }
82
+ },
83
+ {
84
+ prop: 'createTime', label: '创建时间',
85
+ search: {
86
+ el: 'el-date-picker',
87
+ span: 2, // 搜索区占 2 列
88
+ props: { type: 'datetimerange', valueFormat: 'YYYY-MM-DD HH:mm:ss' }
89
+ }
90
+ }
91
+ ],
92
+ // 2. 顶部操作按钮
93
+ topList: [
94
+ { text: '新增', type: 'primary', fun: () => openDialog('add') },
95
+ { text: '批量删除', type: 'danger', fun: (scope) => batchDelete(scope.selectedListIds) }
96
+ ],
97
+ // 3. 表格列
98
+ columns: [
99
+ { type: 'selection', fixed: 'left', width: 55 }, // 多选列
100
+ { prop: 'username', label: '用户姓名', width: 150 },
101
+ { prop: 'status', label: '状态', tag: true, enum: getUserStatus, fieldNames: { label: 'userLabel', value: 'userStatus' } },
102
+ { prop: 'createTime', label: '创建时间', width: 180 },
103
+ {
104
+ prop: 'operation', label: '操作', fixed: 'right', width: 160,
105
+ btnLList: [
106
+ { text: '编辑', fun: (row) => openDialog('edit', row) },
107
+ { id: 'delete', text: '删除', type: 'danger', fun: (row) => deleteRow(row) } // id:'delete' 自动二次确认
108
+ ]
109
+ }
110
+ ]
111
+ })
112
+
113
+ const onEdit = (row) => openDialog('edit', row)
114
+ </script>
115
+ ```
116
+
117
+ > 💡 **导入约定**
118
+ > - 组件:`import { JztQueryTable } from '@jzt-spd/components'`
119
+ > - 类型:`import type { ProTableInstance, TableConfigProps, ColumnProps, TopButtonProps, tabsListProps } from '@jzt-spd/components/JztQueryTable'`
120
+
121
+ ---
122
+
123
+ ## 三、组件结构与数据流
124
+
125
+ ```
126
+ JztQueryTable/
127
+ ├── index.vue # 主组件:搜索区 + 表头按钮 + el-table + 分页
128
+ ├── interface/index.ts # 类型定义(ColumnProps / TopButtonProps / TableConfigProps 等)
129
+ ├── components/
130
+ │ ├── TableColumn.vue # 单列渲染:render/插槽/字典格式化/tag/小数/千分号
131
+ │ ├── Pagination.vue # 分页
132
+ │ ├── ColumnsSetting.vue # 列配置弹窗(显隐/排序/每页条数)
133
+ │ └── ...
134
+ └── hooks/
135
+ ├── useQueryTable.ts # useTable(请求/分页)+ useOperation(展开)
136
+ └── useSelection.ts # 多选状态管理
137
+ ```
138
+
139
+ **数据流**:
140
+
141
+ ```
142
+ tableConfig.queryItems ──> JztSearchForm(搜索区,Grid 布局)
143
+
144
+ ▼ 点击搜索/重置
145
+ useTable.getTableList()
146
+
147
+ requestApi(搜索参数 + 分页) ▼
148
+ ┌── dataCallback(可选,处理返回结构)──┐
149
+ ▼ │
150
+ { result/list, totalCount } ──> tableData ──> el-table 渲染
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 四、组件 Props(ProTableProps)
156
+
157
+ | 属性 | 类型 | 默认值 | 说明 |
158
+ | --- | --- | --- | --- |
159
+ | `tableConfig` | `TableConfigProps` | **必填** | 表格三合一配置:`{ queryItems, columns, topList }` |
160
+ | `requestApi` | `(params) => Promise` | - | 请求表格数据的接口(与 `data` 二选一) |
161
+ | `data` | `any[]` | - | **静态数据**;传了它则不会用 `requestApi`,并支持前端分页 |
162
+ | `requestAuto` | `boolean` | `true` | 初始化时是否自动请求 |
163
+ | `dataCallback` | `(data) => data` | - | 对接口返回数据做结构转换(见 [第五章](#五数据请求与分页约定)) |
164
+ | `initParam` | `object` | `{}` | 初始化请求参数,**每次请求都会带上**;变化会触发重新请求 |
165
+ | `pagination` | `boolean` | `true` | 是否显示分页 |
166
+ | `rowKey` | `string` | `'id'` | 行唯一标识(多选 / 树形 / 单选必填) |
167
+ | `border` | `boolean` | `true` | 是否显示纵向边框 |
168
+ | `toolButton` | `boolean \| Array` | `true` | 右上角功能按钮:`['refresh','setting','fullScreen']` |
169
+ | `columnConfig` | `ColumnConfigProps` | - | 特殊列配置(选择 / 序号 / 展开 / 排序) |
170
+ | `tabsList` | `tabsListProps[]` | - | Tab 页签数据 |
171
+ | `tabActive` / `tabActiveName` | `string \| number` | - | Tab 当前值 / 绑定到 `initParam` 的 key |
172
+ | `tabType` | `'' \| 'card' \| 'border-card'` | `''` | Tab 样式 |
173
+ | `tabIsTop` | `boolean` | `false` | Tab 是否放在搜索区顶部 |
174
+ | `tabChangeSearch` | `boolean` | `false` | 切换 Tab 是否自动重新请求 |
175
+ | `searchCol` | `number \| object` | `{xs:1,sm:2,md:3,lg:4,xl:5,xxl:6}` | 搜索区每行列数(响应式) |
176
+ | `searchCard` / `tableCard` | `boolean` | `true` | 搜索区 / 表格区是否卡片样式 |
177
+ | `allowCollapsed` / `collapsed` | `boolean` | `true` | 搜索区是否可折叠 / 默认折叠 |
178
+ | `validQuery` | `boolean` | `false` | 是否校验搜索条件必填项 |
179
+ | `searchClear` | `boolean` | `true` | 搜索时是否清空已勾选 / 单选 |
180
+ | `gridParams` | `{ gridId, url }` | - | 开启**列配置持久化**必须传 |
181
+ | `rowClassName` | `Function \| string` | - | 行样式 class |
182
+ | `emptyText` | `string` | `'暂无内容'` | 空数据文案 |
183
+ | `minHeight` | `string \| false` | `'170px'` | 表格最小高度 |
184
+
185
+ > **透传**:其余所有属性 / 事件(如 `highlight-current-row`、`show-summary`、`span-method`、`@row-click`)都通过 `$attrs` 透传给 `el-table`,按 Element Plus 文档用即可。
186
+
187
+ ---
188
+
189
+ ## 五、数据请求与分页约定
190
+
191
+ ### 1. 后端返回结构约定
192
+
193
+ 组件默认按如下结构解析返回数据:
194
+
195
+ ```ts
196
+ {
197
+ result: [...] | list: [...], // 列表数据(result 优先,其次 list)
198
+ totalCount: 100 // 总条数(分页时)
199
+ }
200
+ ```
201
+
202
+ 如果你的接口字段不一致,用 `dataCallback` 转换:
203
+
204
+ ```ts
205
+ :data-callback="(data) => ({ list: data.items, totalCount: data.total })"
206
+ ```
207
+
208
+ ### 2. 分页参数约定
209
+
210
+ - 发给后端的分页参数为 `{ pageIndex, pageSize }`,其中 **`pageIndex` 从 0 开始**(组件内部已做 `pageIndex - 1`)。
211
+ - 前端 `pageable.pageIndex` 从 1 开始(仅用于展示)。
212
+ - `pagination=false` 时,不发分页参数,且 `data` 若是数组则直接展示。
213
+
214
+ ### 3. 请求前对参数加工
215
+
216
+ - `searchFilter: (form) => form`:在请求前对合并后的参数做最后加工。
217
+ - `requestError: (err) => void`:请求失败回调。
218
+ - `tableLoaded: (data) => void`:数据加载完成回调。
219
+
220
+ ### 4. 静态数据 + 前端分页
221
+
222
+ 传 `:data="arr"` 且不传 `requestApi`,组件自动做**前端切片分页**。
223
+
224
+ ---
225
+
226
+ ## 六、tableConfig.columns 列配置
227
+
228
+ 每个元素是 `ColumnProps`(继承 `el-table-column` 全部属性):
229
+
230
+ | 字段 | 类型 | 说明 |
231
+ | --- | --- | --- |
232
+ | `prop` | `string` | 字段名,**支持多级**:`'user.detail.age'` |
233
+ | `label` | `string` | 列标题 |
234
+ | `type` | `TypeProps` | 特殊列:`'index' \| 'selection' \| 'radio' \| 'expand' \| 'sort'` |
235
+ | `width` / `minWidth` / `fixed` / `align` | - | 同 `el-table-column`(`align` 默认 `'left'`) |
236
+ | `isShow` | `boolean \| Ref<boolean>` | 是否显示该列(默认 `true`) |
237
+ | `isSetting` | `boolean \| Ref<boolean>` | 是否在列配置弹窗中可被用户调整(默认 `true`) |
238
+ | `enum` | 见 [第八章](#八enum-字典) | 字典,自动格式化单元格内容 |
239
+ | `isFilterEnum` | `boolean` | 单元格是否按 enum 格式化(默认 `true`;若 enum 仅用于搜索可设 `false`) |
240
+ | `fieldNames` | `{ label, value, children }` | 自定义字典字段映射 |
241
+ | `tag` | `boolean` | 是否用 `el-tag` 展示(配合 enum 的 `tagType`) |
242
+ | `render` | `(scope) => VNode` | 自定义单元格内容(tsx) |
243
+ | `headerRender` | `(scope) => VNode` | 自定义表头(tsx) |
244
+ | `formatter` | `(scope) => string` | 格式化单元格值 |
245
+ | `decimal` | `number \| 'numDecimal' \| 'countDecimal'` | 小数位数(`numDecimal`=金额配置、`countDecimal`=数量配置) |
246
+ | `isPerMille` | `boolean` | 是否千分号分隔 |
247
+ | `isUnderline` | `boolean` | 是否下划线 link 样式(配合 `fun` 点击) |
248
+ | `fun` | `(scope) => void` | 单元格点击事件(配合 `isUnderline`) |
249
+ | `_children` | `ColumnProps[]` | **多级表头** |
250
+ | `btnLList` | `TopButtonProps[]` | **操作列按钮**(`prop` 必须为 `'operation'`) |
251
+ | `selectable` | `(row, index) => boolean` | 多选时某行是否可选 |
252
+
253
+ > **单元格渲染优先级**(`TableColumn.vue`):
254
+ > `render` > `插槽(#prop)` > `isUnderline 可点击文本` > `enum 字典格式化 / formatter / decimal` > 原始值。
255
+ >
256
+ > **表头渲染优先级**:`headerRender` > `插槽(#propHeader)` > `label`。
257
+
258
+ ---
259
+
260
+ ## 七、tableConfig.queryItems 搜索配置
261
+
262
+ 搜索项就是「列 + search 配置」。每个元素是 `QueryItemsProps`:
263
+
264
+ | 字段 | 说明 |
265
+ | --- | --- |
266
+ | `prop` | 搜索字段名(提交接口的字段) |
267
+ | `label` | 标签 |
268
+ | `search` | **核心**,`SearchProps`,配置搜索框(见下表) |
269
+ | `enum` / `fieldNames` | 字典(与列共用) |
270
+ | `isSetFirstValue` | 是否默认选中第一项(异步字典) |
271
+ | `cascader` / `isAwait` | 级联:声明下级 / 等待上级 |
272
+ | `change` | `(scope) => void`,`scope` 含 `searchForm`、`value` 等 |
273
+ | `rules` | 校验规则(配合 `validQuery`) |
274
+ | `isShow` | 是否在搜索区显示 |
275
+
276
+ **`SearchProps` 关键字段:**
277
+
278
+ | 字段 | 类型 | 说明 |
279
+ | --- | --- | --- |
280
+ | `el` | `SearchType` | 控件类型,**必须带 `el-` 前缀**:`'el-input'`、`'el-select'`、`'el-date-picker'`、`'el-tree-select'`、`'el-cascader'`、`'el-radio-group'` 等 |
281
+ | `props` | `object` | 透传给控件(如 `placeholder`、`filterable`、`type`、`valueFormat`) |
282
+ | `events` | `object` | 透传事件(如 `change`、`focus`) |
283
+ | `defaultValue` | `any` | 默认值(重置时回到此值) |
284
+ | `span` | `number` | 占用列数,默认 `1`(与表格列的 24 栅格不同,这里是搜索区 Grid 列数) |
285
+ | `offset` | `number` | 左侧偏移列数 |
286
+ | `order` | `number` | 排序(数值越小越靠前) |
287
+ | `key` | `string` | 提交字段名与 `prop` 不同时用 `key` 指定 |
288
+ | `tooltip` | `string` | 搜索提示 |
289
+ | `render` | `(scope) => VNode` | 完全自定义搜索控件(tsx) |
290
+ | `isEnter` | `boolean` | 是否开启回车搜索 |
291
+
292
+ > ⚠️ **常见错误**:旧文档写 `search: { el: 'input' }`,**当前版本必须写 `el: 'el-input'`**(带前缀),否则不渲染。
293
+
294
+ ---
295
+
296
+ ## 八、enum 字典
297
+
298
+ `enum` 三种写法,**列与搜索项共用一份**:
299
+
300
+ ```ts
301
+ // 1. 静态数组
302
+ enum: [{ label: '启用', value: 1 }, { label: '禁用', value: 0 }]
303
+
304
+ // 2. 异步接口(函数,无参)
305
+ enum: getUserStatus
306
+
307
+ // 3. 异步接口(带参)
308
+ enum: (val) => getChildList(val) // 级联时 val = 上级值
309
+ enum: async () => { const res = await getX(); return res } // 自定义处理
310
+ ```
311
+
312
+ - 字段名不是 `label/value` 时,配 `fieldNames` 映射:
313
+ ```ts
314
+ fieldNames: { label: 'userLabel', value: 'userStatus' }
315
+ ```
316
+ - 列默认会用 enum 把 `value` 反显成 `label`;若 enum 只想给搜索框用,设 `isFilterEnum: false`。
317
+ - 配合 `tag: true`,可用 `el-tag` 展示,tag 类型取 enum 项的 `tagType`。
318
+
319
+ ---
320
+
321
+ ## 九、顶部按钮 topList 与操作列 operation
322
+
323
+ 两者结构相同,都是 `TopButtonProps[]`:
324
+
325
+ | 字段 | 说明 |
326
+ | --- | --- |
327
+ | `id` | 唯一标识;**`id: 'delete'` 会自动弹出二次确认** |
328
+ | `text` | 按钮文字 |
329
+ | `type` | `'primary' \| 'success' \| 'warning' \| 'danger' \| 'info'` |
330
+ | `fun` | 点击回调(见下方参数说明) |
331
+ | `hasPerm` | 权限标识,无权限自动隐藏 |
332
+ | `icon` / `plain` / `link` / `round` / `circle` / `loading` / `disabled` | 同 `el-button` |
333
+ | `hideFun` / `disabledFn` | 动态隐藏 / 禁用(返回 boolean) |
334
+ | `secondConfirm` | 自定义二次确认文案(配合弹窗确认) |
335
+ | `render` / `slotName` | 自定义按钮内容(tsx / 插槽) |
336
+ | `badgeProps` | 红点 / 数字角标 |
337
+
338
+ **回调参数:**
339
+
340
+ - **顶部按钮** `fun(scope)`:`scope` = `{ radio, radioItem, isSelected, selectedList, selectedListIds, searchParam, searchForm }`
341
+ - `searchForm` 是经过格式化(删除空值、拆分时间范围)后的查询参数。
342
+ - **操作列按钮** `fun(row)`:直接是当前行数据。
343
+ - 删除类按钮写 `id: 'delete'`,组件自动 `ElMessageBox.confirm('确定删除该条数据吗?')`,确认后才执行 `fun`。
344
+
345
+ ```ts
346
+ // 顶部按钮:拿勾选数据
347
+ { text: '批量删除', type: 'danger', fun: (scope) => batchDelete(scope.selectedListIds) }
348
+
349
+ // 操作列:自动二次确认
350
+ { id: 'delete', text: '删除', type: 'danger', fun: (row) => deleteApi({ id: row.id }) }
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 十、特殊列 columnConfig(选择/序号/展开/排序)
356
+
357
+ 除了在 `columns` 里写 `{ type: 'selection' }`,也可用 `columnConfig` 统一配置:
358
+
359
+ ```ts
360
+ :column-config="{
361
+ layout: 'selection,radio,index,expand,sort', // 逗号分隔的布局顺序
362
+ selectionProps: { fixed: 'left', width: 55 },
363
+ radioProps: { width: 70 },
364
+ indexProps: { label: '#' },
365
+ expandProps: { render: (scope) => <div>{scope.row.detail}</div> }, // 或用 #expand 插槽
366
+ sortProps: { label: '排序' }
367
+ }"
368
+ ```
369
+
370
+ - `layout` 支持的类型:`selection`(多选)、`radio`(单选)、`index`(序号)、`expand`(展开)、`sort`(拖拽排序)。
371
+ - 各 `xxxProps` 透传给对应的 `el-table-column`。
372
+
373
+ ---
374
+
375
+ ## 十一、多选与单选
376
+
377
+ - **多选**:`type: 'selection'`,勾选数据通过插槽 `scope` 或 ref 拿到。
378
+ - **单选**:`type: 'radio'`,组件维护 `radio` / `radioItem`,点击行或单选框触发 `radioChange`。
379
+ - 跨页多选需配 `row-key`,并在 selection 列加 `reserve-selection`。
380
+
381
+ **获取选中数据(任选其一):**
382
+
383
+ ```ts
384
+ // 方式1:ref
385
+ const { isSelected, selectedList, selectedListIds, radio, radioItem } = proTable.value?.getTableCheckedValue()
386
+
387
+ // 方式2:插槽 scope(见 #tableHeader)
388
+ <template #tableHeader="scope">
389
+ <el-button :disabled="!scope.isSelected" @click="batchDelete(scope.selectedListIds)">批量删除</el-button>
390
+ </template>
391
+ ```
392
+
393
+ 清空选中:`proTable.value?.clearSelection()`。
394
+
395
+ ---
396
+
397
+ ## 十二、Tabs 页签切换
398
+
399
+ ```ts
400
+ :tabs-list="[
401
+ { label: '全部', value: 0 },
402
+ { label: '待审核', value: 1, tabCount: 5 } // tabCount 显示数量角标
403
+ ]"
404
+ tab-active-name="activeType" // 绑定到 initParam.activeType
405
+ @tab-change="(val) => onTabChange(val)"
406
+ ```
407
+
408
+ - 切换 Tab 会把值写入 `initParam[tabActiveName]`,并可通过 `tabChangeSearch` 自动重新请求。
409
+ - **不同 Tab 不同列**:在 `@tab-change` 里动态替换 `tableConfig.columns`。
410
+ - 每个 Tab 可配独立 `gridId`(用于独立的列配置);若所有 Tab 的 `gridId` 一致或都没有,则共享同一份列配置。
411
+
412
+ ---
413
+
414
+ ## 十三、自定义渲染(render / 插槽 / 表头)
415
+
416
+ ### 1. tsx render(最灵活)
417
+
418
+ ```tsx
419
+ {
420
+ prop: 'status', label: '状态',
421
+ render: (scope) => (
422
+ <el-tag type={scope.row.status ? 'success' : 'danger'}>
423
+ {scope.row.status ? '启用' : '禁用'}
424
+ </el-tag>
425
+ )
426
+ }
427
+ ```
428
+
429
+ ### 2. 作用域插槽(无需 tsx)
430
+
431
+ ```vue
432
+ <!-- 单元格:#prop -->
433
+ <template #status="scope">
434
+ <el-tag>{{ scope.row.status ? '启用' : '禁用' }}</el-tag>
435
+ </template>
436
+
437
+ <!-- 表头:#propHeader -->
438
+ <template #createTimeHeader="scope">
439
+ <span style="color: #3475fa">{{ scope.column.label }}</span>
440
+ </template>
441
+ ```
442
+
443
+ ### 3. 表头 headerRender
444
+
445
+ ```tsx
446
+ { prop: 'createTime', label: '创建时间',
447
+ headerRender: (scope) => <el-button onClick={...}>{scope.column.label}</el-button> }
448
+ ```
449
+
450
+ ---
451
+
452
+ ## 十四、暴露的属性和方法(ref)
453
+
454
+ 通过 `proTable.value.xxx` 调用:
455
+
456
+ | 属性 / 方法 | 说明 |
457
+ | --- | --- |
458
+ | `element` | `el-table` 实例,可调用任意原生方法(如 `element.toggleRowSelection()`) |
459
+ | `tableData` | 当前展示的数据 |
460
+ | `pageable` | 分页信息 `{ pageIndex, pageSize, total }` |
461
+ | `searchParam` / `searchInitParam` | 当前 / 初始查询参数 |
462
+ | `formattedSearchForm` | 格式化后的查询参数 |
463
+ | `isSelected` / `selectedList` / `selectedListIds` | 多选状态 |
464
+ | **`getTableList()`** | 重新请求(携带当前查询 + 分页)—— **操作后刷新常用** |
465
+ | `search()` | 查询(重置到第 1 页) |
466
+ | `reset()` | 重置查询参数并重新请求 |
467
+ | `searchAndClear()` | 查询并清空勾选 / 单选 |
468
+ | `handleSizeChange(n)` / `handleCurrentChange(n)` | 改变每页条数 / 当前页 |
469
+ | `clearSelection()` | 清空多选 |
470
+ | `setRadioItem(key, row)` | 设置单选 |
471
+ | `getTableCheckedValue()` | 一次性拿到所有选中数据 |
472
+ | `toggleExpandAll(status?)` | 展开 / 折叠全部 |
473
+ | `changeTableData(cb)` | 回调式修改表格数据 |
474
+ | `saveColumns()` | 保存列配置 |
475
+ | `enumMap` | 当前表格所有字典数据(Map) |
476
+
477
+ **典型用法**:增删改后刷新 → `proTable.value?.getTableList()`。
478
+
479
+ ---
480
+
481
+ ## 十五、事件
482
+
483
+ | 事件 | 回调参数 | 说明 |
484
+ | --- | --- | --- |
485
+ | `search` | `searchForm` | 点击搜索 |
486
+ | `reset` | - | 点击重置 |
487
+ | `selectionChange` | `{ isSelected, selectedList, selectedListIds }` | 多选变化 |
488
+ | `radioChange` | `(key, row)` | 单选变化 |
489
+ | `tabChange` | `val` | Tab 切换 |
490
+ | `dragSort` | `{ newIndex, oldIndex }` | 拖拽排序完成 |
491
+ | `rowClick` | `(row, column)` | 行点击 |
492
+ | `cellClick` | `(row, column)` | 单元格点击 |
493
+ | `getEndColumns` | `{ allColumns, checkColumns, pageSize, id }` | 列配置变化 |
494
+ | `refreshColumnTable` | `type` | 列配置确定 / 重置 |
495
+
496
+ > el-table 原生事件(`sort-change`、`filter-change` 等)同样通过 `$attrs` 透传,直接 `@sort-change="..."` 即可。
497
+
498
+ ---
499
+
500
+ ## 十六、插槽
501
+
502
+ | 插槽 | 说明 |
503
+ | --- | --- |
504
+ | `tableHeader` | 表头左侧操作区(常用,含 `scope` 选中数据) |
505
+ | `toolBtnLeft` / `toolBtnRight` | 顶部按钮左右两侧(含 `scope`) |
506
+ | `tableTops` | 表格最顶部(搜索区下方、表格上方) |
507
+ | `tableBottom` | 表格底部(如「+ 新增行」按钮) |
508
+ | `rightBtn` / `formTopSlot` | 搜索区右侧 / 顶部 |
509
+ | `queryTableMain` | 整个表格主体(完全自定义) |
510
+ | `append` | 表格最后一行之后的内容 |
511
+ | `empty` / `empty-desc` | 空数据自定义 |
512
+ | `pagination` / `pageFooter` | 分页区 |
513
+ | `#prop` / `#propHeader` | 单元格 / 表头作用域插槽 |
514
+ | `expand` | 展开行内容 |
515
+ | `settingBtnLeft` / `toolButton` | 功能按钮区扩展 |
516
+
517
+ ---
518
+
519
+ ## 十七、列配置功能
520
+
521
+ 让用户自定义「列显隐、顺序、每页条数」并保存到后端:
522
+
523
+ ```ts
524
+ :tool-button="['refresh', 'setting', 'fullScreen']"
525
+ :grid-params="{ gridId: 'your-grid-id', url: '/api/column-setting' }"
526
+ ```
527
+
528
+ - 传了 `gridParams.url` 后,右上角齿轮按钮(`setting`)才会弹出列配置弹窗。
529
+ - 不传 `gridParams` 时,`setting` 仅为前端临时调整(刷新丢失)。
530
+ - 变化后通过 `@get-end-columns` 把结果回传给业务侧持久化。
531
+
532
+ ---
533
+
534
+ ## 十八、常见踩坑点
535
+
536
+ 1. **搜索 `el` 必须带 `el-` 前缀**:写 `'el-input'` 而非 `'input'`(旧文档的写法已失效)。可用类型见 `SearchType`。
537
+
538
+ 2. **配置走 `table-config`,不是顶级 `columns`**:当前版本用 `:table-config="{ queryItems, columns, topList }"`,旧文档的 `:columns="..."` 已废弃。
539
+
540
+ 3. **分页 `pageIndex` 从 0 开始**:组件已自动转换,后端按 0 起算;如果你的后端从 1 起算,需在 `requestApi` 里自行 `+1`。
541
+
542
+ 4. **返回字段不是 `result/totalCount`**:务必用 `dataCallback` 转成 `{ result/list, totalCount }`,否则表格空、分页失效。
543
+
544
+ 5. **多选 / 树形必须配 `row-key`**,跨页多选还需 selection 列的 `reserve-selection`。
545
+
546
+ 6. **删除按钮的二次确认**:操作列按钮 `id` 设为 `'delete'` 才会自动弹确认框;自定义文案用 `secondConfirm`。顶部按钮**不会**自动确认,需自己在 `fun` 里 `ElMessageBox.confirm`。
547
+
548
+ 7. **刷新表格统一用 `getTableList()`**:增删改后调 `proTable.value?.getTableList()`,不要手动改 `tableData`。
549
+
550
+ 8. **`initParam` 变化会自动重新请求**:适合做「外部条件驱动表格」(如 Tab、外部筛选)。若不希望自动请求,设 `:request-auto="false"` 后手动调 `getTableList()`。
551
+
552
+ 9. **静态数据 `data` 与 `requestApi` 互斥**:传了 `data` 就不会请求接口,且默认走前端分页。
553
+
554
+ 10. **`isShow` 可用 Ref 动态控制列显隐**:`isShow: toRef(myRef)`,适合「切换显示某列」的场景。
555
+
556
+ ---
557
+
558
+ ## 附:真实示例参考
559
+
560
+ | 场景 | 参考文件 |
561
+ | --- | --- |
562
+ | **官方完整示例(搜索/Tab/操作列/渲染)** | `spd-main-layout/src/views/proTable/useQueryTable/index.vue` |
563
+ | 复杂表格(多级表头 / 合计 / 合并 / 行样式) | `spd-main-layout/src/views/proTable/useComplexProTable/index.vue` |
564
+ | 树形过滤 + 表格 | `spd-main-layout/src/views/proTable/useTreeFilter/index.vue` |
565
+ | 业务列表(订单配送率) | `spd-basic-system/src/views/order/orderDeliveryRate/index.vue` |
566
+
567
+ > 建议新人先跑 `useQueryTable/index.vue`,对照本文档逐项调试,覆盖了 90% 的常用功能。
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <!-- 列设置 -->
3
+ <el-drawer v-model="drawerVisible" title="列设置" size="450px">
4
+ <div class="table-main">
5
+ <el-table
6
+ :data="colSetting"
7
+ :border="true"
8
+ row-key="prop"
9
+ default-expand-all
10
+ :tree-props="{ children: '_children' }"
11
+ >
12
+ <el-table-column prop="label" align="center" label="列名" />
13
+ <el-table-column
14
+ v-slot="scope"
15
+ prop="isShow"
16
+ align="center"
17
+ label="显示"
18
+ >
19
+ <el-switch
20
+ v-model="scope.row.isShow"
21
+ @change="changeShow(scope.row)"
22
+ ></el-switch>
23
+ </el-table-column>
24
+ <el-table-column
25
+ v-slot="scope"
26
+ prop="sortable"
27
+ align="center"
28
+ label="排序"
29
+ >
30
+ <el-switch v-model="scope.row.sortable"></el-switch>
31
+ </el-table-column>
32
+ <template #empty>
33
+ <div class="table-empty">
34
+ <img src="../assets//images//notData.png" alt="notData" />
35
+ <div>暂无可配置列</div>
36
+ </div>
37
+ </template>
38
+ </el-table>
39
+ </div>
40
+ </el-drawer>
41
+ </template>
42
+
43
+ <script setup lang="ts" name="ColSetting">
44
+ import { ref } from "vue";
45
+ import { ColumnProps } from "../interface";
46
+
47
+ defineProps<{ colSetting: ColumnProps[] }>();
48
+
49
+ const drawerVisible = ref<boolean>(false);
50
+
51
+ const openColSetting = () => {
52
+ drawerVisible.value = true;
53
+ };
54
+ const changeShow = (row: ColumnProps) => {
55
+ row.isHide = !row.isShow;
56
+ };
57
+
58
+ defineExpose({
59
+ openColSetting,
60
+ });
61
+ </script>
62
+
63
+ <style scoped lang="scss">
64
+ .cursor-move {
65
+ cursor: move;
66
+ }
67
+ </style>