@tplc/business 0.0.14 → 0.0.16

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 (42) hide show
  1. package/action.d.ts +22 -0
  2. package/components/lcb-filter/api.ts +56 -0
  3. package/components/lcb-filter/components/ActionView/index.vue +40 -0
  4. package/components/lcb-filter/components/ComponentGroup/index.vue +86 -0
  5. package/components/lcb-filter/components/ComponentGroup/type.ts +6 -0
  6. package/components/lcb-filter/components/FilterSelect/index.vue +68 -0
  7. package/components/lcb-filter/components/FilterSelect/type.ts +3 -0
  8. package/components/lcb-filter/components/FilterSlider/index.vue +70 -0
  9. package/components/lcb-filter/components/FilterSlider/types.ts +3 -0
  10. package/components/lcb-filter/components/SelectTagView/index.vue +50 -0
  11. package/components/lcb-filter/components/TagSelect/index.vue +159 -0
  12. package/components/lcb-filter/components/TagSelect/type.ts +5 -0
  13. package/components/lcb-filter/components/TreeSelect/index.vue +131 -0
  14. package/components/lcb-filter/components/TreeSelect/type.ts +3 -0
  15. package/components/lcb-filter/hooks/useSelect.ts +45 -0
  16. package/components/lcb-filter/index.scss +30 -0
  17. package/components/lcb-filter/lcb-filter.vue +125 -0
  18. package/components/lcb-filter/types.ts +18 -0
  19. package/components/lcb-nav/lcb-nav.vue +5 -5
  20. package/components/lcb-nav/types.ts +2 -2
  21. package/global.d.ts +1 -0
  22. package/index.ts +7 -1
  23. package/package.json +2 -2
  24. package/types/components/lcb-filter/api.d.ts +49 -0
  25. package/types/components/lcb-filter/components/ActionView/index.vue.d.ts +34 -0
  26. package/types/components/lcb-filter/components/ComponentGroup/index.vue.d.ts +32 -0
  27. package/types/components/lcb-filter/components/ComponentGroup/type.d.ts +5 -0
  28. package/types/components/lcb-filter/components/FilterSelect/index.vue.d.ts +41 -0
  29. package/types/components/lcb-filter/components/FilterSelect/type.d.ts +2 -0
  30. package/types/components/lcb-filter/components/FilterSlider/index.vue.d.ts +36 -0
  31. package/types/components/lcb-filter/components/FilterSlider/types.d.ts +2 -0
  32. package/types/components/lcb-filter/components/SelectTagView/index.vue.d.ts +39 -0
  33. package/types/components/lcb-filter/components/TagSelect/index.vue.d.ts +39 -0
  34. package/types/components/lcb-filter/components/TagSelect/type.d.ts +4 -0
  35. package/types/components/lcb-filter/components/TreeSelect/index.vue.d.ts +41 -0
  36. package/types/components/lcb-filter/components/TreeSelect/type.d.ts +2 -0
  37. package/types/components/lcb-filter/hooks/useSelect.d.ts +25 -0
  38. package/types/components/lcb-filter/lcb-filter.vue.d.ts +57 -0
  39. package/types/components/lcb-filter/types.d.ts +17 -0
  40. package/types/components/lcb-img-nav/lcb-img-nav.vue.d.ts +1 -1
  41. package/types/components/lcb-nav/types.d.ts +2 -2
  42. package/types/components/lcb-user-top/lcb-user-top.vue.d.ts +1 -1
package/action.d.ts CHANGED
@@ -4,3 +4,25 @@ export interface ActionView {
4
4
  link: string
5
5
  url?: string
6
6
  }
7
+ export type IResData<T> = {
8
+ code: number
9
+ msg: string
10
+ data: T
11
+ }
12
+
13
+ export interface LcbGlobal {
14
+ http: {
15
+ get<T>(url: string, query?: Record<string, any> | undefined): Promise<IResData<T>>
16
+ post<T>(
17
+ url: string,
18
+ data?: Record<string, any> | undefined,
19
+ query?: Record<string, any> | undefined,
20
+ ): Promise<IResData<T>>
21
+ }
22
+ }
23
+
24
+ global {
25
+ interface Uni {
26
+ $lcb: LcbGlobal
27
+ }
28
+ }
@@ -0,0 +1,56 @@
1
+ import { Option } from './types'
2
+ export interface LcbFilterResult {
3
+ btnComponent: BtnComponent
4
+ filterTags: FilterTags
5
+ filterComponent: FilterComponent[]
6
+ }
7
+
8
+ export interface FilterComponent {
9
+ fitlerName?: string
10
+ component: string
11
+ defaultValue?: string
12
+ defaultName?: string
13
+ valueName: string
14
+ componentProps: ComponentProps3
15
+ }
16
+
17
+ interface ComponentProps3 {
18
+ mode?: 'multiple' | 'single'
19
+ apiPath?: string
20
+ options?: Option[]
21
+ componentList?: ComponentList[]
22
+ }
23
+
24
+ export interface ComponentList {
25
+ fitlerName: string
26
+ component: string
27
+ valueName: string
28
+ defaultValue?: string | string[] | number[]
29
+ componentProps: ComponentProps
30
+ }
31
+
32
+ export interface FilterTags {
33
+ component: string
34
+ valueName: string
35
+ defaultValue?: string
36
+ defaultName?: string
37
+ componentProps: ComponentProps
38
+ }
39
+
40
+ export interface ComponentProps {
41
+ mode?: 'multiple' | 'single'
42
+ max: number
43
+ min: number
44
+ apiPath: string
45
+ options: Option[]
46
+ unit?: string
47
+ }
48
+
49
+ interface BtnComponent {
50
+ postRequest: string
51
+ }
52
+
53
+ export const getFilterDetail = (val: string) =>
54
+ uni.$lcb.http.post<LcbFilterResult>('/pageDecoration/filter/detail', {
55
+ pageFilterType: val,
56
+ })
@@ -0,0 +1,40 @@
1
+ <template>
2
+ <view class="action-view flex justify-center items-center">
3
+ <wd-button type="info" @click="emits('cancel')" :disabled="disabled">清空</wd-button>
4
+ <wd-button type="primary" custom-class="flex-1" @click="emits('submit')">查看房屋</wd-button>
5
+ </view>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ defineOptions({
10
+ name: 'ActionView',
11
+ options: {
12
+ addGlobalClass: true,
13
+ virtualHost: true,
14
+ styleIsolation: 'shared',
15
+ },
16
+ })
17
+ defineProps({
18
+ disabled: {
19
+ type: Boolean,
20
+ },
21
+ })
22
+ const emits = defineEmits(['cancel', 'submit'])
23
+ </script>
24
+ <style lang="scss" scoped>
25
+ .action-view {
26
+ width: 100%;
27
+ height: 118rpx;
28
+ background: #ffffff;
29
+ box-shadow: 0rpx 0rpx 6rpx 0rpx rgba(0, 0, 0, 0.04);
30
+ gap: 50rpx;
31
+ padding: 20rpx 52rpx;
32
+ box-sizing: border-box;
33
+ :deep(.wd-button) {
34
+ height: 100%;
35
+ line-height: 100%;
36
+ font-size: 30rpx;
37
+ border-radius: 40rpx;
38
+ }
39
+ }
40
+ </style>
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <view class="px-5 pt-2 pb-4 bg-#fafafa max-h-70vh overflow-y-auto box-border">
3
+ <view v-for="child in componentList" :key="child.valueName" class="mb-12rpx">
4
+ <view class="lcb-filter__group-title mt2">
5
+ {{ child.fitlerName }}
6
+ <view v-if="child.componentProps.mode === 'multiple'" class="!text-22rpx">可多选</view>
7
+ </view>
8
+ <view class="grid grid-cols-4 gap-22rpx" v-if="child.component === 'tagSelect'">
9
+ <TagSelect v-bind="child.componentProps" v-model="innerFilter[child.valueName]" />
10
+ </view>
11
+ <FilterSlider
12
+ v-if="child.component === 'slider'"
13
+ v-bind="child.componentProps"
14
+ v-model="innerFilter[child.valueName]"
15
+ />
16
+ </view>
17
+ </view>
18
+ <ActionView :disabled="disabled" @cancel="onCancel" @submit="onSubmit" />
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { computed, ref } from 'vue'
23
+ import { ComponentGroupProps } from './type'
24
+ import FilterSlider from '../FilterSlider/index.vue'
25
+ import TagSelect from '../TagSelect/index.vue'
26
+ import ActionView from '../ActionView/index.vue'
27
+ defineOptions({
28
+ name: 'ComponentGroup',
29
+ options: {
30
+ addGlobalClass: true,
31
+ virtualHost: true,
32
+ styleIsolation: 'shared',
33
+ },
34
+ })
35
+
36
+ const props = defineProps<ComponentGroupProps>()
37
+ const emits = defineEmits(['submit'])
38
+ const innerFilter = ref<Record<string, any>>({})
39
+ props.componentList?.forEach((item) => {
40
+ innerFilter.value[item.valueName] = props.filter?.[item.valueName]
41
+ })
42
+ const disabled = computed(() => {
43
+ const list = (props.componentList || []).filter((item) => {
44
+ if (item.component === 'slider') {
45
+ return (
46
+ item.componentProps.min === innerFilter.value[item.valueName][0] &&
47
+ item.componentProps.max === innerFilter.value[item.valueName][1]
48
+ )
49
+ } else {
50
+ return !innerFilter.value[item.valueName]
51
+ }
52
+ })
53
+ return list.length === (props.componentList || [])?.length
54
+ })
55
+
56
+ const onCancel = () => {
57
+ props.componentList?.forEach((key) => {
58
+ if (key.component === 'slider') {
59
+ innerFilter.value[key.valueName] = key.defaultValue
60
+ } else {
61
+ innerFilter.value[key.valueName] = key.defaultValue
62
+ }
63
+ })
64
+ }
65
+ const onSubmit = () => {
66
+ emits('submit', innerFilter.value)
67
+ }
68
+ </script>
69
+ <style lang="scss" scoped>
70
+ .lcb-filter {
71
+ &__group-title {
72
+ font-size: 30rpx;
73
+ color: #333;
74
+ font-weight: 500;
75
+ margin-bottom: 24rpx;
76
+ display: flex;
77
+ align-items: center;
78
+
79
+ > view {
80
+ font-size: 24rpx;
81
+ color: #999999;
82
+ margin-left: 20rpx;
83
+ }
84
+ }
85
+ }
86
+ </style>
@@ -0,0 +1,6 @@
1
+ import { ComponentList } from '../../api'
2
+
3
+ export interface ComponentGroupProps {
4
+ componentList?: ComponentList[]
5
+ filter?: Record<string, string>
6
+ }
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <view class="max-h-60vh overflow-y-auto">
3
+ <view
4
+ v-for="item in options"
5
+ :key="`${item.value}`"
6
+ @click="onItemClick(item)"
7
+ class="filter-select flex justify-between items-center"
8
+ :class="getChecked(item) ? 'filter-select__choose' : ''"
9
+ >
10
+ {{ item.label }}
11
+ <wd-icon name="check" custom-class="choose_icon" v-if="model === item.value" />
12
+ </view>
13
+ </view>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { FilterSelectProps } from './type'
18
+ import useSelect from '../../hooks/useSelect'
19
+ import { watch, watchEffect } from 'vue'
20
+ defineOptions({
21
+ name: 'FilterSelect',
22
+ options: {
23
+ addGlobalClass: true,
24
+ virtualHost: true,
25
+ styleIsolation: 'shared',
26
+ },
27
+ })
28
+ const props = defineProps<FilterSelectProps>()
29
+ const model = defineModel<string | string[]>()
30
+ const modelTitle = defineModel<string>('title')
31
+ const { onItemClick, options, getChecked } = useSelect(props, { model })
32
+ const emits = defineEmits(['submit'])
33
+ watchEffect(() => {
34
+ modelTitle.value = options.value?.find((v) => v.value === model.value)?.label
35
+ })
36
+ watch(
37
+ () => model.value,
38
+ (val) => {
39
+ emits('submit')
40
+ },
41
+ )
42
+ </script>
43
+ <style lang="scss" scoped>
44
+ @import '@tplc/wot/components/common/abstracts/variable';
45
+ .choose_icon {
46
+ color: $-color-theme;
47
+ font-size: $-cell-arrow-size;
48
+ }
49
+ .filter-select {
50
+ padding: 30rpx 40rpx;
51
+ font-size: $-cell-title-fs;
52
+ position: relative;
53
+ &__choose {
54
+ color: $-color-theme;
55
+ // 透明背景颜色
56
+ &::after {
57
+ content: '';
58
+ position: absolute;
59
+ top: 0;
60
+ left: 0;
61
+ width: 100%;
62
+ height: 100%;
63
+ opacity: 0.05;
64
+ background-color: $-color-theme;
65
+ }
66
+ }
67
+ }
68
+ </style>
@@ -0,0 +1,3 @@
1
+ import { FilterItemProps } from '../../types'
2
+
3
+ export interface FilterSelectProps extends FilterItemProps {}
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <view>
3
+ <wd-slider hide-label :max="max" :min="min" v-model="innerValue" @dragend="onDragend" />
4
+ <view class="grid grid-cols-4 gap-4 mt-2">
5
+ <SelectTagView
6
+ v-for="item in options"
7
+ :key="item.label"
8
+ :title="item.label"
9
+ :custom="item.value === 'custom'"
10
+ :checked="getChecked(item.value)"
11
+ @click="onItemClick(item.value)"
12
+ />
13
+ </view>
14
+ </view>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { FilterSliderProps } from './types'
19
+ import SelectTagView from '../SelectTagView/index.vue'
20
+ import { ref, watch } from 'vue'
21
+ defineOptions({
22
+ name: 'FilterSlider',
23
+ options: {
24
+ addGlobalClass: true,
25
+ virtualHost: true,
26
+ styleIsolation: 'shared',
27
+ },
28
+ })
29
+ const props = defineProps<FilterSliderProps>()
30
+ const model = defineModel<number[]>()
31
+ const innerValue = ref<number[]>()
32
+ const onItemClick = (value) => {
33
+ if (Array.isArray(value)) {
34
+ if (value[1]) {
35
+ innerValue.value = value
36
+ } else {
37
+ innerValue.value = [value[0], value[0]]
38
+ }
39
+ }
40
+ }
41
+ watch(
42
+ () => model.value,
43
+ (val) => {
44
+ if (val?.length === 1) {
45
+ innerValue.value = [val[0], val[0]]
46
+ } else {
47
+ innerValue.value = val
48
+ }
49
+ },
50
+ { immediate: true },
51
+ )
52
+ const onDragend = () => {
53
+ if (innerValue.value?.[0] === props.max && innerValue.value?.[1] === props.max) {
54
+ model.value = [innerValue.value?.[0]]
55
+ } else {
56
+ model.value = innerValue.value
57
+ }
58
+ }
59
+
60
+ const getChecked = (value) => {
61
+ if (Array.isArray(value) && innerValue.value) {
62
+ if (value[1]) {
63
+ return innerValue.value[0] === value[0] && innerValue.value[1] === value[1]
64
+ } else {
65
+ return innerValue.value[0] === value[0] && innerValue.value[1] === value[0]
66
+ }
67
+ }
68
+ }
69
+ </script>
70
+ <style lang="scss" scoped></style>
@@ -0,0 +1,3 @@
1
+ import { ComponentProps } from '../../api'
2
+
3
+ export interface FilterSliderProps extends ComponentProps {}
@@ -0,0 +1,50 @@
1
+ <template>
2
+ <view
3
+ class="select-tag"
4
+ :class="[{ 'select-tag-checked': checked, 'select-tag-small': size === 'small' }]"
5
+ >
6
+ {{ title }}
7
+ </view>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ defineOptions({
12
+ name: 'SelectTagView',
13
+ options: {
14
+ addGlobalClass: true,
15
+ virtualHost: true,
16
+ styleIsolation: 'shared',
17
+ },
18
+ })
19
+ defineProps<{ title: string; checked?: boolean; size?: 'small' | 'normal' }>()
20
+ </script>
21
+ <style lang="scss" scoped>
22
+ @import '@tplc/wot/components/common/abstracts/variable';
23
+ .select-tag {
24
+ width: 150rpx;
25
+ height: 56rpx;
26
+ background: #f5f5f7;
27
+ border-radius: 53rpx;
28
+ font-size: 24rpx;
29
+ color: #333333;
30
+ line-height: 56rpx;
31
+ overflow: hidden;
32
+ text-overflow: ellipsis;
33
+ white-space: nowrap;
34
+ padding: 0 6rpx;
35
+ box-sizing: border-box;
36
+ text-align: center;
37
+ margin-bottom: 16rpx;
38
+ &-checked {
39
+ background: $-color-theme;
40
+ color: #ffffff;
41
+ }
42
+ &-small {
43
+ width: 112rpx !important;
44
+ height: 42rpx !important;
45
+ border-radius: 21rpx !important;
46
+ line-height: 42rpx !important;
47
+ margin-bottom: 0rpx !important;
48
+ }
49
+ }
50
+ </style>
@@ -0,0 +1,159 @@
1
+ <template>
2
+ <view v-for="item in options" :key="`${item.value}`" @click="onItemClick(item)">
3
+ <template v-if="item.custom">
4
+ <view
5
+ class="select-tag"
6
+ @click.stop="onCustomClick(item)"
7
+ :class="{
8
+ 'select-tag-checked': showInput || getChecked(item),
9
+ }"
10
+ >
11
+ <view v-if="showInput" class="flex justify-center items-center">
12
+ <input
13
+ type="number"
14
+ class="text-24rpx min-w-5"
15
+ focus
16
+ v-model="inputValue"
17
+ @input="onInput(item, $event)"
18
+ @blur="onBlur(item)"
19
+ @confirm="onConfirm"
20
+ />
21
+ </view>
22
+ <template v-else>
23
+ <view v-if="item.value" class="text-22rpx leading-none">
24
+ <view @click.stop="onCustomClick(item, true)">{{ item.label }}</view>
25
+ <view class="text-16rpx mt-4rpx">自定义</view>
26
+ </view>
27
+ <view v-else>自定义</view>
28
+ </template>
29
+ </view>
30
+ </template>
31
+ <SelectTagView
32
+ :title="item.label"
33
+ :checked="getChecked(item)"
34
+ :custom="item.value === 'custom'"
35
+ :size="size"
36
+ v-else
37
+ />
38
+ </view>
39
+ </template>
40
+
41
+ <script setup lang="ts">
42
+ import { TagSelectProps } from './type'
43
+ import useSelect from '../../hooks/useSelect'
44
+ import { ref, watchEffect, nextTick } from 'vue'
45
+ import SelectTagView from '../SelectTagView/index.vue'
46
+ import { Option } from '../../types'
47
+ defineOptions({
48
+ name: 'TagSelect',
49
+ options: {
50
+ addGlobalClass: true,
51
+ virtualHost: true,
52
+ styleIsolation: 'shared',
53
+ },
54
+ })
55
+ const props = defineProps<TagSelectProps>()
56
+ const model = defineModel<string | string[]>()
57
+ const modelTitle = defineModel<string>('title')
58
+ const showInput = ref(false)
59
+ const inputValue = ref('')
60
+ const { options, onItemClick, getChecked } = useSelect(props, { model })
61
+ watchEffect(() => {
62
+ modelTitle.value = options.value?.find((v) => v.value === model.value)?.label
63
+ })
64
+ const onCustomClick = (item: Option, edit = false) => {
65
+ if (item.value && !edit) {
66
+ onItemClick(item)
67
+ } else {
68
+ inputValue.value = `${item.value}`
69
+ if (props.mode !== 'multiple') {
70
+ model.value = undefined
71
+ }
72
+ showInput.value = true
73
+ }
74
+ }
75
+ const onBlur = (item: Option) => {
76
+ if (inputValue.value) {
77
+ item.label = inputValue.value + item.unit
78
+ item.value = inputValue.value
79
+ onItemClick(item)
80
+ } else {
81
+ item.value = ''
82
+ if (props.mode !== 'multiple') {
83
+ model.value = undefined
84
+ }
85
+ }
86
+ showInput.value = false
87
+ }
88
+ const onConfirm = () => {
89
+ showInput.value = false
90
+ }
91
+ const onInput = (item: Option, e) => {
92
+ if (item.max && parseInt(e.detail.value) > item.max) {
93
+ uni.showToast({
94
+ title: item.unit + '数最多输入 ' + item.max,
95
+ icon: 'none',
96
+ })
97
+ nextTick(() => {
98
+ inputValue.value = item.max + ''
99
+ })
100
+ } else if (item.min && parseInt(e.detail.value) < item.min) {
101
+ uni.showToast({
102
+ title: item.unit + '数最少输入 ' + item.min,
103
+ icon: 'none',
104
+ })
105
+ nextTick(() => {
106
+ inputValue.value = item.min + ''
107
+ })
108
+ }
109
+ }
110
+ </script>
111
+ <style lang="scss" scoped>
112
+ @import '@tplc/wot/components/common/abstracts/variable';
113
+ .choose_icon {
114
+ color: $-color-theme;
115
+ font-size: $-cell-arrow-size;
116
+ }
117
+ .filter-select {
118
+ font-size: $-cell-title-fs;
119
+ position: relative;
120
+ &__choose {
121
+ color: $-color-theme;
122
+ // 透明背景颜色
123
+ &::after {
124
+ content: '';
125
+ position: absolute;
126
+ top: 0;
127
+ left: 0;
128
+ width: 100%;
129
+ height: 100%;
130
+ opacity: 0.05;
131
+ background-color: $-color-theme;
132
+ }
133
+ }
134
+ }
135
+ .select-tag {
136
+ width: 150rpx;
137
+ height: 56rpx;
138
+ background: #f5f5f7;
139
+ border-radius: 53rpx;
140
+ font-size: 24rpx;
141
+ color: #333333;
142
+ line-height: 56rpx;
143
+ overflow: hidden;
144
+ text-overflow: ellipsis;
145
+ white-space: nowrap;
146
+ padding: 0 12rpx;
147
+ box-sizing: border-box;
148
+ text-align: center;
149
+ display: flex;
150
+ justify-content: center;
151
+ align-items: center;
152
+ flex-direction: column;
153
+ margin-bottom: 16rpx;
154
+ &-checked {
155
+ background: $-color-theme;
156
+ color: #ffffff;
157
+ }
158
+ }
159
+ </style>
@@ -0,0 +1,5 @@
1
+ import { FilterItemProps } from '../../types'
2
+
3
+ export interface TagSelectProps extends FilterItemProps {
4
+ size?: 'small' | 'normal'
5
+ }