@tplc/business 0.7.23 → 0.7.24
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.7.24](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.19...v0.7.24) (2025-12-21)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### 🚀 Chore | 构建/工程依赖/工具
|
|
9
|
+
|
|
10
|
+
* **release:** 0.7.20 ([3c69dbe](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/3c69dbe444fc50cc4f470ffa316a379c739dab4a))
|
|
11
|
+
* **release:** 0.7.21 ([2c0db34](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/2c0db34f5ec241cc0297d68a5755c7ae7a75dcb8))
|
|
12
|
+
* **release:** 0.7.22 ([31f6a87](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/31f6a874afdc82a4a58a8ef610194757666144d0))
|
|
13
|
+
* **release:** 0.7.23 ([9c56ffd](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/9c56ffd1c060c2d81105a1049654f80081674d10))
|
|
14
|
+
* **release:** 1.0.16 ([6ab5880](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/6ab588048db6a5b3d58529ad239f895c989d6d03))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### ✨ Features | 新功能
|
|
18
|
+
|
|
19
|
+
* 优化lcb ([4508077](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/45080773f73d2f9329de373d2b713eb5b644dea0))
|
|
20
|
+
* 优化pay ([fdead3a](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/fdead3a80ca5d7de1de0eff9745e35e2c4fca6db))
|
|
21
|
+
* 兼容点击 ([8027588](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/8027588f38760e3de6b01205ec982eceb91f0e86))
|
|
22
|
+
* 新增progress ([474bcac](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/474bcac7c08cb234e1d6945036d8ca4d64fa4f88))
|
|
23
|
+
* 更新wot-number ([f700988](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/f700988b15ec08eef0d1b8c140483eca8e0afed4))
|
|
24
|
+
* 调整数据结构 ([b6f0d24](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/b6f0d24a26d3d4dbf05ca855ed687d6819eb7c0f))
|
|
25
|
+
|
|
5
26
|
### [0.7.23](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v1.0.16...v0.7.23) (2025-12-18)
|
|
6
27
|
|
|
7
28
|
|
|
@@ -4,23 +4,12 @@
|
|
|
4
4
|
v-bind="actionProps"
|
|
5
5
|
:renderMode="actionProps?.jumpType ? 'button' : 'noClick'"
|
|
6
6
|
>
|
|
7
|
-
<view
|
|
8
|
-
:style="{
|
|
9
|
-
display: display,
|
|
10
|
-
gap: transformValueUnit(gap),
|
|
11
|
-
overflowX: scrollX ? 'auto' : 'hidden',
|
|
12
|
-
...innerStyle,
|
|
13
|
-
}"
|
|
14
|
-
class="h-full"
|
|
15
|
-
>
|
|
7
|
+
<view :style="containerStyle" class="h-full">
|
|
16
8
|
<view
|
|
17
9
|
v-for="(item, index) in list"
|
|
18
10
|
:key="item.id"
|
|
19
11
|
class="slot-wrapper z-1"
|
|
20
|
-
:style="
|
|
21
|
-
...getStyle(index),
|
|
22
|
-
overflowX,
|
|
23
|
-
}"
|
|
12
|
+
:style="getItemStyle(index)"
|
|
24
13
|
>
|
|
25
14
|
<slot :item="item" />
|
|
26
15
|
</view>
|
|
@@ -30,7 +19,7 @@
|
|
|
30
19
|
</template>
|
|
31
20
|
|
|
32
21
|
<script setup lang="ts">
|
|
33
|
-
import { computed, watch } from 'vue'
|
|
22
|
+
import { computed, watch, ref } from 'vue'
|
|
34
23
|
import { LcbAreaProps } from './types'
|
|
35
24
|
import { getFlexStyle, transformValueUnit } from '@tplc/business/utils/transform'
|
|
36
25
|
import { get } from 'lodash-es'
|
|
@@ -51,29 +40,66 @@ const props = withDefaults(defineProps<LcbAreaProps>(), {
|
|
|
51
40
|
overflowX: 'initial',
|
|
52
41
|
})
|
|
53
42
|
const { userStore, innerDynamicData, pageInfo } = useDynamicData()
|
|
54
|
-
|
|
43
|
+
|
|
44
|
+
// 缓存 gap 的单位转换
|
|
45
|
+
const gapValue = computed(() => transformValueUnit(props.gap))
|
|
46
|
+
|
|
47
|
+
// 优化容器样式计算,合并为一个 computed
|
|
48
|
+
const containerStyle = computed(() => {
|
|
49
|
+
const baseStyle = {
|
|
50
|
+
display: props.display,
|
|
51
|
+
gap: gapValue.value,
|
|
52
|
+
overflowX: (props.scrollX ? 'auto' : 'hidden') as 'auto' | 'hidden',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
55
|
if (props.display === 'grid') {
|
|
56
56
|
return {
|
|
57
|
+
...baseStyle,
|
|
57
58
|
gridTemplateColumns: `repeat(${props.gridColumns}, minmax(0, 1fr))`,
|
|
58
59
|
}
|
|
59
60
|
}
|
|
61
|
+
|
|
60
62
|
return {
|
|
63
|
+
...baseStyle,
|
|
61
64
|
flexDirection: props.displayFlex,
|
|
62
65
|
alignItems: 'stretch',
|
|
63
66
|
}
|
|
64
67
|
})
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
|
|
69
|
+
// 缓存 itemAlign 的 flex 样式,避免每个 item 都重复计算
|
|
70
|
+
const itemAlignStyle = computed(() => getFlexStyle(props.itemAlign))
|
|
71
|
+
|
|
72
|
+
// 优化: 缓存公共样式部分
|
|
73
|
+
const commonItemStyle = computed(() => ({
|
|
74
|
+
...itemAlignStyle.value,
|
|
75
|
+
overflowX: props.overflowX,
|
|
76
|
+
}))
|
|
77
|
+
|
|
78
|
+
// 优化: 缓存每个 item 的样式,避免重复计算
|
|
79
|
+
const itemStylesCache = computed(() => {
|
|
80
|
+
const isGrid = props.display === 'grid'
|
|
81
|
+
const isFlex = props.display === 'flex'
|
|
82
|
+
const flexShrink = props.flexShrink ?? 1
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
props.list?.map((_, index) => {
|
|
86
|
+
const flex = props.areaFlexs?.[index]?.flex ?? 1
|
|
87
|
+
const colSpan = props.areaItems?.[index]?.colSpan
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...commonItemStyle.value,
|
|
91
|
+
gridColumn: colSpan && isGrid ? `span ${colSpan} / span ${colSpan}` : undefined,
|
|
92
|
+
flex: isFlex ? flex || 'auto' : undefined,
|
|
93
|
+
flexShrink: isFlex ? flexShrink : undefined,
|
|
94
|
+
width: flex ? '100%' : 'auto',
|
|
95
|
+
}
|
|
96
|
+
}) || []
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// 优化: 使用缓存的样式
|
|
101
|
+
const getItemStyle = (index: number) => {
|
|
102
|
+
return itemStylesCache.value[index] || commonItemStyle.value
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
const getData = async () => {
|
|
@@ -88,16 +114,26 @@ const getData = async () => {
|
|
|
88
114
|
|
|
89
115
|
/** 处理跳转链接中的动态参数 */
|
|
90
116
|
const actionProps = computed(() => {
|
|
117
|
+
// 优化: 提前返回,避免不必要的计算
|
|
91
118
|
if (props.dynamicActionKey) {
|
|
92
119
|
return get(innerDynamicData.value, props.dynamicActionKey)
|
|
93
120
|
}
|
|
121
|
+
|
|
122
|
+
// 优化: 只在 jumpUrl 存在时才调用 getDynamicData
|
|
123
|
+
const jumpUrl = props.action?.jumpUrl
|
|
124
|
+
if (!jumpUrl) {
|
|
125
|
+
return props.action
|
|
126
|
+
}
|
|
127
|
+
|
|
94
128
|
return {
|
|
95
129
|
...props.action,
|
|
96
|
-
jumpUrl: getDynamicData(
|
|
130
|
+
jumpUrl: getDynamicData(jumpUrl, {
|
|
97
131
|
store: innerDynamicData.value,
|
|
98
132
|
}),
|
|
99
133
|
}
|
|
100
134
|
})
|
|
135
|
+
|
|
136
|
+
// 优化: 移除 deep: true,只监听引用变化
|
|
101
137
|
watch(
|
|
102
138
|
() => props.dataSource,
|
|
103
139
|
() => {
|
|
@@ -105,43 +141,55 @@ watch(
|
|
|
105
141
|
},
|
|
106
142
|
{
|
|
107
143
|
immediate: true,
|
|
108
|
-
deep: true,
|
|
109
144
|
},
|
|
110
145
|
)
|
|
146
|
+
|
|
147
|
+
// 优化: 提取比较逻辑,减少重复代码
|
|
148
|
+
const compareValues = (value: any, compareValue: string, compareType: string) => {
|
|
149
|
+
switch (compareType) {
|
|
150
|
+
case '=':
|
|
151
|
+
return `${value}` === compareValue
|
|
152
|
+
case '>=':
|
|
153
|
+
return value >= compareValue
|
|
154
|
+
case '<=':
|
|
155
|
+
return value <= compareValue
|
|
156
|
+
case '>':
|
|
157
|
+
return value > compareValue
|
|
158
|
+
case '<':
|
|
159
|
+
return value < compareValue
|
|
160
|
+
case '!=':
|
|
161
|
+
return value !== compareValue
|
|
162
|
+
case 'includes':
|
|
163
|
+
return value?.includes?.(compareValue) ?? false
|
|
164
|
+
default:
|
|
165
|
+
return `${value}` === compareValue
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 优化: 缓存数据源
|
|
170
|
+
const dependDataSource = computed(() => {
|
|
171
|
+
return props.keyFromUser ? userStore?.userInfo : innerDynamicData.value
|
|
172
|
+
})
|
|
173
|
+
|
|
111
174
|
const showArea = computed(() => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
case '>':
|
|
128
|
-
return value > compareValue
|
|
129
|
-
case '<':
|
|
130
|
-
return value < compareValue
|
|
131
|
-
case '!=':
|
|
132
|
-
return value !== compareValue
|
|
133
|
-
case 'includes':
|
|
134
|
-
return value.includes(compareValue)
|
|
135
|
-
default:
|
|
136
|
-
return `${value}` === compareValue
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (value === undefined) {
|
|
140
|
-
return false
|
|
141
|
-
}
|
|
142
|
-
return props.reverse ? !value : Boolean(value)
|
|
175
|
+
// 优化: 提前返回,避免不必要的计算
|
|
176
|
+
if (!props.dependKey) {
|
|
177
|
+
return true
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const value = get(dependDataSource.value, props.dependKey)
|
|
181
|
+
|
|
182
|
+
// 优化: 合并 undefined 检查
|
|
183
|
+
if (value === undefined) {
|
|
184
|
+
return false
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (props.dependKeyCompareValue) {
|
|
188
|
+
const compareValue = `${props.dependKeyCompareValue}`
|
|
189
|
+
return compareValues(value, compareValue, props.compareType || '=')
|
|
143
190
|
}
|
|
144
|
-
|
|
191
|
+
|
|
192
|
+
return props.reverse ? !value : Boolean(value)
|
|
145
193
|
})
|
|
146
194
|
</script>
|
|
147
195
|
|
|
@@ -1,27 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view
|
|
3
|
-
:style="
|
|
4
|
-
width: position === 'absolute' ? '100%' : '',
|
|
5
|
-
padding: `${transformValueUnit(paddingTop || paddingVertical || 0)} ${transformValueUnit(paddingRight || paddingHorizontal || 0)} ${transformValueUnit(paddingBottom || paddingVertical || 0)} ${transformValueUnit(paddingLeft || paddingHorizontal || 0)}`,
|
|
6
|
-
borderRadius: `${transformValueUnit(topRadius || radius)} ${transformValueUnit(topRadius || radius)} ${transformValueUnit(bottomRadius || radius)} ${transformValueUnit(bottomRadius || radius)}`,
|
|
7
|
-
color,
|
|
8
|
-
background: `${backgroundPosition} / ${backgroundSize} ${backgroundRepeat} url('${dynamicBgImage}'), ${innerBackgroundColor || 'transparent'}`,
|
|
9
|
-
fontSize: transformValueUnit(fontSize),
|
|
10
|
-
fontWeight,
|
|
11
|
-
borderColor,
|
|
12
|
-
zIndex,
|
|
13
|
-
position,
|
|
14
|
-
borderWidth: transformValueUnit(borderWidth),
|
|
15
|
-
boxShadow:
|
|
16
|
-
shadowColor && shadowSize ? `0px 0px ${blurSize}px ${shadowSize}px ${shadowColor}` : '',
|
|
17
|
-
textAlign,
|
|
18
|
-
margin: `${transformValueUnit(floatUp ? -floatUp : marginTop || 0)} ${transformValueUnit(marginRight || marginHorizontal || 0)} ${transformValueUnit(marginBottom || 0)} ${transformValueUnit(marginLeft || marginHorizontal || 0)}`,
|
|
19
|
-
...customStyle,
|
|
20
|
-
...getFlexStyle(align),
|
|
21
|
-
...getInnerStyle,
|
|
22
|
-
}"
|
|
3
|
+
:style="getFinalStyle"
|
|
23
4
|
class="box-border overflow-hidden relative"
|
|
24
|
-
:class="customClass"
|
|
5
|
+
:class="props.customClass"
|
|
25
6
|
>
|
|
26
7
|
<slot />
|
|
27
8
|
</view>
|
|
@@ -69,11 +50,11 @@ const innerBackgroundColor = computed(() => {
|
|
|
69
50
|
const getInnerStyle = computed(() => {
|
|
70
51
|
const innerStyle = Object.keys(props.dynamicStyleOptions || {}).reduce(
|
|
71
52
|
(acc, key) => {
|
|
72
|
-
const currentOptions = props.dynamicStyleOptions[key]
|
|
53
|
+
const currentOptions = props.dynamicStyleOptions?.[key]
|
|
73
54
|
if (key) {
|
|
74
55
|
const data = get(
|
|
75
|
-
currentOptions
|
|
76
|
-
currentOptions
|
|
56
|
+
currentOptions?.keyFrom === 'user' ? userStore?.userInfo : innerDynamicData.value,
|
|
57
|
+
currentOptions?.dynamicKey || '',
|
|
77
58
|
)
|
|
78
59
|
if (data) {
|
|
79
60
|
acc[key] = data
|
|
@@ -86,4 +67,64 @@ const getInnerStyle = computed(() => {
|
|
|
86
67
|
)
|
|
87
68
|
return innerStyle
|
|
88
69
|
})
|
|
70
|
+
|
|
71
|
+
// 缓存 padding 计算结果,减少 transformValueUnit 调用次数
|
|
72
|
+
const paddingStyle = computed(() => {
|
|
73
|
+
const top = transformValueUnit(props.paddingTop || props.paddingVertical || 0)
|
|
74
|
+
const right = transformValueUnit(props.paddingRight || props.paddingHorizontal || 0)
|
|
75
|
+
const bottom = transformValueUnit(props.paddingBottom || props.paddingVertical || 0)
|
|
76
|
+
const left = transformValueUnit(props.paddingLeft || props.paddingHorizontal || 0)
|
|
77
|
+
return `${top} ${right} ${bottom} ${left}`
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// 缓存 margin 计算结果
|
|
81
|
+
const marginStyle = computed(() => {
|
|
82
|
+
const top = transformValueUnit(props.floatUp ? -props.floatUp : props.marginTop || 0)
|
|
83
|
+
const right = transformValueUnit(props.marginRight || props.marginHorizontal || 0)
|
|
84
|
+
const bottom = transformValueUnit(props.marginBottom || 0)
|
|
85
|
+
const left = transformValueUnit(props.marginLeft || props.marginHorizontal || 0)
|
|
86
|
+
return `${top} ${right} ${bottom} ${left}`
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// 缓存 borderRadius 计算结果,避免重复计算
|
|
90
|
+
const borderRadiusStyle = computed(() => {
|
|
91
|
+
const topR = props.topRadius || props.radius
|
|
92
|
+
const bottomR = props.bottomRadius || props.radius
|
|
93
|
+
const topValue = transformValueUnit(topR)
|
|
94
|
+
const bottomValue = transformValueUnit(bottomR)
|
|
95
|
+
return `${topValue} ${topValue} ${bottomValue} ${bottomValue}`
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// 优化 background 字符串拼接
|
|
99
|
+
const backgroundStyle = computed(() => {
|
|
100
|
+
const bgImage = dynamicBgImage.value
|
|
101
|
+
const bgColor = innerBackgroundColor.value || 'transparent'
|
|
102
|
+
if (!bgImage) return bgColor
|
|
103
|
+
return `${props.backgroundPosition} / ${props.backgroundSize} ${props.backgroundRepeat} url('${bgImage}'), ${bgColor}`
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const getFinalStyle = computed(() => {
|
|
107
|
+
return {
|
|
108
|
+
width: props.position === 'absolute' ? '100%' : '',
|
|
109
|
+
padding: paddingStyle.value,
|
|
110
|
+
borderRadius: borderRadiusStyle.value,
|
|
111
|
+
color: props.color,
|
|
112
|
+
background: backgroundStyle.value,
|
|
113
|
+
fontSize: transformValueUnit(props.fontSize),
|
|
114
|
+
fontWeight: props.fontWeight,
|
|
115
|
+
borderColor: props.borderColor,
|
|
116
|
+
zIndex: props.zIndex,
|
|
117
|
+
position: props.position,
|
|
118
|
+
borderWidth: transformValueUnit(props.borderWidth),
|
|
119
|
+
boxShadow:
|
|
120
|
+
props.shadowColor && props.shadowSize
|
|
121
|
+
? `0px 0px ${props.blurSize}px ${props.shadowSize}px ${props.shadowColor}`
|
|
122
|
+
: '',
|
|
123
|
+
textAlign: props.textAlign,
|
|
124
|
+
margin: marginStyle.value,
|
|
125
|
+
...props.customStyle,
|
|
126
|
+
...getFlexStyle(props.align),
|
|
127
|
+
...getInnerStyle.value,
|
|
128
|
+
}
|
|
129
|
+
})
|
|
89
130
|
</script>
|
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<template v-if="mode === 'noStyle'">{{ innerValue }}</template>
|
|
3
|
-
<view
|
|
4
|
-
class="w-full h-full"
|
|
5
|
-
:style="{
|
|
6
|
-
...innerStyle,
|
|
7
|
-
}"
|
|
8
|
-
v-else-if="dynamicKey && hideWhenDynamicKeyNotExist ? dynamicValue : true"
|
|
9
|
-
>
|
|
3
|
+
<view class="w-full h-full" :style="wrapperStyle" v-else-if="shouldRender">
|
|
10
4
|
<lcb-block
|
|
11
|
-
v-bind="
|
|
5
|
+
v-bind="blockProps"
|
|
12
6
|
:customClass="`${customClass} border-solid`"
|
|
13
|
-
:customStyle="
|
|
14
|
-
width: props.fillWidth ? '100%' : 'fit-content',
|
|
15
|
-
height: props.fillHeight ? '100%' : 'fit-content',
|
|
16
|
-
...innerItemStyle,
|
|
17
|
-
}"
|
|
7
|
+
:customStyle="blockCustomStyle"
|
|
18
8
|
>
|
|
19
9
|
<lcb-action-view
|
|
20
10
|
v-bind="actionProps"
|
|
21
11
|
@avatar="onAvatar"
|
|
22
12
|
@click="handleClick"
|
|
23
|
-
:customStyle="
|
|
13
|
+
:customStyle="actionPaddingStyle"
|
|
24
14
|
:renderMode="!actionProps?.jumpType && mode !== 'image' ? 'noClick' : 'button'"
|
|
25
15
|
>
|
|
26
|
-
<view class="!flex items-center justify-center" :style="
|
|
16
|
+
<view class="!flex items-center justify-center" :style="iconGapStyle">
|
|
27
17
|
<wd-icon
|
|
28
18
|
v-if="icon"
|
|
29
19
|
:name="icon"
|
|
@@ -90,6 +80,7 @@ const props = withDefaults(defineProps<LcbButtonProps>(), {
|
|
|
90
80
|
})
|
|
91
81
|
const { userStore, innerDynamicData } = useDynamicData()
|
|
92
82
|
|
|
83
|
+
// 优化: 缓存 store 数据源
|
|
93
84
|
const store = computed(() => {
|
|
94
85
|
return props.keyFromUser ? userStore?.userInfo : innerDynamicData.value
|
|
95
86
|
})
|
|
@@ -102,6 +93,14 @@ const dynamicValue = computed(() => {
|
|
|
102
93
|
return value
|
|
103
94
|
})
|
|
104
95
|
|
|
96
|
+
// 优化: 提取条件渲染逻辑,避免在模板中重复计算
|
|
97
|
+
const shouldRender = computed(() => {
|
|
98
|
+
if (!props.dynamicKey || !props.hideWhenDynamicKeyNotExist) {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
return !!dynamicValue.value
|
|
102
|
+
})
|
|
103
|
+
|
|
105
104
|
const handleClick = () => {
|
|
106
105
|
if (props.mode === 'image' && innerValue.value && props.enablePreview) {
|
|
107
106
|
uni.previewImage({
|
|
@@ -122,16 +121,25 @@ const innerValue = computed(() => {
|
|
|
122
121
|
|
|
123
122
|
/** 处理跳转链接中的动态参数 */
|
|
124
123
|
const actionProps = computed(() => {
|
|
124
|
+
// 优化: 提前返回,避免不必要的计算
|
|
125
125
|
if (props.dynamicActionKey) {
|
|
126
126
|
return get(innerDynamicData.value, props.dynamicActionKey)
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
// 优化: 只在 jumpUrl 存在时才调用 getDynamicData
|
|
130
|
+
const jumpUrl = props.action?.jumpUrl
|
|
131
|
+
if (!jumpUrl) {
|
|
132
|
+
return props.action
|
|
133
|
+
}
|
|
134
|
+
|
|
128
135
|
return {
|
|
129
136
|
...props.action,
|
|
130
|
-
jumpUrl: getDynamicData(
|
|
137
|
+
jumpUrl: getDynamicData(jumpUrl, {
|
|
131
138
|
store: store.value,
|
|
132
139
|
}),
|
|
133
140
|
}
|
|
134
141
|
})
|
|
142
|
+
|
|
135
143
|
const onAvatar = (headImgUrl) => {
|
|
136
144
|
userStore?.updateUser(
|
|
137
145
|
{
|
|
@@ -140,6 +148,8 @@ const onAvatar = (headImgUrl) => {
|
|
|
140
148
|
true,
|
|
141
149
|
)
|
|
142
150
|
}
|
|
151
|
+
|
|
152
|
+
// 优化: 缓存样式对象,避免在模板中创建新对象
|
|
143
153
|
const innerStyle = computed(() => {
|
|
144
154
|
return getFlexStyle(props.align)
|
|
145
155
|
})
|
|
@@ -148,7 +158,29 @@ const innerItemStyle = computed(() => {
|
|
|
148
158
|
return getFlexStyle(props.itemAlign)
|
|
149
159
|
})
|
|
150
160
|
|
|
151
|
-
|
|
161
|
+
// 优化: 将 wrapper 样式单独提取
|
|
162
|
+
const wrapperStyle = computed(() => innerStyle.value)
|
|
163
|
+
|
|
164
|
+
// 优化: 将 iconGap 样式单独提取,避免模板字符串
|
|
165
|
+
const iconGapStyle = computed(() => `gap: ${props.iconGap}rpx`)
|
|
166
|
+
|
|
167
|
+
// 优化: 将 block customStyle 单独提取
|
|
168
|
+
const blockCustomStyle = computed(() => ({
|
|
169
|
+
width: props.fillWidth ? '100%' : 'fit-content',
|
|
170
|
+
height: props.fillHeight ? '100%' : 'fit-content',
|
|
171
|
+
...innerItemStyle.value,
|
|
172
|
+
}))
|
|
173
|
+
|
|
174
|
+
// 优化: 分离 padding 计算和 block props,避免解构整个 props
|
|
175
|
+
const actionPaddingStyle = computed(() => {
|
|
176
|
+
const top = transformValueUnit(props.paddingTop || props.paddingVertical)
|
|
177
|
+
const right = transformValueUnit(props.paddingRight || props.paddingHorizontal)
|
|
178
|
+
const bottom = transformValueUnit(props.paddingBottom || props.paddingVertical)
|
|
179
|
+
const left = transformValueUnit(props.paddingLeft || props.paddingHorizontal)
|
|
180
|
+
return `width: 100%; padding:${top} ${right} ${bottom} ${left}`
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const blockProps = computed(() => {
|
|
152
184
|
const {
|
|
153
185
|
paddingVertical,
|
|
154
186
|
paddingHorizontal,
|
|
@@ -158,9 +190,7 @@ const styleOptions = computed<[Record<string, any>, string]>(() => {
|
|
|
158
190
|
paddingRight,
|
|
159
191
|
...other
|
|
160
192
|
} = props
|
|
161
|
-
|
|
162
|
-
const actionStyle = `width: 100%; padding:${transformValueUnit(paddingTop || paddingVertical)} ${transformValueUnit(paddingRight || paddingHorizontal)} ${transformValueUnit(paddingBottom || paddingVertical)} ${transformValueUnit(paddingLeft || paddingHorizontal)}`
|
|
163
|
-
return [other, actionStyle]
|
|
193
|
+
return other
|
|
164
194
|
})
|
|
165
195
|
</script>
|
|
166
196
|
<style lang="scss" scoped>
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
<lcb-block v-bind="$props" :custom-style="listStyle" v-if="renderList.length">
|
|
3
3
|
<view
|
|
4
4
|
v-for="(item, index) in renderList"
|
|
5
|
-
:key="index"
|
|
5
|
+
:key="getItemKey(item, index)"
|
|
6
6
|
class="flex-shrink-0"
|
|
7
|
-
:style="
|
|
8
|
-
width: childrenAutoWidth ? 'auto' : width ? transformValueUnit(width) : '100%',
|
|
9
|
-
}"
|
|
7
|
+
:style="itemStyle"
|
|
10
8
|
>
|
|
11
9
|
<slot :data="item" :list="list" />
|
|
12
10
|
</view>
|
|
@@ -15,7 +13,7 @@
|
|
|
15
13
|
|
|
16
14
|
<script setup lang="ts">
|
|
17
15
|
import { transformValueUnit } from '@tplc/business/utils/transform'
|
|
18
|
-
import { computed, ref,
|
|
16
|
+
import { computed, ref, watch, shallowRef } from 'vue'
|
|
19
17
|
import useDynamicData from '../../hooks/useDynamicData'
|
|
20
18
|
import { dynamicRequest } from '../../utils/request'
|
|
21
19
|
import { LcbWrapperListProps } from './types'
|
|
@@ -32,9 +30,16 @@ const props = withDefaults(defineProps<LcbWrapperListProps>(), {
|
|
|
32
30
|
flexDirection: 'column',
|
|
33
31
|
})
|
|
34
32
|
const { userStore, innerDynamicData } = useDynamicData()
|
|
35
|
-
|
|
33
|
+
// 使用 shallowRef 优化大数组的响应式性能
|
|
34
|
+
const renderList = shallowRef<unknown[]>([])
|
|
35
|
+
const isLoading = ref(false)
|
|
36
36
|
|
|
37
|
+
// 优化:检查是否需要转换,避免不必要的遍历
|
|
37
38
|
const transformStringArrayToObjectArray = (data: unknown[]): unknown[] => {
|
|
39
|
+
// 如果第一个元素已经是对象,假设整个数组都是对象
|
|
40
|
+
if (data.length > 0 && typeof data[0] === 'object' && data[0] !== null) {
|
|
41
|
+
return data
|
|
42
|
+
}
|
|
38
43
|
return data.map((item) => {
|
|
39
44
|
if (typeof item === 'string') {
|
|
40
45
|
return { label: item }
|
|
@@ -43,31 +48,68 @@ const transformStringArrayToObjectArray = (data: unknown[]): unknown[] => {
|
|
|
43
48
|
})
|
|
44
49
|
}
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
// 优化:使用 watch 替代 watchEffect,明确依赖并添加防抖和加载状态
|
|
52
|
+
let loadingTimer: ReturnType<typeof setTimeout> | null = null
|
|
53
|
+
watch(
|
|
54
|
+
() => [props.dataSource, innerDynamicData.value, userStore?.userInfo] as const,
|
|
55
|
+
async ([dataSource, dynamicData, userInfo]) => {
|
|
56
|
+
// 防抖处理,避免频繁请求
|
|
57
|
+
if (loadingTimer) {
|
|
58
|
+
clearTimeout(loadingTimer)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
loadingTimer = setTimeout(async () => {
|
|
62
|
+
// 防止重复请求
|
|
63
|
+
if (isLoading.value) return
|
|
64
|
+
|
|
65
|
+
isLoading.value = true
|
|
66
|
+
try {
|
|
67
|
+
let data: unknown[] = []
|
|
68
|
+
const result = await dynamicRequest(dataSource, dynamicData, userInfo)
|
|
69
|
+
if (result) {
|
|
70
|
+
data = result
|
|
71
|
+
}
|
|
72
|
+
// 使用 shallowRef 时需要重新赋值整个数组
|
|
73
|
+
renderList.value = Array.isArray(data) ? transformStringArrayToObjectArray(data) : []
|
|
74
|
+
} finally {
|
|
75
|
+
isLoading.value = false
|
|
76
|
+
}
|
|
77
|
+
}, 100) // 100ms 防抖
|
|
78
|
+
},
|
|
79
|
+
{ immediate: true },
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
// 优化:缓存 key 获取逻辑,减少属性访问
|
|
83
|
+
const getItemKey = (item: any, index: number) => {
|
|
84
|
+
return item?.id ?? item?.key ?? index
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 优化:提取 gap 计算,避免在 listStyle 中重复计算
|
|
88
|
+
const gapValue = computed(() => transformValueUnit(props.gap))
|
|
89
|
+
|
|
90
|
+
// 优化:简化条件判断逻辑
|
|
91
|
+
const itemStyle = computed(() => {
|
|
92
|
+
let width = '100%'
|
|
93
|
+
if (props.childrenAutoWidth) {
|
|
94
|
+
width = 'auto'
|
|
95
|
+
} else if (props.width) {
|
|
96
|
+
width = transformValueUnit(props.width)
|
|
55
97
|
}
|
|
56
|
-
|
|
57
|
-
renderList.value = Array.isArray(data) ? transformStringArrayToObjectArray(data) : []
|
|
98
|
+
return { width }
|
|
58
99
|
})
|
|
100
|
+
|
|
101
|
+
// 优化:减少对象创建,使用固定的样式对象
|
|
59
102
|
const listStyle = computed(() => {
|
|
60
|
-
|
|
103
|
+
return {
|
|
61
104
|
width: '100%',
|
|
62
105
|
display: props.display,
|
|
63
106
|
'flex-direction': props.flexDirection,
|
|
64
|
-
gap:
|
|
107
|
+
gap: gapValue.value,
|
|
65
108
|
'align-items': 'stretch',
|
|
66
109
|
'grid-template-columns': `repeat(${props.gridColumns}, minmax(0, 1fr))`,
|
|
67
110
|
'overflow-x': props.scrollX ? 'auto' : 'hidden',
|
|
68
111
|
'white-space': props.scrollX ? 'nowrap' : 'normal',
|
|
69
112
|
}
|
|
70
|
-
return style
|
|
71
113
|
})
|
|
72
114
|
</script>
|
|
73
115
|
|