@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,184 @@
1
+ <template>
2
+ <view class="dd-skeleton">
3
+ <template v-if="loading">
4
+ <!-- text -->
5
+ <template v-if="type === 'text'">
6
+ <view class="dd-skeleton__line dd-skeleton__line--title"></view>
7
+ <view
8
+ v-for="(w, i) in lineWidths"
9
+ :key="i"
10
+ class="dd-skeleton__line"
11
+ :style="{ width: w + '%' }"
12
+ ></view>
13
+ </template>
14
+
15
+ <!-- card -->
16
+ <template v-else-if="type === 'card'">
17
+ <view class="dd-skeleton__card">
18
+ <view class="dd-skeleton__block dd-skeleton__card-img"></view>
19
+ <view class="dd-skeleton__card-body">
20
+ <view class="dd-skeleton__block dd-skeleton__line dd-skeleton__line--title"></view>
21
+ <view class="dd-skeleton__block dd-skeleton__line" style="width: 80%"></view>
22
+ <view class="dd-skeleton__block dd-skeleton__line" style="width: 60%"></view>
23
+ </view>
24
+ </view>
25
+ </template>
26
+
27
+ <!-- list -->
28
+ <template v-else-if="type === 'list'">
29
+ <view
30
+ v-for="i in rows"
31
+ :key="i"
32
+ class="dd-skeleton__list-item"
33
+ >
34
+ <view v-if="avatar" class="dd-skeleton__block dd-skeleton__avatar dd-skeleton__avatar--sm"></view>
35
+ <view class="dd-skeleton__list-text">
36
+ <view class="dd-skeleton__block dd-skeleton__line" style="width: 40%"></view>
37
+ <view class="dd-skeleton__block dd-skeleton__line" style="width: 70%"></view>
38
+ </view>
39
+ </view>
40
+ </template>
41
+
42
+ <!-- avatar -->
43
+ <template v-else-if="type === 'avatar'">
44
+ <view class="dd-skeleton__block dd-skeleton__avatar dd-skeleton__avatar--lg"></view>
45
+ </template>
46
+
47
+ <!-- image -->
48
+ <template v-else-if="type === 'image'">
49
+ <view class="dd-skeleton__block dd-skeleton__image-block"></view>
50
+ </template>
51
+ </template>
52
+
53
+ <slot v-else></slot>
54
+ </view>
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ import { computed } from 'vue'
59
+
60
+ interface Props {
61
+ type?: 'text' | 'card' | 'list' | 'avatar' | 'image'
62
+ rows?: number
63
+ avatar?: boolean
64
+ loading?: boolean
65
+ }
66
+
67
+ const props = withDefaults(defineProps<Props>(), {
68
+ type: 'text',
69
+ rows: 3,
70
+ avatar: false,
71
+ loading: true,
72
+ })
73
+
74
+ // 递减宽度 [100,92,85,75,65]% 循环
75
+ const WIDTHS = [100, 92, 85, 75, 65]
76
+
77
+ const lineWidths = computed(() => {
78
+ const arr: number[] = []
79
+ for (let i = 0; i < props.rows; i++) {
80
+ arr.push(WIDTHS[i % WIDTHS.length])
81
+ }
82
+ return arr
83
+ })
84
+ </script>
85
+
86
+ <style lang="scss" scoped>
87
+ @import '../../scss/variables';
88
+ @import '../../scss/mixins';
89
+
90
+ .dd-skeleton {
91
+ width: 100%;
92
+
93
+ &__block {
94
+ position: relative;
95
+ overflow: hidden;
96
+ background: $dd-neutral-800; // var(--dd-skeleton-bg)
97
+
98
+ &::after {
99
+ content: '';
100
+ position: absolute;
101
+ inset: 0;
102
+ transform: translateX(-100%);
103
+ background: linear-gradient(
104
+ 90deg,
105
+ transparent,
106
+ rgba(255, 255, 255, 0.08),
107
+ transparent
108
+ );
109
+ animation: dd-skeleton-shimmer 1.5s ease-in-out infinite;
110
+ }
111
+ }
112
+
113
+ &__line {
114
+ height: 28rpx;
115
+ border-radius: $dd-radius-sm;
116
+ margin-bottom: 20rpx;
117
+ @extend .dd-skeleton__block;
118
+
119
+ &--title {
120
+ height: 36rpx;
121
+ width: 50%;
122
+ margin-bottom: 24rpx;
123
+ }
124
+ }
125
+
126
+ &__card {
127
+ border-radius: $dd-radius-lg;
128
+ overflow: hidden;
129
+ }
130
+
131
+ &__card-img {
132
+ width: 100%;
133
+ padding-bottom: 56.25%; // 16:9
134
+ border-radius: $dd-radius-lg;
135
+ }
136
+
137
+ &__card-body {
138
+ padding: $dd-space-4;
139
+ }
140
+
141
+ &__list-item {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: $dd-space-3;
145
+ padding: $dd-space-3 0;
146
+ @include dd-hairline-bottom($dd-border-subtle);
147
+ }
148
+
149
+ &__list-text {
150
+ flex: 1;
151
+ min-width: 0;
152
+ }
153
+
154
+ &__avatar {
155
+ border-radius: 50%;
156
+
157
+ &--sm {
158
+ width: 80rpx;
159
+ height: 80rpx;
160
+ flex-shrink: 0;
161
+ }
162
+
163
+ &--lg {
164
+ width: 128rpx;
165
+ height: 128rpx;
166
+ }
167
+ }
168
+
169
+ &__image-block {
170
+ width: 100%;
171
+ padding-bottom: 100%; // 1:1
172
+ border-radius: $dd-radius-lg;
173
+ }
174
+ }
175
+
176
+ @keyframes dd-skeleton-shimmer {
177
+ 0% {
178
+ transform: translateX(-100%);
179
+ }
180
+ 100% {
181
+ transform: translateX(100%);
182
+ }
183
+ }
184
+ </style>
@@ -0,0 +1,227 @@
1
+ <template>
2
+ <view class="dd-slider" :class="{ 'dd-slider--disabled': disabled }">
3
+ <view
4
+ class="dd-slider__track"
5
+ :style="trackStyle"
6
+ @touchstart="onTouchStart"
7
+ @touchmove.stop.prevent="onTouchMove"
8
+ @touchend="onTouchEnd"
9
+ @mousedown="onMouseDown"
10
+ >
11
+ <view class="dd-slider__fill" :style="fillStyle"></view>
12
+ <view class="dd-slider__thumb" :class="{ 'dd-slider__thumb--active': dragging }" :style="thumbStyle">
13
+ <view v-if="showTooltip && dragging" class="dd-slider__tooltip">
14
+ <text class="dd-slider__tooltip-text">{{ displayValue }}</text>
15
+ </view>
16
+ </view>
17
+ </view>
18
+ </view>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { computed, getCurrentInstance, ref } from 'vue'
23
+
24
+ interface Props {
25
+ modelValue?: number
26
+ min?: number
27
+ max?: number
28
+ step?: number
29
+ disabled?: boolean
30
+ showTooltip?: boolean
31
+ activeColor?: string
32
+ trackColor?: string
33
+ }
34
+
35
+ const props = withDefaults(defineProps<Props>(), {
36
+ modelValue: 0,
37
+ min: 0,
38
+ max: 100,
39
+ step: 1,
40
+ disabled: false,
41
+ showTooltip: false,
42
+ activeColor: '',
43
+ trackColor: '',
44
+ })
45
+
46
+ const emit = defineEmits<{
47
+ (e: 'update:modelValue', val: number): void
48
+ (e: 'change', val: number): void
49
+ (e: 'drag-start'): void
50
+ (e: 'drag-end'): void
51
+ }>()
52
+
53
+ const instance = getCurrentInstance()
54
+ const dragging = ref(false)
55
+ const trackRect = ref({ left: 0, width: 1 })
56
+
57
+ const percent = computed(() => {
58
+ if (props.max === props.min) return 0
59
+ const p = ((props.modelValue - props.min) / (props.max - props.min)) * 100
60
+ return Math.max(0, Math.min(100, p))
61
+ })
62
+
63
+ const displayValue = computed(() => {
64
+ const v = props.modelValue
65
+ return Number.isInteger(v) ? v : Number(v.toFixed(1))
66
+ })
67
+
68
+ const fillStyle = computed(() => {
69
+ const s: Record<string, string> = { width: percent.value + '%' }
70
+ if (props.activeColor) s.background = props.activeColor
71
+ return s
72
+ })
73
+ const thumbStyle = computed(() => {
74
+ const s: Record<string, string> = { left: percent.value + '%' }
75
+ if (props.activeColor) s.borderColor = props.activeColor
76
+ return s
77
+ })
78
+ const trackStyle = computed(() => {
79
+ return props.trackColor ? { background: props.trackColor } : {}
80
+ })
81
+
82
+ function queryRect(): Promise<any> {
83
+ return new Promise((resolve) => {
84
+ const query = uni.createSelectorQuery().in(instance!.proxy as any)
85
+ query
86
+ .select('.dd-slider__track')
87
+ .boundingClientRect((rect) => resolve(rect as any))
88
+ .exec()
89
+ })
90
+ }
91
+
92
+ function snap(val: number) {
93
+ if (props.step <= 1) return val // ponytail: step<=1 视为连续,不做吸附
94
+ const steps = Math.round((val - props.min) / props.step)
95
+ return props.min + steps * props.step
96
+ }
97
+
98
+ function updateFromClientX(x: number) {
99
+ const { left, width } = trackRect.value
100
+ let p = (x - left) / (width || 1)
101
+ p = Math.max(0, Math.min(1, p))
102
+ let val = props.min + p * (props.max - props.min)
103
+ val = snap(val)
104
+ val = Math.max(props.min, Math.min(props.max, val))
105
+ emit('update:modelValue', val)
106
+ emit('change', val)
107
+ }
108
+
109
+ async function startDrag(clientX: number) {
110
+ if (props.disabled) return
111
+ const rect = await queryRect()
112
+ if (rect) trackRect.value = { left: rect.left, width: rect.width || 1 }
113
+ dragging.value = true
114
+ emit('drag-start')
115
+ updateFromClientX(clientX)
116
+ }
117
+
118
+ function onTouchStart(e: any) {
119
+ if (props.disabled) return
120
+ const x = e.touches?.[0]?.clientX ?? 0
121
+ startDrag(x)
122
+ }
123
+ function onTouchMove(e: any) {
124
+ if (!dragging.value) return
125
+ const x = e.touches?.[0]?.clientX ?? 0
126
+ updateFromClientX(x)
127
+ }
128
+ function onTouchEnd() {
129
+ if (!dragging.value) return
130
+ dragging.value = false
131
+ emit('drag-end')
132
+ }
133
+
134
+ function onMouseDown(e: any) {
135
+ // #ifdef H5
136
+ if (props.disabled) return
137
+ startDrag(e.clientX)
138
+ const onMove = (ev: MouseEvent) => updateFromClientX(ev.clientX)
139
+ const onUp = () => {
140
+ dragging.value = false
141
+ emit('drag-end')
142
+ window.removeEventListener('mousemove', onMove)
143
+ window.removeEventListener('mouseup', onUp)
144
+ }
145
+ window.addEventListener('mousemove', onMove)
146
+ window.addEventListener('mouseup', onUp)
147
+ // #endif
148
+ }
149
+ </script>
150
+
151
+ <style lang="scss" scoped>
152
+ @import '../../scss/variables';
153
+ @import '../../scss/mixins';
154
+
155
+ .dd-slider {
156
+ padding: 20rpx 0;
157
+
158
+ &__track {
159
+ position: relative;
160
+ width: 100%;
161
+ height: 6rpx;
162
+ border-radius: $dd-radius-full;
163
+ background: $dd-neutral-800;
164
+ }
165
+
166
+ &__fill {
167
+ position: absolute;
168
+ top: 0;
169
+ left: 0;
170
+ height: 100%;
171
+ border-radius: $dd-radius-full;
172
+ background: $dd-gradient-primary-h;
173
+ box-shadow: 0 0 8rpx rgba($dd-primary-400, 0.6);
174
+ }
175
+
176
+ &__thumb {
177
+ position: absolute;
178
+ top: 50%;
179
+ width: 32rpx;
180
+ height: 32rpx;
181
+ background: #ffffff;
182
+ border: 4rpx solid $dd-primary-400;
183
+ border-radius: 50%;
184
+ box-shadow: $dd-shadow-glow-gold-lg;
185
+ transform: translate(-50%, -50%);
186
+ @include dd-transition(transform 0.1s ease, box-shadow 0.2s ease);
187
+
188
+ &--active {
189
+ transform: translate(-50%, -50%) scale(1.1);
190
+ }
191
+ }
192
+
193
+ &__tooltip {
194
+ position: absolute;
195
+ bottom: calc(100% + 16rpx);
196
+ left: 50%;
197
+ transform: translateX(-50%);
198
+ padding: 6rpx 16rpx;
199
+ @include dd-glass;
200
+ border-radius: $dd-radius-sm;
201
+ white-space: nowrap;
202
+
203
+ &::after {
204
+ content: '';
205
+ position: absolute;
206
+ bottom: -6rpx;
207
+ left: 50%;
208
+ transform: translateX(-50%);
209
+ border: 6rpx solid transparent;
210
+ border-top-color: $dd-glass-border;
211
+ border-bottom: 0;
212
+ }
213
+ }
214
+
215
+ &__tooltip-text {
216
+ font-size: 24rpx;
217
+ font-weight: 600;
218
+ color: $dd-primary-300;
219
+ line-height: 1.2;
220
+ }
221
+
222
+ &--disabled {
223
+ opacity: 0.4;
224
+ @include dd-no-touch;
225
+ }
226
+ }
227
+ </style>
@@ -0,0 +1,174 @@
1
+ <template>
2
+ <view
3
+ class="dd-stat-card"
4
+ :class="[`dd-stat-card--${variant}`]"
5
+ :hover-class="hoverable ? 'dd-stat-card--press' : ''"
6
+ :hover-stay-time="100"
7
+ @click="onClick"
8
+ >
9
+ <view class="dd-stat-card__main">
10
+ <text v-if="title" class="dd-stat-card__title">{{ title }}</text>
11
+ <view class="dd-stat-card__value-row">
12
+ <text v-if="prefix" class="dd-stat-card__prefix">{{ prefix }}</text>
13
+ <text class="dd-stat-card__value">{{ displayValue }}</text>
14
+ <view v-if="trend !== 'none'" class="dd-stat-card__trend" :class="`dd-stat-card__trend--${trend}`">
15
+ <dd-icon :name="trend === 'up' ? 'arrow-up' : 'arrow-down'" class="dd-stat-card__trend-arrow" />
16
+ <text v-if="trendValue" class="dd-stat-card__trend-value">{{ trendValue }}</text>
17
+ </view>
18
+ </view>
19
+ <slot></slot>
20
+ </view>
21
+ <view v-if="$slots.footer" class="dd-stat-card__footer">
22
+ <slot name="footer"></slot>
23
+ </view>
24
+ </view>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { computed, useSlots } from 'vue'
29
+ import DdIcon from '../dd-icon/dd-icon.vue'
30
+
31
+ interface Props {
32
+ variant?: 'primary' | 'success' | 'warning' | 'error' | 'accent' | 'neutral'
33
+ trend?: 'none' | 'up' | 'down'
34
+ title?: string
35
+ value?: string | number
36
+ prefix?: string
37
+ trendValue?: string
38
+ hoverable?: boolean
39
+ }
40
+
41
+ const props = withDefaults(defineProps<Props>(), {
42
+ variant: 'primary',
43
+ trend: 'none',
44
+ title: '',
45
+ value: '',
46
+ prefix: '',
47
+ trendValue: '',
48
+ hoverable: true,
49
+ })
50
+
51
+ const emit = defineEmits<{ (e: 'click', val: Event): void }>()
52
+
53
+ const slots = useSlots()
54
+ void slots
55
+
56
+ const displayValue = computed(() => String(props.value ?? ''))
57
+
58
+ function onClick(e: Event) {
59
+ emit('click', e)
60
+ }
61
+ </script>
62
+
63
+ <style lang="scss" scoped>
64
+ @import '../../scss/variables';
65
+ @import '../../scss/mixins';
66
+
67
+ .dd-stat-card {
68
+ box-sizing: border-box;
69
+ display: flex;
70
+ flex-direction: column;
71
+ overflow: hidden;
72
+ border-radius: $dd-radius-lg;
73
+ padding: $dd-space-4;
74
+ @include dd-transition(transform 0.2s ease);
75
+
76
+ &__main {
77
+ display: flex;
78
+ flex-direction: column;
79
+ }
80
+
81
+ &__title {
82
+ font-size: $dd-font-size-caption;
83
+ font-weight: 500;
84
+ color: rgba(255, 255, 255, 0.75);
85
+ line-height: 1.4;
86
+ }
87
+
88
+ &__value-row {
89
+ display: flex;
90
+ align-items: baseline;
91
+ gap: 8rpx;
92
+ margin-top: $dd-space-1;
93
+ }
94
+
95
+ &__prefix {
96
+ font-size: $dd-font-size-h3;
97
+ font-weight: 700;
98
+ color: #ffffff;
99
+ }
100
+
101
+ &__value {
102
+ font-size: $dd-font-size-h1;
103
+ font-weight: 800;
104
+ color: #ffffff;
105
+ line-height: 1.2;
106
+ }
107
+
108
+ &__trend {
109
+ display: inline-flex;
110
+ align-items: baseline;
111
+ gap: 4rpx;
112
+ font-size: $dd-font-size-caption;
113
+ font-weight: 600;
114
+ margin-left: $dd-space-1;
115
+
116
+ &-arrow {
117
+ font-size: $dd-font-size-caption;
118
+ line-height: 1;
119
+ }
120
+
121
+ &--up {
122
+ color: #ffffff;
123
+ }
124
+
125
+ &--down {
126
+ color: rgba(255, 255, 255, 0.8);
127
+ }
128
+ }
129
+
130
+ &__footer {
131
+ margin-top: $dd-space-3;
132
+ padding-top: $dd-space-3;
133
+ border-top: 1px solid rgba(255, 255, 255, 0.15);
134
+ font-size: $dd-font-size-caption;
135
+ color: rgba(255, 255, 255, 0.7);
136
+ }
137
+
138
+ &--press {
139
+ transform: scale(0.98);
140
+ }
141
+ }
142
+
143
+ // === 6 渐变变体 ===
144
+ .dd-stat-card--primary {
145
+ background: $dd-gradient-primary;
146
+ &:active {
147
+ box-shadow: $dd-shadow-glow-gold-lg;
148
+ }
149
+ }
150
+
151
+ .dd-stat-card--accent {
152
+ background: $dd-gradient-accent-wide;
153
+ &:active {
154
+ box-shadow: $dd-shadow-glow-blue-lg;
155
+ }
156
+ }
157
+
158
+ .dd-stat-card--success {
159
+ background: $dd-gradient-success;
160
+ }
161
+
162
+ .dd-stat-card--warning {
163
+ background: $dd-gradient-warning;
164
+ }
165
+
166
+ .dd-stat-card--error {
167
+ background: $dd-gradient-error;
168
+ }
169
+
170
+ .dd-stat-card--neutral {
171
+ background: $dd-gradient-neutral;
172
+ border: 1px solid $dd-neutral-600;
173
+ }
174
+ </style>