vue3-smart-table 0.0.2 → 0.0.3
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/README.md +403 -42
- package/dist/vue3-smart-table.cjs.js +12 -2
- package/dist/vue3-smart-table.css +1 -1
- package/dist/vue3-smart-table.es.js +296 -240
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SmartTable 使用文档
|
|
2
2
|
|
|
3
3
|
## 概览
|
|
4
4
|
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
- 配置驱动(columns 即 schema)
|
|
8
8
|
- 权限解耦(不依赖 store / 登录体系)
|
|
9
9
|
- 操作列智能显示(无可见按钮 → 整列隐藏)
|
|
10
|
-
-
|
|
11
|
-
- 单元格渲染器体系(renderer
|
|
10
|
+
- 列显隐持久化(只缓存 visible)
|
|
11
|
+
- 单元格渲染器体系(renderer 插件化 + 插槽可自定义复杂列)
|
|
12
|
+
- 保留 SFC 模板渲染 input / input-number / select
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -17,8 +18,12 @@
|
|
|
17
18
|
```txt
|
|
18
19
|
SmartTable/
|
|
19
20
|
├─ column/
|
|
20
|
-
│ ├─
|
|
21
|
-
│
|
|
21
|
+
│ ├─ renderer
|
|
22
|
+
│ ├─ index.ts # createRenderer 工厂
|
|
23
|
+
│ ├─ input.vue # 可编辑 input
|
|
24
|
+
│ ├─ inputNumber.vue # 可编辑 input-number
|
|
25
|
+
│ └─ select.vue # 可编辑 select
|
|
26
|
+
│ └─ index.vue # TableColumn 子组件
|
|
22
27
|
├─ hooks/
|
|
23
28
|
│ ├─ useOperationColumn.ts # 操作列按钮可见性 / 宽度逻辑
|
|
24
29
|
│ └─ useTableColumns.ts # 列显隐缓存(只缓存 visible)
|
|
@@ -55,10 +60,8 @@ export interface ColumnConfig<R = any> {
|
|
|
55
60
|
|
|
56
61
|
visible?: boolean
|
|
57
62
|
inControl?: boolean
|
|
58
|
-
|
|
59
63
|
render?: string
|
|
60
|
-
|
|
61
|
-
editType?: 'input' | 'number' | 'select'
|
|
64
|
+
slot?: string // render为slot时可自定slot否则使用key
|
|
62
65
|
|
|
63
66
|
renderProps?: Record<string, any>
|
|
64
67
|
columnProps?: Record<string, any>
|
|
@@ -78,6 +81,7 @@ export interface ColumnConfig<R = any> {
|
|
|
78
81
|
- `selection / index / operation` 为 **核心列**
|
|
79
82
|
- 核心列必须:`inControl = false`
|
|
80
83
|
- 普通列通过 `visible` 控制显示 / 隐藏
|
|
84
|
+
- 可通过 render 使用内置 renderer 或自定义插槽
|
|
81
85
|
|
|
82
86
|
---
|
|
83
87
|
|
|
@@ -119,9 +123,74 @@ export interface ButtonConfig<R = any> {
|
|
|
119
123
|
| `dict` | 字典映射 |
|
|
120
124
|
| `map` | key-value 映射 |
|
|
121
125
|
| `formatter` | 自定义格式化 |
|
|
122
|
-
| `editable` | 可编辑单元格(input / number / select) |
|
|
123
126
|
| `icon` | iconfont / svg / url |
|
|
127
|
+
| `input` | 可编辑单元格 |
|
|
128
|
+
| `input-number` | 可编辑单元格 |
|
|
129
|
+
| `select` | 可编辑单元格 |
|
|
130
|
+
| `button` | 单行按钮 |
|
|
131
|
+
| `link` | 单行链接 |
|
|
132
|
+
| `slot` | 自定义插槽,插槽名称默认用 key |
|
|
133
|
+
|
|
134
|
+
### 插槽自定义复杂列
|
|
135
|
+
- 如果某一列过于复杂,可通过 #key 插槽完全自定义:
|
|
136
|
+
- 自定义插槽 render="slot",插槽名称默认用 key,配置slot="xx"可自定义slot名称
|
|
137
|
+
```vue
|
|
138
|
+
<script>
|
|
139
|
+
const columns = [{
|
|
140
|
+
key: "attachments",
|
|
141
|
+
label: "自定义复杂列",
|
|
142
|
+
visible: true,
|
|
143
|
+
render: 'slot',
|
|
144
|
+
slot: 'attachments',
|
|
145
|
+
columnProps: { minWidth: 100, align: 'right'},
|
|
146
|
+
}]
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<template>
|
|
150
|
+
<SmartTable :columns="columns" :data="tableData">
|
|
151
|
+
<template #attachments="{ row }">
|
|
152
|
+
<div v-for="(item, index) in row.attachments" :key="index">
|
|
153
|
+
<el-image v-if="item.fileType === 1" :src="item.thumbnailUrl" :preview-src-list="row.imgPaths"/>
|
|
154
|
+
<el-button v-if="item.fileType === 0" type="text" @click="download(item.fileUrl)">下载日志</el-button>
|
|
155
|
+
<div v-if="item.fileType === 2" @click="handleVideo(item.fileUrl)">
|
|
156
|
+
<img :src="item.thumbnailUrl" alt="video"/>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</template>
|
|
160
|
+
</SmartTable>
|
|
161
|
+
</template>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 编辑型 渲染器
|
|
165
|
+
- 支持类型:input / number / select
|
|
166
|
+
- 支持事件:
|
|
167
|
+
- cellChange(row, col) 值变化
|
|
168
|
+
- cellBlur(row, col) 失去焦点
|
|
169
|
+
- cellEnter(row, col) 回车事件(input)
|
|
170
|
+
```vue
|
|
171
|
+
<script>
|
|
172
|
+
const columns = [
|
|
173
|
+
{
|
|
174
|
+
key: "selectId",
|
|
175
|
+
label: "可选单元格",
|
|
176
|
+
render: "select",
|
|
177
|
+
renderProps: { options: [{label:'选中-1', value:1}, {label:'选中-2', value:2}] }
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
key: "orderNum",
|
|
181
|
+
label: "输入单元格",
|
|
182
|
+
render: "input-number",
|
|
183
|
+
renderProps: { min: 0, max: 150 }
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
key: "username",
|
|
187
|
+
label: "姓名",
|
|
188
|
+
render: "input"
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
</script>
|
|
124
192
|
|
|
193
|
+
```
|
|
125
194
|
### copy 示例
|
|
126
195
|
|
|
127
196
|
```ts
|
|
@@ -143,12 +212,39 @@ export interface ButtonConfig<R = any> {
|
|
|
143
212
|
key: 'avatar',
|
|
144
213
|
label: '头像',
|
|
145
214
|
render: 'img',
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
215
|
+
columnProps: { minWidth: 150},
|
|
216
|
+
renderProps: {
|
|
217
|
+
width: '60px',
|
|
218
|
+
height: '60px',
|
|
219
|
+
fit: 'cover',
|
|
220
|
+
placeholder: '--'
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
key: 'gallery',
|
|
225
|
+
label: '相册',
|
|
226
|
+
render: 'img',
|
|
227
|
+
columnProps: { minWidth: 150},
|
|
228
|
+
renderProps: {
|
|
229
|
+
width: '100px',
|
|
230
|
+
height: '100px'
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
const tableData = reactive([
|
|
234
|
+
{ id: 3, name: 'Charlie', code: '9525', status: 0, map: 1, regionCode:'海外', orderNum: 1, selectId: 2,
|
|
235
|
+
avatar: 'https://iconfont.alicdn.com/p/illus_3d/file/UMAqlm6KX5gw/8e357f00-9a4e-44c4-b0c5-bbed255cff24.png' ,
|
|
236
|
+
gallery: [
|
|
237
|
+
'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
|
|
238
|
+
'https://iconfont.alicdn.com/p/illus_3d/file/UMAqlm6KX5gw/8e357f00-9a4e-44c4-b0c5-bbed255cff24.png',
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
])
|
|
149
242
|
```
|
|
150
|
-
-
|
|
151
|
-
-
|
|
243
|
+
- 单张图片:正常显示并支持预览
|
|
244
|
+
- 多张图片:显示第一张,右侧显示剩余数量(如:+2)
|
|
245
|
+
- 支持 previewSrcList 自定义预览列表
|
|
246
|
+
- 支持图片大小(width/height)、填充模式(fit)配置
|
|
247
|
+
- 无图片时显示占位符或空内容
|
|
152
248
|
|
|
153
249
|
### map 示例
|
|
154
250
|
|
|
@@ -208,23 +304,7 @@ const Enables = [
|
|
|
208
304
|
```
|
|
209
305
|
- 使用自定义函数格式化显示内容
|
|
210
306
|
|
|
211
|
-
### editable 渲染器
|
|
212
307
|
|
|
213
|
-
```ts
|
|
214
|
-
{
|
|
215
|
-
key: 'age',
|
|
216
|
-
label: '年龄',
|
|
217
|
-
render: 'editable',
|
|
218
|
-
editable: true,
|
|
219
|
-
editType: 'number',
|
|
220
|
-
renderProps: { min: 0, max: 120 }
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
- 支持类型:input / number / select
|
|
224
|
-
- 支持事件:
|
|
225
|
-
- cellChange(row, key) 值变化
|
|
226
|
-
- cellBlur(row, key) 失去焦点
|
|
227
|
-
- cellEnter(row, key) 回车事件(input)
|
|
228
308
|
### icon 示例
|
|
229
309
|
```ts
|
|
230
310
|
{
|
|
@@ -237,7 +317,21 @@ const Enables = [
|
|
|
237
317
|
- 支持网络图片 URL
|
|
238
318
|
- 支持 svg 字符串
|
|
239
319
|
- 支持 iconfont class
|
|
320
|
+
|
|
321
|
+
### button / link
|
|
322
|
+
```ts
|
|
323
|
+
{ key: 'action', label: '操作', render: 'button', renderProps: { label: '编辑', type: 'text' } }
|
|
324
|
+
{ key: 'url', label: '查看', render: 'link', renderProps: { label: '详情', href: 'https://example.com', blank: true } }
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
- 支持事件:
|
|
328
|
+
- cellClick(row, col) 点击事件
|
|
329
|
+
|
|
330
|
+
|
|
240
331
|
## 5. useTableColumns(列显隐缓存)
|
|
332
|
+
```ts
|
|
333
|
+
const { columns } = useTableColumns(defaultColumns, { pageKey: 'user-list', userId: currentUserId })
|
|
334
|
+
```
|
|
241
335
|
|
|
242
336
|
### 设计原则
|
|
243
337
|
|
|
@@ -257,26 +351,293 @@ const { columns } = useTableColumns(defaultColumns, {
|
|
|
257
351
|
- 不传则不启用缓存
|
|
258
352
|
|
|
259
353
|
---
|
|
354
|
+
## 6. 事件
|
|
355
|
+
- 支持类型:input / number / select
|
|
356
|
+
- 支持事件:
|
|
357
|
+
- cellChange(row, col) 值变化
|
|
358
|
+
- cellBlur(row, col) 失去焦点
|
|
359
|
+
- cellEnter(row, col) 回车事件(input)
|
|
360
|
+
- cellClick(row, col) 点击事件
|
|
260
361
|
|
|
261
|
-
|
|
362
|
+
|
|
363
|
+
## 7. 使用示例
|
|
262
364
|
|
|
263
365
|
```vue
|
|
366
|
+
<!-- 全局注册 -->
|
|
367
|
+
import { createApp } from 'vue'
|
|
368
|
+
import App from './App.vue'
|
|
369
|
+
import { SmartTable } from 'vue3-smart-table'
|
|
370
|
+
|
|
371
|
+
const app = createApp(App)
|
|
372
|
+
app.component('SmartTable', SmartTable)
|
|
373
|
+
app.mount('#app')
|
|
374
|
+
|
|
375
|
+
<!-- 或者局部注册 -->
|
|
376
|
+
<script setup>
|
|
377
|
+
import { SmartTable } from 'vue3-smart-table'
|
|
378
|
+
</script>
|
|
379
|
+
|
|
264
380
|
<SmartTable
|
|
265
|
-
:data="tableData"
|
|
266
381
|
v-model:columns="columns"
|
|
267
|
-
:
|
|
268
|
-
:
|
|
269
|
-
|
|
382
|
+
:border="true"
|
|
383
|
+
:loading="loading"
|
|
384
|
+
:pageKey="route.name"
|
|
385
|
+
:rowKey="'appId'"
|
|
386
|
+
:data="tabList"
|
|
387
|
+
:userId="userInfo?.userId"
|
|
388
|
+
:permissions="userStore.permissions"
|
|
270
389
|
@cellChange="onCellChange"
|
|
271
|
-
|
|
390
|
+
@cellBlur="onCellBlur"
|
|
391
|
+
@cellEnter="onCellEnter"
|
|
392
|
+
@cellClick="onCellClick" >
|
|
393
|
+
<!-- 自定义复杂列 -->
|
|
394
|
+
<template #attachments="{ row }">
|
|
395
|
+
<div v-for="(item, index) in row.attachments" :key="index">
|
|
396
|
+
<el-image v-if="item.fileType === 1" :src="item.thumbnailUrl" :preview-src-list="row.imgPaths"/>
|
|
397
|
+
<el-button v-if="item.fileType === 0" type="text" @click="download(item.fileUrl)">下载日志</el-button>
|
|
398
|
+
<div v-if="item.fileType === 2" @click="handleVideo(item.fileUrl)">
|
|
399
|
+
<img :src="item.thumbnailUrl" alt="video"/>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
</template>
|
|
403
|
+
</SmartTable>
|
|
404
|
+
```
|
|
405
|
+
## 完整示例代码
|
|
406
|
+

|
|
407
|
+
```vue
|
|
408
|
+
<template>
|
|
409
|
+
<div class="demo-container" style="padding: 20px;">
|
|
410
|
+
<h2>Demo</h2>
|
|
411
|
+
<SmartTable
|
|
412
|
+
class="h-400px"
|
|
413
|
+
class-name="table-flex"
|
|
414
|
+
:border="true"
|
|
415
|
+
:loading="loading"
|
|
416
|
+
:pageKey="'route.name'"
|
|
417
|
+
:rowKey="'id'"
|
|
418
|
+
:data="tableData"
|
|
419
|
+
v-model:columns="columns"
|
|
420
|
+
:userId="'userId'"
|
|
421
|
+
:permissions="permissions"
|
|
422
|
+
@cell-blur="onCellBlur"
|
|
423
|
+
@cell-enter="onCellEnter"
|
|
424
|
+
@cell-change="onCellChange"
|
|
425
|
+
@cell-click="onCellClick"
|
|
426
|
+
/>
|
|
427
|
+
</div>
|
|
428
|
+
</template>
|
|
429
|
+
|
|
430
|
+
<script setup lang="ts" name="APP">
|
|
431
|
+
import { reactive, ref } from 'vue'
|
|
432
|
+
import { SmartTable } from 'vue3-smart-table'
|
|
433
|
+
const loading = ref(false)
|
|
434
|
+
const Enables = [
|
|
435
|
+
{ label: '启用', value: 1, listClass: 'primary' },
|
|
436
|
+
{ label: '禁用', value: 0, listClass: 'warning' }
|
|
437
|
+
]
|
|
438
|
+
const buttonConfigs = [
|
|
439
|
+
{ permission: 'edit', label: '编辑', type: 'primary', action: (row: any) => console.log(row)},
|
|
440
|
+
{ permission: 'view', label:'删除', type: 'danger', action: (row: any) => console.log(row)},
|
|
441
|
+
{ permission: 'copy', label: '复制', type: 'success', action: (row: any) => console.log(row)},
|
|
442
|
+
]
|
|
443
|
+
const permissions = ['edit', 'view']
|
|
444
|
+
const columns = ref([
|
|
445
|
+
{
|
|
446
|
+
type: 'selection',
|
|
447
|
+
key: 'index',
|
|
448
|
+
inControl: false,
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
type: 'index',
|
|
452
|
+
key: 'index',
|
|
453
|
+
label: '序号',
|
|
454
|
+
inControl: false,
|
|
455
|
+
columnProps: { width: 60}
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
type: 'operation',
|
|
459
|
+
key: 'opt',
|
|
460
|
+
label: '操作',
|
|
461
|
+
inControl: false,
|
|
462
|
+
buttons: buttonConfigs,
|
|
463
|
+
columnProps: {
|
|
464
|
+
fixed: "right",
|
|
465
|
+
align: "left"
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
key: 'action',
|
|
470
|
+
label: '按钮',
|
|
471
|
+
render: 'button',
|
|
472
|
+
renderProps: {
|
|
473
|
+
label: '编辑',
|
|
474
|
+
type: 'text'
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
key: 'url',
|
|
479
|
+
label: 'li单元格',
|
|
480
|
+
render: 'link',
|
|
481
|
+
renderProps: {
|
|
482
|
+
label: '查看详情',
|
|
483
|
+
href: 'https://example.com',
|
|
484
|
+
blank: true
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
key: "selectId",
|
|
489
|
+
label: "可选单元格",
|
|
490
|
+
visible: true,
|
|
491
|
+
render: 'select',
|
|
492
|
+
columnProps: { minWidth: 150},
|
|
493
|
+
renderProps:{
|
|
494
|
+
options: [
|
|
495
|
+
{label: '选中-1', value: 1},
|
|
496
|
+
{label: '选中-2', value: 2},
|
|
497
|
+
]
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
key: "orderNum",
|
|
502
|
+
label: "输入单元格",
|
|
503
|
+
visible: true,
|
|
504
|
+
render: 'input-number',
|
|
505
|
+
columnProps: { minWidth: 150, sortable: true}
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
key: 'avatar',
|
|
509
|
+
label: '头像',
|
|
510
|
+
render: 'img',
|
|
511
|
+
columnProps: { minWidth: 150, sortable: true},
|
|
512
|
+
renderProps: {
|
|
513
|
+
width: '60px',
|
|
514
|
+
height: '60px',
|
|
515
|
+
fit: 'cover',
|
|
516
|
+
placeholder: '--'
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
key: 'gallery',
|
|
521
|
+
label: '相册',
|
|
522
|
+
render: 'img',
|
|
523
|
+
columnProps: { minWidth: 150, sortable: true},
|
|
524
|
+
renderProps: {
|
|
525
|
+
width: '100px',
|
|
526
|
+
height: '100px'
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
key: 'name',
|
|
531
|
+
label: 'Name',
|
|
532
|
+
visible: true,
|
|
533
|
+
render: 'html'
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
key: "code",
|
|
537
|
+
label: "系统标识",
|
|
538
|
+
visible: true,
|
|
539
|
+
render: "copy",
|
|
540
|
+
columnProps: { minWidth: 160, sortable: true}
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
key: "status",
|
|
544
|
+
label: "状态",
|
|
545
|
+
visible: true,
|
|
546
|
+
render: "dict",
|
|
547
|
+
renderProps: {
|
|
548
|
+
options: Enables,
|
|
549
|
+
},
|
|
550
|
+
columnProps: { minWidth: 80, sortable: true}
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
key: 'map',
|
|
554
|
+
label: 'Map',
|
|
555
|
+
visible: true,
|
|
556
|
+
render: 'map',
|
|
557
|
+
renderProps: { options: { 1: 'Active', 0: 'Inactive' } }
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
key: "regionCode",
|
|
561
|
+
label: "区域",
|
|
562
|
+
visible: true,
|
|
563
|
+
render: "formatter",
|
|
564
|
+
columnProps: { minWidth: 100, sortable: true, align: 'left'},
|
|
565
|
+
formatter: (val: string) => `${val}-123`,
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
key: "regionCode",
|
|
569
|
+
label: "自定义复杂列",
|
|
570
|
+
visible: true,
|
|
571
|
+
columnProps: { minWidth: 100, align: 'right'},
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
key: "handling.feedbackId",
|
|
575
|
+
label: "key.key取值",
|
|
576
|
+
visible: true,
|
|
577
|
+
columnProps: { minWidth: 100, align: 'right'},
|
|
578
|
+
},
|
|
579
|
+
])
|
|
580
|
+
|
|
581
|
+
const tableData = reactive([
|
|
582
|
+
{ id: 1, name: 'Alice', code: '9527', status: 1, map: 1, regionCode:'海外', orderNum: 1, selectId: 1 },
|
|
583
|
+
{ id: 2, name: 'Bob', code: '9526', status: 1, map: 1, regionCode:'海外', orderNum: 1, selectId: 1 },
|
|
584
|
+
{ id: 3, name: 'Charlie', code: '9525', status: 0, map: 1, regionCode:'海外', orderNum: 1, selectId: 2 },
|
|
585
|
+
{ id: 3, name: 'Charlie', code: '9525', status: 0, map: 1, regionCode:'海外', orderNum: 1, selectId: 2,
|
|
586
|
+
avatar: 'https://iconfont.alicdn.com/p/illus_3d/file/UMAqlm6KX5gw/8e357f00-9a4e-44c4-b0c5-bbed255cff24.png' ,
|
|
587
|
+
gallery: [
|
|
588
|
+
'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
|
|
589
|
+
'https://iconfont.alicdn.com/p/illus_3d/file/UMAqlm6KX5gw/8e357f00-9a4e-44c4-b0c5-bbed255cff24.png',
|
|
590
|
+
],
|
|
591
|
+
attachments: [
|
|
592
|
+
{
|
|
593
|
+
"id": 1337611,
|
|
594
|
+
"feedbackId": 1334127,
|
|
595
|
+
"fileType": 1,
|
|
596
|
+
"fileUrl": "http://xxxxxxxxxxxxxxxx/attachment/cn.com.blackview.dashcam/2025/12/17/193000-1334127-1.jpg",
|
|
597
|
+
"fileSize": 298696,
|
|
598
|
+
"thumbnailUrl": "http://xxxxxxxxxxxxxxxxxx/attachment/cn.com.blackview.dashcam/2025/12/17/193000-1334127-1-thumbnail.jpg"
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
"id": 1337612,
|
|
602
|
+
"feedbackId": 1334127,
|
|
603
|
+
"fileType": 0,
|
|
604
|
+
"fileUrl": "http://xxxxxxxxxxxxxxxxx/attachment/cn.com.blackview.dashcam/2025/12/17/193000-1334127-2.txt",
|
|
605
|
+
"fileSize": 1619,
|
|
606
|
+
"thumbnailUrl": null
|
|
607
|
+
}
|
|
608
|
+
],
|
|
609
|
+
handling: {
|
|
610
|
+
"id": 1334076,
|
|
611
|
+
"feedbackId": 1334160,
|
|
612
|
+
"problemCategory": null,
|
|
613
|
+
"handlePerson": null,
|
|
614
|
+
"handleTime": "2025-12-19 09:51:05",
|
|
615
|
+
"handleRemark": null,
|
|
616
|
+
"handleStatus": 1,
|
|
617
|
+
"callbackStatus": 1,
|
|
618
|
+
"solveStatus": 1
|
|
619
|
+
}
|
|
620
|
+
},
|
|
621
|
+
])
|
|
622
|
+
|
|
623
|
+
// 编辑单元格回调
|
|
624
|
+
const onCellBlur = (row: any, col: any) => {
|
|
625
|
+
console.log('cell blur:', row, col)
|
|
626
|
+
}
|
|
627
|
+
const onCellEnter = (row: any, col: any) => {
|
|
628
|
+
console.log('cell enter:', row, col)
|
|
629
|
+
}
|
|
630
|
+
const onCellChange = (row: any, col: any) => {
|
|
631
|
+
console.log('cell Change:', row, col)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const onCellClick = (row: any, col: any) => {
|
|
635
|
+
console.log('cell button click:', row, col)
|
|
636
|
+
}
|
|
637
|
+
</script>
|
|
638
|
+
|
|
272
639
|
```
|
|
273
640
|
|
|
274
|
-
## 7. 设计边界说明
|
|
275
|
-
|
|
276
|
-
- SmartTable **不关心权限系统如何实现**
|
|
277
|
-
- permission 只是 string 比对
|
|
278
|
-
- renderer 只负责 UI,不处理权限
|
|
279
|
-
- 操作列是否显示由 SmartTable 统一决策
|
|
280
641
|
|
|
281
642
|
|
|
282
643
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("vue"),v=require("element-plus");function g(n,t){if(!(!n||!t))return t.split(".").reduce((r,o)=>r==null?void 0:r[o],n)}function S(n,t,r){if(!n||!t)return;const o=t.split("."),l=o.pop(),c=o.reduce((s,a)=>(s[a]||(s[a]={}),s[a]),n);c[l]=r}const A=e.defineComponent({__name:"input",props:{row:{},col:{},onCellBlur:{type:Function},onCellEnter:{type:Function}},setup(n){const t=n,r=e.ref(g(t.row,t.col.key));e.watch(r,c=>{S(t.row,t.col.key,c)});const o=()=>{var c;return(c=t.onCellBlur)==null?void 0:c.call(t,t.row,t.col)},l=()=>{var c;return(c=t.onCellEnter)==null?void 0:c.call(t,t.row,t.col)};return(c,s)=>{const a=e.resolveComponent("el-input");return e.openBlock(),e.createBlock(a,e.mergeProps({modelValue:r.value,"onUpdate:modelValue":s[0]||(s[0]=m=>r.value=m)},{placeholder:"",size:"small",clearable:!0,...n.col.renderProps},{onBlur:o,onKeyup:e.withKeys(l,["enter"])}),null,16,["modelValue"])}}}),O=e.defineComponent({__name:"inputNumber",props:{row:{},col:{},onCellChange:{type:Function},onCellBlur:{type:Function},onCellEnter:{type:Function}},setup(n){const t=n,r=e.ref(g(t.row,t.col.key));e.watch(r,c=>{var s;S(t.row,t.col.key,c),(s=t.onCellChange)==null||s.call(t,t.row,t.col)});const o=()=>{var c;return(c=t.onCellBlur)==null?void 0:c.call(t,t.row,t.col)},l=()=>{var c;return(c=t.onCellEnter)==null?void 0:c.call(t,t.row,t.col)};return(c,s)=>{const a=e.resolveComponent("el-input-number");return e.openBlock(),e.createBlock(a,e.mergeProps({modelValue:r.value,"onUpdate:modelValue":s[0]||(s[0]=m=>r.value=m)},{min:0,max:99999,controls:!1,size:"small",...n.col.renderProps},{onBlur:o,onKeyup:e.withKeys(l,["enter"])}),null,16,["modelValue"])}}}),I=e.defineComponent({__name:"select",props:{row:{},col:{},onCellChange:{type:Function},onCellBlur:{type:Function},onCellEnter:{type:Function}},setup(n){const t=n,r=e.ref(g(t.row,t.col.key));e.watch(r,s=>{S(t.row,t.col.key,s)});const o=()=>{var s;return(s=t.onCellChange)==null?void 0:s.call(t,t.row,t.col)},l=()=>{var s;return(s=t.onCellBlur)==null?void 0:s.call(t,t.row,t.col)},c=()=>{var s;return(s=t.onCellEnter)==null?void 0:s.call(t,t.row,t.col)};return(s,a)=>{const m=e.resolveComponent("el-option"),d=e.resolveComponent("el-select");return e.openBlock(),e.createBlock(d,e.mergeProps({modelValue:r.value,"onUpdate:modelValue":a[0]||(a[0]=h=>r.value=h)},{placeholder:"请选择",size:"small",clearable:!0,...n.col.renderProps},{onChange:o,onBlur:l,onKeyup:e.withKeys(c,["enter"])}),{default:e.withCtx(()=>{var h;return[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(((h=n.col.renderProps)==null?void 0:h.options)||[],u=>(e.openBlock(),e.createBlock(m,{key:u.value,label:u.label,value:u.value},null,8,["label","value"]))),128))]}),_:1},16,["modelValue"])}}}),P=n=>e.defineComponent({props:["row","col","onCellChange","onCellBlur","onCellEnter","onClick"],setup(t){return()=>e.h(n,t)}});function L(n){return typeof n.formatter=="function"}function M(){return{input:P(A),"input-number":P(O),select:P(I),button:n=>{const t=n.col.renderProps||{},r=g(n.row,n.col.key);return e.h(v.ElButton,{type:t.type||"primary",...t,onClick:()=>{var o;return(o=n.onClick)==null?void 0:o.call(n,n.row,n.col)}},()=>t.label||r)},link:n=>{const t=n.col.renderProps||{},r=g(n.row,n.col.key);return e.h("a",{href:t.href||"#",target:t.blank?"_blank":"_self",style:t.style||"color:#409EFF;cursor:pointer;",onClick:o=>{var l;o.preventDefault(),(l=n.onClick)==null||l.call(n,n.row,n.col)}},t.label||r)},html:n=>{var r;const t=g(n.row,n.col.key);return e.h("div",{class:"line-clamp-2",innerHTML:t??"",...((r=n.col)==null?void 0:r.renderProps)||{}})},copy:n=>{const t=g(n.row,n.col.key)??"";return e.h("div",{class:"copy-wrapper",style:"position: relative; display: inline-block;"},[e.h("span",{class:"copy-text line-clamp-1",style:"padding-right: 20px;"},t),e.h("span",{class:"copy-btn",style:`
|
|
2
2
|
position: absolute;
|
|
3
3
|
right: 0;
|
|
4
4
|
top: 50%;
|
|
@@ -8,4 +8,14 @@
|
|
|
8
8
|
font-size: 12px;
|
|
9
9
|
color: #409EFF;
|
|
10
10
|
user-select: none;
|
|
11
|
-
`,onClick:()=>{if(t)try{if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(t).then(()=>{
|
|
11
|
+
`,onClick:()=>{if(t)try{if(navigator.clipboard&&navigator.clipboard.writeText)navigator.clipboard.writeText(t).then(()=>{v.ElMessage.success("复制成功")}).catch(()=>{v.ElMessage.error("复制失败")});else{const r=document.createElement("textarea");r.value=t,r.style.position="fixed",r.style.opacity="0",document.body.appendChild(r),r.select();const o=document.execCommand("copy");document.body.removeChild(r),o?v.ElMessage.success("复制成功"):v.ElMessage.error("复制失败")}}catch{v.ElMessage.error("复制失败")}}},"📋")])},img:n=>{var s;const t=g(n.row,n.col.key)??"",r=((s=n.col)==null?void 0:s.renderProps)||{},l=t?Array.isArray(t)?t.filter(a=>a&&typeof a=="string"):[t]:[];if(l.length===0)return r.placeholder||"";const c={width:r.width||"80px",height:r.height||"80px",marginRight:l.length>1?"4px":"0",...r.style||{}};return l.length===1?e.h(v.ElImage,{src:l[0],previewSrcList:r.previewSrcList||l,fit:r.fit||"contain",style:c,...r}):(console.log(r.previewSrcList),e.h("div",{style:"display: flex; align-items: center; position: relative"},[e.h(v.ElImage,{src:l[0],previewSrcList:r.previewSrcList||l,fit:r.fit||"contain",style:c,...r}),l.length>1&&e.h("span",{style:`
|
|
12
|
+
margin-left: 8px;
|
|
13
|
+
font-size: 12px;
|
|
14
|
+
color: #666;
|
|
15
|
+
background: #f0f0f0;
|
|
16
|
+
padding: 2px 6px;
|
|
17
|
+
border-radius: 2px;
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 0;
|
|
20
|
+
right: 0;
|
|
21
|
+
`,title:`共 ${l.length} 张图片`},`+${l.length-1}`)]))},dict:n=>{const t=g(n.row,n.col.key)??"",r=n.col.renderProps||{},o=r.options??[],l=r.showValue??!1;if(t==null||t==="")return"";const c=Array.isArray(t)?t.map(String):[String(t)],s=o.filter(d=>c.includes(String(d.value))),a=c.filter(d=>!o.some(h=>String(h.value)===d)),m=s.map((d,h)=>e.h(v.ElTag,{key:d.value,type:d.listClass,class:d.cssClass,disableTransitions:!0},{default:()=>d.label+" "}));return l&&a.length>0&&m.push(e.h("span",{},a.join(" "))),e.h("div",{},m)},map:n=>{var o;const t=g(n.row,n.col.key)??"",r=((o=n.col.renderProps)==null?void 0:o.options)??{};return t!=null?r[t]??"":""},formatter:n=>{var l;const{col:t,row:r}=n,o=g(n.row,n.col.key)??"";return L(t)?(l=t.formatter)==null?void 0:l.call(t,o,r):o??""},icon:n=>{const t=g(n.row,n.col.key)??"",r=n.col.renderProps||{};return t?/^https?:\/\//.test(t)?e.h(v.ElImage,{src:t,previewSrcList:[t],fit:"contain",style:"width:40px;height:40px",...r}):/^\s*<svg[\s\S]*<\/svg>\s*$/.test(t)?e.h("div",{innerHTML:t,style:`width:40px;height:40px;display:inline-block;${r.style||""}`,...r}):e.h("i",{class:t,style:`font-size:20px;${r.style||""}`,...r}):""}}}function T(n,t=10,r=[]){const l="*:*:*",c=i=>{if(!i)return!0;const p=Array.isArray(i)?i:[i];return r.some(f=>f===l||p.includes(f))},s=e.computed(()=>n.some(i=>c(i.permission))),a=e.computed(()=>n.filter(p=>c(p.permission)).slice(0,t).reduce((p,f)=>p+(f.width??60),0)),m=(i,p)=>c(i.permission)&&(i.visible?i.visible(p):!0),d=i=>n.filter(f=>m(f,i)).slice(0,t).reduce((f,b)=>f+(b.width??60),0);return{hasAnyButton:s,optWidth:a,hasAnyVisibleButton:i=>i!=null&&i.length?i.some(p=>n.some(f=>m(f,p))):!1,getMaxOptWidth:i=>i!=null&&i.length?i.reduce((p,f)=>Math.max(p,d(f)),0):a.value,getVisibleButtons:i=>n.filter(p=>m(p,i)).slice(0,t)}}const W=["title"],z=e.defineComponent({__name:"index",props:{col:{type:Object,required:!0},permissions:{type:Array,default:()=>[]}},emits:["cellBlur","cellEnter","cellChange","cellClick"],setup(n,{emit:t}){const r=n,o=t,{col:l}=e.toRefs(r),c=(y,C)=>o("cellChange",y,C),s=(y,C)=>o("cellBlur",y,C),a=(y,C)=>o("cellEnter",y,C),m=(y,C)=>o("cellClick",y,C),d=M(),{hasAnyButton:h,hasAnyVisibleButton:u,optWidth:k,getMaxOptWidth:i,getVisibleButtons:p}=T(l.value.buttons||[],l.value.maxbtn??10,r.permissions||[]),f=e.computed(()=>(l.value.buttons||[]).length?(l.value.__rows||[]).length?u(l.value.__rows||[]):h.value:!1),b=e.computed(()=>l.value.__rows?i(l.value.__rows):k.value);function x(y){return!(y.type==="selection"||y.type==="index"||y.type==="operation"&&!f.value||y.visible===!1)}return(y,C)=>{const _=e.resolveComponent("el-table-column"),$=e.resolveComponent("el-button");return e.unref(l).type==="selection"?(e.openBlock(),e.createBlock(_,e.mergeProps({key:0,type:"selection"},e.unref(l).columnProps),null,16)):e.unref(l).type==="index"?(e.openBlock(),e.createBlock(_,e.mergeProps({key:1,type:"index",label:e.unref(l).label||"#",align:"center"},e.unref(l).columnProps),null,16,["label"])):e.unref(l).type==="operation"&&f.value?(e.openBlock(),e.createBlock(_,e.mergeProps({key:2,label:e.unref(l).label||"操作",align:"center"},{...e.unref(l).columnProps,width:b.value}),{default:e.withCtx(({row:w})=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(p)(w),B=>(e.openBlock(),e.createBlock($,{key:B.label,type:B.type||"primary",link:"",onClick:E=>B.action(w)},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(B.label),1)]),_:2},1032,["type","onClick"]))),128))]),_:1},16,["label"])):x(e.unref(l))?(e.openBlock(),e.createBlock(_,e.mergeProps({key:3,label:e.unref(l).label,align:"center"},e.unref(l).columnProps||{}),{default:e.withCtx(w=>{var B,E,V,F;return[e.unref(l).render==="slot"&&y.$slots[((B=e.unref(l))==null?void 0:B.slot)||e.unref(l).key]?e.renderSlot(y.$slots,((E=e.unref(l))==null?void 0:E.slot)||e.unref(l).key,e.normalizeProps(e.mergeProps({key:0},w))):e.unref(l).render&&e.unref(d)[e.unref(l).render]?(e.openBlock(),e.createBlock(e.resolveDynamicComponent(e.unref(d)[e.unref(l).render]),{key:1,row:w.row,col:e.unref(l),onCellChange:c,onCellBlur:s,onCellEnter:a,onClick:m},null,40,["row","col"])):(e.openBlock(),e.createElementBlock("span",{key:2,style:e.normalizeStyle(((V=e.unref(l).renderProps)==null?void 0:V.style)||""),class:e.normalizeClass(((F=e.unref(l).renderProps)==null?void 0:F.class)||""),title:e.unref(g)(w.row,e.unref(l).key)},e.toDisplayString(e.unref(g)(w.row,e.unref(l).key)),15,W))]}),_:3},16,["label"])):e.createCommentVNode("",!0)}}}),D="table_columns_";function N(n,t){return`${D}${n}_${t}`}function K(n,t){if(!(t!=null&&t.length))return n;const r=new Map(t.map(o=>[o.key,o]));return n.map(o=>{const l=r.get(o.key);return l?{...o,visible:typeof l.visible=="boolean"?l.visible:o.visible}:o})}function R(n,t){const{pageKey:r,userId:o,storage:l=localStorage}=t||{},s=o?N(o,r||""):null,a=s?l.getItem(s):null,m=e.ref(K(n,a?JSON.parse(a):[]));return e.watch(m,d=>{if(!s)return;const h=d.map(u=>({key:u.key,visible:u.visible,columnOpts:u.columnOpts}));l.setItem(s,JSON.stringify(h))},{deep:!0}),{columns:m,setColumns(d){m.value=K(n,d),s&&l.setItem(s,JSON.stringify(d))},resetColumns(){m.value=n,s&&l.removeItem(s)}}}const q=e.defineComponent({__name:"index",props:{data:{type:Array,default:()=>[]},columns:{type:Array,default:()=>[]},pageKey:String,rowKey:{type:String,default:"id"},loading:{type:Boolean,default:!1},permissions:{type:Array,default:()=>[]},userId:{type:[String,Number],default:""}},emits:["update:columns","cellChange","cellBlur","cellEnter","cell-click"],setup(n,{expose:t,emit:r}){const o=n,l=r,{columns:c}=R(o.columns,{pageKey:o.pageKey??"",userId:o.userId??""});e.watch(c,u=>l("update:columns",u),{deep:!0,immediate:!0});const s=(u,k)=>l("cellChange",u,k),a=(u,k)=>{l("cellBlur",u,k)},m=(u,k)=>{console.log("enter"),l("cellEnter",u,k)},d=(u,k)=>{k&&l("cell-click",u,k)},h=e.ref();return t({tableRef:h}),(u,k)=>{const i=e.resolveComponent("el-table"),p=e.resolveDirective("loading");return e.withDirectives((e.openBlock(),e.createBlock(i,e.mergeProps({ref_key:"tableRef",ref:h},u.$attrs,{data:n.data,"row-key":n.rowKey,class:"smart-table"}),{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(c),f=>(e.openBlock(),e.createBlock(z,{key:f.key,col:f,permissions:n.permissions,onCellChange:s,onCellBlur:a,onCellEnter:m,onCellClick:d},e.createSlots({_:2},[e.renderList(e.unref(c),b=>({name:b.key,fn:e.withCtx(x=>[e.renderSlot(u.$slots,b.key,e.mergeProps({ref_for:!0},x),void 0,!0)])}))]),1032,["col","permissions"]))),128))]),_:3},16,["data","row-key"])),[[p,n.loading]])}}}),J=(n,t)=>{const r=n.__vccOpts||n;for(const[o,l]of t)r[o]=l;return r},U=J(q,[["__scopeId","data-v-25f299e7"]]);exports.SmartTable=U;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.copy-wrapper:hover .copy-btn{display:inline-block!important}.smart-table[data-v-
|
|
1
|
+
.copy-wrapper:hover .copy-btn{display:inline-block!important}.smart-table[data-v-25f299e7]{width:100%}
|