@a2simcode/ui 0.0.180 → 0.0.181
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/dist/components/input-layer/index.d.ts +11 -3
- package/dist/components/input-layer/src/input-layer.vue.d.ts +9 -2
- package/dist/components/table-panel/index.d.ts +3 -0
- package/dist/components/table-panel/src/table-panel.vue.d.ts +8 -0
- package/dist/simcode-ui.es.js +2738 -2723
- package/dist/simcode-ui.umd.js +2 -2
- package/dist/stats.html +1 -1
- package/docs/components/input-layer.md +15 -0
- package/docs/components/meta/input-layer.ts +17 -1
- package/docs/components/meta/table-panel.ts +5 -0
- package/docs/components/table-panel.md +15 -0
- package/docs/examples/input-layer/render-vnode.vue +127 -0
- package/docs/examples/table-panel/mask.vue +151 -0
- package/package.json +1 -1
|
@@ -15,12 +15,27 @@
|
|
|
15
15
|
</template>
|
|
16
16
|
</Demo>
|
|
17
17
|
|
|
18
|
+
## 自定义渲染
|
|
19
|
+
|
|
20
|
+
通过 `render-v-node` 透传到底层 `j-table-panel`,可以用 VNode 自定义每条记录的展示方式(例如卡片列表)。示例中点击卡片会设置选中项,点击“确定”后回填到输入框。
|
|
21
|
+
|
|
22
|
+
<Demo :source-code="inputLayerRenderVNodeCode">
|
|
23
|
+
<template #source>
|
|
24
|
+
<input-layer-render-vnode />
|
|
25
|
+
</template>
|
|
26
|
+
<template #description>
|
|
27
|
+
`render-v-node` 接收当前行数据并返回 VNode;点击卡片设置选中项后,InputLayer 会从表格面板的选中项回填值与文本。
|
|
28
|
+
</template>
|
|
29
|
+
</Demo>
|
|
30
|
+
|
|
18
31
|
## API
|
|
19
32
|
|
|
20
33
|
<ApiTable :data="inputLayerApi" componentName="input-layer" />
|
|
21
34
|
|
|
22
35
|
<script setup>
|
|
23
36
|
import InputLayerBasic from '../examples/input-layer/basic.vue'
|
|
37
|
+
import InputLayerRenderVnode from '../examples/input-layer/render-vnode.vue'
|
|
24
38
|
import inputLayerBasicCode from '../examples/input-layer/basic.vue?raw'
|
|
39
|
+
import inputLayerRenderVNodeCode from '../examples/input-layer/render-vnode.vue?raw'
|
|
25
40
|
import inputLayerApi from './meta/input-layer'
|
|
26
41
|
</script>
|
|
@@ -92,6 +92,11 @@ export default {
|
|
|
92
92
|
"description": "加载数据方法",
|
|
93
93
|
"type": "TSFunctionType"
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
"name": "renderVNode",
|
|
97
|
+
"description": "自定义渲染 VNode",
|
|
98
|
+
"type": "TSFunctionType"
|
|
99
|
+
},
|
|
95
100
|
{
|
|
96
101
|
"name": "getText",
|
|
97
102
|
"description": "获取显示值方法",
|
|
@@ -195,7 +200,13 @@ export default {
|
|
|
195
200
|
}
|
|
196
201
|
],
|
|
197
202
|
"slots": [],
|
|
198
|
-
"methods": [
|
|
203
|
+
"methods": [
|
|
204
|
+
{
|
|
205
|
+
"name": "getTablePanelRef",
|
|
206
|
+
"description": "",
|
|
207
|
+
"type": "() => void"
|
|
208
|
+
}
|
|
209
|
+
],
|
|
199
210
|
"types": [
|
|
200
211
|
{
|
|
201
212
|
"name": "InputLayerProps",
|
|
@@ -280,6 +291,11 @@ export default {
|
|
|
280
291
|
"type": "(params: any) => Promise<any>",
|
|
281
292
|
"description": "加载数据方法"
|
|
282
293
|
},
|
|
294
|
+
{
|
|
295
|
+
"name": "renderVNode",
|
|
296
|
+
"type": "(params: Record<string, any>) => VNode",
|
|
297
|
+
"description": "自定义渲染 VNode"
|
|
298
|
+
},
|
|
283
299
|
{
|
|
284
300
|
"name": "getText",
|
|
285
301
|
"type": "(value: string) => string",
|
|
@@ -181,6 +181,11 @@ export default {
|
|
|
181
181
|
"description": "是否显示全屏按钮",
|
|
182
182
|
"type": "boolean",
|
|
183
183
|
"default": "true"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"name": "renderVNode",
|
|
187
|
+
"description": "自定义渲染 VNode",
|
|
188
|
+
"type": "(params: Record<string, any>) => import('vue').VNode"
|
|
184
189
|
}
|
|
185
190
|
],
|
|
186
191
|
"events": [
|
|
@@ -166,6 +166,19 @@
|
|
|
166
166
|
</template>
|
|
167
167
|
</Demo>
|
|
168
168
|
|
|
169
|
+
## 自定义渲染(掩码示例)
|
|
170
|
+
|
|
171
|
+
通过 `render-v-node` 可以跳过内置表格渲染,使用 VNode 自定义每条记录的展示方式。下面示例用卡片布局展示行数据,并支持一键切换手机号/邮箱的掩码显示。
|
|
172
|
+
|
|
173
|
+
<Demo :source-code="tablePanelMaskCode">
|
|
174
|
+
<template #source>
|
|
175
|
+
<table-panel-mask />
|
|
176
|
+
</template>
|
|
177
|
+
<template #description>
|
|
178
|
+
`render-v-node` 接收当前行数据并返回 VNode;建议同时配置 `row-key`,便于列表渲染时保持稳定 key。
|
|
179
|
+
</template>
|
|
180
|
+
</Demo>
|
|
181
|
+
|
|
169
182
|
## API
|
|
170
183
|
|
|
171
184
|
<ApiTable :data="tablePanelApi" componentName="table-panel" />
|
|
@@ -222,6 +235,7 @@ import TablePanelBatchOperations from '../examples/table-panel/batch-operations.
|
|
|
222
235
|
import TablePanelSubTableLazy from '../examples/table-panel/sub-table-lazy.vue'
|
|
223
236
|
import TablePanelGetSelection from '../examples/table-panel/get-selection.vue'
|
|
224
237
|
import TablePanelButtonVisibility from '../examples/table-panel/button-visibility.vue'
|
|
238
|
+
import TablePanelMask from '../examples/table-panel/mask.vue'
|
|
225
239
|
import tablePanelApi from './meta/table-panel'
|
|
226
240
|
|
|
227
241
|
import tablePanelBasicCode from '../examples/table-panel/basic.vue?raw'
|
|
@@ -233,4 +247,5 @@ import tablePanelBatchOperationsCode from '../examples/table-panel/batch-operati
|
|
|
233
247
|
import tablePanelSubTableLazyCode from '../examples/table-panel/sub-table-lazy.vue?raw'
|
|
234
248
|
import tablePanelGetSelectionCode from '../examples/table-panel/get-selection.vue?raw'
|
|
235
249
|
import tablePanelButtonVisibilityCode from '../examples/table-panel/button-visibility.vue?raw'
|
|
250
|
+
import tablePanelMaskCode from '../examples/table-panel/mask.vue?raw'
|
|
236
251
|
</script>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="width: 420px">
|
|
3
|
+
<j-input-layer
|
|
4
|
+
ref="inputLayerRef"
|
|
5
|
+
v-model="value"
|
|
6
|
+
placeholder="请选择用户(卡片渲染)"
|
|
7
|
+
:columns="columns"
|
|
8
|
+
:load-layer-data="loadData"
|
|
9
|
+
:width="800"
|
|
10
|
+
:height="500"
|
|
11
|
+
value-key="id"
|
|
12
|
+
label-key="name"
|
|
13
|
+
:render-v-node="renderVNode"
|
|
14
|
+
@change="handleChange"
|
|
15
|
+
/>
|
|
16
|
+
<div style="margin-top: 10px">选中值: {{ value }}</div>
|
|
17
|
+
</div>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<script setup lang="ts">
|
|
21
|
+
import { ref, h } from 'vue'
|
|
22
|
+
|
|
23
|
+
const value = ref('')
|
|
24
|
+
const selectedId = ref<string>('')
|
|
25
|
+
const inputLayerRef = ref<any>()
|
|
26
|
+
|
|
27
|
+
const handleChange = (data: any) => {
|
|
28
|
+
console.log(data, 'data')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const columns = [
|
|
32
|
+
{
|
|
33
|
+
id: 'id',
|
|
34
|
+
config: {
|
|
35
|
+
label: 'ID',
|
|
36
|
+
width: 80,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'name',
|
|
41
|
+
config: {
|
|
42
|
+
label: '姓名',
|
|
43
|
+
width: 120,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'age',
|
|
48
|
+
config: {
|
|
49
|
+
label: '年龄',
|
|
50
|
+
width: 80,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'email',
|
|
55
|
+
config: {
|
|
56
|
+
label: '邮箱',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
const loadData = async () => {
|
|
62
|
+
return [
|
|
63
|
+
{ id: '1', name: '张三', age: 25, email: 'zhangsan@example.com' },
|
|
64
|
+
{ id: '2', name: '李四', age: 30, email: 'lisi@example.com' },
|
|
65
|
+
{ id: '3', name: '王五', age: 28, email: 'wangwu@example.com' },
|
|
66
|
+
{ id: '4', name: '赵六', age: 35, email: 'zhaoliu@example.com' },
|
|
67
|
+
{ id: '5', name: '孙七', age: 22, email: 'sunqi@example.com' },
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const setSelection = (row: Record<string, any>) => {
|
|
72
|
+
selectedId.value = String(row.id ?? '')
|
|
73
|
+
const tablePanelRef = inputLayerRef.value?.getTablePanelRef?.()
|
|
74
|
+
tablePanelRef?.setSelection?.([row])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const renderVNode = (row: Record<string, any>) => {
|
|
78
|
+
const isActive = selectedId.value === String(row.id ?? '')
|
|
79
|
+
|
|
80
|
+
return h(
|
|
81
|
+
'div',
|
|
82
|
+
{
|
|
83
|
+
class: ['input-layer-card', isActive ? 'is-active' : ''],
|
|
84
|
+
onClick: () => setSelection(row),
|
|
85
|
+
},
|
|
86
|
+
[
|
|
87
|
+
h('div', { class: 'input-layer-card__title' }, [h('span', row.name), h('span', row.id)]),
|
|
88
|
+
h('div', { class: 'input-layer-card__meta' }, `年龄:${row.age}`),
|
|
89
|
+
h('div', { class: 'input-layer-card__meta' }, `邮箱:${row.email}`),
|
|
90
|
+
]
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<style scoped>
|
|
96
|
+
.input-layer-card {
|
|
97
|
+
padding: 12px 14px;
|
|
98
|
+
border: 1px solid var(--j-color-border);
|
|
99
|
+
border-radius: 6px;
|
|
100
|
+
background: var(--j-color-bg-container);
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
transition: border-color 0.15s ease;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.input-layer-card + .input-layer-card {
|
|
106
|
+
margin-top: 10px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.input-layer-card.is-active {
|
|
110
|
+
border-color: var(--j-color-primary);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.input-layer-card__title {
|
|
114
|
+
display: flex;
|
|
115
|
+
justify-content: space-between;
|
|
116
|
+
gap: 10px;
|
|
117
|
+
margin-bottom: 6px;
|
|
118
|
+
color: var(--j-color-text);
|
|
119
|
+
font-weight: 600;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.input-layer-card__meta {
|
|
123
|
+
line-height: 20px;
|
|
124
|
+
font-size: 12px;
|
|
125
|
+
color: var(--j-color-text-2);
|
|
126
|
+
}
|
|
127
|
+
</style>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="table-panel-mask-demo">
|
|
3
|
+
<j-table-panel
|
|
4
|
+
row-key="id"
|
|
5
|
+
:columns="columns"
|
|
6
|
+
:load-data="loadData"
|
|
7
|
+
:buttons="buttons"
|
|
8
|
+
:render-v-node="renderVNode"
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { ref, h } from 'vue'
|
|
15
|
+
|
|
16
|
+
const isMasked = ref(true)
|
|
17
|
+
|
|
18
|
+
const columns = ref([
|
|
19
|
+
{
|
|
20
|
+
id: 'id',
|
|
21
|
+
type: '',
|
|
22
|
+
config: { label: 'ID', width: 120 },
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'name',
|
|
26
|
+
type: '',
|
|
27
|
+
config: {
|
|
28
|
+
label: '姓名',
|
|
29
|
+
filter: { isSearchKeyword: true },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'phone',
|
|
34
|
+
type: '',
|
|
35
|
+
config: { label: '手机号' },
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'email',
|
|
39
|
+
type: '',
|
|
40
|
+
config: { label: '邮箱' },
|
|
41
|
+
},
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
const buttons = ref([
|
|
45
|
+
{
|
|
46
|
+
label: '切换掩码',
|
|
47
|
+
icon: 'mdi:eye',
|
|
48
|
+
click: () => {
|
|
49
|
+
isMasked.value = !isMasked.value
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
const data = [
|
|
55
|
+
{ id: 'U-1001', name: '张三', phone: '13812345678', email: 'zhangsan@example.com' },
|
|
56
|
+
{ id: 'U-1002', name: '李四', phone: '18698765432', email: 'lisi@example.com' },
|
|
57
|
+
{ id: 'U-1003', name: '王五', phone: '15900001111', email: 'wangwu@example.com' },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
const loadData = async () => data
|
|
61
|
+
|
|
62
|
+
const maskPhone = (value?: string) => {
|
|
63
|
+
if (!value) return ''
|
|
64
|
+
if (value.length <= 7) return value
|
|
65
|
+
return `${value.slice(0, 3)}****${value.slice(-4)}`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const maskEmail = (value?: string) => {
|
|
69
|
+
if (!value) return ''
|
|
70
|
+
const at = value.indexOf('@')
|
|
71
|
+
if (at <= 1) return value
|
|
72
|
+
return `${value.slice(0, 1)}****${value.slice(at)}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const renderVNode = (row: Record<string, any>) => {
|
|
76
|
+
const phoneText = isMasked.value ? maskPhone(row.phone) : row.phone
|
|
77
|
+
const emailText = isMasked.value ? maskEmail(row.email) : row.email
|
|
78
|
+
|
|
79
|
+
return h('div', { class: 'mask-row' }, [
|
|
80
|
+
h('div', { class: 'mask-row__main' }, [
|
|
81
|
+
h('div', { class: 'mask-row__title' }, [
|
|
82
|
+
h('span', { class: 'mask-row__name' }, row.name),
|
|
83
|
+
h('span', { class: 'mask-row__id' }, row.id),
|
|
84
|
+
]),
|
|
85
|
+
h('div', { class: 'mask-row__kv' }, [
|
|
86
|
+
h('span', { class: 'mask-row__k' }, '手机号'),
|
|
87
|
+
h('span', { class: 'mask-row__v' }, phoneText),
|
|
88
|
+
]),
|
|
89
|
+
h('div', { class: 'mask-row__kv' }, [
|
|
90
|
+
h('span', { class: 'mask-row__k' }, '邮箱'),
|
|
91
|
+
h('span', { class: 'mask-row__v' }, emailText),
|
|
92
|
+
]),
|
|
93
|
+
]),
|
|
94
|
+
])
|
|
95
|
+
}
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
.table-panel-mask-demo {
|
|
100
|
+
position: relative;
|
|
101
|
+
width: 100%;
|
|
102
|
+
height: 500px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.mask-row {
|
|
106
|
+
padding: 12px 16px;
|
|
107
|
+
border: 1px solid var(--j-color-border);
|
|
108
|
+
border-radius: 6px;
|
|
109
|
+
background: var(--j-color-bg-container);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.mask-row + .mask-row {
|
|
113
|
+
margin-top: 10px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.mask-row__title {
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: baseline;
|
|
119
|
+
justify-content: space-between;
|
|
120
|
+
margin-bottom: 8px;
|
|
121
|
+
gap: 12px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.mask-row__name {
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
color: var(--j-color-text);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.mask-row__id {
|
|
130
|
+
font-size: 12px;
|
|
131
|
+
color: var(--j-color-text-3);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.mask-row__kv {
|
|
135
|
+
display: flex;
|
|
136
|
+
gap: 10px;
|
|
137
|
+
line-height: 20px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.mask-row__k {
|
|
141
|
+
width: 48px;
|
|
142
|
+
flex: 0 0 auto;
|
|
143
|
+
color: var(--j-color-text-2);
|
|
144
|
+
font-size: 12px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.mask-row__v {
|
|
148
|
+
color: var(--j-color-text);
|
|
149
|
+
font-size: 12px;
|
|
150
|
+
}
|
|
151
|
+
</style>
|