@ebiz/designer-components 0.1.46 → 0.1.47
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/designer-components.css +1 -1
- package/dist/index.mjs +7129 -7068
- package/package.json +1 -1
- package/src/components/EbizDetailItem.vue +164 -70
- package/src/components/EbizDetailView.vue +84 -36
- package/src/views/EbizDetailViewDemo.vue +221 -10
package/package.json
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<t-col :span="finalSpan" :xs="finalXs" :sm="finalSm" :md="finalMd"
|
|
3
|
+
:lg="finalLg" :xl="finalXl" :xxl="finalXxl">
|
|
4
|
+
<div class="ebiz-detail-item" :class="{
|
|
5
|
+
'vertical-layout': finalLayout === 'vertical',
|
|
6
|
+
'horizontal-layout': finalLayout === 'horizontal',
|
|
5
7
|
}">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
<!-- 标签部分 -->
|
|
9
|
+
<div class="detail-label" :style="{
|
|
10
|
+
width: finalLayout === 'horizontal' ? `${finalLabelWidth}px` : 'auto',
|
|
11
|
+
color: finalLabelColor
|
|
10
12
|
}">
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<div class="field-value">
|
|
17
|
-
<slot name="default" :data="displayValue">
|
|
13
|
+
{{ label }}
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<!-- 值部分 -->
|
|
17
|
+
<div class="detail-value">
|
|
18
18
|
<!-- 文本类型 -->
|
|
19
|
-
<span v-if="type === 'text'"
|
|
20
|
-
|
|
19
|
+
<span v-if="type === 'text'">{{ value || '-' }}</span>
|
|
20
|
+
|
|
21
|
+
<!-- 数字类型 -->
|
|
22
|
+
<span v-else-if="type === 'number'">{{ formatNumber(value) }}</span>
|
|
23
|
+
|
|
24
|
+
<!-- 货币类型 -->
|
|
25
|
+
<span v-else-if="type === 'currency'" class="currency-value">
|
|
26
|
+
{{ formatCurrency(value) }}
|
|
21
27
|
</span>
|
|
22
|
-
|
|
28
|
+
|
|
23
29
|
<!-- 用户类型 -->
|
|
24
30
|
<div v-else-if="type === 'user'" class="user-value">
|
|
25
31
|
<div class="user-list">
|
|
@@ -35,65 +41,52 @@
|
|
|
35
41
|
</div>
|
|
36
42
|
</div>
|
|
37
43
|
</div>
|
|
38
|
-
|
|
44
|
+
|
|
39
45
|
<!-- 文件类型 -->
|
|
40
46
|
<div v-else-if="type === 'file'" class="file-value">
|
|
41
47
|
<EbizFileList :files="fileList" :mode="fileMode" :showDownload="showDownload"
|
|
42
48
|
@download="handleDownloadFile" />
|
|
43
49
|
</div>
|
|
44
|
-
|
|
50
|
+
|
|
45
51
|
<!-- 日期类型 -->
|
|
46
|
-
<span v-else-if="type === 'date'"
|
|
47
|
-
|
|
48
|
-
</span>
|
|
49
|
-
|
|
52
|
+
<span v-else-if="type === 'date'">{{ formatDate(value) }}</span>
|
|
53
|
+
|
|
50
54
|
<!-- 状态类型 -->
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
</div>
|
|
56
|
-
|
|
55
|
+
<t-tag v-else-if="type === 'status'" :theme="getStatusTheme(value)">
|
|
56
|
+
{{ getStatusText(value) }}
|
|
57
|
+
</t-tag>
|
|
58
|
+
|
|
57
59
|
<!-- 标签类型 -->
|
|
58
60
|
<div v-else-if="type === 'tags'" class="tags-value">
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
<template v-if="Array.isArray(value) && value.length > 0">
|
|
62
|
+
<t-tag v-for="tag in value" :key="tag.id || tag" size="small" class="tag-item">
|
|
63
|
+
{{ typeof tag === 'object' ? tag.name : tag }}
|
|
64
|
+
</t-tag>
|
|
65
|
+
</template>
|
|
66
|
+
<span v-else>{{ value || '-' }}</span>
|
|
62
67
|
</div>
|
|
63
|
-
|
|
68
|
+
|
|
64
69
|
<!-- 链接类型 -->
|
|
65
|
-
<
|
|
66
|
-
{{
|
|
67
|
-
</
|
|
68
|
-
|
|
69
|
-
<!--
|
|
70
|
-
<div v-else-if="type === 'html'" class="html-value" v-html="
|
|
71
|
-
|
|
72
|
-
<!-- 数字类型 -->
|
|
73
|
-
<span v-else-if="type === 'number'" class="number-value">
|
|
74
|
-
{{ formatNumber(displayValue) }}
|
|
75
|
-
</span>
|
|
76
|
-
|
|
77
|
-
<!-- 货币类型 -->
|
|
78
|
-
<span v-else-if="type === 'currency'" class="currency-value">
|
|
79
|
-
{{ formatCurrency(displayValue) }}
|
|
80
|
-
</span>
|
|
81
|
-
|
|
70
|
+
<a v-else-if="type === 'link'" :href="getLinkUrl(value)" class="link-value" @click="handleLinkClick">
|
|
71
|
+
{{ getLinkText(value) }}
|
|
72
|
+
</a>
|
|
73
|
+
|
|
74
|
+
<!-- HTML类型 -->
|
|
75
|
+
<div v-else-if="type === 'html'" class="html-value" v-html="value"></div>
|
|
76
|
+
|
|
82
77
|
<!-- 默认文本 -->
|
|
83
|
-
<span v-else
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<div v-if="description" class="field-description">
|
|
89
|
-
{{ description }}
|
|
78
|
+
<span v-else>{{ value || '-' }}</span>
|
|
79
|
+
|
|
80
|
+
<!-- 描述信息 -->
|
|
81
|
+
<div v-if="description" class="description">{{ description }}</div>
|
|
82
|
+
</div>
|
|
90
83
|
</div>
|
|
91
|
-
|
|
92
|
-
</div>
|
|
84
|
+
</t-col>
|
|
93
85
|
</template>
|
|
94
86
|
|
|
95
87
|
<script setup>
|
|
96
88
|
import { computed, defineProps, defineEmits, inject } from 'vue'
|
|
89
|
+
import { Tag as TTag, Button as TButton, Icon as TIcon, Avatar as TAvatar, Col as TCol } from 'tdesign-vue-next'
|
|
97
90
|
import EbizFileList from './EbizFileList.vue'
|
|
98
91
|
|
|
99
92
|
const props = defineProps({
|
|
@@ -114,15 +107,42 @@ const props = defineProps({
|
|
|
114
107
|
type: [String, Number, Array, Object],
|
|
115
108
|
default: ''
|
|
116
109
|
},
|
|
117
|
-
required: {
|
|
118
|
-
type: Boolean,
|
|
119
|
-
default: false
|
|
120
|
-
},
|
|
121
110
|
description: {
|
|
122
111
|
type: String,
|
|
123
112
|
default: ''
|
|
124
113
|
},
|
|
125
114
|
|
|
115
|
+
// 栅格布局配置
|
|
116
|
+
span: {
|
|
117
|
+
type: Number,
|
|
118
|
+
default: 12,
|
|
119
|
+
validator: (value) => value >= 1 && value <= 24
|
|
120
|
+
},
|
|
121
|
+
xs: {
|
|
122
|
+
type: Number,
|
|
123
|
+
default: undefined
|
|
124
|
+
},
|
|
125
|
+
sm: {
|
|
126
|
+
type: Number,
|
|
127
|
+
default: undefined
|
|
128
|
+
},
|
|
129
|
+
md: {
|
|
130
|
+
type: Number,
|
|
131
|
+
default: undefined
|
|
132
|
+
},
|
|
133
|
+
lg: {
|
|
134
|
+
type: Number,
|
|
135
|
+
default: undefined
|
|
136
|
+
},
|
|
137
|
+
xl: {
|
|
138
|
+
type: Number,
|
|
139
|
+
default: undefined
|
|
140
|
+
},
|
|
141
|
+
xxl: {
|
|
142
|
+
type: Number,
|
|
143
|
+
default: undefined
|
|
144
|
+
},
|
|
145
|
+
|
|
126
146
|
// 布局配置
|
|
127
147
|
layout: {
|
|
128
148
|
type: String,
|
|
@@ -168,6 +188,28 @@ const finalLabelWidth = computed(() => props.labelWidth || detailViewConfig?.lab
|
|
|
168
188
|
const finalLabelColor = computed(() => props.labelColor || detailViewConfig?.labelColor || '#999999')
|
|
169
189
|
const finalGap = computed(() => props.gap !== undefined ? props.gap : (detailViewConfig?.gap ?? 16))
|
|
170
190
|
|
|
191
|
+
// 栅格配置
|
|
192
|
+
const finalSpan = computed(() => props.span)
|
|
193
|
+
const finalXs = computed(() => props.xs)
|
|
194
|
+
const finalSm = computed(() => props.sm)
|
|
195
|
+
const finalMd = computed(() => props.md)
|
|
196
|
+
const finalLg = computed(() => props.lg)
|
|
197
|
+
const finalXl = computed(() => props.xl)
|
|
198
|
+
const finalXxl = computed(() => props.xxl)
|
|
199
|
+
|
|
200
|
+
// 暴露栅格配置给父组件
|
|
201
|
+
defineExpose({
|
|
202
|
+
gridConfig: computed(() => ({
|
|
203
|
+
span: finalSpan.value,
|
|
204
|
+
xs: finalXs.value,
|
|
205
|
+
sm: finalSm.value,
|
|
206
|
+
md: finalMd.value,
|
|
207
|
+
lg: finalLg.value,
|
|
208
|
+
xl: finalXl.value,
|
|
209
|
+
xxl: finalXxl.value
|
|
210
|
+
}))
|
|
211
|
+
})
|
|
212
|
+
|
|
171
213
|
// 显示值
|
|
172
214
|
const displayValue = computed(() => {
|
|
173
215
|
return props.value !== undefined && props.value !== null ? props.value : ''
|
|
@@ -237,17 +279,65 @@ const getStatusTheme = (status) => {
|
|
|
237
279
|
return statusMap[status] || 'default'
|
|
238
280
|
}
|
|
239
281
|
|
|
282
|
+
// 获取状态文本
|
|
283
|
+
const getStatusText = (status) => {
|
|
284
|
+
if (typeof status === 'object' && status !== null) {
|
|
285
|
+
return status.text || status.status || status
|
|
286
|
+
}
|
|
287
|
+
return status
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 获取用户头像
|
|
291
|
+
const getUserAvatar = (user) => {
|
|
292
|
+
if (typeof user === 'object' && user !== null && user.avatar) {
|
|
293
|
+
return user.avatar
|
|
294
|
+
}
|
|
295
|
+
return null
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 获取用户初始
|
|
299
|
+
const getUserInitial = (user) => {
|
|
300
|
+
if (typeof user === 'object' && user !== null && user.name) {
|
|
301
|
+
return user.name.charAt(0)
|
|
302
|
+
}
|
|
303
|
+
return 'U'
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// 获取用户名称
|
|
307
|
+
const getUserName = (user) => {
|
|
308
|
+
if (typeof user === 'object' && user !== null && user.name) {
|
|
309
|
+
return user.name
|
|
310
|
+
}
|
|
311
|
+
return user
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// 获取链接 URL
|
|
315
|
+
const getLinkUrl = (link) => {
|
|
316
|
+
if (typeof link === 'object' && link !== null && link.url) {
|
|
317
|
+
return link.url
|
|
318
|
+
}
|
|
319
|
+
return link
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 获取链接文本
|
|
323
|
+
const getLinkText = (link) => {
|
|
324
|
+
if (typeof link === 'object' && link !== null && link.text) {
|
|
325
|
+
return link.text
|
|
326
|
+
}
|
|
327
|
+
return link
|
|
328
|
+
}
|
|
329
|
+
|
|
240
330
|
// 事件处理
|
|
241
331
|
const handleDownloadFile = (file) => {
|
|
242
332
|
emits('download-file', file)
|
|
243
333
|
}
|
|
244
334
|
|
|
245
|
-
const handleUserClick = (
|
|
246
|
-
emits('user-click',
|
|
335
|
+
const handleUserClick = () => {
|
|
336
|
+
emits('user-click', userList.value[0]) // Assuming userList is an array of users
|
|
247
337
|
}
|
|
248
338
|
|
|
249
|
-
const handleLinkClick = (
|
|
250
|
-
emits('link-click',
|
|
339
|
+
const handleLinkClick = () => {
|
|
340
|
+
emits('link-click', linkValue.value)
|
|
251
341
|
}
|
|
252
342
|
</script>
|
|
253
343
|
|
|
@@ -263,11 +353,15 @@ const handleLinkClick = (link) => {
|
|
|
263
353
|
border-bottom: none;
|
|
264
354
|
}
|
|
265
355
|
|
|
266
|
-
.ebiz-detail-item.vertical {
|
|
356
|
+
.ebiz-detail-item.vertical-layout {
|
|
267
357
|
flex-direction: column;
|
|
268
358
|
}
|
|
269
359
|
|
|
270
|
-
.
|
|
360
|
+
.ebiz-detail-item.horizontal-layout {
|
|
361
|
+
flex-direction: row;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.detail-label {
|
|
271
365
|
font-size: 14px;
|
|
272
366
|
line-height: 1.5;
|
|
273
367
|
font-weight: 500;
|
|
@@ -294,7 +388,7 @@ const handleLinkClick = (link) => {
|
|
|
294
388
|
min-width: 0;
|
|
295
389
|
}
|
|
296
390
|
|
|
297
|
-
.
|
|
391
|
+
.description {
|
|
298
392
|
font-size: 12px;
|
|
299
393
|
color: #999999;
|
|
300
394
|
margin-top: 4px;
|
|
@@ -2,18 +2,29 @@
|
|
|
2
2
|
<div class="ebiz-detail-view" v-loading="loading">
|
|
3
3
|
<!-- 正常内容 -->
|
|
4
4
|
<div class="detail-content">
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
gap: `${gap}px`
|
|
8
|
-
}">
|
|
9
|
-
<slot name="default" :data="finalData">
|
|
5
|
+
<t-row :gutter="gap" class="detail-fields">
|
|
6
|
+
<slot name="default" :data="finalData" :wrapItem="wrapItem">
|
|
10
7
|
<!-- 默认内容:根据fields配置或自动生成 -->
|
|
11
8
|
<template v-if="fields.length > 0">
|
|
12
9
|
<template v-for="field in fields" :key="field.key">
|
|
13
|
-
<slot :name="'detail-item-' + field.key" :data="finalData">
|
|
14
|
-
<EbizDetailItem
|
|
15
|
-
:
|
|
16
|
-
:
|
|
10
|
+
<slot :name="'detail-item-' + field.key" :data="finalData" :field="field" :wrapItem="wrapItem">
|
|
11
|
+
<EbizDetailItem
|
|
12
|
+
:label="field.label"
|
|
13
|
+
:value="getFieldValue(field.key)"
|
|
14
|
+
:type="field.type || 'text'"
|
|
15
|
+
:required="field.required"
|
|
16
|
+
:description="field.description"
|
|
17
|
+
:fileMode="field.fileMode"
|
|
18
|
+
:showDownload="field.showDownload"
|
|
19
|
+
:span="getFieldSpan(field)"
|
|
20
|
+
:xs="getFieldResponsiveSpan(field, 'xs')"
|
|
21
|
+
:sm="getFieldResponsiveSpan(field, 'sm')"
|
|
22
|
+
:md="getFieldResponsiveSpan(field, 'md')"
|
|
23
|
+
:lg="getFieldResponsiveSpan(field, 'lg')"
|
|
24
|
+
:xl="getFieldResponsiveSpan(field, 'xl')"
|
|
25
|
+
:xxl="getFieldResponsiveSpan(field, 'xxl')"
|
|
26
|
+
@download-file="handleDownloadFile"
|
|
27
|
+
@user-click="handleUserClick"
|
|
17
28
|
@link-click="handleLinkClick" />
|
|
18
29
|
</slot>
|
|
19
30
|
</template>
|
|
@@ -21,16 +32,22 @@
|
|
|
21
32
|
|
|
22
33
|
<!-- 自动生成字段 -->
|
|
23
34
|
<template v-else>
|
|
24
|
-
<EbizDetailItem
|
|
35
|
+
<EbizDetailItem
|
|
36
|
+
v-for="(value, key) in finalData"
|
|
37
|
+
:key="key"
|
|
38
|
+
:label="key"
|
|
39
|
+
:value="value"
|
|
40
|
+
:span="getDefaultSpan()" />
|
|
25
41
|
</template>
|
|
26
42
|
</slot>
|
|
27
|
-
</
|
|
43
|
+
</t-row>
|
|
28
44
|
</div>
|
|
29
45
|
</div>
|
|
30
46
|
</template>
|
|
31
47
|
|
|
32
48
|
<script setup>
|
|
33
49
|
import { computed, defineProps, defineEmits, ref, watch, onMounted, provide } from 'vue'
|
|
50
|
+
import { Row as TRow } from 'tdesign-vue-next'
|
|
34
51
|
import EbizDetailItem from './EbizDetailItem.vue'
|
|
35
52
|
import { dataService } from '../index'
|
|
36
53
|
|
|
@@ -89,6 +106,19 @@ const props = defineProps({
|
|
|
89
106
|
default: '#666666'
|
|
90
107
|
},
|
|
91
108
|
|
|
109
|
+
// 响应式配置
|
|
110
|
+
responsive: {
|
|
111
|
+
type: Object,
|
|
112
|
+
default: () => ({
|
|
113
|
+
xs: 1, // <576px 单列
|
|
114
|
+
sm: 1, // ≥576px 单列
|
|
115
|
+
md: 2, // ≥768px 双列
|
|
116
|
+
lg: 2, // ≥992px 双列
|
|
117
|
+
xl: 3, // ≥1200px 三列
|
|
118
|
+
xxl: 4 // ≥1400px 四列
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
|
|
92
122
|
// 自动加载
|
|
93
123
|
autoLoad: {
|
|
94
124
|
type: Boolean,
|
|
@@ -103,7 +133,6 @@ const loading = ref(false)
|
|
|
103
133
|
const error = ref('')
|
|
104
134
|
const apiData = ref({})
|
|
105
135
|
|
|
106
|
-
|
|
107
136
|
// 计算最终数据源
|
|
108
137
|
const finalData = computed(() => {
|
|
109
138
|
return Object.keys(props.data).length > 0 ? props.data : apiData.value
|
|
@@ -118,6 +147,47 @@ const getFieldValue = (key) => {
|
|
|
118
147
|
return value !== undefined ? value : ''
|
|
119
148
|
}
|
|
120
149
|
|
|
150
|
+
// 计算字段在24栅格中的span值
|
|
151
|
+
const getFieldSpan = (field) => {
|
|
152
|
+
if (props.layout === 'vertical') {
|
|
153
|
+
return 24 // 垂直布局时占满整行
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const span = field.span || 1
|
|
157
|
+
const maxSpan = Math.min(span, props.columns)
|
|
158
|
+
return Math.floor(24 / props.columns) * maxSpan
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// 计算字段在不同断点下的span值
|
|
162
|
+
const getFieldResponsiveSpan = (field, breakpoint) => {
|
|
163
|
+
if (props.layout === 'vertical') {
|
|
164
|
+
return 24
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 获取该断点下的列数
|
|
168
|
+
const columnsAtBreakpoint = props.responsive[breakpoint] || props.columns
|
|
169
|
+
const span = field.span || 1
|
|
170
|
+
const maxSpan = Math.min(span, columnsAtBreakpoint)
|
|
171
|
+
|
|
172
|
+
return Math.floor(24 / columnsAtBreakpoint) * maxSpan
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 获取默认span值(用于自动生成字段)
|
|
176
|
+
const getDefaultSpan = () => {
|
|
177
|
+
if (props.layout === 'vertical') {
|
|
178
|
+
return 24
|
|
179
|
+
}
|
|
180
|
+
return Math.floor(24 / props.columns)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 包装函数,用于在slot中直接返回EbizDetailItem(因为t-col已在组件内部)
|
|
184
|
+
const wrapItem = (detailItemVnode, spanConfig = {}) => {
|
|
185
|
+
// 现在EbizDetailItem内部已包含t-col,直接返回vnode即可
|
|
186
|
+
return detailItemVnode
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
121
191
|
// 加载数据
|
|
122
192
|
const loadData = async () => {
|
|
123
193
|
if (!props.apiConfig && !props.fetchUrl) return
|
|
@@ -144,11 +214,6 @@ const loadData = async () => {
|
|
|
144
214
|
}
|
|
145
215
|
}
|
|
146
216
|
|
|
147
|
-
// 重试加载
|
|
148
|
-
const handleRetry = () => {
|
|
149
|
-
loadData()
|
|
150
|
-
}
|
|
151
|
-
|
|
152
217
|
// 事件处理
|
|
153
218
|
const handleDownloadFile = (file) => {
|
|
154
219
|
emits('download-file', file)
|
|
@@ -191,7 +256,7 @@ provide('detailViewConfig', {
|
|
|
191
256
|
layout: props.layout,
|
|
192
257
|
labelWidth: props.labelWidth,
|
|
193
258
|
labelColor: props.labelColor,
|
|
194
|
-
gap: 0 //
|
|
259
|
+
gap: 0 // 由t-row的gutter统一管理间距
|
|
195
260
|
})
|
|
196
261
|
|
|
197
262
|
// 组件挂载时自动加载
|
|
@@ -268,29 +333,12 @@ defineExpose({
|
|
|
268
333
|
color: #333333;
|
|
269
334
|
}
|
|
270
335
|
|
|
271
|
-
|
|
272
|
-
display: grid;
|
|
273
|
-
gap: 16px;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.detail-fields.vertical-layout {
|
|
277
|
-
grid-template-columns: 1fr;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.group-title {
|
|
281
|
-
grid-column: 1 / -1;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/* 响应式布局 */
|
|
336
|
+
/* 移动端优化已经通过TDesign的响应式断点自动处理 */
|
|
285
337
|
@media (max-width: 768px) {
|
|
286
338
|
.ebiz-detail-view {
|
|
287
339
|
padding: 16px;
|
|
288
340
|
}
|
|
289
341
|
|
|
290
|
-
.detail-fields {
|
|
291
|
-
grid-template-columns: 1fr !important;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
342
|
.group-title-text {
|
|
295
343
|
font-size: 16px;
|
|
296
344
|
}
|