@tplc/business 0.7.23 → 0.7.25

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,41 @@
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.25](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.23...v0.7.25) (2025-12-22)
6
+
7
+
8
+ ### 🚀 Chore | 构建/工程依赖/工具
9
+
10
+ * **release:** 0.7.24 ([a8d4228](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/a8d4228a98f33d853e333cecf223731cf737091a))
11
+
12
+
13
+ ### ✨ Features | 新功能
14
+
15
+ * 优化lcb ([4508077](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/45080773f73d2f9329de373d2b713eb5b644dea0))
16
+ * 优化渲染 ([7ad9de0](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/7ad9de0f1effd3ba99d341927ce71ae8de2deec6))
17
+ * 去掉异步 ([ee2e200](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/ee2e2000d17f36ba049727aab365631b8652ab50))
18
+
19
+ ### [0.7.24](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.19...v0.7.24) (2025-12-21)
20
+
21
+
22
+ ### 🚀 Chore | 构建/工程依赖/工具
23
+
24
+ * **release:** 0.7.20 ([3c69dbe](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/3c69dbe444fc50cc4f470ffa316a379c739dab4a))
25
+ * **release:** 0.7.21 ([2c0db34](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/2c0db34f5ec241cc0297d68a5755c7ae7a75dcb8))
26
+ * **release:** 0.7.22 ([31f6a87](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/31f6a874afdc82a4a58a8ef610194757666144d0))
27
+ * **release:** 0.7.23 ([9c56ffd](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/9c56ffd1c060c2d81105a1049654f80081674d10))
28
+ * **release:** 1.0.16 ([6ab5880](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/6ab588048db6a5b3d58529ad239f895c989d6d03))
29
+
30
+
31
+ ### ✨ Features | 新功能
32
+
33
+ * 优化lcb ([4508077](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/45080773f73d2f9329de373d2b713eb5b644dea0))
34
+ * 优化pay ([fdead3a](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/fdead3a80ca5d7de1de0eff9745e35e2c4fca6db))
35
+ * 兼容点击 ([8027588](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/8027588f38760e3de6b01205ec982eceb91f0e86))
36
+ * 新增progress ([474bcac](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/474bcac7c08cb234e1d6945036d8ca4d64fa4f88))
37
+ * 更新wot-number ([f700988](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/f700988b15ec08eef0d1b8c140483eca8e0afed4))
38
+ * 调整数据结构 ([b6f0d24](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/b6f0d24a26d3d4dbf05ca855ed687d6819eb7c0f))
39
+
5
40
  ### [0.7.23](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v1.0.16...v0.7.23) (2025-12-18)
6
41
 
7
42
 
@@ -1,31 +1,27 @@
1
1
  <template>
2
2
  <lcb-block v-bind="$props" v-if="showArea">
3
- <lcb-action-view
4
- v-bind="actionProps"
5
- :renderMode="actionProps?.jumpType ? 'button' : 'noClick'"
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
- >
3
+ <lcb-action-view v-bind="actionProps" v-if="actionProps?.jumpType">
4
+ <view :style="containerStyle" class="h-full">
16
5
  <view
17
6
  v-for="(item, index) in list"
18
7
  :key="item.id"
19
8
  class="slot-wrapper z-1"
20
- :style="{
21
- ...getStyle(index),
22
- overflowX,
23
- }"
9
+ :style="getItemStyle(index)"
24
10
  >
25
11
  <slot :item="item" />
26
12
  </view>
27
13
  </view>
28
14
  </lcb-action-view>
15
+ <view :style="containerStyle" class="h-full" v-else>
16
+ <view
17
+ v-for="(item, index) in list"
18
+ :key="item.id"
19
+ class="slot-wrapper z-1"
20
+ :style="getItemStyle(index)"
21
+ >
22
+ <slot :item="item" />
23
+ </view>
24
+ </view>
29
25
  </lcb-block>
30
26
  </template>
31
27
 
@@ -51,29 +47,66 @@ const props = withDefaults(defineProps<LcbAreaProps>(), {
51
47
  overflowX: 'initial',
52
48
  })
53
49
  const { userStore, innerDynamicData, pageInfo } = useDynamicData()
54
- const innerStyle = computed(() => {
50
+
51
+ // 缓存 gap 的单位转换
52
+ const gapValue = computed(() => transformValueUnit(props.gap))
53
+
54
+ // 优化容器样式计算,合并为一个 computed
55
+ const containerStyle = computed(() => {
56
+ const baseStyle = {
57
+ display: props.display,
58
+ gap: gapValue.value,
59
+ overflowX: (props.scrollX ? 'auto' : 'hidden') as 'auto' | 'hidden',
60
+ }
61
+
55
62
  if (props.display === 'grid') {
56
63
  return {
64
+ ...baseStyle,
57
65
  gridTemplateColumns: `repeat(${props.gridColumns}, minmax(0, 1fr))`,
58
66
  }
59
67
  }
68
+
60
69
  return {
70
+ ...baseStyle,
61
71
  flexDirection: props.displayFlex,
62
72
  alignItems: 'stretch',
63
73
  }
64
74
  })
65
- const getStyle = (index: number) => {
66
- const flex = props.areaFlexs?.[index]?.flex ?? 1
67
- return {
68
- ...getFlexStyle(props.itemAlign),
69
- gridColumn:
70
- props.areaItems?.[index]?.colSpan && props.display === 'grid'
71
- ? `span ${props.areaItems?.[index]?.colSpan} / span ${props.areaItems?.[index]?.colSpan}`
72
- : undefined,
73
- flex: props.display === 'flex' ? flex || 'auto' : undefined,
74
- flexShrink: props.display === 'flex' ? (props.flexShrink ?? 1) : undefined,
75
- width: flex ? '100%' : 'auto',
76
- }
75
+
76
+ // 缓存 itemAlign flex 样式,避免每个 item 都重复计算
77
+ const itemAlignStyle = computed(() => getFlexStyle(props.itemAlign))
78
+
79
+ // 优化: 缓存公共样式部分
80
+ const commonItemStyle = computed(() => ({
81
+ ...itemAlignStyle.value,
82
+ overflowX: props.overflowX,
83
+ }))
84
+
85
+ // 优化: 缓存每个 item 的样式,避免重复计算
86
+ const itemStylesCache = computed(() => {
87
+ const isGrid = props.display === 'grid'
88
+ const isFlex = props.display === 'flex'
89
+ const flexShrink = props.flexShrink ?? 1
90
+
91
+ return (
92
+ props.list?.map((_, index) => {
93
+ const flex = props.areaFlexs?.[index]?.flex ?? 1
94
+ const colSpan = props.areaItems?.[index]?.colSpan
95
+
96
+ return {
97
+ ...commonItemStyle.value,
98
+ gridColumn: colSpan && isGrid ? `span ${colSpan} / span ${colSpan}` : undefined,
99
+ flex: isFlex ? flex || 'auto' : undefined,
100
+ flexShrink: isFlex ? flexShrink : undefined,
101
+ width: flex ? '100%' : 'auto',
102
+ }
103
+ }) || []
104
+ )
105
+ })
106
+
107
+ // 优化: 使用缓存的样式
108
+ const getItemStyle = (index: number) => {
109
+ return itemStylesCache.value[index] || commonItemStyle.value
77
110
  }
78
111
 
79
112
  const getData = async () => {
@@ -88,16 +121,27 @@ const getData = async () => {
88
121
 
89
122
  /** 处理跳转链接中的动态参数 */
90
123
  const actionProps = computed(() => {
124
+ const jumpUrl = props.action?.jumpUrl
125
+ if (!jumpUrl) {
126
+ return props.action
127
+ }
128
+
129
+ // 优化: 提前返回,避免不必要的计算
91
130
  if (props.dynamicActionKey) {
92
131
  return get(innerDynamicData.value, props.dynamicActionKey)
93
132
  }
133
+
134
+ // 优化: 只在 jumpUrl 存在时才调用 getDynamicData
135
+
94
136
  return {
95
137
  ...props.action,
96
- jumpUrl: getDynamicData(props.action?.jumpUrl, {
138
+ jumpUrl: getDynamicData(jumpUrl, {
97
139
  store: innerDynamicData.value,
98
140
  }),
99
141
  }
100
142
  })
143
+
144
+ // 优化: 移除 deep: true,只监听引用变化
101
145
  watch(
102
146
  () => props.dataSource,
103
147
  () => {
@@ -105,43 +149,55 @@ watch(
105
149
  },
106
150
  {
107
151
  immediate: true,
108
- deep: true,
109
152
  },
110
153
  )
154
+
155
+ // 优化: 提取比较逻辑,减少重复代码
156
+ const compareValues = (value: any, compareValue: string, compareType: string) => {
157
+ switch (compareType) {
158
+ case '=':
159
+ return `${value}` === compareValue
160
+ case '>=':
161
+ return value >= compareValue
162
+ case '<=':
163
+ return value <= compareValue
164
+ case '>':
165
+ return value > compareValue
166
+ case '<':
167
+ return value < compareValue
168
+ case '!=':
169
+ return value !== compareValue
170
+ case 'includes':
171
+ return value?.includes?.(compareValue) ?? false
172
+ default:
173
+ return `${value}` === compareValue
174
+ }
175
+ }
176
+
177
+ // 优化: 缓存数据源
178
+ const dependDataSource = computed(() => {
179
+ return props.keyFromUser ? userStore?.userInfo : innerDynamicData.value
180
+ })
181
+
111
182
  const showArea = computed(() => {
112
- if (props.dependKey) {
113
- const value = get(
114
- props.keyFromUser ? userStore?.userInfo : innerDynamicData.value,
115
- props.dependKey,
116
- )
117
-
118
- if (props.dependKeyCompareValue) {
119
- const compareValue = `${props.dependKeyCompareValue}`
120
- switch (props.compareType) {
121
- case '=':
122
- return `${value}` === compareValue
123
- case '>=':
124
- return value >= compareValue
125
- case '<=':
126
- return value <= compareValue
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)
183
+ // 优化: 提前返回,避免不必要的计算
184
+ if (!props.dependKey) {
185
+ return true
186
+ }
187
+
188
+ const value = get(dependDataSource.value, props.dependKey)
189
+
190
+ // 优化: 合并 undefined 检查
191
+ if (value === undefined) {
192
+ return false
143
193
  }
144
- return true
194
+
195
+ if (props.dependKeyCompareValue) {
196
+ const compareValue = `${props.dependKeyCompareValue}`
197
+ return compareValues(value, compareValue, props.compareType || '=')
198
+ }
199
+
200
+ return props.reverse ? !value : Boolean(value)
145
201
  })
146
202
  </script>
147
203
 
@@ -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.keyFrom === 'user' ? userStore?.userInfo : innerDynamicData.value,
76
- currentOptions.dynamicKey,
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>
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <lcb-action-view
3
+ v-bind="actionProps"
4
+ @avatar="onAvatar"
5
+ @click="handleClick"
6
+ :customStyle="actionPaddingStyle"
7
+ :renderMode="!actionProps?.jumpType && mode !== 'image' ? 'noClick' : 'button'"
8
+ >
9
+ <lcb-button-content
10
+ v-bind="props"
11
+ :innerValue="innerValue"
12
+ :store="store"
13
+ :dynamicValue="dynamicValue"
14
+ />
15
+ </lcb-action-view>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { LcbButtonProps } from '../../types'
20
+ import { computed } from 'vue'
21
+ import { getDynamicData } from '../../../../utils/utils'
22
+ import { transformValueUnit } from '../../../../utils/transform'
23
+ import { LcbActionViewProps } from '../../../lcb-action-view/types'
24
+ import LcbButtonContent from '../lcb-button-content/index.vue'
25
+ defineOptions({
26
+ name: 'LcbButtonActionContent',
27
+ options: {
28
+ addGlobalClass: true,
29
+ virtualHost: true,
30
+ styleIsolation: 'shared',
31
+ },
32
+ })
33
+ const props = defineProps<
34
+ LcbButtonProps & {
35
+ innerValue?: string
36
+ actionProps: LcbActionViewProps
37
+ store: any
38
+ userStore: any
39
+ dynamicValue?: string
40
+ }
41
+ >()
42
+ // 优化: 分离 padding 计算和 block props,避免解构整个 props
43
+ const actionPaddingStyle = computed(() => {
44
+ const top = transformValueUnit(props.paddingTop || props.paddingVertical)
45
+ const right = transformValueUnit(props.paddingRight || props.paddingHorizontal)
46
+ const bottom = transformValueUnit(props.paddingBottom || props.paddingVertical)
47
+ const left = transformValueUnit(props.paddingLeft || props.paddingHorizontal)
48
+ return `width: 100%; padding:${top} ${right} ${bottom} ${left}`
49
+ })
50
+
51
+ const onAvatar = (headImgUrl) => {
52
+ props.userStore?.updateUser(
53
+ {
54
+ headImgUrl,
55
+ },
56
+ true,
57
+ )
58
+ }
59
+
60
+ const handleClick = () => {
61
+ if (props.mode === 'image' && innerValue.value && props.enablePreview) {
62
+ uni.previewImage({
63
+ urls: [innerValue.value],
64
+ current: innerValue.value,
65
+ })
66
+ }
67
+ }
68
+ const innerValue = computed(() => {
69
+ if (props.mode === 'image') {
70
+ return props.dynamicValue || props.url
71
+ }
72
+ return getDynamicData(props.text, {
73
+ store: props.store,
74
+ defaultText: props.textDefaultValue,
75
+ })
76
+ })
77
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <view class="!flex items-center justify-center" :style="iconGapStyle">
3
+ <wd-icon
4
+ v-if="icon"
5
+ :name="icon"
6
+ class-prefix="lcb"
7
+ :size="transformValueUnit(iconSize)"
8
+ :color="iconColor"
9
+ />
10
+ <template v-if="mode === 'image'">
11
+ <wd-img
12
+ :src="innerValue"
13
+ :width="transformValueUnit(imageWidth)"
14
+ :mode="imageHeight ? 'aspectFill' : 'widthFix'"
15
+ :height="imageHeight ? transformValueUnit(imageHeight) : 'auto'"
16
+ />
17
+ </template>
18
+ <template v-else-if="mode === 'qrcode'">
19
+ <wd-qr-code :size="qrCodeSize" canvasId="qrCode" :value="innerValue" v-if="innerValue" />
20
+ </template>
21
+ <template v-else-if="mode === 'progress'">
22
+ <wd-progress :value="innerValue" v-bind="progressProps" />
23
+ </template>
24
+ <text
25
+ v-else
26
+ style="line-height: 1.54"
27
+ class="whitespace-pre-wrap"
28
+ :class="lineClamp ? `line-clamp-${lineClamp}` : ''"
29
+ >
30
+ {{ innerValue }}
31
+ </text>
32
+ </view>
33
+ </template>
34
+
35
+ <script setup lang="ts">
36
+ import { LcbButtonProps } from '../../types'
37
+ import { transformValueUnit } from '../../../../utils/transform'
38
+ import { computed } from 'vue'
39
+ defineOptions({
40
+ name: 'LcnButtonContent',
41
+ options: {
42
+ addGlobalClass: true,
43
+ virtualHost: true,
44
+ styleIsolation: 'shared',
45
+ },
46
+ })
47
+ const props = defineProps<
48
+ LcbButtonProps & { innerValue?: string; store: any; dynamicValue?: string }
49
+ >()
50
+
51
+ // 优化: 将 iconGap 样式单独提取,避免模板字符串
52
+ const iconGapStyle = computed(() => `gap: ${props.iconGap}rpx`)
53
+ </script>
@@ -1,74 +1,37 @@
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"
3
+ <lcb-block
4
+ v-bind="blockProps"
5
+ :customClass="`${customClass} border-solid w-full h-full`"
6
+ :customStyle="blockCustomStyle"
7
+ v-else-if="shouldRender"
9
8
  >
10
- <lcb-block
11
- v-bind="styleOptions[0]"
12
- :customClass="`${customClass} border-solid`"
13
- :customStyle="{
14
- width: props.fillWidth ? '100%' : 'fit-content',
15
- height: props.fillHeight ? '100%' : 'fit-content',
16
- ...innerItemStyle,
17
- }"
18
- >
19
- <lcb-action-view
20
- v-bind="actionProps"
21
- @avatar="onAvatar"
22
- @click="handleClick"
23
- :customStyle="styleOptions[1]"
24
- :renderMode="!actionProps?.jumpType && mode !== 'image' ? 'noClick' : 'button'"
25
- >
26
- <view class="!flex items-center justify-center" :style="`gap: ${iconGap}rpx`">
27
- <wd-icon
28
- v-if="icon"
29
- :name="icon"
30
- class-prefix="lcb"
31
- :size="transformValueUnit(iconSize)"
32
- :color="iconColor"
33
- />
34
- <template v-if="mode === 'image'">
35
- <wd-img
36
- :src="innerValue"
37
- :width="transformValueUnit(imageWidth)"
38
- :mode="imageHeight ? 'aspectFill' : 'widthFix'"
39
- :height="imageHeight ? transformValueUnit(imageHeight) : 'auto'"
40
- />
41
- </template>
42
- <template v-else-if="mode === 'qrcode'">
43
- <wd-qr-code
44
- :size="qrCodeSize"
45
- canvasId="qrCode"
46
- :value="innerValue"
47
- v-if="innerValue"
48
- />
49
- </template>
50
- <template v-else-if="mode === 'progress'">
51
- <wd-progress :value="innerValue" v-bind="progressProps" />
52
- </template>
53
- <text
54
- v-else
55
- style="line-height: 1.54"
56
- class="whitespace-pre-wrap"
57
- :class="lineClamp ? `line-clamp-${lineClamp}` : ''"
58
- >
59
- {{ innerValue }}
60
- </text>
61
- </view>
62
- </lcb-action-view>
63
- </lcb-block>
64
- </view>
9
+ <lcb-button-action
10
+ v-bind="props"
11
+ v-if="actionProps?.jumpType || mode === 'image'"
12
+ :innerValue="innerValue"
13
+ :actionProps="actionProps"
14
+ :store="store"
15
+ :userStore="userStore"
16
+ :dynamicValue="dynamicValue"
17
+ />
18
+ <lcb-button-content
19
+ v-else
20
+ v-bind="props"
21
+ :innerValue="innerValue"
22
+ :store="store"
23
+ :dynamicValue="dynamicValue"
24
+ />
25
+ </lcb-block>
65
26
  </template>
66
27
 
67
28
  <script setup lang="ts">
68
29
  import { get } from 'lodash-es'
69
30
  import { computed } from 'vue'
70
31
  import useDynamicData from '../../hooks/useDynamicData'
71
- import { getFlexStyle, transformValueUnit } from '../../utils/transform'
32
+ import { getFlexStyle } from '../../utils/transform'
33
+ import LcbButtonAction from './components/lcb-button-action/index.vue'
34
+ import LcbButtonContent from './components/lcb-button-content/index.vue'
72
35
  import { getDynamicData } from '../../utils/utils'
73
36
  import { LcbButtonProps } from './types'
74
37
  defineOptions({
@@ -90,6 +53,7 @@ const props = withDefaults(defineProps<LcbButtonProps>(), {
90
53
  })
91
54
  const { userStore, innerDynamicData } = useDynamicData()
92
55
 
56
+ // 优化: 缓存 store 数据源
93
57
  const store = computed(() => {
94
58
  return props.keyFromUser ? userStore?.userInfo : innerDynamicData.value
95
59
  })
@@ -102,14 +66,14 @@ const dynamicValue = computed(() => {
102
66
  return value
103
67
  })
104
68
 
105
- const handleClick = () => {
106
- if (props.mode === 'image' && innerValue.value && props.enablePreview) {
107
- uni.previewImage({
108
- urls: [innerValue.value],
109
- current: innerValue.value,
110
- })
69
+ // 优化: 提取条件渲染逻辑,避免在模板中重复计算
70
+ const shouldRender = computed(() => {
71
+ if (!props.dynamicKey || !props.hideWhenDynamicKeyNotExist) {
72
+ return true
111
73
  }
112
- }
74
+ return !!dynamicValue.value
75
+ })
76
+
113
77
  const innerValue = computed(() => {
114
78
  if (props.mode === 'image') {
115
79
  return dynamicValue.value || props.url
@@ -122,33 +86,34 @@ const innerValue = computed(() => {
122
86
 
123
87
  /** 处理跳转链接中的动态参数 */
124
88
  const actionProps = computed(() => {
89
+ // 优化: 只在 jumpUrl 存在时才调用 getDynamicData
90
+ const jumpUrl = props.action?.jumpUrl
91
+ if (!jumpUrl) {
92
+ return props.action
93
+ }
94
+
95
+ // 优化: 提前返回,避免不必要的计算
125
96
  if (props.dynamicActionKey) {
126
97
  return get(innerDynamicData.value, props.dynamicActionKey)
127
98
  }
99
+
128
100
  return {
129
101
  ...props.action,
130
- jumpUrl: getDynamicData(props.action?.jumpUrl, {
102
+ jumpUrl: getDynamicData(jumpUrl, {
131
103
  store: store.value,
132
104
  }),
133
105
  }
134
106
  })
135
- const onAvatar = (headImgUrl) => {
136
- userStore?.updateUser(
137
- {
138
- headImgUrl,
139
- },
140
- true,
141
- )
142
- }
143
- const innerStyle = computed(() => {
144
- return getFlexStyle(props.align)
145
- })
146
107
 
147
- const innerItemStyle = computed(() => {
148
- return getFlexStyle(props.itemAlign)
149
- })
108
+ // 优化: block customStyle 单独提取
109
+ const blockCustomStyle = computed(() => ({
110
+ width: props.fillWidth ? '100%' : 'fit-content',
111
+ height: props.fillHeight ? '100%' : 'fit-content',
112
+ ...getFlexStyle(props.itemAlign),
113
+ ...getFlexStyle(props.align),
114
+ }))
150
115
 
151
- const styleOptions = computed<[Record<string, any>, string]>(() => {
116
+ const blockProps = computed(() => {
152
117
  const {
153
118
  paddingVertical,
154
119
  paddingHorizontal,
@@ -158,9 +123,7 @@ const styleOptions = computed<[Record<string, any>, string]>(() => {
158
123
  paddingRight,
159
124
  ...other
160
125
  } = props
161
-
162
- const actionStyle = `width: 100%; padding:${transformValueUnit(paddingTop || paddingVertical)} ${transformValueUnit(paddingRight || paddingHorizontal)} ${transformValueUnit(paddingBottom || paddingVertical)} ${transformValueUnit(paddingLeft || paddingHorizontal)}`
163
- return [other, actionStyle]
126
+ return other
164
127
  })
165
128
  </script>
166
129
  <style lang="scss" scoped>
@@ -2,20 +2,18 @@
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
- <slot :data="item" :list="list" />
9
+ <slot :data="item" />
12
10
  </view>
13
11
  </lcb-block>
14
12
  </template>
15
13
 
16
14
  <script setup lang="ts">
17
15
  import { transformValueUnit } from '@tplc/business/utils/transform'
18
- import { computed, ref, watchEffect } from 'vue'
16
+ import { computed, 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,15 @@ const props = withDefaults(defineProps<LcbWrapperListProps>(), {
32
30
  flexDirection: 'column',
33
31
  })
34
32
  const { userStore, innerDynamicData } = useDynamicData()
35
- const renderList = ref<unknown[]>([{ walletAmount: 1 }])
33
+ // 使用 shallowRef 优化大数组的响应式性能
34
+ const renderList = shallowRef<unknown[]>([])
36
35
 
36
+ // 优化:检查是否需要转换,避免不必要的遍历
37
37
  const transformStringArrayToObjectArray = (data: unknown[]): unknown[] => {
38
+ // 如果第一个元素已经是对象,假设整个数组都是对象
39
+ if (data.length > 0 && typeof data[0] === 'object' && data[0] !== null) {
40
+ return data
41
+ }
38
42
  return data.map((item) => {
39
43
  if (typeof item === 'string') {
40
44
  return { label: item }
@@ -43,21 +47,41 @@ const transformStringArrayToObjectArray = (data: unknown[]): unknown[] => {
43
47
  })
44
48
  }
45
49
 
46
- watchEffect(async () => {
47
- let data: unknown[] = []
48
- const dynamicData = await dynamicRequest(
49
- props.dataSource,
50
- innerDynamicData.value,
51
- userStore?.userInfo,
52
- )
53
- if (dynamicData) {
54
- data = dynamicData
50
+ watch(
51
+ () => [props.dataSource, innerDynamicData.value, userStore?.userInfo] as const,
52
+
53
+ async ([dataSource, dynamicData, userInfo]) => {
54
+ // 防止重复请求
55
+ let data: unknown[] = []
56
+ const result = await dynamicRequest(dataSource, dynamicData, userInfo)
57
+ if (result) {
58
+ data = result
59
+ }
60
+ // 使用 shallowRef 时需要重新赋值整个数组
61
+ renderList.value = Array.isArray(data) ? transformStringArrayToObjectArray(data) : []
62
+ },
63
+ { immediate: true },
64
+ )
65
+
66
+ // 优化:缓存 key 获取逻辑,减少属性访问
67
+ const getItemKey = (item: any, index: number) => {
68
+ return item?.id ?? item?.key ?? index
69
+ }
70
+
71
+ // 优化:简化条件判断逻辑
72
+ const itemStyle = computed(() => {
73
+ let width = '100%'
74
+ if (props.childrenAutoWidth) {
75
+ width = 'auto'
76
+ } else if (props.width) {
77
+ width = transformValueUnit(props.width)
55
78
  }
56
- // 转换字符串数组为对象数组的辅助函数
57
- renderList.value = Array.isArray(data) ? transformStringArrayToObjectArray(data) : []
79
+ return { width }
58
80
  })
81
+
82
+ // 优化:减少对象创建,使用固定的样式对象
59
83
  const listStyle = computed(() => {
60
- const style = {
84
+ return {
61
85
  width: '100%',
62
86
  display: props.display,
63
87
  'flex-direction': props.flexDirection,
@@ -67,7 +91,6 @@ const listStyle = computed(() => {
67
91
  'overflow-x': props.scrollX ? 'auto' : 'hidden',
68
92
  'white-space': props.scrollX ? 'nowrap' : 'normal',
69
93
  }
70
- return style
71
94
  })
72
95
  </script>
73
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tplc/business",
3
- "version": "0.7.23",
3
+ "version": "0.7.25",
4
4
  "keywords": [
5
5
  "业务组件"
6
6
  ],
@@ -1,6 +1,7 @@
1
1
  import { LcbAreaProps } from './types'
2
2
  declare function __VLS_template(): {
3
3
  default?(_: { item: LcbAreaProps }): any
4
+ default?(_: { item: LcbAreaProps }): any
4
5
  }
5
6
  declare const __VLS_component: import('vue').DefineComponent<
6
7
  __VLS_WithDefaults<
@@ -0,0 +1,49 @@
1
+ import { LcbButtonProps } from '../../types'
2
+ import { LcbActionViewProps } from '../../../lcb-action-view/types'
3
+ declare const _default: import('vue').DefineComponent<
4
+ __VLS_TypePropsToOption<
5
+ LcbButtonProps & {
6
+ innerValue?: string
7
+ actionProps: LcbActionViewProps
8
+ store: any
9
+ userStore: any
10
+ dynamicValue?: string
11
+ }
12
+ >,
13
+ {},
14
+ unknown,
15
+ {},
16
+ {},
17
+ import('vue').ComponentOptionsMixin,
18
+ import('vue').ComponentOptionsMixin,
19
+ {},
20
+ string,
21
+ import('vue').PublicProps,
22
+ Readonly<
23
+ import('vue').ExtractPropTypes<
24
+ __VLS_TypePropsToOption<
25
+ LcbButtonProps & {
26
+ innerValue?: string
27
+ actionProps: LcbActionViewProps
28
+ store: any
29
+ userStore: any
30
+ dynamicValue?: string
31
+ }
32
+ >
33
+ >
34
+ >,
35
+ {},
36
+ {}
37
+ >
38
+ export default _default
39
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T
40
+ type __VLS_TypePropsToOption<T> = {
41
+ [K in keyof T]-?: {} extends Pick<T, K>
42
+ ? {
43
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>
44
+ }
45
+ : {
46
+ type: import('vue').PropType<T[K]>
47
+ required: true
48
+ }
49
+ }
@@ -0,0 +1,44 @@
1
+ import { LcbButtonProps } from '../../types'
2
+ declare const _default: import('vue').DefineComponent<
3
+ __VLS_TypePropsToOption<
4
+ LcbButtonProps & {
5
+ innerValue?: string
6
+ store: any
7
+ dynamicValue?: string
8
+ }
9
+ >,
10
+ {},
11
+ unknown,
12
+ {},
13
+ {},
14
+ import('vue').ComponentOptionsMixin,
15
+ import('vue').ComponentOptionsMixin,
16
+ {},
17
+ string,
18
+ import('vue').PublicProps,
19
+ Readonly<
20
+ import('vue').ExtractPropTypes<
21
+ __VLS_TypePropsToOption<
22
+ LcbButtonProps & {
23
+ innerValue?: string
24
+ store: any
25
+ dynamicValue?: string
26
+ }
27
+ >
28
+ >
29
+ >,
30
+ {},
31
+ {}
32
+ >
33
+ export default _default
34
+ type __VLS_NonUndefinedable<T> = T extends undefined ? never : T
35
+ type __VLS_TypePropsToOption<T> = {
36
+ [K in keyof T]-?: {} extends Pick<T, K>
37
+ ? {
38
+ type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>
39
+ }
40
+ : {
41
+ type: import('vue').PropType<T[K]>
42
+ required: true
43
+ }
44
+ }
@@ -1,6 +1,6 @@
1
1
  import { LcbWrapperListProps } from './types'
2
2
  declare function __VLS_template(): {
3
- default?(_: { data: unknown; list: import('../lcb-area/types').LcbAreaProps[] | undefined }): any
3
+ default?(_: { data: unknown }): any
4
4
  }
5
5
  declare const __VLS_component: import('vue').DefineComponent<
6
6
  __VLS_WithDefaults<