@ebiz/designer-components 0.1.46 → 0.1.48
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 +8481 -8403
- package/package.json +1 -1
- package/src/components/EbizDetailItem.vue +216 -90
- package/src/components/EbizDetailView.vue +84 -36
- package/src/components/EbizDiv.vue +8 -1
- package/src/views/EbizDetailViewDemo.vue +221 -10
package/package.json
CHANGED
|
@@ -1,99 +1,99 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
'
|
|
2
|
+
<t-col :span="finalSpan" :xs="finalXs" :sm="finalSm" :md="finalMd" :lg="finalLg" :xl="finalXl" :xxl="finalXxl">
|
|
3
|
+
<div class="ebiz-detail-item" :class="{
|
|
4
|
+
'vertical-layout': finalLayout === 'vertical',
|
|
5
|
+
'horizontal-layout': finalLayout === 'horizontal',
|
|
5
6
|
}">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
<!-- 标签部分 -->
|
|
8
|
+
<div class="detail-label" :style="{
|
|
9
|
+
width: finalLayout === 'horizontal' ? `${finalLabelWidth}px` : 'auto',
|
|
10
|
+
color: finalLabelColor
|
|
10
11
|
}">
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<div class="user-
|
|
32
|
-
<div class="user-
|
|
33
|
-
|
|
12
|
+
{{ label }}
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<!-- 值部分 -->
|
|
16
|
+
<div class="detail-value">
|
|
17
|
+
<slot name="default" :data="value">
|
|
18
|
+
<!-- 文本类型 -->
|
|
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) }}
|
|
27
|
+
</span>
|
|
28
|
+
|
|
29
|
+
<!-- 用户类型 -->
|
|
30
|
+
<div v-else-if="type === 'user'" class="user-value">
|
|
31
|
+
<div class="user-list">
|
|
32
|
+
<div v-for="user in userList" :key="user.id" class="user-item" @click="handleUserClick(user)">
|
|
33
|
+
<div class="user-avatar">
|
|
34
|
+
<img v-if="user.avatar" :src="user.avatar" />
|
|
35
|
+
<div v-else class="user-avatar-default">{{ user.name?.[0] || 'U' }}</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="user-info">
|
|
38
|
+
<div class="user-name">{{ user.name }}</div>
|
|
39
|
+
<div class="user-dept">{{ user.department }}</div>
|
|
40
|
+
</div>
|
|
34
41
|
</div>
|
|
35
42
|
</div>
|
|
36
43
|
</div>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<!-- 文件类型 -->
|
|
40
|
-
<div v-else-if="type === 'file'" class="file-value">
|
|
41
|
-
<EbizFileList :files="fileList" :mode="fileMode" :showDownload="showDownload"
|
|
42
|
-
@download="handleDownloadFile" />
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<!-- 日期类型 -->
|
|
46
|
-
<span v-else-if="type === 'date'" class="date-value">
|
|
47
|
-
{{ formatDate(displayValue) }}
|
|
48
|
-
</span>
|
|
49
|
-
|
|
50
|
-
<!-- 状态类型 -->
|
|
51
|
-
<div v-else-if="type === 'status'" class="status-value">
|
|
52
|
-
<t-tag :theme="getStatusTheme(statusValue?.status)" size="small">
|
|
53
|
-
{{ statusValue?.text }}
|
|
54
|
-
</t-tag>
|
|
55
|
-
</div>
|
|
56
44
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
45
|
+
<!-- 文件类型 -->
|
|
46
|
+
<div v-else-if="type === 'file'" class="file-value">
|
|
47
|
+
<EbizFileList :files="fileList" :mode="fileMode" :showDownload="showDownload"
|
|
48
|
+
@download="handleDownloadFile" />
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- 日期类型 -->
|
|
52
|
+
<span v-else-if="type === 'date'">{{ formatDate(value) }}</span>
|
|
53
|
+
|
|
54
|
+
<!-- 时间类型 -->
|
|
55
|
+
<span v-else-if="type === 'time'">{{ formatTime(value) }}</span>
|
|
56
|
+
|
|
57
|
+
<!-- 日期时间类型 -->
|
|
58
|
+
<span v-else-if="type === 'datetime'">{{ formatDateTime(value) }}</span>
|
|
59
|
+
|
|
60
|
+
<!-- 状态类型 -->
|
|
61
|
+
<t-tag v-else-if="type === 'status'" :theme="getStatusTheme(value)">
|
|
62
|
+
{{ getStatusText(value) }}
|
|
61
63
|
</t-tag>
|
|
62
|
-
</div>
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
<!-- 标签类型 -->
|
|
66
|
+
<div v-else-if="type === 'tags'" class="tags-value">
|
|
67
|
+
<template v-if="Array.isArray(value) && value.length > 0">
|
|
68
|
+
<t-tag v-for="tag in value" :key="tag.id || tag" size="small" class="tag-item">
|
|
69
|
+
{{ typeof tag === 'object' ? tag.name : tag }}
|
|
70
|
+
</t-tag>
|
|
71
|
+
</template>
|
|
72
|
+
<span v-else>{{ value || '-' }}</span>
|
|
73
|
+
</div>
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
<!-- 链接类型 -->
|
|
76
|
+
<a v-else-if="type === 'link'" :href="getLinkUrl(value)" class="link-value" @click="handleLinkClick">
|
|
77
|
+
{{ getLinkText(value) }}
|
|
78
|
+
</a>
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{{ formatNumber(displayValue) }}
|
|
75
|
-
</span>
|
|
80
|
+
<!-- HTML类型 -->
|
|
81
|
+
<div v-else-if="type === 'html'" class="html-value" v-html="value"></div>
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{{ formatCurrency(displayValue) }}
|
|
80
|
-
</span>
|
|
83
|
+
<!-- 默认文本 -->
|
|
84
|
+
<span v-else>{{ value || '-' }}</span>
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
<!-- 描述信息 -->
|
|
87
|
+
<div v-if="description" class="description">{{ description }}</div>
|
|
88
|
+
</slot>
|
|
89
|
+
</div>
|
|
85
90
|
</div>
|
|
86
|
-
|
|
87
|
-
<!-- 字段描述 -->
|
|
88
|
-
<div v-if="description" class="field-description">
|
|
89
|
-
{{ description }}
|
|
90
|
-
</div>
|
|
91
|
-
|
|
92
|
-
</div>
|
|
91
|
+
</t-col>
|
|
93
92
|
</template>
|
|
94
93
|
|
|
95
94
|
<script setup>
|
|
96
95
|
import { computed, defineProps, defineEmits, inject } from 'vue'
|
|
96
|
+
import { Tag as TTag, Button as TButton, Icon as TIcon, Avatar as TAvatar, Col as TCol } from 'tdesign-vue-next'
|
|
97
97
|
import EbizFileList from './EbizFileList.vue'
|
|
98
98
|
|
|
99
99
|
const props = defineProps({
|
|
@@ -106,7 +106,7 @@ const props = defineProps({
|
|
|
106
106
|
type: String,
|
|
107
107
|
default: 'text',
|
|
108
108
|
validator: (value) => [
|
|
109
|
-
'text', 'user', 'file', 'date', 'status',
|
|
109
|
+
'text', 'user', 'file', 'date', 'time', 'datetime', 'status',
|
|
110
110
|
'tags', 'link', 'html', 'number', 'currency'
|
|
111
111
|
].includes(value)
|
|
112
112
|
},
|
|
@@ -114,15 +114,42 @@ const props = defineProps({
|
|
|
114
114
|
type: [String, Number, Array, Object],
|
|
115
115
|
default: ''
|
|
116
116
|
},
|
|
117
|
-
required: {
|
|
118
|
-
type: Boolean,
|
|
119
|
-
default: false
|
|
120
|
-
},
|
|
121
117
|
description: {
|
|
122
118
|
type: String,
|
|
123
119
|
default: ''
|
|
124
120
|
},
|
|
125
121
|
|
|
122
|
+
// 栅格布局配置
|
|
123
|
+
span: {
|
|
124
|
+
type: Number,
|
|
125
|
+
default: 12,
|
|
126
|
+
validator: (value) => value >= 1 && value <= 24
|
|
127
|
+
},
|
|
128
|
+
xs: {
|
|
129
|
+
type: Number,
|
|
130
|
+
default: undefined
|
|
131
|
+
},
|
|
132
|
+
sm: {
|
|
133
|
+
type: Number,
|
|
134
|
+
default: undefined
|
|
135
|
+
},
|
|
136
|
+
md: {
|
|
137
|
+
type: Number,
|
|
138
|
+
default: undefined
|
|
139
|
+
},
|
|
140
|
+
lg: {
|
|
141
|
+
type: Number,
|
|
142
|
+
default: undefined
|
|
143
|
+
},
|
|
144
|
+
xl: {
|
|
145
|
+
type: Number,
|
|
146
|
+
default: undefined
|
|
147
|
+
},
|
|
148
|
+
xxl: {
|
|
149
|
+
type: Number,
|
|
150
|
+
default: undefined
|
|
151
|
+
},
|
|
152
|
+
|
|
126
153
|
// 布局配置
|
|
127
154
|
layout: {
|
|
128
155
|
type: String,
|
|
@@ -168,6 +195,28 @@ const finalLabelWidth = computed(() => props.labelWidth || detailViewConfig?.lab
|
|
|
168
195
|
const finalLabelColor = computed(() => props.labelColor || detailViewConfig?.labelColor || '#999999')
|
|
169
196
|
const finalGap = computed(() => props.gap !== undefined ? props.gap : (detailViewConfig?.gap ?? 16))
|
|
170
197
|
|
|
198
|
+
// 栅格配置
|
|
199
|
+
const finalSpan = computed(() => props.span)
|
|
200
|
+
const finalXs = computed(() => props.xs)
|
|
201
|
+
const finalSm = computed(() => props.sm)
|
|
202
|
+
const finalMd = computed(() => props.md)
|
|
203
|
+
const finalLg = computed(() => props.lg)
|
|
204
|
+
const finalXl = computed(() => props.xl)
|
|
205
|
+
const finalXxl = computed(() => props.xxl)
|
|
206
|
+
|
|
207
|
+
// 暴露栅格配置给父组件
|
|
208
|
+
defineExpose({
|
|
209
|
+
gridConfig: computed(() => ({
|
|
210
|
+
span: finalSpan.value,
|
|
211
|
+
xs: finalXs.value,
|
|
212
|
+
sm: finalSm.value,
|
|
213
|
+
md: finalMd.value,
|
|
214
|
+
lg: finalLg.value,
|
|
215
|
+
xl: finalXl.value,
|
|
216
|
+
xxl: finalXxl.value
|
|
217
|
+
}))
|
|
218
|
+
})
|
|
219
|
+
|
|
171
220
|
// 显示值
|
|
172
221
|
const displayValue = computed(() => {
|
|
173
222
|
return props.value !== undefined && props.value !== null ? props.value : ''
|
|
@@ -214,6 +263,31 @@ const formatDate = (date) => {
|
|
|
214
263
|
})
|
|
215
264
|
}
|
|
216
265
|
|
|
266
|
+
// 格式化时间
|
|
267
|
+
const formatTime = (time) => {
|
|
268
|
+
if (!time) return ''
|
|
269
|
+
const d = new Date(time)
|
|
270
|
+
return d.toLocaleTimeString('zh-CN', {
|
|
271
|
+
hour: '2-digit',
|
|
272
|
+
minute: '2-digit',
|
|
273
|
+
second: '2-digit'
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 格式化日期时间
|
|
278
|
+
const formatDateTime = (datetime) => {
|
|
279
|
+
if (!datetime) return ''
|
|
280
|
+
const d = new Date(datetime)
|
|
281
|
+
return d.toLocaleString('zh-CN', {
|
|
282
|
+
year: 'numeric',
|
|
283
|
+
month: '2-digit',
|
|
284
|
+
day: '2-digit',
|
|
285
|
+
hour: '2-digit',
|
|
286
|
+
minute: '2-digit',
|
|
287
|
+
second: '2-digit'
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
217
291
|
// 格式化数字
|
|
218
292
|
const formatNumber = (num) => {
|
|
219
293
|
if (num === null || num === undefined || num === '') return ''
|
|
@@ -237,17 +311,65 @@ const getStatusTheme = (status) => {
|
|
|
237
311
|
return statusMap[status] || 'default'
|
|
238
312
|
}
|
|
239
313
|
|
|
314
|
+
// 获取状态文本
|
|
315
|
+
const getStatusText = (status) => {
|
|
316
|
+
if (typeof status === 'object' && status !== null) {
|
|
317
|
+
return status.text || status.status || status
|
|
318
|
+
}
|
|
319
|
+
return status
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 获取用户头像
|
|
323
|
+
const getUserAvatar = (user) => {
|
|
324
|
+
if (typeof user === 'object' && user !== null && user.avatar) {
|
|
325
|
+
return user.avatar
|
|
326
|
+
}
|
|
327
|
+
return null
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 获取用户初始
|
|
331
|
+
const getUserInitial = (user) => {
|
|
332
|
+
if (typeof user === 'object' && user !== null && user.name) {
|
|
333
|
+
return user.name.charAt(0)
|
|
334
|
+
}
|
|
335
|
+
return 'U'
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 获取用户名称
|
|
339
|
+
const getUserName = (user) => {
|
|
340
|
+
if (typeof user === 'object' && user !== null && user.name) {
|
|
341
|
+
return user.name
|
|
342
|
+
}
|
|
343
|
+
return user
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 获取链接 URL
|
|
347
|
+
const getLinkUrl = (link) => {
|
|
348
|
+
if (typeof link === 'object' && link !== null && link.url) {
|
|
349
|
+
return link.url
|
|
350
|
+
}
|
|
351
|
+
return link
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 获取链接文本
|
|
355
|
+
const getLinkText = (link) => {
|
|
356
|
+
if (typeof link === 'object' && link !== null && link.text) {
|
|
357
|
+
return link.text
|
|
358
|
+
}
|
|
359
|
+
return link
|
|
360
|
+
}
|
|
361
|
+
|
|
240
362
|
// 事件处理
|
|
241
363
|
const handleDownloadFile = (file) => {
|
|
242
364
|
emits('download-file', file)
|
|
243
365
|
}
|
|
244
366
|
|
|
245
|
-
const handleUserClick = (
|
|
246
|
-
emits('user-click',
|
|
367
|
+
const handleUserClick = () => {
|
|
368
|
+
emits('user-click', userList.value[0]) // Assuming userList is an array of users
|
|
247
369
|
}
|
|
248
370
|
|
|
249
|
-
const handleLinkClick = (
|
|
250
|
-
emits('link-click',
|
|
371
|
+
const handleLinkClick = () => {
|
|
372
|
+
emits('link-click', linkValue.value)
|
|
251
373
|
}
|
|
252
374
|
</script>
|
|
253
375
|
|
|
@@ -263,11 +385,15 @@ const handleLinkClick = (link) => {
|
|
|
263
385
|
border-bottom: none;
|
|
264
386
|
}
|
|
265
387
|
|
|
266
|
-
.ebiz-detail-item.vertical {
|
|
388
|
+
.ebiz-detail-item.vertical-layout {
|
|
267
389
|
flex-direction: column;
|
|
268
390
|
}
|
|
269
391
|
|
|
270
|
-
.
|
|
392
|
+
.ebiz-detail-item.horizontal-layout {
|
|
393
|
+
flex-direction: row;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.detail-label {
|
|
271
397
|
font-size: 14px;
|
|
272
398
|
line-height: 1.5;
|
|
273
399
|
font-weight: 500;
|
|
@@ -294,7 +420,7 @@ const handleLinkClick = (link) => {
|
|
|
294
420
|
min-width: 0;
|
|
295
421
|
}
|
|
296
422
|
|
|
297
|
-
.
|
|
423
|
+
.description {
|
|
298
424
|
font-size: 12px;
|
|
299
425
|
color: #999999;
|
|
300
426
|
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
|
}
|
|
@@ -9,6 +9,13 @@ const props = defineProps({
|
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
const isShow = computed(() => {
|
|
12
|
+
if( !props.permissionKey ) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const developMode = localStorage.getItem('ebiz-develop-mode')
|
|
16
|
+
if (developMode === 'designer') {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
try {
|
|
14
21
|
const permissionKeysStr = localStorage.getItem('permissionKeys') || '[]';
|
|
@@ -29,6 +36,6 @@ watch(() => props.key, checkPermission, { immediate: true });
|
|
|
29
36
|
|
|
30
37
|
<template>
|
|
31
38
|
<div v-if="isShow">
|
|
32
|
-
<slot></slot>
|
|
39
|
+
<slot name="default"></slot>
|
|
33
40
|
</div>
|
|
34
41
|
</template>
|