@tplc/business 0.7.73 → 0.7.75

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,25 @@
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.75](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.74...v0.7.75) (2026-01-18)
6
+
7
+
8
+ ### 🐛 Bug Fixes | Bug 修复
9
+
10
+ * **lcb-map:** replace loading GIF with a webp image and remove the old GIF file ([6f69363](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/6f693637439cc77ff651713dd3708da23c3712d0))
11
+
12
+
13
+ ### ✨ Features | 新功能
14
+
15
+ * **lcb-area:** add IS_EDITOR_MODE constant and optimize lcb-area and lcb-block components for fixed positioning and style caching ([ba958ae](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/ba958ae70cdfb74068799319d2ef4226738a6065))
16
+
17
+ ### [0.7.74](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.73...v0.7.74) (2026-01-17)
18
+
19
+
20
+ ### ✨ Features | 新功能
21
+
22
+ * **lcb-product:** add headImgStyle property to LcbProductProps and LcbProductItemProps, update ItemValue component to use wd-img for head image rendering ([2ae9d8f](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/commit/2ae9d8f51700a275ce37a53faebcb1109f753094))
23
+
5
24
  ### [0.7.73](https://gitlab888.30jia.com.cn/tourism-front/zero-code-pro/compare/v0.7.72...v0.7.73) (2026-01-17)
6
25
 
7
26
 
@@ -26,7 +26,7 @@
26
26
  </template>
27
27
 
28
28
  <script setup lang="ts">
29
- import { computed, watch } from 'vue'
29
+ import { computed, watch, shallowRef } from 'vue'
30
30
  import { LcbAreaProps } from './types'
31
31
  import { getFlexStyle, transformValueUnit } from '@tplc/business/utils/transform'
32
32
  import { get } from 'lodash-es'
@@ -48,65 +48,84 @@ const props = withDefaults(defineProps<LcbAreaProps>(), {
48
48
  })
49
49
  const { userStore, innerDynamicData, pageInfo } = useDynamicData()
50
50
 
51
- // 缓存 gap 的单位转换
52
- const gapValue = computed(() => transformValueUnit(props.gap))
51
+ // 优化:使用 shallowRef 缓存 flex 样式
52
+ const itemAlignStyleCache = shallowRef<Record<string, any> | null>(null)
53
+ const cachedItemAlign = shallowRef<string>('')
53
54
 
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',
55
+ const getItemAlignStyle = () => {
56
+ const align = props.itemAlign
57
+ if (!align) return {}
58
+ if (align === cachedItemAlign.value && itemAlignStyleCache.value) {
59
+ return itemAlignStyleCache.value
60
60
  }
61
+ cachedItemAlign.value = align
62
+ itemAlignStyleCache.value = getFlexStyle(align)
63
+ return itemAlignStyleCache.value
64
+ }
65
+
66
+ // 优化:缓存 gap 和容器样式计算
67
+ const containerStyle = computed(() => {
68
+ const gapValue = transformValueUnit(props.gap)
69
+ const overflowX = (props.scrollX ? 'auto' : 'hidden') as 'auto' | 'hidden'
61
70
 
62
71
  if (props.display === 'grid') {
63
72
  return {
64
- ...baseStyle,
73
+ display: 'grid',
74
+ gap: gapValue,
75
+ overflowX,
65
76
  gridTemplateColumns: `repeat(${props.gridColumns}, minmax(0, 1fr))`,
66
77
  }
67
78
  }
68
79
 
69
80
  return {
70
- ...baseStyle,
81
+ display: 'flex',
82
+ gap: gapValue,
83
+ overflowX,
71
84
  flexDirection: props.displayFlex,
72
85
  alignItems: 'stretch',
73
86
  }
74
87
  })
75
88
 
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 的样式,避免重复计算
89
+ // 优化: 使用 Map 缓存每个 item 的样式,只在依赖变化时重新计算
86
90
  const itemStylesCache = computed(() => {
91
+ if (!props.list?.length) return new Map<number, Record<string, any>>()
92
+
87
93
  const isGrid = props.display === 'grid'
88
94
  const isFlex = props.display === 'flex'
89
95
  const flexShrink = props.flexShrink ?? 1
96
+ const itemAlignStyle = getItemAlignStyle()
97
+ const overflowX = props.overflowX
98
+
99
+ const cache = new Map<number, Record<string, any>>()
90
100
 
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
- )
101
+ for (let i = 0; i < props.list.length; i++) {
102
+ const flex = props.areaFlexs?.[i]?.flex ?? 1
103
+ const colSpan = props.areaItems?.[i]?.colSpan
104
+
105
+ const style: Record<string, any> = {
106
+ ...itemAlignStyle,
107
+ overflowX,
108
+ }
109
+
110
+ if (isGrid && colSpan) {
111
+ style.gridColumn = `span ${colSpan} / span ${colSpan}`
112
+ }
113
+
114
+ if (isFlex) {
115
+ style.flex = flex ?? 'auto'
116
+ style.flexShrink = flexShrink
117
+ style.width = flex ? '100%' : 'auto'
118
+ }
119
+
120
+ cache.set(i, style)
121
+ }
122
+
123
+ return cache
105
124
  })
106
125
 
107
- // 优化: 使用缓存的样式
126
+ // 优化: 直接从 Map 获取缓存的样式
108
127
  const getItemStyle = (index: number) => {
109
- return itemStylesCache.value[index] || commonItemStyle.value
128
+ return itemStylesCache.value.get(index) || { overflowX: props.overflowX }
110
129
  }
111
130
 
112
131
  const getData = async () => {
@@ -140,26 +159,20 @@ watch(
140
159
  },
141
160
  )
142
161
 
143
- // 优化: 提取比较逻辑,减少重复代码
162
+ // 优化: 提取比较逻辑,减少重复代码 - 使用静态对象减少创建
163
+ const compareOperations = {
164
+ '=': (value: any, compareValue: string) => `${value}` === compareValue,
165
+ '>=': (value: any, compareValue: string) => value >= compareValue,
166
+ '<=': (value: any, compareValue: string) => value <= compareValue,
167
+ '>': (value: any, compareValue: string) => value > compareValue,
168
+ '<': (value: any, compareValue: string) => value < compareValue,
169
+ '!=': (value: any, compareValue: string) => value !== compareValue,
170
+ includes: (value: any, compareValue: string) => value?.includes?.(compareValue) ?? false,
171
+ } as const
172
+
144
173
  const compareValues = (value = '', compareValue: string, compareType: string) => {
145
- switch (compareType) {
146
- case '=':
147
- return `${value}` === compareValue
148
- case '>=':
149
- return value >= compareValue
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 'includes':
159
- return value?.includes?.(compareValue) ?? false
160
- default:
161
- return `${value}` === compareValue
162
- }
174
+ const operation = compareOperations[compareType as keyof typeof compareOperations]
175
+ return operation ? operation(value, compareValue) : `${value}` === compareValue
163
176
  }
164
177
 
165
178
  // 优化: 缓存数据源
@@ -23,7 +23,6 @@ export interface LcbAreaProps extends LcbBlockProps {
23
23
  keyFromUser?: boolean
24
24
  reverse?: boolean
25
25
  dependKeyCompareValue?: string
26
- position?: 'relative' | 'absolute'
27
26
  compareType?: '=' | '>=' | '<=' | '>' | '<' | '!=' | 'includes'
28
27
  dataSource?: DataSource
29
28
  action?: LcbActionViewProps
@@ -1,19 +1,16 @@
1
1
  <template>
2
- <view
3
- :style="getFinalStyle"
4
- class="box-border overflow-hidden relative"
5
- :class="props.customClass"
6
- >
2
+ <view :style="getFinalStyle" class="box-border overflow-hidden" :class="props.customClass">
7
3
  <slot />
8
4
  </view>
9
5
  </template>
10
6
 
11
7
  <script setup lang="ts">
12
8
  import { get } from 'lodash-es'
13
- import { computed } from 'vue'
9
+ import { computed, inject, ref, shallowRef } from 'vue'
14
10
  import useDynamicData from '../../hooks/useDynamicData'
15
11
  import { getFlexStyle, transformValueUnit } from '../../utils/transform'
16
12
  import { LcbBlockInnerProps } from './types'
13
+ import { IS_EDITOR_MODE } from '../../constants'
17
14
  defineOptions({
18
15
  name: 'LcbBlock',
19
16
  options: {
@@ -32,19 +29,42 @@ const props = withDefaults(defineProps<LcbBlockInnerProps>(), {
32
29
  position: 'relative',
33
30
  })
34
31
  const { userStore, innerDynamicData } = useDynamicData()
32
+ const isEditorMode = inject(IS_EDITOR_MODE, ref(false))
33
+
34
+ // 使用 shallowRef 缓存 flex 样式,避免重复计算
35
+ const flexStyleCache = shallowRef<Record<string, any> | null>(null)
36
+ const cachedAlign = shallowRef<string>('')
37
+
38
+ const getFlexStyleCached = (align: string | undefined) => {
39
+ if (!align) return {}
40
+ if (align === cachedAlign.value && flexStyleCache.value) {
41
+ return flexStyleCache.value
42
+ }
43
+ cachedAlign.value = align
44
+ flexStyleCache.value = getFlexStyle(align)
45
+ return flexStyleCache.value
46
+ }
47
+
35
48
  const dynamicBgImage = computed(() => {
36
- return (
37
- (props.dynamicBgImage && get(innerDynamicData.value, props.dynamicBgImage)) ||
38
- props.backgroundImage
39
- )
49
+ // 优化:如果没有动态背景图,直接返回静态背景图
50
+ if (!props.dynamicBgImage) return props.backgroundImage
51
+ return get(innerDynamicData.value, props.dynamicBgImage) || props.backgroundImage
40
52
  })
41
- // 透明度+颜色
53
+
54
+ // 透明度+颜色 - 优化:减少字符串拼接
42
55
  const innerBackgroundColor = computed(() => {
43
- if (!props.backgroundColor) return ''
44
- if (props.backgroundColor.length === 7) {
45
- return props.backgroundColor + Math.floor(props.opacity * 255).toString(16)
56
+ const bgColor = props.backgroundColor
57
+ if (!bgColor) return ''
58
+ // 如果已经是 8 位(包含透明度),直接返回
59
+ if (bgColor.length === 9) return bgColor
60
+ // 如果是 7 位,添加透明度
61
+ if (bgColor.length === 7) {
62
+ const alpha = Math.floor(props.opacity * 255)
63
+ .toString(16)
64
+ .padStart(2, '0')
65
+ return bgColor + alpha
46
66
  }
47
- return props.backgroundColor
67
+ return bgColor
48
68
  })
49
69
 
50
70
  const getInnerStyle = computed(() => {
@@ -58,46 +78,44 @@ const getInnerStyle = computed(() => {
58
78
  return {}
59
79
  })
60
80
 
61
- // 缓存 padding 计算结果,减少 transformValueUnit 调用次数
62
- const paddingStyle = computed(() => {
63
- const top = transformValueUnit(props.paddingTop || props.paddingVertical || 0)
64
- const right = transformValueUnit(props.paddingRight || props.paddingHorizontal || 0)
65
- const bottom = transformValueUnit(props.paddingBottom || props.paddingVertical || 0)
66
- const left = transformValueUnit(props.paddingLeft || props.paddingHorizontal || 0)
67
- return `${top} ${right} ${bottom} ${left}`
68
- })
81
+ // 优化:合并 padding/margin/borderRadius 计算,减少函数调用
82
+ const spacingStyles = computed(() => {
83
+ const pTop = props.paddingTop ?? props.paddingVertical ?? 0
84
+ const pRight = props.paddingRight ?? props.paddingHorizontal ?? 0
85
+ const pBottom = props.paddingBottom ?? props.paddingVertical ?? 0
86
+ const pLeft = props.paddingLeft ?? props.paddingHorizontal ?? 0
69
87
 
70
- // 缓存 margin 计算结果
71
- const marginStyle = computed(() => {
72
- const top = transformValueUnit(props.floatUp ? -props.floatUp : props.marginTop || 0)
73
- const right = transformValueUnit(props.marginRight || props.marginHorizontal || 0)
74
- const bottom = transformValueUnit(props.marginBottom || 0)
75
- const left = transformValueUnit(props.marginLeft || props.marginHorizontal || 0)
76
- return `${top} ${right} ${bottom} ${left}`
77
- })
88
+ const mTop = props.floatUp ? -props.floatUp : (props.marginTop ?? 0)
89
+ const mRight = props.marginRight ?? props.marginHorizontal ?? 0
90
+ const mBottom = props.marginBottom ?? 0
91
+ const mLeft = props.marginLeft ?? props.marginHorizontal ?? 0
92
+
93
+ const topR = props.topRadius ?? props.radius
94
+ const bottomR = props.bottomRadius ?? props.radius
78
95
 
79
- // 缓存 borderRadius 计算结果,避免重复计算
80
- const borderRadiusStyle = computed(() => {
81
- const topR = props.topRadius || props.radius
82
- const bottomR = props.bottomRadius || props.radius
83
- const topValue = transformValueUnit(topR)
84
- const bottomValue = transformValueUnit(bottomR)
85
- return `${topValue} ${topValue} ${bottomValue} ${bottomValue}`
96
+ return {
97
+ padding: `${transformValueUnit(pTop)} ${transformValueUnit(pRight)} ${transformValueUnit(pBottom)} ${transformValueUnit(pLeft)}`,
98
+ margin: `${transformValueUnit(mTop)} ${transformValueUnit(mRight)} ${transformValueUnit(mBottom)} ${transformValueUnit(mLeft)}`,
99
+ borderRadius:
100
+ topR || bottomR
101
+ ? `${transformValueUnit(topR)} ${transformValueUnit(topR)} ${transformValueUnit(bottomR)} ${transformValueUnit(bottomR)}`
102
+ : undefined,
103
+ }
86
104
  })
87
105
 
88
- // 优化 background 字符串拼接
106
+ // 优化:background 字符串拼接
89
107
  const backgroundStyle = computed(() => {
90
108
  const bgImage = dynamicBgImage.value
91
109
  const bgColor = innerBackgroundColor.value || 'transparent'
92
110
  if (!bgImage) return bgColor
111
+ // 使用模板字符串减少拼接次数
93
112
  return `${props.backgroundPosition} / ${props.backgroundSize} ${props.backgroundRepeat} url('${bgImage}'), ${bgColor}`
94
113
  })
95
114
 
96
115
  const getFinalStyle = computed(() => {
97
- return {
98
- width: props.position === 'absolute' ? '100%' : '',
99
- padding: paddingStyle.value,
100
- borderRadius: borderRadiusStyle.value,
116
+ // 优化:减少对象创建和条件判断
117
+ const style: Record<string, any> = {
118
+ ...spacingStyles.value,
101
119
  color: props.color,
102
120
  background: backgroundStyle.value,
103
121
  fontSize: transformValueUnit(props.fontSize),
@@ -106,14 +124,49 @@ const getFinalStyle = computed(() => {
106
124
  zIndex: props.zIndex,
107
125
  position: props.position,
108
126
  borderWidth: transformValueUnit(props.borderWidth),
109
- boxShadow:
110
- props.shadowColor && props.shadowSize
111
- ? `0px 0px ${props.blurSize}px ${props.shadowSize}px ${props.shadowColor}`
112
- : '',
113
127
  textAlign: props.textAlign,
114
- margin: marginStyle.value,
128
+ }
129
+
130
+ // 优化:只在需要时添加 boxShadow
131
+ if (props.shadowColor && props.shadowSize) {
132
+ style.boxShadow = `0px 0px ${props.blurSize}px ${props.shadowSize}px ${props.shadowColor}`
133
+ }
134
+
135
+ const actualPosition = props.position
136
+
137
+ // 优化:只在编辑器模式下输出调试信息
138
+ if (isEditorMode.value && (actualPosition === 'fixed' || actualPosition === 'absolute')) {
139
+ console.log('[lcb-block] Position Debug:', {
140
+ propsPosition: props.position,
141
+ actualPosition,
142
+ top: props.top,
143
+ left: props.left,
144
+ right: props.right,
145
+ bottom: props.bottom,
146
+ zIndex: props.zIndex,
147
+ width: props.width,
148
+ height: props.height,
149
+ })
150
+ }
151
+
152
+ // 处理定位相关的样式 - 优化:合并条件判断
153
+ if (actualPosition === 'absolute' || actualPosition === 'fixed') {
154
+ if (props.top !== undefined) style.top = transformValueUnit(props.top)
155
+ if (props.left !== undefined) style.left = transformValueUnit(props.left)
156
+ if (props.right !== undefined) style.right = transformValueUnit(props.right)
157
+ if (props.bottom !== undefined) style.bottom = transformValueUnit(props.bottom)
158
+ if (props.width !== undefined) style.width = transformValueUnit(props.width)
159
+ if (props.height !== undefined) style.height = transformValueUnit(props.height)
160
+ } else if (actualPosition === 'relative') {
161
+ if (props.top !== undefined) style.top = transformValueUnit(props.top)
162
+ if (props.left !== undefined) style.left = transformValueUnit(props.left)
163
+ }
164
+
165
+ // 优化:最后再合并其他样式,减少对象展开次数
166
+ return {
167
+ ...style,
115
168
  ...props.customStyle,
116
- ...getFlexStyle(props.align),
169
+ ...getFlexStyleCached(props.align),
117
170
  ...getInnerStyle.value,
118
171
  }
119
172
  })
@@ -30,7 +30,7 @@ export interface LcbBlockProps {
30
30
  marginLeft?: number
31
31
  marginRight?: number
32
32
  zIndex?: number
33
- position?: 'relative' | 'absolute'
33
+ position?: 'relative' | 'absolute' | 'fixed'
34
34
  color?: string
35
35
  fontSize?: number
36
36
  fontWeight?: number
@@ -59,6 +59,13 @@ export interface LcbBlockProps {
59
59
  // 动态样式集合
60
60
  dynamicStyleOptions?: DynamicOptions
61
61
  dynamicBgImage?: string
62
+ // fixed/absolute 定位相关
63
+ top?: number
64
+ left?: number
65
+ right?: number
66
+ bottom?: number
67
+ width?: number
68
+ height?: number
62
69
  }
63
70
  export interface LcbBlockInnerProps extends LcbBlockProps {
64
71
  [key: string]: any
@@ -1,7 +1,6 @@
1
1
  import { LcbBlockProps } from '../lcb-block/types'
2
2
 
3
3
  export interface LcbGapProps extends LcbBlockProps {
4
- height?: number | string
5
4
  safeAreaBottom?: boolean
6
5
  bgColor?: string
7
6
  safeAreaTop?: boolean
@@ -82,7 +82,10 @@
82
82
  </view>
83
83
  <!-- loading 加载地球gif -->
84
84
  <view class="absolute top-0 left-0 w-full h-full flex-center z-2" v-if="isLoading">
85
- <image src="./images/earth.gif" class="w-137rpx h-120rpx mt--180rpx" />
85
+ <image
86
+ src="https://oss.30jia.com.cn/material/0/1001/1462205640919076864/20260117220318/earth.webp"
87
+ class="w-137rpx h-120rpx mt--180rpx"
88
+ />
86
89
  </view>
87
90
  </view>
88
91
  </view>
package/constants.ts CHANGED
@@ -15,3 +15,5 @@ export const PAGE_DYNAMIC_DATA = 'page_dynamic_data'
15
15
  export const WRAPPER_ITEM_KEY = 'wrapper_item_key'
16
16
  /** 是否显示tabbar */
17
17
  export const SHOW_TABBAR = 'show_tabbar'
18
+ /** 是否在编辑器模式 */
19
+ export const IS_EDITOR_MODE = 'is_editor_mode'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tplc/business",
3
- "version": "0.7.73",
3
+ "version": "0.7.75",
4
4
  "keywords": [
5
5
  "业务组件"
6
6
  ],
@@ -23,7 +23,6 @@ export interface LcbAreaProps extends LcbBlockProps {
23
23
  keyFromUser?: boolean
24
24
  reverse?: boolean
25
25
  dependKeyCompareValue?: string
26
- position?: 'relative' | 'absolute'
27
26
  compareType?: '=' | '>=' | '<=' | '>' | '<' | '!=' | 'includes'
28
27
  dataSource?: DataSource
29
28
  action?: LcbActionViewProps
@@ -29,7 +29,7 @@ export interface LcbBlockProps {
29
29
  marginLeft?: number
30
30
  marginRight?: number
31
31
  zIndex?: number
32
- position?: 'relative' | 'absolute'
32
+ position?: 'relative' | 'absolute' | 'fixed'
33
33
  color?: string
34
34
  fontSize?: number
35
35
  fontWeight?: number
@@ -56,6 +56,12 @@ export interface LcbBlockProps {
56
56
  | 'bottom-right'
57
57
  dynamicStyleOptions?: DynamicOptions
58
58
  dynamicBgImage?: string
59
+ top?: number
60
+ left?: number
61
+ right?: number
62
+ bottom?: number
63
+ width?: number
64
+ height?: number
59
65
  }
60
66
  export interface LcbBlockInnerProps extends LcbBlockProps {
61
67
  [key: string]: any
@@ -1,6 +1,5 @@
1
1
  import { LcbBlockProps } from '../lcb-block/types'
2
2
  export interface LcbGapProps extends LcbBlockProps {
3
- height?: number | string
4
3
  safeAreaBottom?: boolean
5
4
  bgColor?: string
6
5
  safeAreaTop?: boolean
@@ -20,13 +20,19 @@ declare const __VLS_component: import('vue').DefineComponent<
20
20
  >,
21
21
  {
22
22
  mode: 'map' | 'list'
23
+ width: number
24
+ height: number
23
25
  backgroundColor: string
24
26
  color: string
25
27
  customStyle: Record<string, any>
26
28
  customClass: string
27
29
  zIndex: number
30
+ left: number
31
+ top: number
32
+ bottom: number
33
+ right: number
28
34
  radius: number
29
- position: 'relative' | 'absolute'
35
+ position: 'relative' | 'absolute' | 'fixed'
30
36
  backgroundImage: string
31
37
  border: boolean
32
38
  imageRadius: number
@@ -15,3 +15,5 @@ export declare const PAGE_DYNAMIC_DATA = 'page_dynamic_data'
15
15
  export declare const WRAPPER_ITEM_KEY = 'wrapper_item_key'
16
16
  /** 是否显示tabbar */
17
17
  export declare const SHOW_TABBAR = 'show_tabbar'
18
+ /** 是否在编辑器模式 */
19
+ export declare const IS_EDITOR_MODE = 'is_editor_mode'
Binary file