@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
- const innerStyle = computed(() => {
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
- 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
- }
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(props.action?.jumpUrl, {
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
- 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)
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
- return true
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.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>
@@ -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="styleOptions[0]"
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="styleOptions[1]"
13
+ :customStyle="actionPaddingStyle"
24
14
  :renderMode="!actionProps?.jumpType && mode !== 'image' ? 'noClick' : 'button'"
25
15
  >
26
- <view class="!flex items-center justify-center" :style="`gap: ${iconGap}rpx`">
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(props.action?.jumpUrl, {
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
- const styleOptions = computed<[Record<string, any>, string]>(() => {
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, watchEffect } from 'vue'
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
- const renderList = ref<unknown[]>([{ walletAmount: 1 }])
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
- 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
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
- const style = {
103
+ return {
61
104
  width: '100%',
62
105
  display: props.display,
63
106
  'flex-direction': props.flexDirection,
64
- gap: transformValueUnit(props.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.23",
3
+ "version": "0.7.24",
4
4
  "keywords": [
5
5
  "业务组件"
6
6
  ],