@tplc/business 0.0.37 → 0.0.39

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.
Files changed (28) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/components/lcb-area-picker/api/index.ts +16 -0
  3. package/components/lcb-area-picker/lcb-area-picker.vue +12 -6
  4. package/components/lcb-area-picker/types.ts +1 -1
  5. package/components/lcb-list/api.ts +1 -1
  6. package/components/lcb-list/components/FilterList/index.vue +30 -2
  7. package/components/lcb-list/components/FilterList/type.ts +2 -0
  8. package/components/lcb-list/lcb-list.vue +7 -15
  9. package/components/lcb-list/types.ts +16 -0
  10. package/components/lcb-nav/lcb-nav.vue +7 -1
  11. package/components/lcb-product/lcb-product.vue +30 -25
  12. package/components/lcb-product/types.ts +7 -11
  13. package/components/lcb-product-item/lcb-product-item.vue +146 -83
  14. package/components/lcb-product-item/readme.md +22 -0
  15. package/components/lcb-product-item/types.ts +55 -11
  16. package/package.json +2 -2
  17. package/types/components/lcb-area-picker/api/index.d.ts +10 -0
  18. package/types/components/lcb-area-picker/lcb-area-picker.vue.d.ts +15 -3
  19. package/types/components/lcb-area-picker/types.d.ts +3 -1
  20. package/types/components/lcb-list/components/FilterList/type.d.ts +2 -0
  21. package/types/components/lcb-list/lcb-list.vue.d.ts +5 -30
  22. package/types/components/lcb-list/types.d.ts +4 -0
  23. package/types/components/lcb-product/lcb-product.vue.d.ts +8 -3
  24. package/types/components/lcb-product/types.d.ts +3 -11
  25. package/types/components/lcb-product-item/lcb-product-item.vue.d.ts +37 -19
  26. package/types/components/lcb-product-item/types.d.ts +36 -11
  27. package/types/utils/createReuse.d.ts +59 -0
  28. package/utils/createReuse.tsx +121 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
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.0.39](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/compare/v0.0.38...v0.0.39) (2024-10-23)
6
+
7
+
8
+ ### ✨ Features | 新功能
9
+
10
+ * 优化 lcb-list 相关逻辑 ([c81aeb8](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/c81aeb8e0f9b3b5931f6c227133e2b7a314fa089))
11
+ * 优化 product-item 实现 ([40ad6c6](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/40ad6c6ee4beb15a66210771b2e32972484a0341))
12
+ * 修改返回按钮尺寸 ([7c946aa](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/7c946aa3cd4c9d02b663044618eededa8542ba8b))
13
+
14
+ ### [0.0.38](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/compare/v0.1.17...v0.0.38) (2024-10-22)
15
+
16
+
17
+ ### 🚀 Chore | 构建/工程依赖/工具
18
+
19
+ * husky changed ([96139b0](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/96139b0e4f3fb469a62fd433f9446cfdf6a65016))
20
+
21
+
22
+ ### ✨ Features | 新功能
23
+
24
+ * city 支持mode ([9686a0e](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/9686a0e1f19c4b05d936e1b674933b4744c1b3ba))
25
+ * gap支持safe-top ([d21834c](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/d21834cb8b67da98667fc6216624dfa1962790e5))
26
+ * 优化 lcb-list 过滤器吸顶逻辑;增加 unocss 插件触发文件 ([ff8d6ef](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/ff8d6ef0c7703823f1a7833482d3b17bb272ae82))
27
+ * 调整 lcb-product-item 样式与逻辑 ([22810b7](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/commit/22810b709783b44920a778d9c038e028dcce06ad))
28
+
5
29
  ### [0.0.37](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/compare/v0.0.36...v0.0.37) (2024-10-21)
6
30
 
7
31
  ### [0.0.36](http://gitlab888.30jia.com.cn/bhBank/zero-code-pro/compare/v0.0.35...v0.0.36) (2024-10-21)
@@ -43,3 +43,19 @@ export const getProvinceCityArea = async () => {
43
43
  }
44
44
  })
45
45
  }
46
+
47
+ export const getProvinceCity = async () => {
48
+ const { data } = await uni.$lcb.http.post<ProvinceCityArea[]>('/provinces/provinceCityArea')
49
+ return data.map(({ provinceId, provinceName, cityList }) => {
50
+ return {
51
+ label: provinceName,
52
+ value: provinceId,
53
+ children: cityList.map(({ cityId, cityName }) => {
54
+ return {
55
+ label: cityName,
56
+ value: cityId,
57
+ }
58
+ }),
59
+ }
60
+ })
61
+ }
@@ -13,7 +13,7 @@
13
13
  <script setup lang="ts">
14
14
  import { onMounted, ref, watch } from 'vue'
15
15
  import { LcbAreaPickerProps } from './types'
16
- import { AreaOptions, getProvinceCityArea } from './api'
16
+ import { AreaOptions, getProvinceCity, getProvinceCityArea } from './api'
17
17
  import { ColPickerColumnChangeOption } from '@tplc/wot/types/components/wd-col-picker/types'
18
18
  defineOptions({
19
19
  name: 'LcbAreaPicker',
@@ -23,7 +23,7 @@ defineOptions({
23
23
  styleIsolation: 'shared',
24
24
  },
25
25
  })
26
- withDefaults(defineProps<LcbAreaPickerProps>(), {})
26
+ const props = withDefaults(defineProps<LcbAreaPickerProps>(), { mode: 'area' })
27
27
  const emits = defineEmits<{
28
28
  (event: 'confirm', payload: AreaOptions[]): void
29
29
  }>()
@@ -40,7 +40,7 @@ const columnChange = ({ selectedItem, resolve, finish }: ColPickerColumnChangeOp
40
40
  }
41
41
  }
42
42
  onMounted(async () => {
43
- const data = await getProvinceCityArea()
43
+ const data = props.mode === 'area' ? await getProvinceCityArea() : await getProvinceCity()
44
44
  area.value = [data]
45
45
  })
46
46
  const stopWatch = watch(
@@ -57,9 +57,15 @@ const stopWatch = watch(
57
57
  return acc
58
58
  }, [] as string[])
59
59
  const cityItem = areas[0].find((item) => item.value === value[0])
60
- const areaItem = cityItem?.children?.find((item) => item.value === value[1])
61
- if (cityItem?.children && areaItem?.children)
62
- area.value = [areas[0], cityItem.children, areaItem.children]
60
+ if (cityItem?.children) {
61
+ if (props.mode === 'area') {
62
+ const areaItem = cityItem?.children?.find((item) => item.value === value[1])
63
+ if (areaItem?.children) area.value = [areas[0], cityItem.children, areaItem.children]
64
+ } else {
65
+ area.value = [areas[0], cityItem.children]
66
+ }
67
+ }
68
+
63
69
  stopWatch()
64
70
  }
65
71
  },
@@ -1,3 +1,3 @@
1
1
  export interface LcbAreaPickerProps {
2
- // Define the component's prop types here
2
+ mode: 'city' | 'area'
3
3
  }
@@ -62,6 +62,6 @@ interface BtnComponent {
62
62
  }
63
63
 
64
64
  export const getFilterDetail = (val: string) =>
65
- uni.$lcb.http.post<LcbFilterResult>('/pageDecoration/list//detail', {
65
+ uni.$lcb.http.post<LcbFilterResult>('/pageDecoration/list/detail', {
66
66
  pageListType: val,
67
67
  })
@@ -7,14 +7,15 @@
7
7
  @query="queryList"
8
8
  use-page-scroll
9
9
  >
10
- <lcb-product v-bind="{ ...productProps }" :items="dataList" />
10
+ <lcb-product v-bind="{ ...productProps }" :listType="listType" :items="normalizeDataList" />
11
11
  </z-paging>
12
12
  </template>
13
13
 
14
14
  <script setup lang="ts">
15
- import { ref, watch } from 'vue'
15
+ import { computed, ref, watch } from 'vue'
16
16
  import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging'
17
17
  import { LcbFilterListProps } from './type'
18
+ import { formatJson } from '../../../../utils/utils'
18
19
 
19
20
  defineOptions({
20
21
  name: 'FilterList',
@@ -28,6 +29,26 @@ const props = defineProps<LcbFilterListProps>()
28
29
  const dataList = ref<unknown[]>([])
29
30
  const paging = ref()
30
31
  useZPaging(paging)
32
+
33
+ const normalizeDataList = computed(() => {
34
+ const list = dataList.value?.map?.((item: any) => {
35
+ const imgArr = formatJson(item.coverImg, [])
36
+ const tags = formatJson(item.tags, [])
37
+ return {
38
+ ...item,
39
+ image: imgArr?.[1] ?? imgArr?.[0],
40
+ title: item?.productName,
41
+ tags,
42
+ price: item?.price,
43
+ location: item?.cityAreaName,
44
+ priceSuffix: item?.behindUnit,
45
+ }
46
+ })
47
+ // console.log('list', list)
48
+
49
+ return list
50
+ })
51
+
31
52
  watch(
32
53
  () => props.url,
33
54
  async (val) => {
@@ -67,6 +88,13 @@ const queryList = async (page: number, limit: number) => {
67
88
  paging.value.complete(false)
68
89
  }
69
90
  }
91
+
92
+ // watch(
93
+ // () => dataList.value,
94
+ // () => {
95
+ // console.log('dataList', dataList.value)
96
+ // },
97
+ // )
70
98
  </script>
71
99
  <style lang="scss" scoped>
72
100
  .action-view {
@@ -2,8 +2,10 @@ import { LcbListInfo } from '../../api'
2
2
  import { LcbProductProps } from '../../../lcb-product/types'
3
3
  export interface PageListProps {
4
4
  emptyImage?: string
5
+ listType?: LcbProductProps['listType']
5
6
  productProps?: LcbProductProps
6
7
  }
7
8
  export interface LcbFilterListProps extends PageListProps, LcbListInfo {
8
9
  filter?: Record<string, any>
10
+ test?: any
9
11
  }
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <view v-if="info">
3
- <wd-sticky @sticky="sticky = $event">
2
+ <view v-if="info" class="bg-gray-100">
3
+ <wd-sticky @sticky="sticky = $event" :offset-top="0.0001">
4
4
  <view
5
5
  :class="{
6
6
  'lcb-filter__border-top': showPlain ? false : border,
@@ -67,13 +67,13 @@
67
67
  </view>
68
68
  </wd-sticky>
69
69
 
70
- <FilterList v-bind="{ ...info.listInfo, ...pageListProps, filter }" />
70
+ <FilterList v-bind="{ ...info.listInfo, listType, filter }" test2="11" />
71
71
  </view>
72
72
  </template>
73
73
 
74
74
  <script setup lang="ts">
75
75
  import { computed, inject, Ref, ref, watch } from 'vue'
76
- import { LcbListProps } from './types'
76
+ import { LcbListProps, defaultLcbListProps } from './types'
77
77
  import { FilterComponent, getFilterDetail, LcbFilterResult } from './api'
78
78
  import FilterSelect from './components/FilterSelect/index.vue'
79
79
  import TreeSelect from './components/TreeSelect/index.vue'
@@ -83,6 +83,7 @@ import FilterList from './components/FilterList/index.vue'
83
83
  import FilterTabs from './components/FilterTabs/index.vue'
84
84
  import './index.scss'
85
85
  import { FORM_KEY } from '../../constants'
86
+
86
87
  defineOptions({
87
88
  name: 'LcbList',
88
89
  options: {
@@ -93,23 +94,14 @@ defineOptions({
93
94
  })
94
95
  const form = inject<Ref<Record<string, any>>>(FORM_KEY)
95
96
  const dropMenu = ref()
96
- const props = withDefaults(defineProps<LcbListProps>(), {
97
- pageFilterType: 'hotelTravelFilter',
98
- border: true,
99
- styleMode: 'default',
100
- pageListProps: () => ({
101
- productProps: {
102
- styleGroup: 1,
103
- },
104
- }),
105
- })
97
+ const props = withDefaults(defineProps<LcbListProps>(), defaultLcbListProps as any)
106
98
  const info = ref<LcbFilterResult>()
107
99
  const filter = ref<Record<string, any>>({})
108
100
  const titleObj = ref<Record<string, any>>({})
109
101
  /** 是否悬停 */
110
102
  const sticky = ref(false)
111
103
  const showPlain = computed(() => {
112
- console.log(props.styleMode, sticky.value, 'showPlain')
104
+ // console.log(props.styleMode, sticky.value, 'showPlain')
113
105
  return props.styleMode === 'plain' && !sticky.value
114
106
  })
115
107
  watch(
@@ -1,10 +1,12 @@
1
1
  import { PageListProps } from './components/FilterList/type'
2
+ import { LcbProductProps } from '../lcb-product/types'
2
3
 
3
4
  export interface LcbListProps {
4
5
  pageFilterType?: string
5
6
  pageListProps?: PageListProps
6
7
  border?: boolean
7
8
  styleMode?: 'default' | 'plain'
9
+ listType?: LcbProductProps['listType']
8
10
  }
9
11
  export interface Option {
10
12
  label: string
@@ -20,4 +22,18 @@ export interface FilterItemProps {
20
22
  mode?: 'multiple' | 'single'
21
23
  apiPath?: string
22
24
  options?: Option[]
25
+ test?: 1
26
+ }
27
+
28
+ export const defaultLcbListProps: LcbListProps = {
29
+ pageFilterType: 'hotelTravelFilter',
30
+ border: true,
31
+ styleMode: 'default',
32
+ listType: 'grid',
33
+ // @ts-ignore
34
+ pageListProps: () => ({
35
+ productProps: {
36
+ // listType: 'list',
37
+ },
38
+ }),
23
39
  }
@@ -47,7 +47,13 @@
47
47
  </view>
48
48
  </view>
49
49
  <template v-else-if="back">
50
- <wd-icon class-prefix="lcb" name="zuo_left" @click="toBack" :color="contentColor" />
50
+ <wd-icon
51
+ class-prefix="lcb"
52
+ name="zuo_left"
53
+ @click="toBack"
54
+ :color="contentColor"
55
+ size="44rpx"
56
+ />
51
57
  </template>
52
58
  <view class="ml-2" v-if="titleLocation === 'left'">
53
59
  <Search
@@ -1,15 +1,34 @@
1
1
  <template>
2
2
  <lcb-block v-bind="$props">
3
- <template v-if="styleGroup !== 2">
4
- <view v-for="(item, index) in items" :key="index">
5
- <!-- <view>{{ JSON.stringify(item) }}</view> -->
6
- <lcb-product-item :styleProps="styleProps" :idx="index" v-bind="item" />
3
+ <view class="flex flex-col gap-2 p-2" v-if="listType === 'list'">
4
+ <view v-for="(item, index) in items" :key="`${item?.productId}:${index}`">
5
+ <lcb-product-item v-bind="item" />
7
6
  </view>
8
- </template>
9
- <scroll-view v-if="styleGroup == 2" scroll-x class="w-full whitespace-nowrap">
10
- <view class="flex shrink-0">
11
- <view v-for="(item, index) in items" :key="index">
12
- <lcb-product-item :styleProps="styleProps" :idx="index" v-bind="item" />
7
+ </view>
8
+
9
+ <view class="flex p-1 flex-wrap" v-if="listType === 'grid'">
10
+ <view v-for="(item, index) in items" :key="`${item?.productId}:${index}`" class="w-1/2">
11
+ <view class="p-1 overflow-hidden">
12
+ <view class="rounded overflow-hidden h-560rpx">
13
+ <lcb-product-item
14
+ v-bind="item"
15
+ layoutType="vertical"
16
+ class="!h-560rpx"
17
+ imageClass="!h-300rpx"
18
+ />
19
+ </view>
20
+ </view>
21
+ </view>
22
+ </view>
23
+
24
+ <scroll-view v-if="listType == 'horizontal'" scroll-x>
25
+ <view class="p-2 flex gap-2 whitespace-nowrap overflow-auto flex-nowrap">
26
+ <view
27
+ v-for="(item, index) in items"
28
+ :key="`${item?.productId}:${index}`"
29
+ class="!w-66vw flex-shrink-0"
30
+ >
31
+ <lcb-product-item v-bind="item" imageClass="!w-1/2" />
13
32
  </view>
14
33
  </view>
15
34
  </scroll-view>
@@ -18,7 +37,7 @@
18
37
 
19
38
  <script setup lang="ts">
20
39
  import { computed } from 'vue'
21
- import { LcbProductProps } from './types'
40
+ import { LcbProductProps, defaultLcbProductProps } from './types'
22
41
  defineOptions({
23
42
  name: 'LcbProduct',
24
43
  options: {
@@ -27,21 +46,7 @@ defineOptions({
27
46
  styleIsolation: 'shared',
28
47
  },
29
48
  })
30
- const props = withDefaults(defineProps<LcbProductProps>(), {})
31
- const styleProps = computed(() => {
32
- // console.log('props.items', JSON.stringify(props.items))
33
- return {
34
- styleGroup: props.styleGroup,
35
- productStyle: props.productStyle,
36
- productTitle: props.productTitle,
37
- showTags: props.showTags,
38
- showPrice: props.showPrice,
39
- showScribePrice: props.showScribePrice,
40
- showCart: props.showCart,
41
- showLocation: props.showLocation,
42
- showCollection: props.showCollection,
43
- }
44
- })
49
+ const props = withDefaults(defineProps<LcbProductProps>(), defaultLcbProductProps as any)
45
50
  </script>
46
51
 
47
52
  <style lang="scss" scoped></style>
@@ -1,14 +1,10 @@
1
1
  export interface LcbProductProps {
2
2
  // Define the component's prop types here
3
- styleGroup?: 1 | 2 | 3 | 4 // 1列表 2 左右滑动 3一行两个 4瀑布流
4
- items?: Record<string, any>
5
- marginHorizontal?: number
6
- productStyle?: 'image' | 'flat' | 'card' | 'border' // 图片, 扁平, 卡片, 描边
7
- productTitle?: 0 | 1 | 2 // 0默认 1 1行 2 2行
8
- showTags?: boolean
9
- showPrice?: boolean
10
- showScribePrice?: boolean
11
- showCart?: boolean
12
- showLocation?: boolean
13
- showCollection?: boolean
3
+ listType?: 'list' | 'horizontal' | 'grid' | 'waterfall' // 1列表 2 左右滑动 3一行两个 4瀑布流
4
+ items?: Record<string, any>[]
5
+ }
6
+
7
+ export const defaultLcbProductProps: LcbProductProps = {
8
+ listType: 'list',
9
+ items: [],
14
10
  }
@@ -1,50 +1,16 @@
1
- <template>
2
- <view
3
- :style="{
4
- paddingTop: `${transformValueUnit(15)}`,
5
- paddingBottom: `${transformValueUnit(15)}`,
6
- paddingLeft: styleProps?.styleGroup != 1 && idx > 0 ? transformValueUnit(15) : 0,
7
- width: styleProps?.styleGroup != 1 ? transformValueUnit(picWidth) : 'auto',
8
- // paddingHorizontal: styleProps?.styleGroup == 1 ? null : 15,
9
- }"
10
- >
11
- <view :class="`${styleProps?.styleGroup == 1 ? 'flex' : ''}`">
12
- <wd-img
13
- :src="realImg"
14
- :width="transformValueUnit(picWidth)"
15
- :height="transformValueUnit(picHeight)"
16
- />
17
- <view
18
- v-if="styleProps?.productStyle != 'image'"
19
- :class="`${styleProps?.styleGroup == 1 ? 'flex-1 pl-20rpx' : 'pt-20rpx'} pb-20rpx${styleProps?.productTitle == 1 ? ' ellipsis ' : ''}`"
20
- >
21
- <view
22
- :class="`${styleProps?.productTitle == 1 ? 'ellipsis' : styleProps?.productTitle == 2 ? 'mulEllipsis' : 'wordBreak'} text-32rpx color-#333`"
23
- >
24
- {{ productName }}
25
- </view>
26
- <lcb-tags v-if="!!styleProps?.showTags" :mode="2" :tagRadius="3" :items="tags" />
27
- <view>{{ subTitle }}</view>
28
- <view class="flex items-center mt-15rpx">
29
- <view v-if="!!styleProps?.showPrice" class="text-30rpx color-red">¥</view>
30
- <view v-if="!!styleProps?.showPrice" class="text-32rpx color-red">{{ price }}</view>
31
- <view
32
- v-if="!!styleProps?.showScribePrice"
33
- class="line-through text-22rpx color-#999 ml-10rpx pt-5rpx"
34
- >
35
- ¥{{ scribePrice }}
36
- </view>
37
- </view>
38
- </view>
39
- </view>
40
- </view>
41
- </template>
42
-
43
1
  <script setup lang="ts">
44
- import { computed } from 'vue'
2
+ import { computed, useAttrs } from 'vue'
45
3
  import { transformValueUnit } from '../../utils/transform'
46
- import { LcbProductItemProps } from './types'
4
+ import { LcbProductItemProps, defaultLcbProductItemProps } from './types'
47
5
  import { formatJson } from '../../utils/utils'
6
+ import { createReusableTemplate } from '../../utils/createReuse'
7
+ import { isArray } from '@tplc/wot/components/common/util'
8
+
9
+ const [DefineMainContent, MainContent] = createReusableTemplate()
10
+ const [DefineSectionWrapper, SectionWrapper] = createReusableTemplate()
11
+ const [DefineSection, Section] = createReusableTemplate()
12
+ const [DefineSectionGroup, SectionGroup] = createReusableTemplate()
13
+
48
14
  defineOptions({
49
15
  name: 'LcbProductItem',
50
16
  options: {
@@ -54,44 +20,141 @@ defineOptions({
54
20
  },
55
21
  })
56
22
 
57
- const props = withDefaults(defineProps<LcbProductItemProps>(), {
58
- idx: 0,
59
- picWidth: 300,
60
- picHeight: 200,
61
- })
62
- const realImg = computed(() => {
63
- console.log('props', props.styleProps)
64
- const imgArr = formatJson(props.coverImg, [])
65
- return imgArr[1] || imgArr[0]
66
- })
23
+ const props = withDefaults(defineProps<LcbProductItemProps>(), defaultLcbProductItemProps)
24
+ const attrs = useAttrs()
25
+
26
+ // console.log('attrs', attrs)
67
27
  </script>
68
28
 
69
- <style lang="scss" scoped>
70
- .ellipsis {
71
- overflow: hidden;
72
- white-space: nowrap;
73
- text-overflow: ellipsis;
74
- }
75
-
76
- .noEllip {
77
- white-space: normal;
78
- text-overflow: clip;
79
- }
80
-
81
- .wordBreak {
82
- word-wrap: break-word;
83
- word-break: break-all;
84
- white-space: normal;
85
- }
86
-
87
- .mulEllipsis {
88
- /*! autoprefixer: off */
89
- white-space: normal;
90
- display: -webkit-box;
91
- -webkit-box-orient: vertical;
92
- text-overflow: ellipsis;
93
- overflow: hidden;
94
- word-break: break-all;
95
- -webkit-line-clamp: 2;
96
- }
97
- </style>
29
+ <template>
30
+ <!-- 内容容器定义 -->
31
+ <DefineSectionWrapper v-slot="{ prop, $slots, class: className }">
32
+ <view
33
+ v-if="
34
+ (props?.[`${prop}Visible`] ?? true) &&
35
+ !!props?.[`${prop}`] &&
36
+ (isArray(props?.[`${prop}`]) ? props?.[`${prop}`]?.length > 0 : true)
37
+ "
38
+ :class="[className, props?.[`${prop}Class`]]"
39
+ >
40
+ <component v-if="!!$slots.default" :is="$slots.default" />
41
+ <view v-else>{{ props?.[`${prop}`] }}</view>
42
+ </view>
43
+ </DefineSectionWrapper>
44
+
45
+ <!-- 定义各个内容展示节点 -->
46
+ <DefineSection v-slot="{ prop, class: className }">
47
+ <!-- 图片 -->
48
+ <SectionWrapper v-if="prop === 'image'" prop="image" :class="className">
49
+ <image :src="image" class="w-full h-full" mode="aspectFill" />
50
+ </SectionWrapper>
51
+
52
+ <!-- 标题 -->
53
+ <SectionWrapper
54
+ :class="className"
55
+ v-if="prop === 'title'"
56
+ prop="title"
57
+ class="text-ellipsis line-clamp-2"
58
+ />
59
+
60
+ <!-- 位置 -->
61
+ <SectionWrapper
62
+ :class="className"
63
+ v-if="prop === 'location'"
64
+ prop="location"
65
+ class="text-gray-500 text-22rpx flex gap-3rpx items-center"
66
+ >
67
+ <wd-icon name="location" size="32rpx"></wd-icon>
68
+ <view>{{ location }}</view>
69
+ </SectionWrapper>
70
+
71
+ <!-- 标签 -->
72
+ <SectionWrapper
73
+ :class="className"
74
+ v-if="prop === 'tags'"
75
+ prop="tags"
76
+ class="flex gap-1 whitespace-nowrap overflow-auto"
77
+ >
78
+ <wd-tag
79
+ v-for="tag in tags"
80
+ :key="tag"
81
+ class="!text-20rpx"
82
+ :class="tagsClass"
83
+ plain
84
+ type="primary"
85
+ >
86
+ {{ tag }}
87
+ </wd-tag>
88
+ </SectionWrapper>
89
+
90
+ <!-- 价格单位 -->
91
+ <SectionWrapper
92
+ :class="className"
93
+ v-if="prop === 'priceUnit'"
94
+ prop="priceUnit"
95
+ class="text-red-500 font-bold text-22rpx"
96
+ />
97
+
98
+ <!-- 价格 -->
99
+ <SectionWrapper
100
+ :class="className"
101
+ v-if="prop === 'price'"
102
+ prop="price"
103
+ class="text-red-500 font-bold text-27rpx"
104
+ />
105
+
106
+ <!-- 价格后缀 -->
107
+ <SectionWrapper
108
+ :class="className"
109
+ v-if="prop === 'priceSuffix'"
110
+ prop="priceSuffix"
111
+ class="text-22rpx"
112
+ />
113
+
114
+ <!-- 价格区域 -->
115
+ </DefineSection>
116
+
117
+ <!-- 内容组定义 -->
118
+ <DefineSectionGroup v-slot="{ group, class: className }">
119
+ <view :class="className">
120
+ <Section v-for="prop in group" :prop="prop" :key="prop" />
121
+ </view>
122
+ </DefineSectionGroup>
123
+
124
+ <!-- 主内容的展示节点 -->
125
+ <DefineMainContent v-slot="{ class: className }">
126
+ <view
127
+ :class="className"
128
+ class="flex flex-col flex-1 gap-2 justify-between text-26rpx overflow-hidden"
129
+ >
130
+ <SectionGroup
131
+ class="flex flex-col gap-2 overflow-hidden"
132
+ :group="['title', 'location', 'tags']"
133
+ />
134
+ <view class="flex gap-2">
135
+ <SectionGroup
136
+ class="flex gap-[4rpx] items-end"
137
+ :group="['priceUnit', 'price', 'priceSuffix']"
138
+ />
139
+ </view>
140
+ </view>
141
+ </DefineMainContent>
142
+
143
+ <!-- 横向布局 -->
144
+ <view
145
+ v-if="layoutType === 'horizontal'"
146
+ :class="attrs?.class"
147
+ class="flex gap-3 px-2 py-3 bg-white"
148
+ >
149
+ <Section prop="image" class="w-1/3 h-220rpx bg-gray-100 rounded-sm overflow-hidden" />
150
+ <MainContent />
151
+ </view>
152
+
153
+ <!-- 竖向布局 -->
154
+ <view v-if="layoutType === 'vertical'" :class="attrs?.class" class="flex flex-col gap-2 bg-white">
155
+ <Section prop="image" class="overflow-hidden" />
156
+ <MainContent class="p-2" />
157
+ </view>
158
+ </template>
159
+
160
+ <style lang="scss" scoped></style>
@@ -0,0 +1,22 @@
1
+ # 产品展示组件
2
+
3
+ 属性说明
4
+
5
+
6
+ layoutType
7
+ image
8
+ title
9
+ subTitle
10
+ price
11
+ priceUnit
12
+ originPrice
13
+ originPriceUnit
14
+ lowestPrice
15
+ lowestPriceUnit
16
+ tags
17
+ location
18
+ distance
19
+
20
+ 尝试使用 grid 进行布局
21
+
22
+
@@ -1,14 +1,58 @@
1
1
  export interface LcbProductItemProps {
2
2
  // Define the component's prop types here
3
- coverImg?: string
4
- picWidth?: number
5
- picHeight?: number
6
- productName?: string
7
- tags?: string | Record<string, any>
8
- subTitle?: string
9
- price?: string | number
10
- priceColor?: string
11
- scribePrice?: string | number
12
- styleProps?: Record<string, any>
13
- idx?: number
3
+
4
+ layoutType?: 'vertical' | 'horizontal'
5
+ image?: any
6
+ title?: any
7
+ subTitle?: any
8
+ price?: any
9
+ priceUnit?: any
10
+ priceSuffix?: any
11
+ originPrice?: any
12
+ originPriceUnit?: any
13
+ tags?: any
14
+ location?: any
15
+ distance?: any
16
+
17
+ imageVisible?: boolean
18
+ titleVisible?: boolean
19
+ subTitleVisible?: boolean
20
+ priceVisible?: boolean
21
+ priceUnitVisible?: boolean
22
+ priceSuffixVisible?: boolean
23
+ originPriceVisible?: boolean
24
+ originPriceUnitVisible?: boolean
25
+ tagsVisible?: boolean
26
+ locationVisible?: boolean
27
+ distanceVisible?: boolean
28
+
29
+ imageClass?: string
30
+ titleClass?: string
31
+ subTitleClass?: string
32
+ priceClass?: string
33
+ priceUnitClass?: string
34
+ priceSuffixClass?: string
35
+ originPriceClass?: string
36
+ originPriceUnitClass?: string
37
+ tagsClass?: string
38
+ tagsWrapperClass?: string
39
+ locationClass?: string
40
+ distanceClass?: string
41
+ }
42
+
43
+ export const defaultLcbProductItemProps: LcbProductItemProps = {
44
+ layoutType: 'horizontal',
45
+ priceUnit: '¥',
46
+ originPriceUnit: '¥',
47
+ imageVisible: true,
48
+ titleVisible: true,
49
+ subTitleVisible: true,
50
+ priceVisible: true,
51
+ priceUnitVisible: true,
52
+ priceSuffixVisible: true,
53
+ originPriceVisible: true,
54
+ originPriceUnitVisible: true,
55
+ tagsVisible: true,
56
+ locationVisible: true,
57
+ distanceVisible: true,
14
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tplc/business",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
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": "0.1.16"
14
+ "@tplc/wot": "0.1.17"
15
15
  },
16
16
  "engines": {
17
17
  "node": ">=18",
@@ -35,4 +35,14 @@ export declare const getProvinceCityArea: () => Promise<
35
35
  }[]
36
36
  }[]
37
37
  >
38
+ export declare const getProvinceCity: () => Promise<
39
+ {
40
+ label: string
41
+ value: string
42
+ children: {
43
+ label: string
44
+ value: string
45
+ }[]
46
+ }[]
47
+ >
38
48
  export {}
@@ -8,7 +8,12 @@ declare function __VLS_template(): {
8
8
  default?(_: { text: string[] | undefined }): any
9
9
  }
10
10
  declare const __VLS_component: import('vue').DefineComponent<
11
- __VLS_WithDefaults<__VLS_TypePropsToOption<__VLS_PublicProps>, {}>,
11
+ __VLS_WithDefaults<
12
+ __VLS_TypePropsToOption<__VLS_PublicProps>,
13
+ {
14
+ mode: string
15
+ }
16
+ >,
12
17
  {},
13
18
  unknown,
14
19
  {},
@@ -23,13 +28,20 @@ declare const __VLS_component: import('vue').DefineComponent<
23
28
  import('vue').PublicProps,
24
29
  Readonly<
25
30
  import('vue').ExtractPropTypes<
26
- __VLS_WithDefaults<__VLS_TypePropsToOption<__VLS_PublicProps>, {}>
31
+ __VLS_WithDefaults<
32
+ __VLS_TypePropsToOption<__VLS_PublicProps>,
33
+ {
34
+ mode: string
35
+ }
36
+ >
27
37
  >
28
38
  > & {
29
39
  onConfirm?: ((payload: AreaOptions[]) => any) | undefined
30
40
  'onUpdate:modelValue'?: ((modelValue: string[]) => any) | undefined
31
41
  },
32
- {},
42
+ {
43
+ mode: 'city' | 'area'
44
+ },
33
45
  {}
34
46
  >
35
47
  declare const _default: __VLS_WithTemplateSlots<
@@ -1 +1,3 @@
1
- export interface LcbAreaPickerProps {}
1
+ export interface LcbAreaPickerProps {
2
+ mode: 'city' | 'area'
3
+ }
@@ -2,8 +2,10 @@ import { LcbListInfo } from '../../api'
2
2
  import { LcbProductProps } from '../../../lcb-product/types'
3
3
  export interface PageListProps {
4
4
  emptyImage?: string
5
+ listType?: LcbProductProps['listType']
5
6
  productProps?: LcbProductProps
6
7
  }
7
8
  export interface LcbFilterListProps extends PageListProps, LcbListInfo {
8
9
  filter?: Record<string, any>
10
+ test?: any
9
11
  }
@@ -1,19 +1,7 @@
1
1
  import { LcbListProps } from './types'
2
2
  import './index.scss'
3
3
  declare const _default: import('vue').DefineComponent<
4
- __VLS_WithDefaults<
5
- __VLS_TypePropsToOption<LcbListProps>,
6
- {
7
- pageFilterType: string
8
- border: boolean
9
- styleMode: string
10
- pageListProps: () => {
11
- productProps: {
12
- styleGroup: number
13
- }
14
- }
15
- }
16
- >,
4
+ __VLS_WithDefaults<__VLS_TypePropsToOption<LcbListProps>, any>,
17
5
  {},
18
6
  unknown,
19
7
  {},
@@ -24,27 +12,14 @@ declare const _default: import('vue').DefineComponent<
24
12
  string,
25
13
  import('vue').PublicProps,
26
14
  Readonly<
27
- import('vue').ExtractPropTypes<
28
- __VLS_WithDefaults<
29
- __VLS_TypePropsToOption<LcbListProps>,
30
- {
31
- pageFilterType: string
32
- border: boolean
33
- styleMode: string
34
- pageListProps: () => {
35
- productProps: {
36
- styleGroup: number
37
- }
38
- }
39
- }
40
- >
41
- >
15
+ import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<LcbListProps>, any>>
42
16
  >,
43
17
  {
44
- border: boolean
18
+ listType: 'horizontal' | 'list' | 'grid' | 'waterfall'
45
19
  pageFilterType: string
46
- pageListProps: import('./components/FilterList/type').PageListProps
20
+ border: boolean
47
21
  styleMode: 'default' | 'plain'
22
+ pageListProps: import('./components/FilterList/type').PageListProps
48
23
  },
49
24
  {}
50
25
  >
@@ -1,9 +1,11 @@
1
1
  import { PageListProps } from './components/FilterList/type'
2
+ import { LcbProductProps } from '../lcb-product/types'
2
3
  export interface LcbListProps {
3
4
  pageFilterType?: string
4
5
  pageListProps?: PageListProps
5
6
  border?: boolean
6
7
  styleMode?: 'default' | 'plain'
8
+ listType?: LcbProductProps['listType']
7
9
  }
8
10
  export interface Option {
9
11
  label: string
@@ -18,4 +20,6 @@ export interface FilterItemProps {
18
20
  mode?: 'multiple' | 'single'
19
21
  apiPath?: string
20
22
  options?: Option[]
23
+ test?: 1
21
24
  }
25
+ export declare const defaultLcbListProps: LcbListProps
@@ -1,6 +1,6 @@
1
1
  import { LcbProductProps } from './types'
2
2
  declare const _default: import('vue').DefineComponent<
3
- __VLS_WithDefaults<__VLS_TypePropsToOption<LcbProductProps>, {}>,
3
+ __VLS_WithDefaults<__VLS_TypePropsToOption<LcbProductProps>, any>,
4
4
  {},
5
5
  unknown,
6
6
  {},
@@ -11,9 +11,14 @@ declare const _default: import('vue').DefineComponent<
11
11
  string,
12
12
  import('vue').PublicProps,
13
13
  Readonly<
14
- import('vue').ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<LcbProductProps>, {}>>
14
+ import('vue').ExtractPropTypes<
15
+ __VLS_WithDefaults<__VLS_TypePropsToOption<LcbProductProps>, any>
16
+ >
15
17
  >,
16
- {},
18
+ {
19
+ items: Record<string, any>[]
20
+ listType: 'list' | 'horizontal' | 'grid' | 'waterfall'
21
+ },
17
22
  {}
18
23
  >
19
24
  export default _default
@@ -1,13 +1,5 @@
1
1
  export interface LcbProductProps {
2
- styleGroup?: 1 | 2 | 3 | 4
3
- items?: Record<string, any>
4
- marginHorizontal?: number
5
- productStyle?: 'image' | 'flat' | 'card' | 'border'
6
- productTitle?: 0 | 1 | 2
7
- showTags?: boolean
8
- showPrice?: boolean
9
- showScribePrice?: boolean
10
- showCart?: boolean
11
- showLocation?: boolean
12
- showCollection?: boolean
2
+ listType?: 'list' | 'horizontal' | 'grid' | 'waterfall'
3
+ items?: Record<string, any>[]
13
4
  }
5
+ export declare const defaultLcbProductProps: LcbProductProps
@@ -1,13 +1,6 @@
1
1
  import { LcbProductItemProps } from './types'
2
2
  declare const _default: import('vue').DefineComponent<
3
- __VLS_WithDefaults<
4
- __VLS_TypePropsToOption<LcbProductItemProps>,
5
- {
6
- idx: number
7
- picWidth: number
8
- picHeight: number
9
- }
10
- >,
3
+ __VLS_WithDefaults<__VLS_TypePropsToOption<LcbProductItemProps>, LcbProductItemProps>,
11
4
  {},
12
5
  unknown,
13
6
  {},
@@ -19,20 +12,45 @@ declare const _default: import('vue').DefineComponent<
19
12
  import('vue').PublicProps,
20
13
  Readonly<
21
14
  import('vue').ExtractPropTypes<
22
- __VLS_WithDefaults<
23
- __VLS_TypePropsToOption<LcbProductItemProps>,
24
- {
25
- idx: number
26
- picWidth: number
27
- picHeight: number
28
- }
29
- >
15
+ __VLS_WithDefaults<__VLS_TypePropsToOption<LcbProductItemProps>, LcbProductItemProps>
30
16
  >
31
17
  >,
32
18
  {
33
- idx: number
34
- picWidth: number
35
- picHeight: number
19
+ title: any
20
+ image: any
21
+ location: any
22
+ layoutType: 'vertical' | 'horizontal'
23
+ imageClass: string
24
+ subTitle: any
25
+ price: any
26
+ priceUnit: any
27
+ priceSuffix: any
28
+ originPrice: any
29
+ originPriceUnit: any
30
+ tags: any
31
+ distance: any
32
+ imageVisible: boolean
33
+ titleVisible: boolean
34
+ subTitleVisible: boolean
35
+ priceVisible: boolean
36
+ priceUnitVisible: boolean
37
+ priceSuffixVisible: boolean
38
+ originPriceVisible: boolean
39
+ originPriceUnitVisible: boolean
40
+ tagsVisible: boolean
41
+ locationVisible: boolean
42
+ distanceVisible: boolean
43
+ titleClass: string
44
+ subTitleClass: string
45
+ priceClass: string
46
+ priceUnitClass: string
47
+ priceSuffixClass: string
48
+ originPriceClass: string
49
+ originPriceUnitClass: string
50
+ tagsClass: string
51
+ tagsWrapperClass: string
52
+ locationClass: string
53
+ distanceClass: string
36
54
  },
37
55
  {}
38
56
  >
@@ -1,13 +1,38 @@
1
1
  export interface LcbProductItemProps {
2
- coverImg?: string
3
- picWidth?: number
4
- picHeight?: number
5
- productName?: string
6
- tags?: string | Record<string, any>
7
- subTitle?: string
8
- price?: string | number
9
- priceColor?: string
10
- scribePrice?: string | number
11
- styleProps?: Record<string, any>
12
- idx?: number
2
+ layoutType?: 'vertical' | 'horizontal'
3
+ image?: any
4
+ title?: any
5
+ subTitle?: any
6
+ price?: any
7
+ priceUnit?: any
8
+ priceSuffix?: any
9
+ originPrice?: any
10
+ originPriceUnit?: any
11
+ tags?: any
12
+ location?: any
13
+ distance?: any
14
+ imageVisible?: boolean
15
+ titleVisible?: boolean
16
+ subTitleVisible?: boolean
17
+ priceVisible?: boolean
18
+ priceUnitVisible?: boolean
19
+ priceSuffixVisible?: boolean
20
+ originPriceVisible?: boolean
21
+ originPriceUnitVisible?: boolean
22
+ tagsVisible?: boolean
23
+ locationVisible?: boolean
24
+ distanceVisible?: boolean
25
+ imageClass?: string
26
+ titleClass?: string
27
+ subTitleClass?: string
28
+ priceClass?: string
29
+ priceUnitClass?: string
30
+ priceSuffixClass?: string
31
+ originPriceClass?: string
32
+ originPriceUnitClass?: string
33
+ tagsClass?: string
34
+ tagsWrapperClass?: string
35
+ locationClass?: string
36
+ distanceClass?: string
13
37
  }
38
+ export declare const defaultLcbProductItemProps: LcbProductItemProps
@@ -0,0 +1,59 @@
1
+ import type { DefineComponent, Slot } from 'vue'
2
+ type ObjectLiteralWithPotentialObjectLiterals = Record<string, Record<string, any> | undefined>
3
+ type GenerateSlotsFromSlotMap<T extends ObjectLiteralWithPotentialObjectLiterals> = {
4
+ [K in keyof T]: Slot<T[K]>
5
+ }
6
+ export type DefineTemplateComponent<
7
+ Bindings extends Record<string, any>,
8
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
9
+ > = DefineComponent & {
10
+ new (): {
11
+ $slots: {
12
+ default: (
13
+ _: Bindings & {
14
+ $slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps>
15
+ },
16
+ ) => any
17
+ }
18
+ }
19
+ }
20
+ export type ReuseTemplateComponent<
21
+ Bindings extends Record<string, any>,
22
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
23
+ > = DefineComponent<Bindings> & {
24
+ new (): {
25
+ $slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps>
26
+ }
27
+ }
28
+ export type ReusableTemplatePair<
29
+ Bindings extends Record<string, any>,
30
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
31
+ > = [
32
+ DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>,
33
+ ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>,
34
+ ] & {
35
+ define: DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>
36
+ reuse: ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>
37
+ }
38
+ export interface CreateReusableTemplateOptions {
39
+ /**
40
+ * Inherit attrs from reuse component.
41
+ *
42
+ * @default true
43
+ */
44
+ inheritAttrs?: boolean
45
+ }
46
+ /**
47
+ * This function creates `define` and `reuse` components in pair,
48
+ * It also allow to pass a generic to bind with type.
49
+ *
50
+ * @see https://vueuse.org/createReusableTemplate
51
+ */
52
+ export declare function createReusableTemplate<
53
+ Bindings extends Record<string, any>,
54
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals = Record<
55
+ 'default',
56
+ undefined
57
+ >,
58
+ >(options?: CreateReusableTemplateOptions): ReusableTemplatePair<Bindings, MapSlotNameToSlotProps>
59
+ export {}
@@ -0,0 +1,121 @@
1
+ import type { DefineComponent, Slot } from 'vue'
2
+ import { defineComponent, shallowRef } from 'vue'
3
+
4
+ type ObjectLiteralWithPotentialObjectLiterals = Record<string, Record<string, any> | undefined>
5
+
6
+ type GenerateSlotsFromSlotMap<T extends ObjectLiteralWithPotentialObjectLiterals> = {
7
+ [K in keyof T]: Slot<T[K]>
8
+ }
9
+
10
+ export type DefineTemplateComponent<
11
+ Bindings extends Record<string, any>,
12
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
13
+ > = DefineComponent & {
14
+ new (): {
15
+ $slots: {
16
+ default: (_: Bindings & { $slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps> }) => any
17
+ }
18
+ }
19
+ }
20
+
21
+ export type ReuseTemplateComponent<
22
+ Bindings extends Record<string, any>,
23
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
24
+ > = DefineComponent<Bindings> & {
25
+ new (): { $slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps> }
26
+ }
27
+
28
+ export type ReusableTemplatePair<
29
+ Bindings extends Record<string, any>,
30
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,
31
+ > = [
32
+ DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>,
33
+ ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>,
34
+ ] & {
35
+ define: DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>
36
+ reuse: ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>
37
+ }
38
+
39
+ export interface CreateReusableTemplateOptions {
40
+ /**
41
+ * Inherit attrs from reuse component.
42
+ *
43
+ * @default true
44
+ */
45
+ inheritAttrs?: boolean
46
+ }
47
+
48
+ /**
49
+ * This function creates `define` and `reuse` components in pair,
50
+ * It also allow to pass a generic to bind with type.
51
+ *
52
+ * @see https://vueuse.org/createReusableTemplate
53
+ */
54
+ export function createReusableTemplate<
55
+ Bindings extends Record<string, any>,
56
+ MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals = Record<
57
+ 'default',
58
+ undefined
59
+ >,
60
+ >(
61
+ options: CreateReusableTemplateOptions = {},
62
+ ): ReusableTemplatePair<Bindings, MapSlotNameToSlotProps> {
63
+ const { inheritAttrs = true } = options
64
+
65
+ const render = shallowRef<Slot | undefined>()
66
+
67
+ const define = defineComponent({
68
+ setup(_, { slots }) {
69
+ return () => {
70
+ render.value = slots.default
71
+ }
72
+ },
73
+ }) as unknown as DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>
74
+
75
+ const reuse = defineComponent({
76
+ inheritAttrs,
77
+ setup(_, { attrs, slots }) {
78
+ return () => {
79
+ if (!render.value && process.env.NODE_ENV !== 'production')
80
+ throw new Error('[VueUse] Failed to find the definition of reusable template')
81
+ const vnode = render.value?.({ ...keysToCamelKebabCase(attrs), $slots: slots })
82
+
83
+ return inheritAttrs && vnode?.length === 1 ? vnode[0] : vnode
84
+ }
85
+ },
86
+ }) as unknown as ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>
87
+
88
+ return makeDestructurable({ define, reuse }, [define, reuse]) as any
89
+ }
90
+
91
+ function keysToCamelKebabCase(obj: Record<string, any>) {
92
+ const newObj: typeof obj = {}
93
+ for (const key in obj) newObj[camelize(key)] = obj[key]
94
+ return newObj
95
+ }
96
+ /**
97
+ * @see What the hack? https://antfu.me/posts/destructuring-with-object-or-array
98
+ */
99
+ function makeDestructurable<T extends Record<string, unknown>, A extends readonly any[]>(
100
+ obj: T,
101
+ arr: A,
102
+ ): T & A {
103
+ const clone = { ...obj }
104
+ Object.defineProperty(clone, Symbol.iterator, {
105
+ enumerable: false,
106
+ value() {
107
+ let index = 0
108
+ return {
109
+ next: () => ({
110
+ value: arr[index++],
111
+ done: index > arr.length,
112
+ }),
113
+ }
114
+ },
115
+ })
116
+ return clone as T & A
117
+ }
118
+
119
+ function camelize(str: string) {
120
+ return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
121
+ }