@didaoktv/didaoui-uniapp 1.0.0

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -0
  3. package/components/dd-action-sheet/dd-action-sheet.vue +207 -0
  4. package/components/dd-alert/dd-alert.vue +240 -0
  5. package/components/dd-avatar/dd-avatar.vue +168 -0
  6. package/components/dd-backtop/dd-backtop.vue +75 -0
  7. package/components/dd-badge/dd-badge.vue +150 -0
  8. package/components/dd-button/dd-button.vue +215 -0
  9. package/components/dd-capsule-button/dd-capsule-button.vue +109 -0
  10. package/components/dd-card/dd-card.vue +155 -0
  11. package/components/dd-cell/dd-cell.vue +201 -0
  12. package/components/dd-cell-group/dd-cell-group.vue +56 -0
  13. package/components/dd-champion-card/dd-champion-card.vue +258 -0
  14. package/components/dd-checkbox/dd-checkbox.vue +145 -0
  15. package/components/dd-collapse/dd-collapse.vue +63 -0
  16. package/components/dd-collapse-item/dd-collapse-item.vue +154 -0
  17. package/components/dd-count-down/dd-count-down.vue +163 -0
  18. package/components/dd-date-picker/dd-date-picker.vue +253 -0
  19. package/components/dd-dialog/dd-dialog.vue +264 -0
  20. package/components/dd-divider/dd-divider.vue +104 -0
  21. package/components/dd-drawer/dd-drawer.vue +214 -0
  22. package/components/dd-dropdown-item/dd-dropdown-item.vue +203 -0
  23. package/components/dd-dropdown-menu/dd-dropdown-menu.vue +177 -0
  24. package/components/dd-empty-state/dd-empty-state.vue +140 -0
  25. package/components/dd-feature-grid/dd-feature-grid.vue +139 -0
  26. package/components/dd-field/dd-field.vue +240 -0
  27. package/components/dd-icon/dd-icon.vue +464 -0
  28. package/components/dd-image/dd-image.vue +158 -0
  29. package/components/dd-input/dd-input.vue +172 -0
  30. package/components/dd-list-cell/dd-list-cell.vue +152 -0
  31. package/components/dd-loading/dd-loading.vue +187 -0
  32. package/components/dd-loadmore/dd-loadmore.vue +152 -0
  33. package/components/dd-mini-program-navbar/dd-mini-program-navbar.vue +245 -0
  34. package/components/dd-modal/dd-modal.vue +286 -0
  35. package/components/dd-navigation/dd-navigation.vue +134 -0
  36. package/components/dd-overlay/dd-overlay.vue +114 -0
  37. package/components/dd-picker/dd-picker.vue +203 -0
  38. package/components/dd-popover/dd-popover.vue +190 -0
  39. package/components/dd-popover-item/dd-popover-item.vue +88 -0
  40. package/components/dd-popup/dd-popup.vue +243 -0
  41. package/components/dd-progress/dd-progress.vue +255 -0
  42. package/components/dd-pull-refresh/dd-pull-refresh.vue +232 -0
  43. package/components/dd-radio/dd-radio.vue +129 -0
  44. package/components/dd-rate/dd-rate.vue +133 -0
  45. package/components/dd-room-card/dd-room-card.vue +309 -0
  46. package/components/dd-search-bar/dd-search-bar.vue +176 -0
  47. package/components/dd-segmented-tab/dd-segmented-tab.vue +187 -0
  48. package/components/dd-skeleton/dd-skeleton.vue +184 -0
  49. package/components/dd-slider/dd-slider.vue +227 -0
  50. package/components/dd-stat-card/dd-stat-card.vue +174 -0
  51. package/components/dd-step/dd-step.vue +188 -0
  52. package/components/dd-stepper/dd-stepper.vue +171 -0
  53. package/components/dd-steps/dd-steps.vue +61 -0
  54. package/components/dd-sticky/dd-sticky.vue +132 -0
  55. package/components/dd-swipe/dd-swipe.vue +146 -0
  56. package/components/dd-swipe-action/dd-swipe-action.vue +248 -0
  57. package/components/dd-swipe-item/dd-swipe-item.vue +27 -0
  58. package/components/dd-swipeable-tab/dd-swipeable-tab.vue +202 -0
  59. package/components/dd-switch/dd-switch.vue +106 -0
  60. package/components/dd-tabbar/dd-tabbar.vue +112 -0
  61. package/components/dd-tabbar-item/dd-tabbar-item.vue +122 -0
  62. package/components/dd-tag/dd-tag.vue +178 -0
  63. package/components/dd-toast/dd-toast.vue +195 -0
  64. package/components/dd-top-navbar/dd-top-navbar.vue +184 -0
  65. package/components/dd-upload/dd-upload.vue +187 -0
  66. package/index.ts +127 -0
  67. package/libs/utils.ts +106 -0
  68. package/package.json +58 -0
  69. package/scss/_functions.scss +68 -0
  70. package/scss/_mixins.scss +154 -0
  71. package/scss/_reset.scss +30 -0
  72. package/scss/_variables.scss +317 -0
  73. package/uni.scss +4 -0
@@ -0,0 +1,188 @@
1
+ <template>
2
+ <view class="dd-step" :class="[`dd-step--${direction}`, `dd-step--${status}`]">
3
+ <template v-if="direction === 'horizontal'">
4
+ <view class="dd-step__head">
5
+ <view
6
+ v-if="!isLast"
7
+ class="dd-step__line"
8
+ :class="{ 'dd-step__line--first': isFirst }"
9
+ :style="lineStyle"
10
+ ></view>
11
+ <view class="dd-step__circle" :style="circleStyle">
12
+ <dd-icon v-if="status === 'finish'" name="success" class="dd-step__circle-text" />
13
+ <text v-else class="dd-step__circle-text">{{ index + 1 }}</text>
14
+ </view>
15
+ </view>
16
+ <view class="dd-step__content">
17
+ <text class="dd-step__title">{{ title }}</text>
18
+ <text v-if="description" class="dd-step__desc">{{ description }}</text>
19
+ </view>
20
+ </template>
21
+
22
+ <template v-else>
23
+ <view class="dd-step__head dd-step__head--vertical">
24
+ <view class="dd-step__circle" :style="circleStyle">
25
+ <dd-icon v-if="status === 'finish'" name="success" class="dd-step__circle-text" />
26
+ <text v-else class="dd-step__circle-text">{{ index + 1 }}</text>
27
+ </view>
28
+ <view
29
+ v-if="!isLast"
30
+ class="dd-step__line dd-step__line--vertical"
31
+ :style="lineStyle"
32
+ ></view>
33
+ </view>
34
+ <view class="dd-step__content dd-step__content--vertical">
35
+ <text class="dd-step__title">{{ title }}</text>
36
+ <text v-if="description" class="dd-step__desc">{{ description }}</text>
37
+ </view>
38
+ </template>
39
+ </view>
40
+ </template>
41
+
42
+ <script setup lang="ts">
43
+ import { computed, inject } from 'vue'
44
+ import DdIcon from '../dd-icon/dd-icon.vue'
45
+
46
+ type StepStatus = 'wait' | 'process' | 'finish'
47
+
48
+ interface Props {
49
+ title?: string
50
+ description?: string
51
+ }
52
+
53
+ const props = withDefaults(defineProps<Props>(), {
54
+ title: '',
55
+ description: '',
56
+ })
57
+
58
+ const ctx = inject<any>('ddSteps', null)
59
+ const index = ctx ? ctx.register() : 0
60
+
61
+ const direction = computed<'horizontal' | 'vertical'>(() =>
62
+ ctx ? ctx.direction.value : 'horizontal'
63
+ )
64
+
65
+ const status = computed<StepStatus>(() => {
66
+ if (!ctx) return 'wait'
67
+ const active = ctx.active.value
68
+ if (index < active) return 'finish'
69
+ if (index === active) return 'process'
70
+ return 'wait'
71
+ })
72
+
73
+ const isLast = computed(() => (ctx ? index === ctx.count.value - 1 : true))
74
+ const isFirst = computed(() => index === 0)
75
+
76
+ const activeColor = computed(() => (ctx ? ctx.activeColor.value : '#F5A623'))
77
+ const inactiveColor = computed(() => (ctx ? ctx.inactiveColor.value : '#9E9E9E'))
78
+
79
+ const isActive = computed(() => status.value === 'finish' || status.value === 'process')
80
+
81
+ const circleStyle = computed(() => ({
82
+ background: isActive.value ? activeColor.value : '#2A2A2A',
83
+ color: isActive.value ? '#FFFFFF' : inactiveColor.value,
84
+ }))
85
+
86
+ // ponytail: 水平连接线整段按本步骤 status 着色,current/首个 wait 左半段颜色为近似值
87
+ const lineStyle = computed(() => ({
88
+ background: status.value === 'finish' ? activeColor.value : inactiveColor.value,
89
+ }))
90
+ </script>
91
+
92
+ <style lang="scss" scoped>
93
+ @import '../../scss/variables';
94
+ @import '../../scss/mixins';
95
+
96
+ .dd-step {
97
+ &--horizontal {
98
+ flex: 1;
99
+ display: flex;
100
+ flex-direction: column;
101
+ align-items: center;
102
+ min-width: 0;
103
+ }
104
+
105
+ &__head {
106
+ position: relative;
107
+ width: 100%;
108
+ height: 40rpx;
109
+ display: flex;
110
+ align-items: center;
111
+ justify-content: center;
112
+
113
+ &--vertical {
114
+ width: 40rpx;
115
+ height: auto;
116
+ flex-direction: column;
117
+ align-self: stretch;
118
+ }
119
+ }
120
+
121
+ &__line {
122
+ position: absolute;
123
+ top: 50%;
124
+ left: 0;
125
+ right: 0;
126
+ height: 1px;
127
+ transform: translateY(-50%);
128
+ z-index: 0;
129
+
130
+ &--first {
131
+ left: 50%;
132
+ }
133
+
134
+ &--vertical {
135
+ position: static;
136
+ transform: none;
137
+ width: 1px;
138
+ flex: 1;
139
+ min-height: 32rpx;
140
+ margin: 8rpx 0;
141
+ }
142
+ }
143
+
144
+ &__circle {
145
+ position: relative;
146
+ z-index: 1;
147
+ width: 40rpx;
148
+ height: 40rpx;
149
+ border-radius: $dd-radius-full;
150
+ @include dd-flex-center;
151
+ flex-shrink: 0;
152
+ @include dd-transition(all 0.3s ease);
153
+ }
154
+
155
+ &__circle-text {
156
+ font-size: 20rpx; // font-size-xs
157
+ font-weight: 700;
158
+ line-height: 1;
159
+ }
160
+
161
+ &__content {
162
+ text-align: center;
163
+ margin-top: 8rpx;
164
+
165
+ &--vertical {
166
+ text-align: left;
167
+ flex: 1;
168
+ padding-bottom: 32rpx;
169
+ padding-left: 8rpx;
170
+ margin-top: 0;
171
+ }
172
+ }
173
+
174
+ &__title {
175
+ font-size: $dd-font-size-caption;
176
+ color: $dd-text-primary;
177
+ line-height: $dd-line-height-caption;
178
+ }
179
+
180
+ &__desc {
181
+ display: block;
182
+ margin-top: 4rpx;
183
+ font-size: 20rpx;
184
+ color: $dd-text-tertiary;
185
+ line-height: 1.4;
186
+ }
187
+ }
188
+ </style>
@@ -0,0 +1,171 @@
1
+ <template>
2
+ <view class="dd-stepper" :class="[`dd-stepper--${size}`, { 'dd-stepper--disabled': disabled }]">
3
+ <view
4
+ class="dd-stepper__btn dd-stepper__minus"
5
+ :class="{ 'dd-stepper__btn--disabled': minusDisabled }"
6
+ :hover-class="!minusDisabled ? 'dd-stepper__btn--active' : ''"
7
+ @click="onMinus"
8
+ >
9
+ <dd-icon name="minus" class="dd-stepper__icon" />
10
+ </view>
11
+ <text class="dd-stepper__value">{{ modelValue }}</text>
12
+ <view
13
+ class="dd-stepper__btn dd-stepper__plus"
14
+ :class="{ 'dd-stepper__btn--disabled': plusDisabled }"
15
+ :hover-class="!plusDisabled ? 'dd-stepper__btn--active' : ''"
16
+ @click="onPlus"
17
+ >
18
+ <dd-icon name="plus" class="dd-stepper__icon" />
19
+ </view>
20
+ </view>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import { computed } from 'vue'
25
+ import DdIcon from '../dd-icon/dd-icon.vue'
26
+
27
+ interface Props {
28
+ modelValue?: number
29
+ min?: number
30
+ max?: number
31
+ step?: number
32
+ size?: 'md' | 'sm'
33
+ disabled?: boolean
34
+ disablePlus?: boolean
35
+ disableMinus?: boolean
36
+ }
37
+
38
+ const props = withDefaults(defineProps<Props>(), {
39
+ modelValue: 1,
40
+ min: 1,
41
+ max: 99,
42
+ step: 1,
43
+ size: 'md',
44
+ disabled: false,
45
+ disablePlus: false,
46
+ disableMinus: false,
47
+ })
48
+
49
+ const emit = defineEmits<{
50
+ (e: 'update:modelValue', val: number): void
51
+ (e: 'change', val: number): void
52
+ (e: 'plus'): void
53
+ (e: 'minus'): void
54
+ (e: 'overlimit', val: 'plus' | 'minus'): void
55
+ }>()
56
+
57
+ const minusDisabled = computed(() => props.disabled || props.disableMinus || props.modelValue <= props.min)
58
+ const plusDisabled = computed(() => props.disabled || props.disablePlus || props.modelValue >= props.max)
59
+
60
+ function clamp(val: number) {
61
+ return Math.max(props.min, Math.min(props.max, val))
62
+ }
63
+
64
+ function onMinus() {
65
+ emit('minus')
66
+ if (minusDisabled.value) {
67
+ emit('overlimit', 'minus')
68
+ return
69
+ }
70
+ const next = clamp(props.modelValue - props.step)
71
+ emit('update:modelValue', next)
72
+ emit('change', next)
73
+ }
74
+ function onPlus() {
75
+ emit('plus')
76
+ if (plusDisabled.value) {
77
+ emit('overlimit', 'plus')
78
+ return
79
+ }
80
+ const next = clamp(props.modelValue + props.step)
81
+ emit('update:modelValue', next)
82
+ emit('change', next)
83
+ }
84
+ </script>
85
+
86
+ <style lang="scss" scoped>
87
+ @import '../../scss/variables';
88
+ @import '../../scss/mixins';
89
+
90
+ .dd-stepper {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ gap: 8rpx;
95
+ padding: 4rpx;
96
+ background: $dd-neutral-800;
97
+ border: 1px solid $dd-neutral-700;
98
+ border-radius: $dd-radius-full;
99
+ box-sizing: border-box;
100
+
101
+ &__btn {
102
+ @include dd-flex-center;
103
+ flex-shrink: 0;
104
+ border-radius: 50%;
105
+ background: $dd-gradient-primary-wide;
106
+ @include dd-transition(transform 0.1s ease, opacity 0.2s);
107
+
108
+ &--active {
109
+ transform: scale(0.9);
110
+ }
111
+
112
+ &--disabled {
113
+ background: $dd-neutral-800;
114
+ @include dd-no-touch;
115
+ .dd-stepper__icon {
116
+ color: $dd-neutral-500;
117
+ opacity: 0.4;
118
+ }
119
+ }
120
+ }
121
+
122
+ &__icon {
123
+ color: $dd-neutral-900;
124
+ font-weight: 700;
125
+ line-height: 1;
126
+ }
127
+
128
+ &__value {
129
+ text-align: center;
130
+ color: $dd-neutral-50;
131
+ font-weight: 700;
132
+ line-height: 1;
133
+ @include dd-ellipsis(1);
134
+ }
135
+
136
+ &--disabled {
137
+ opacity: 0.5;
138
+ @include dd-no-touch;
139
+ }
140
+ }
141
+
142
+ .dd-stepper--md {
143
+ height: 72rpx;
144
+ .dd-stepper__btn {
145
+ width: 56rpx;
146
+ height: 56rpx;
147
+ }
148
+ .dd-stepper__icon {
149
+ font-size: 32rpx;
150
+ }
151
+ .dd-stepper__value {
152
+ min-width: 80rpx;
153
+ font-size: 32rpx;
154
+ }
155
+ }
156
+
157
+ .dd-stepper--sm {
158
+ height: 56rpx;
159
+ .dd-stepper__btn {
160
+ width: 44rpx;
161
+ height: 44rpx;
162
+ }
163
+ .dd-stepper__icon {
164
+ font-size: 26rpx;
165
+ }
166
+ .dd-stepper__value {
167
+ min-width: 64rpx;
168
+ font-size: 28rpx;
169
+ }
170
+ }
171
+ </style>
@@ -0,0 +1,61 @@
1
+ <template>
2
+ <view class="dd-steps" :class="[`dd-steps--${direction}`]">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, provide, ref } from 'vue'
9
+
10
+ interface Props {
11
+ /** 当前激活步骤 (0-indexed) */
12
+ active?: number
13
+ direction?: 'horizontal' | 'vertical'
14
+ activeColor?: string
15
+ inactiveColor?: string
16
+ }
17
+
18
+ const props = withDefaults(defineProps<Props>(), {
19
+ active: 0,
20
+ direction: 'horizontal',
21
+ activeColor: '',
22
+ inactiveColor: '',
23
+ })
24
+
25
+ const count = ref(0)
26
+
27
+ // ponytail: register 依赖子组件 setup 执行顺序 (父→子,Vue 一般稳定)
28
+ function register(): number {
29
+ const idx = count.value
30
+ count.value += 1
31
+ return idx
32
+ }
33
+
34
+ provide('ddSteps', {
35
+ active: computed(() => props.active),
36
+ direction: computed(() => props.direction),
37
+ activeColor: computed(() => props.activeColor || '#F5A623'),
38
+ inactiveColor: computed(() => props.inactiveColor || '#9E9E9E'),
39
+ count,
40
+ register,
41
+ })
42
+ </script>
43
+
44
+ <style lang="scss" scoped>
45
+ @import '../../scss/variables';
46
+ @import '../../scss/mixins';
47
+
48
+ .dd-steps {
49
+ width: 100%;
50
+
51
+ &--horizontal {
52
+ display: flex;
53
+ align-items: flex-start;
54
+ }
55
+
56
+ &--vertical {
57
+ display: flex;
58
+ flex-direction: column;
59
+ }
60
+ }
61
+ </style>
@@ -0,0 +1,132 @@
1
+ <template>
2
+ <view
3
+ class="dd-sticky"
4
+ :class="{ 'dd-sticky--disabled': disabled }"
5
+ :style="stickyStyle"
6
+ >
7
+ <slot></slot>
8
+ </view>
9
+ </template>
10
+
11
+ <script setup lang="ts">
12
+ import { computed, getCurrentInstance, onMounted, onUnmounted, ref } from 'vue'
13
+
14
+ interface Props {
15
+ offsetTop?: number // rpx
16
+ zIndex?: number
17
+ disabled?: boolean
18
+ }
19
+
20
+ const props = withDefaults(defineProps<Props>(), {
21
+ offsetTop: 0,
22
+ zIndex: 99,
23
+ disabled: false,
24
+ })
25
+
26
+ const emit = defineEmits<{ (e: 'change', val: { isFixed: boolean; scrollTop: number }): void }>()
27
+
28
+ const isFixed = ref(false)
29
+
30
+ const stickyStyle = computed(() => {
31
+ if (props.disabled) {
32
+ return { position: 'static' as const, top: 'auto' }
33
+ }
34
+ return {
35
+ position: 'sticky' as const,
36
+ top: `${props.offsetTop}rpx`,
37
+ zIndex: props.zIndex,
38
+ width: '100%',
39
+ }
40
+ })
41
+
42
+ let observer: any = null
43
+ const instance = getCurrentInstance()
44
+
45
+ function rpxToPx(rpx: number): number {
46
+ try {
47
+ const info = uni.getWindowInfo()
48
+ return (rpx / 750) * info.windowWidth
49
+ } catch {
50
+ return rpx / 2
51
+ }
52
+ }
53
+
54
+ onMounted(() => {
55
+ if (props.disabled) return
56
+ // #ifdef H5
57
+ setupH5Observer()
58
+ // #endif
59
+ // #ifndef H5
60
+ setupMPObserver()
61
+ // #endif
62
+ })
63
+
64
+ // H5: 使用原生 IntersectionObserver API
65
+ function setupH5Observer() {
66
+ // #ifdef H5
67
+ const el = document.querySelector('.dd-sticky')
68
+ if (!el) return
69
+ const px = rpxToPx(props.offsetTop)
70
+ const io = new IntersectionObserver(
71
+ (entries) => {
72
+ const entry = entries[0]
73
+ const fixed = entry.boundingClientRect.top <= px + 1
74
+ if (fixed !== isFixed.value) {
75
+ isFixed.value = fixed
76
+ emit('change', { isFixed: fixed, scrollTop: window.scrollY })
77
+ }
78
+ },
79
+ { rootMargin: `-${px}px 0px 0px 0px`, threshold: [0, 1] }
80
+ )
81
+ io.observe(el)
82
+ observer = io
83
+ // #endif
84
+ }
85
+
86
+ // MP/App: 使用 uni IntersectionObserver, 需传入组件实例
87
+ function setupMPObserver() {
88
+ // #ifndef H5
89
+ const px = rpxToPx(props.offsetTop)
90
+ const obs = uni.createIntersectionObserver(instance, {
91
+ thresholds: [0, 1],
92
+ })
93
+ obs.relativeToViewport({ top: -px, bottom: 0 })
94
+ obs.observe('.dd-sticky', (res: any) => {
95
+ const fixed = res.boundingClientRect.top <= px + 1
96
+ if (fixed !== isFixed.value) {
97
+ isFixed.value = fixed
98
+ emit('change', { isFixed: fixed, scrollTop: 0 })
99
+ }
100
+ })
101
+ observer = obs
102
+ // #endif
103
+ }
104
+
105
+ onUnmounted(() => {
106
+ if (observer) {
107
+ // #ifdef H5
108
+ observer.disconnect()
109
+ // #endif
110
+ // #ifndef H5
111
+ observer.disconnect()
112
+ // #endif
113
+ observer = null
114
+ }
115
+ })
116
+
117
+ defineExpose({ isFixed })
118
+ </script>
119
+
120
+ <style lang="scss" scoped>
121
+ @import '../../scss/variables';
122
+ @import '../../scss/mixins';
123
+
124
+ .dd-sticky {
125
+ font-family: $dd-font-body;
126
+
127
+ &--disabled {
128
+ position: static;
129
+ top: auto;
130
+ }
131
+ }
132
+ </style>
@@ -0,0 +1,146 @@
1
+ <template>
2
+ <view class="dd-swipe" :style="boxStyle">
3
+ <swiper
4
+ class="dd-swipe__swiper"
5
+ :autoplay="autoplay > 0"
6
+ :interval="autoplay"
7
+ :duration="duration"
8
+ :circular="loop"
9
+ :vertical="vertical"
10
+ :touchable="touchable"
11
+ :current="current"
12
+ @change="onChange"
13
+ >
14
+ <slot></slot>
15
+ </swiper>
16
+ <slot name="indicator" :active="current" :count="count">
17
+ <view v-if="showIndicators && count > 1" class="dd-swipe__indicators" :class="`dd-swipe__indicators--${vertical ? 'vertical' : 'horizontal'}`">
18
+ <view
19
+ v-for="i in count"
20
+ :key="i"
21
+ class="dd-swipe__dot"
22
+ :class="{ 'dd-swipe__dot--active': i - 1 === current }"
23
+ :style="dotStyle(i - 1)"
24
+ @click="jumpTo(i - 1)"
25
+ ></view>
26
+ </view>
27
+ </slot>
28
+ </view>
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import { computed, provide, ref } from 'vue'
33
+
34
+ interface Props {
35
+ width?: string
36
+ height?: string
37
+ autoplay?: number
38
+ loop?: boolean
39
+ vertical?: boolean
40
+ duration?: number
41
+ touchable?: boolean
42
+ showIndicators?: boolean
43
+ indicatorColor?: string
44
+ initial?: number
45
+ }
46
+
47
+ const props = withDefaults(defineProps<Props>(), {
48
+ width: '100%',
49
+ height: '300rpx',
50
+ autoplay: 0,
51
+ loop: true,
52
+ vertical: false,
53
+ duration: 500,
54
+ touchable: true,
55
+ showIndicators: true,
56
+ indicatorColor: '',
57
+ initial: 0,
58
+ })
59
+
60
+ const emit = defineEmits<{
61
+ (e: 'change', index: number): void
62
+ }>()
63
+
64
+ const current = ref(props.initial)
65
+ const count = ref(0)
66
+
67
+ const boxStyle = computed(() => ({ width: props.width, height: props.height }))
68
+
69
+ const activeColor = computed(() => props.indicatorColor || '#F5A623')
70
+
71
+ function dotStyle(index: number) {
72
+ return index === current.value
73
+ ? { background: activeColor.value }
74
+ : {}
75
+ }
76
+
77
+ // provide/inject: dd-swipe-item 挂载时自注册以统计轮播项数量
78
+ provide('ddSwipe', {
79
+ register() {
80
+ count.value++
81
+ },
82
+ unregister() {
83
+ count.value = Math.max(0, count.value - 1)
84
+ },
85
+ })
86
+
87
+ function onChange(e: any) {
88
+ const idx = e?.detail?.current ?? 0
89
+ current.value = idx
90
+ emit('change', idx)
91
+ }
92
+
93
+ function jumpTo(index: number) {
94
+ current.value = index
95
+ }
96
+
97
+ defineExpose({ jumpTo, current })
98
+ </script>
99
+
100
+ <style lang="scss" scoped>
101
+ @import '../../scss/variables';
102
+ @import '../../scss/mixins';
103
+
104
+ .dd-swipe {
105
+ position: relative;
106
+ overflow: hidden;
107
+
108
+ &__swiper {
109
+ width: 100%;
110
+ height: 100%;
111
+ }
112
+
113
+ &__indicators {
114
+ position: absolute;
115
+ display: flex;
116
+ z-index: 2;
117
+
118
+ &--horizontal {
119
+ bottom: $dd-space-3;
120
+ left: 50%;
121
+ transform: translateX(-50%);
122
+ flex-direction: row;
123
+ gap: $dd-space-1;
124
+ }
125
+ &--vertical {
126
+ right: $dd-space-3;
127
+ top: 50%;
128
+ transform: translateY(-50%);
129
+ flex-direction: column;
130
+ gap: $dd-space-1;
131
+ }
132
+ }
133
+
134
+ &__dot {
135
+ width: 12rpx;
136
+ height: 12rpx;
137
+ border-radius: $dd-radius-full;
138
+ background: rgba(255, 255, 255, 0.5);
139
+ @include dd-transition(background 0.3s ease);
140
+
141
+ &--active {
142
+ background: $dd-primary-500;
143
+ }
144
+ }
145
+ }
146
+ </style>