@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,214 @@
1
+ <template>
2
+ <view class="dd-drawer">
3
+ <view
4
+ class="dd-drawer__mask"
5
+ :class="{ 'dd-drawer__mask--show': modelValue }"
6
+ @click="onMaskClick"
7
+ ></view>
8
+ <view
9
+ class="dd-drawer__panel"
10
+ :class="[`dd-drawer__panel--${position}`, { 'dd-drawer__panel--show': modelValue }]"
11
+ :style="{ width: width }"
12
+ >
13
+ <view v-if="$slots.header" class="dd-drawer__header">
14
+ <slot name="header"></slot>
15
+ </view>
16
+ <view v-else class="dd-drawer__header dd-drawer__header--vip">
17
+ <view class="dd-drawer__avatar">
18
+ <image
19
+ v-if="avatar"
20
+ class="dd-drawer__avatar-img"
21
+ :src="avatar"
22
+ mode="aspectFill"
23
+ />
24
+ <text v-else class="dd-drawer__avatar-fallback">{{ username ? username.charAt(0) : '客' }}</text>
25
+ </view>
26
+ <view class="dd-drawer__user">
27
+ <view class="dd-drawer__user-row">
28
+ <text class="dd-drawer__username">{{ username }}</text>
29
+ <text class="dd-drawer__vip-badge">VIP</text>
30
+ </view>
31
+ <text v-if="vipLevel" class="dd-drawer__level">{{ vipLevel }}</text>
32
+ </view>
33
+ </view>
34
+ <scroll-view scroll-y class="dd-drawer__content">
35
+ <slot></slot>
36
+ </scroll-view>
37
+ </view>
38
+ </view>
39
+ </template>
40
+
41
+ <script setup lang="ts">
42
+ import { watch } from 'vue'
43
+
44
+ interface Props {
45
+ modelValue?: boolean
46
+ position?: 'left' | 'right'
47
+ width?: string
48
+ maskClosable?: boolean
49
+ avatar?: string
50
+ username?: string
51
+ vipLevel?: string
52
+ }
53
+
54
+ const props = withDefaults(defineProps<Props>(), {
55
+ modelValue: false,
56
+ position: 'left',
57
+ width: '80%',
58
+ maskClosable: true,
59
+ avatar: '',
60
+ username: '',
61
+ vipLevel: '',
62
+ })
63
+
64
+ const emit = defineEmits<{
65
+ (e: 'update:modelValue', val: boolean): void
66
+ (e: 'open'): void
67
+ (e: 'close'): void
68
+ (e: 'opened'): void
69
+ (e: 'closed'): void
70
+ }>()
71
+
72
+ let timer: ReturnType<typeof setTimeout> | null = null
73
+
74
+ watch(
75
+ () => props.modelValue,
76
+ (val) => {
77
+ if (timer) clearTimeout(timer)
78
+ if (val) {
79
+ emit('open')
80
+ timer = setTimeout(() => emit('opened'), 300)
81
+ } else {
82
+ emit('close')
83
+ timer = setTimeout(() => emit('closed'), 300)
84
+ }
85
+ }
86
+ )
87
+
88
+ function onMaskClick() {
89
+ if (!props.maskClosable) return
90
+ emit('update:modelValue', false)
91
+ }
92
+ </script>
93
+
94
+ <style lang="scss" scoped>
95
+ @import '../../scss/variables';
96
+ @import '../../scss/mixins';
97
+
98
+ .dd-drawer__mask {
99
+ position: fixed;
100
+ inset: 0;
101
+ background: $dd-glass-bg;
102
+ backdrop-filter: blur(8px);
103
+ -webkit-backdrop-filter: blur(8px);
104
+ opacity: 0;
105
+ visibility: hidden;
106
+ z-index: 1000;
107
+ @include dd-transition(opacity 0.3s ease, visibility 0.3s ease);
108
+
109
+ &--show {
110
+ opacity: 1;
111
+ visibility: visible;
112
+ }
113
+ }
114
+
115
+ .dd-drawer__panel {
116
+ position: fixed;
117
+ top: 0;
118
+ bottom: 0;
119
+ display: flex;
120
+ flex-direction: column;
121
+ background: $dd-surface;
122
+ overflow: hidden;
123
+ z-index: 1001;
124
+ // ponytail: cubic-bezier 取 iOS 标准滑出曲线,遮罩淡入同期 0.3s,合上略快收尾
125
+ transition: transform 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
126
+ will-change: transform;
127
+ &--left {
128
+ left: 0;
129
+ transform: translateX(-100%);
130
+ }
131
+
132
+ &--right {
133
+ right: 0;
134
+ transform: translateX(100%);
135
+ }
136
+
137
+ &--show {
138
+ transform: translateX(0);
139
+ }
140
+ }
141
+
142
+ .dd-drawer__header {
143
+ flex-shrink: 0;
144
+
145
+ &--vip {
146
+ padding: 64rpx 32rpx 32rpx;
147
+ background: $dd-gradient-primary;
148
+ display: flex;
149
+ align-items: center;
150
+ gap: 24rpx;
151
+ }
152
+ }
153
+
154
+ .dd-drawer__avatar {
155
+ width: 112rpx;
156
+ height: 112rpx;
157
+ border-radius: 50%;
158
+ background: rgba(255, 255, 255, 0.2);
159
+ border: 2px solid rgba(255, 255, 255, 0.3);
160
+ overflow: hidden;
161
+ @include dd-flex-center;
162
+ flex-shrink: 0;
163
+ }
164
+
165
+ .dd-drawer__avatar-img {
166
+ width: 100%;
167
+ height: 100%;
168
+ }
169
+
170
+ .dd-drawer__avatar-fallback {
171
+ font-size: 40rpx;
172
+ color: $dd-neutral-900;
173
+ font-weight: 700;
174
+ }
175
+
176
+ .dd-drawer__user {
177
+ display: flex;
178
+ flex-direction: column;
179
+ gap: 8rpx;
180
+ min-width: 0;
181
+ }
182
+
183
+ .dd-drawer__user-row {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 12rpx;
187
+ }
188
+
189
+ .dd-drawer__username {
190
+ font-size: $dd-font-size-h3;
191
+ font-weight: 700;
192
+ color: $dd-neutral-900;
193
+ @include dd-ellipsis(1);
194
+ }
195
+
196
+ .dd-drawer__vip-badge {
197
+ background: $dd-surface;
198
+ color: $dd-primary-400;
199
+ font-size: 20rpx;
200
+ font-weight: 700;
201
+ padding: 2rpx 10rpx;
202
+ border-radius: $dd-radius-sm;
203
+ }
204
+
205
+ .dd-drawer__level {
206
+ font-size: $dd-font-size-caption;
207
+ color: rgba(10, 10, 10, 0.7);
208
+ }
209
+
210
+ .dd-drawer__content {
211
+ flex: 1;
212
+ height: 0;
213
+ }
214
+ </style>
@@ -0,0 +1,203 @@
1
+ <template>
2
+ <view class="dd-dropdown-item" :class="{ 'dd-dropdown-item--disabled': disabled }">
3
+ <view
4
+ class="dd-dropdown-item__trigger"
5
+ :class="{ 'dd-dropdown-item__trigger--open': isOpen }"
6
+ :style="{ color: isOpen ? activeColor : '' }"
7
+ @click="onTrigger"
8
+ >
9
+ <text class="dd-dropdown-item__title">{{ displayTitle }}</text>
10
+ <dd-icon
11
+ name="arrow-down"
12
+ class="dd-dropdown-item__arrow"
13
+ :class="{ 'dd-dropdown-item__arrow--open': isOpen }"
14
+ />
15
+ </view>
16
+ <view
17
+ v-if="ctx"
18
+ class="dd-dropdown-item__panel"
19
+ :class="[
20
+ `dd-dropdown-item__panel--${direction}`,
21
+ { 'dd-dropdown-item__panel--open': isOpen },
22
+ ]"
23
+ :style="panelStyle"
24
+ >
25
+ <scroll-view scroll-y class="dd-dropdown-item__scroll">
26
+ <view
27
+ v-for="(opt, i) in options"
28
+ :key="i"
29
+ class="dd-dropdown-item__option"
30
+ :class="{ 'dd-dropdown-item__option--active': opt.value === modelValue }"
31
+ :style="{ color: opt.value === modelValue ? activeColor : '' }"
32
+ @click="onSelect(opt)"
33
+ >
34
+ <image
35
+ v-if="opt.icon"
36
+ class="dd-dropdown-item__option-icon"
37
+ :src="opt.icon"
38
+ mode="aspectFit"
39
+ />
40
+ <text class="dd-dropdown-item__option-text">{{ opt.text }}</text>
41
+ </view>
42
+ </scroll-view>
43
+ </view>
44
+ </view>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ import { computed, inject, ref } from 'vue'
49
+ import DdIcon from '../dd-icon/dd-icon.vue'
50
+
51
+ interface DropdownOption {
52
+ text: string
53
+ value: any
54
+ icon?: string
55
+ }
56
+
57
+ interface Props {
58
+ title?: string
59
+ options?: DropdownOption[]
60
+ modelValue?: any
61
+ disabled?: boolean
62
+ }
63
+
64
+ const props = withDefaults(defineProps<Props>(), {
65
+ title: '',
66
+ options: () => [],
67
+ modelValue: null,
68
+ disabled: false,
69
+ })
70
+
71
+ const emit = defineEmits<{
72
+ (e: 'update:modelValue', val: any): void
73
+ (e: 'change', val: any): void
74
+ }>()
75
+
76
+ const ctx = inject<any>('ddDropdown', null)
77
+ const id = ref(ctx ? ctx.register() : -1)
78
+
79
+ const isOpen = computed(() => ctx && ctx.activeId.value === id.value)
80
+ const direction = computed(() => (ctx ? ctx.direction : 'down'))
81
+ const activeColor = computed(() => (ctx ? ctx.activeColor : '#F5A623'))
82
+
83
+ const displayTitle = computed(() => {
84
+ if (props.title) return props.title
85
+ const sel = props.options.find((o) => o.value === props.modelValue)
86
+ return sel ? sel.text : ''
87
+ })
88
+
89
+ const panelStyle = computed(() => {
90
+ if (!ctx) return {}
91
+ const winH = uni.getWindowInfo?.().windowHeight || 667
92
+ if (direction.value === 'down') {
93
+ return { top: ctx.barRect.bottom + 'px', left: '0', right: '0' }
94
+ }
95
+ return { bottom: winH - ctx.barRect.top + 'px', left: '0', right: '0' }
96
+ })
97
+
98
+ function onTrigger() {
99
+ if (props.disabled || !ctx) return
100
+ ctx.toggle(id.value)
101
+ }
102
+
103
+ function onSelect(opt: DropdownOption) {
104
+ emit('update:modelValue', opt.value)
105
+ emit('change', opt.value)
106
+ ctx?.close()
107
+ }
108
+ </script>
109
+
110
+ <style lang="scss" scoped>
111
+ @import '../../scss/variables';
112
+ @import '../../scss/mixins';
113
+
114
+ .dd-dropdown-item {
115
+ flex: 1;
116
+ min-width: 0;
117
+
118
+ &--disabled {
119
+ opacity: 0.5;
120
+ @include dd-no-touch;
121
+ }
122
+
123
+ &__trigger {
124
+ @include dd-flex-center;
125
+ gap: 8rpx;
126
+ height: 96rpx;
127
+ padding: 0 16rpx;
128
+ color: $dd-text-primary;
129
+ font-size: $dd-font-size-body;
130
+ @include dd-transition(color 0.3s);
131
+ }
132
+
133
+ &__title {
134
+ @include dd-ellipsis(1);
135
+ }
136
+
137
+ &__arrow {
138
+ font-size: 24rpx;
139
+ line-height: 1;
140
+ @include dd-transition(transform 0.3s);
141
+
142
+ &--open {
143
+ transform: rotate(180deg);
144
+ }
145
+ }
146
+
147
+ &__panel {
148
+ position: fixed;
149
+ z-index: 10;
150
+ background: $dd-surface;
151
+ max-height: 50vh;
152
+ overflow: hidden;
153
+ opacity: 0;
154
+ visibility: hidden;
155
+ pointer-events: none;
156
+ @include dd-transition(transform 0.3s ease, opacity 0.3s ease, visibility 0.3s);
157
+
158
+ &--down {
159
+ transform: translateY(-100%);
160
+ }
161
+
162
+ &--up {
163
+ transform: translateY(100%);
164
+ }
165
+
166
+ &--open {
167
+ transform: translateY(0);
168
+ opacity: 1;
169
+ visibility: visible;
170
+ z-index: 11;
171
+ pointer-events: auto;
172
+ }
173
+ }
174
+
175
+ &__scroll {
176
+ max-height: 50vh;
177
+ }
178
+
179
+ &__option {
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 16rpx;
183
+ height: 80rpx;
184
+ padding: 0 32rpx;
185
+ font-size: $dd-font-size-body;
186
+ color: $dd-text-primary;
187
+ @include dd-transition(color 0.2s);
188
+
189
+ &--active {
190
+ font-weight: 600;
191
+ }
192
+ }
193
+
194
+ &__option-icon {
195
+ width: 40rpx;
196
+ height: 40rpx;
197
+ }
198
+
199
+ &__option-text {
200
+ @include dd-ellipsis(1);
201
+ }
202
+ }
203
+ </style>
@@ -0,0 +1,177 @@
1
+ <template>
2
+ <view class="dd-dropdown" :class="{ 'dd-dropdown--active': activeId !== null }" ref="rootRef">
3
+ <view class="dd-dropdown__bar">
4
+ <slot></slot>
5
+ </view>
6
+ <view
7
+ v-if="overlay"
8
+ class="dd-dropdown__overlay"
9
+ :class="{ 'dd-dropdown__overlay--show': activeId !== null }"
10
+ :style="overlayStyle"
11
+ @click="onOverlayClick"
12
+ ></view>
13
+ </view>
14
+ </template>
15
+
16
+ <script lang="ts">
17
+ // 整页级别互斥:同时只允许一个 dd-dropdown-menu 展开下拉
18
+ // ponytail: 模块级单例变量,跨实例共享;页面切换时组件 onUnmounted 会清空,无泄漏
19
+ let activeMenuClose: (() => void) | null = null
20
+ </script>
21
+
22
+ <script setup lang="ts">
23
+ import { getCurrentInstance, onMounted, onUnmounted, provide, reactive, ref } from 'vue'
24
+
25
+ interface Props {
26
+ activeColor?: string
27
+ direction?: 'down' | 'up'
28
+ overlay?: boolean
29
+ closeOnClickOverlay?: boolean
30
+ closeOnClickOutside?: boolean
31
+ }
32
+
33
+ const props = withDefaults(defineProps<Props>(), {
34
+ activeColor: '#F5A623',
35
+ direction: 'down',
36
+ overlay: true,
37
+ closeOnClickOverlay: true,
38
+ closeOnClickOutside: true,
39
+ })
40
+
41
+ const emit = defineEmits<{ (e: 'change', id: number): void }>()
42
+
43
+ const activeId = ref<number | null>(null)
44
+ const counter = ref(0)
45
+ const barRect = reactive({ top: 0, bottom: 0, height: 0 })
46
+ const overlayStyle = ref({})
47
+ const rootRef = ref<any>(null)
48
+ const instance = getCurrentInstance()
49
+
50
+ function register(): number {
51
+ const id = counter.value
52
+ counter.value += 1
53
+ return id
54
+ }
55
+
56
+ function measureBar() {
57
+ const query = uni.createSelectorQuery().in(instance)
58
+ query
59
+ .select('.dd-dropdown__bar')
60
+ .boundingClientRect((rect) => {
61
+ const r = rect as UniApp.NodeInfo
62
+ if (!r) return
63
+ barRect.top = r.top || 0
64
+ barRect.bottom = r.bottom || 0
65
+ barRect.height = r.height || 0
66
+ const winH = uni.getWindowInfo?.().windowHeight || 667
67
+ if (props.direction === 'down') {
68
+ overlayStyle.value = {
69
+ top: barRect.bottom + 'px',
70
+ bottom: '0',
71
+ }
72
+ } else {
73
+ overlayStyle.value = {
74
+ top: '0',
75
+ bottom: winH - barRect.top + 'px',
76
+ }
77
+ }
78
+ })
79
+ .exec()
80
+ }
81
+
82
+ function toggle(id: number) {
83
+ if (activeId.value === id) {
84
+ close()
85
+ } else {
86
+ if (activeMenuClose && activeMenuClose !== close) activeMenuClose()
87
+ activeId.value = id
88
+ activeMenuClose = close
89
+ measureBar()
90
+ }
91
+ }
92
+
93
+ function close() {
94
+ activeId.value = null
95
+ if (activeMenuClose === close) activeMenuClose = null
96
+ }
97
+
98
+ function onOverlayClick() {
99
+ if (props.closeOnClickOverlay) close()
100
+ }
101
+
102
+ provide('ddDropdown', {
103
+ activeId,
104
+ direction: props.direction,
105
+ activeColor: props.activeColor,
106
+ barRect,
107
+ register,
108
+ toggle,
109
+ close,
110
+ })
111
+
112
+ onMounted(measureBar)
113
+ onUnmounted(() => {
114
+ if (activeMenuClose === close) activeMenuClose = null
115
+ })
116
+
117
+ // H5 outside click
118
+ // #ifdef H5
119
+ function onDocClick(e: MouseEvent) {
120
+ if (activeId.value === null) return
121
+ if (!props.closeOnClickOutside) return
122
+ // trigger / option 自有 click 处理,内部点击不在此关闭
123
+ if (rootRef.value?.contains(e.target as Node)) return
124
+ close()
125
+ }
126
+ onMounted(() => {
127
+ document.addEventListener('click', onDocClick)
128
+ })
129
+ onUnmounted(() => {
130
+ document.removeEventListener('click', onDocClick)
131
+ })
132
+ // #endif
133
+ </script>
134
+
135
+ <style lang="scss" scoped>
136
+ @import '../../scss/variables';
137
+ @import '../../scss/mixins';
138
+
139
+ .dd-dropdown {
140
+ position: relative;
141
+ width: 100%;
142
+
143
+ // 激活时整体抬高层级,避免被同页其他菜单的 bar 盖住 panel
144
+ &--active {
145
+ z-index: 100;
146
+ }
147
+ }
148
+
149
+ .dd-dropdown__bar {
150
+ position: relative;
151
+ z-index: 11;
152
+ display: flex;
153
+ flex-direction: row;
154
+ align-items: center;
155
+ height: 96rpx;
156
+ background: $dd-surface;
157
+ @include dd-hairline-bottom($dd-border-subtle);
158
+ }
159
+
160
+ .dd-dropdown__overlay {
161
+ position: fixed;
162
+ left: 0;
163
+ right: 0;
164
+ background: $dd-glass-bg;
165
+ backdrop-filter: blur(8px);
166
+ -webkit-backdrop-filter: blur(8px);
167
+ z-index: 9;
168
+ opacity: 0;
169
+ visibility: hidden;
170
+ @include dd-transition(opacity 0.3s, visibility 0.3s);
171
+
172
+ &--show {
173
+ opacity: 1;
174
+ visibility: visible;
175
+ }
176
+ }
177
+ </style>