@tplc/business 0.7.22 → 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 +29 -0
- package/components/lcb-area/lcb-area.vue +109 -61
- package/components/lcb-block/lcb-block.vue +65 -24
- package/components/lcb-button/lcb-button.vue +53 -20
- package/components/lcb-button/types.ts +4 -1
- package/components/lcb-wrapper-list/lcb-wrapper-list.vue +62 -20
- package/package.json +2 -2
- package/types/components/lcb-button/types.d.ts +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
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
|
+
|
|
26
|
+
### [0.7.23](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v1.0.16...v0.7.23) (2025-12-18)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### ✨ Features | 新功能
|
|
30
|
+
|
|
31
|
+
* 新增progress ([474bcac](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/474bcac7c08cb234e1d6945036d8ca4d64fa4f88))
|
|
32
|
+
* 更新wot-number ([f700988](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/f700988b15ec08eef0d1b8c140483eca8e0afed4))
|
|
33
|
+
|
|
5
34
|
### [0.7.22](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.21...v0.7.22) (2025-12-17)
|
|
6
35
|
|
|
7
36
|
|
|
@@ -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"
|
|
@@ -47,6 +37,9 @@
|
|
|
47
37
|
v-if="innerValue"
|
|
48
38
|
/>
|
|
49
39
|
</template>
|
|
40
|
+
<template v-else-if="mode === 'progress'">
|
|
41
|
+
<wd-progress :value="innerValue" v-bind="progressProps" />
|
|
42
|
+
</template>
|
|
50
43
|
<text
|
|
51
44
|
v-else
|
|
52
45
|
style="line-height: 1.54"
|
|
@@ -87,6 +80,7 @@ const props = withDefaults(defineProps<LcbButtonProps>(), {
|
|
|
87
80
|
})
|
|
88
81
|
const { userStore, innerDynamicData } = useDynamicData()
|
|
89
82
|
|
|
83
|
+
// 优化: 缓存 store 数据源
|
|
90
84
|
const store = computed(() => {
|
|
91
85
|
return props.keyFromUser ? userStore?.userInfo : innerDynamicData.value
|
|
92
86
|
})
|
|
@@ -99,6 +93,14 @@ const dynamicValue = computed(() => {
|
|
|
99
93
|
return value
|
|
100
94
|
})
|
|
101
95
|
|
|
96
|
+
// 优化: 提取条件渲染逻辑,避免在模板中重复计算
|
|
97
|
+
const shouldRender = computed(() => {
|
|
98
|
+
if (!props.dynamicKey || !props.hideWhenDynamicKeyNotExist) {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
return !!dynamicValue.value
|
|
102
|
+
})
|
|
103
|
+
|
|
102
104
|
const handleClick = () => {
|
|
103
105
|
if (props.mode === 'image' && innerValue.value && props.enablePreview) {
|
|
104
106
|
uni.previewImage({
|
|
@@ -119,16 +121,25 @@ const innerValue = computed(() => {
|
|
|
119
121
|
|
|
120
122
|
/** 处理跳转链接中的动态参数 */
|
|
121
123
|
const actionProps = computed(() => {
|
|
124
|
+
// 优化: 提前返回,避免不必要的计算
|
|
122
125
|
if (props.dynamicActionKey) {
|
|
123
126
|
return get(innerDynamicData.value, props.dynamicActionKey)
|
|
124
127
|
}
|
|
128
|
+
|
|
129
|
+
// 优化: 只在 jumpUrl 存在时才调用 getDynamicData
|
|
130
|
+
const jumpUrl = props.action?.jumpUrl
|
|
131
|
+
if (!jumpUrl) {
|
|
132
|
+
return props.action
|
|
133
|
+
}
|
|
134
|
+
|
|
125
135
|
return {
|
|
126
136
|
...props.action,
|
|
127
|
-
jumpUrl: getDynamicData(
|
|
137
|
+
jumpUrl: getDynamicData(jumpUrl, {
|
|
128
138
|
store: store.value,
|
|
129
139
|
}),
|
|
130
140
|
}
|
|
131
141
|
})
|
|
142
|
+
|
|
132
143
|
const onAvatar = (headImgUrl) => {
|
|
133
144
|
userStore?.updateUser(
|
|
134
145
|
{
|
|
@@ -137,6 +148,8 @@ const onAvatar = (headImgUrl) => {
|
|
|
137
148
|
true,
|
|
138
149
|
)
|
|
139
150
|
}
|
|
151
|
+
|
|
152
|
+
// 优化: 缓存样式对象,避免在模板中创建新对象
|
|
140
153
|
const innerStyle = computed(() => {
|
|
141
154
|
return getFlexStyle(props.align)
|
|
142
155
|
})
|
|
@@ -145,7 +158,29 @@ const innerItemStyle = computed(() => {
|
|
|
145
158
|
return getFlexStyle(props.itemAlign)
|
|
146
159
|
})
|
|
147
160
|
|
|
148
|
-
|
|
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(() => {
|
|
149
184
|
const {
|
|
150
185
|
paddingVertical,
|
|
151
186
|
paddingHorizontal,
|
|
@@ -155,9 +190,7 @@ const styleOptions = computed<[Record<string, any>, string]>(() => {
|
|
|
155
190
|
paddingRight,
|
|
156
191
|
...other
|
|
157
192
|
} = props
|
|
158
|
-
|
|
159
|
-
const actionStyle = `width: 100%; padding:${transformValueUnit(paddingTop || paddingVertical)} ${transformValueUnit(paddingRight || paddingHorizontal)} ${transformValueUnit(paddingBottom || paddingVertical)} ${transformValueUnit(paddingLeft || paddingHorizontal)}`
|
|
160
|
-
return [other, actionStyle]
|
|
193
|
+
return other
|
|
161
194
|
})
|
|
162
195
|
</script>
|
|
163
196
|
<style lang="scss" scoped>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { progressProps } from '@tplc/wot/components/wd-progress/types'
|
|
1
2
|
import { LcbActionViewProps } from '../lcb-action-view/types'
|
|
2
3
|
import { LcbBlockProps } from '../lcb-block/types'
|
|
4
|
+
import { ExtractPropTypes } from 'vue'
|
|
3
5
|
|
|
4
6
|
export interface LcbButtonProps extends LcbBlockProps {
|
|
5
7
|
text: string
|
|
6
8
|
action?: LcbActionViewProps
|
|
7
|
-
mode: 'image' | 'text' | 'noStyle' | 'qrcode'
|
|
9
|
+
mode: 'image' | 'text' | 'noStyle' | 'qrcode' | 'progress'
|
|
8
10
|
url?: string
|
|
9
11
|
imageWidth?: number
|
|
10
12
|
imageHeight?: number
|
|
@@ -36,4 +38,5 @@ export interface LcbButtonProps extends LcbBlockProps {
|
|
|
36
38
|
// 动态action key
|
|
37
39
|
dynamicActionKey?: string
|
|
38
40
|
lineClamp?: number
|
|
41
|
+
progressProps?: ExtractPropTypes<typeof progressProps>
|
|
39
42
|
}
|
|
@@ -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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tplc/business",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.24",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"业务组件"
|
|
6
6
|
],
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
13
|
"vue": ">=3.2.47",
|
|
14
|
-
"@tplc/wot": "1.0.
|
|
14
|
+
"@tplc/wot": "1.0.16"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": ">=18",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { progressProps } from '@tplc/wot/components/wd-progress/types'
|
|
1
2
|
import { LcbActionViewProps } from '../lcb-action-view/types'
|
|
2
3
|
import { LcbBlockProps } from '../lcb-block/types'
|
|
4
|
+
import { ExtractPropTypes } from 'vue'
|
|
3
5
|
export interface LcbButtonProps extends LcbBlockProps {
|
|
4
6
|
text: string
|
|
5
7
|
action?: LcbActionViewProps
|
|
6
|
-
mode: 'image' | 'text' | 'noStyle' | 'qrcode'
|
|
8
|
+
mode: 'image' | 'text' | 'noStyle' | 'qrcode' | 'progress'
|
|
7
9
|
url?: string
|
|
8
10
|
imageWidth?: number
|
|
9
11
|
imageHeight?: number
|
|
@@ -32,4 +34,5 @@ export interface LcbButtonProps extends LcbBlockProps {
|
|
|
32
34
|
| 'bottom-right'
|
|
33
35
|
dynamicActionKey?: string
|
|
34
36
|
lineClamp?: number
|
|
37
|
+
progressProps?: ExtractPropTypes<typeof progressProps>
|
|
35
38
|
}
|