@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,145 @@
1
+ <template>
2
+ <view
3
+ class="dd-checkbox"
4
+ :class="[
5
+ `dd-checkbox--${size}`,
6
+ {
7
+ 'dd-checkbox--checked': isChecked,
8
+ 'dd-checkbox--indeterminate': indeterminate,
9
+ 'dd-checkbox--disabled': disabled,
10
+ },
11
+ ]"
12
+ @click="onToggle"
13
+ >
14
+ <view class="dd-checkbox__box">
15
+ <view v-if="indeterminate" class="dd-checkbox__dash"></view>
16
+ <dd-icon v-else-if="isChecked" name="success" class="dd-checkbox__check" />
17
+ </view>
18
+ <text v-if="label || $slots.default" class="dd-checkbox__label">
19
+ <slot>{{ label }}</slot>
20
+ </text>
21
+ </view>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import { computed } from 'vue'
26
+ import DdIcon from '../dd-icon/dd-icon.vue'
27
+
28
+ interface Props {
29
+ modelValue?: boolean | any[]
30
+ value?: any
31
+ indeterminate?: boolean
32
+ disabled?: boolean
33
+ size?: 'md' | 'sm'
34
+ label?: string
35
+ }
36
+
37
+ const props = withDefaults(defineProps<Props>(), {
38
+ modelValue: false,
39
+ value: null,
40
+ indeterminate: false,
41
+ disabled: false,
42
+ size: 'md',
43
+ label: '',
44
+ })
45
+
46
+ const emit = defineEmits<{
47
+ (e: 'update:modelValue', val: boolean | any[]): void
48
+ (e: 'change', val: boolean | any[]): void
49
+ }>()
50
+
51
+ const isChecked = computed(() => {
52
+ if (Array.isArray(props.modelValue)) return props.modelValue.includes(props.value)
53
+ return !!props.modelValue
54
+ })
55
+
56
+ function onToggle() {
57
+ if (props.disabled) return
58
+ if (Array.isArray(props.modelValue)) {
59
+ const arr = props.modelValue.slice()
60
+ const idx = arr.indexOf(props.value)
61
+ if (idx >= 0) arr.splice(idx, 1)
62
+ else arr.push(props.value)
63
+ emit('update:modelValue', arr)
64
+ emit('change', arr)
65
+ } else {
66
+ const next = !props.modelValue
67
+ emit('update:modelValue', next)
68
+ emit('change', next)
69
+ }
70
+ }
71
+ </script>
72
+
73
+ <style lang="scss" scoped>
74
+ @import '../../scss/variables';
75
+ @import '../../scss/mixins';
76
+
77
+ .dd-checkbox {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ gap: 16rpx;
81
+ user-select: none;
82
+
83
+ &__box {
84
+ @include dd-flex-center;
85
+ flex-shrink: 0;
86
+ border-radius: $dd-radius-sm;
87
+ border: 3rpx solid $dd-neutral-700;
88
+ background: transparent;
89
+ box-sizing: border-box;
90
+ @include dd-transition(all 0.2s);
91
+ }
92
+
93
+ &__check {
94
+ color: #ffffff;
95
+ font-size: 24rpx;
96
+ font-weight: 700;
97
+ line-height: 1;
98
+ }
99
+
100
+ &__dash {
101
+ width: 60%;
102
+ height: 4rpx;
103
+ background: $dd-primary-500;
104
+ border-radius: $dd-radius-full;
105
+ }
106
+
107
+ &__label {
108
+ font-size: 28rpx;
109
+ color: $dd-text-primary;
110
+ }
111
+
112
+ &--checked .dd-checkbox__box {
113
+ background: $dd-gradient-primary;
114
+ border-color: $dd-primary-500;
115
+ box-shadow: $dd-shadow-glow-gold-lg;
116
+ }
117
+
118
+ &--indeterminate .dd-checkbox__box {
119
+ background: rgba($dd-primary-500, 0.1);
120
+ border-color: $dd-primary-500;
121
+ }
122
+
123
+ &--disabled {
124
+ opacity: 0.4;
125
+ @include dd-no-touch;
126
+ }
127
+ }
128
+
129
+ .dd-checkbox--md .dd-checkbox__box {
130
+ width: 40rpx;
131
+ height: 40rpx;
132
+ }
133
+ .dd-checkbox--sm {
134
+ .dd-checkbox__box {
135
+ width: 32rpx;
136
+ height: 32rpx;
137
+ }
138
+ .dd-checkbox__check {
139
+ font-size: 20rpx;
140
+ }
141
+ .dd-checkbox__label {
142
+ font-size: 28rpx;
143
+ }
144
+ }
145
+ </style>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <view class="dd-collapse">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import { computed, provide } from 'vue'
9
+
10
+ type Val = string | number
11
+ type Model = Val | Val[]
12
+
13
+ interface Props {
14
+ modelValue?: Model
15
+ accordion?: boolean
16
+ }
17
+
18
+ const props = withDefaults(defineProps<Props>(), {
19
+ modelValue: () => [],
20
+ accordion: false,
21
+ })
22
+
23
+ const emit = defineEmits<{
24
+ (e: 'update:modelValue', val: Model): void
25
+ (e: 'change', val: Model): void
26
+ }>()
27
+
28
+ const normalized = computed<Val[]>(() => {
29
+ const v = props.modelValue
30
+ if (v === '' || v === null || v === undefined) return []
31
+ return Array.isArray(v) ? [...v] : [v as Val]
32
+ })
33
+
34
+ function isExpanded(name: Val): boolean {
35
+ return normalized.value.includes(name)
36
+ }
37
+
38
+ function toggle(name: Val) {
39
+ if (props.accordion) {
40
+ const next: Model = isExpanded(name) ? '' : name
41
+ emit('update:modelValue', next)
42
+ emit('change', next)
43
+ } else {
44
+ const set = new Set(normalized.value)
45
+ if (set.has(name)) set.delete(name)
46
+ else set.add(name)
47
+ const next = Array.from(set)
48
+ emit('update:modelValue', next)
49
+ emit('change', next)
50
+ }
51
+ }
52
+
53
+ provide('ddCollapse', { isExpanded, toggle })
54
+ </script>
55
+
56
+ <style lang="scss" scoped>
57
+ @import '../../scss/variables';
58
+ @import '../../scss/mixins';
59
+
60
+ .dd-collapse {
61
+ background: $dd-surface;
62
+ }
63
+ </style>
@@ -0,0 +1,154 @@
1
+ <template>
2
+ <view
3
+ class="dd-collapse-item"
4
+ :class="{
5
+ 'dd-collapse-item--expanded': expanded,
6
+ 'dd-collapse-item--disabled': disabled,
7
+ 'dd-collapse-item--border': border,
8
+ }"
9
+ >
10
+ <view class="dd-collapse-item__header" @click="onToggle">
11
+ <text class="dd-collapse-item__title">
12
+ <slot name="title">{{ title }}</slot>
13
+ </text>
14
+ <text v-if="value" class="dd-collapse-item__value">{{ value }}</text>
15
+ <dd-icon name="arrow-down" class="dd-collapse-item__arrow" />
16
+ </view>
17
+ <view class="dd-collapse-item__wrap" :style="wrapStyle">
18
+ <view class="dd-collapse-item__content">
19
+ <slot></slot>
20
+ </view>
21
+ </view>
22
+ </view>
23
+ </template>
24
+
25
+ <script setup lang="ts">
26
+ import { computed, getCurrentInstance, inject, nextTick, onMounted, ref, watch } from 'vue'
27
+ import DdIcon from '../dd-icon/dd-icon.vue'
28
+
29
+ interface Props {
30
+ name?: string | number
31
+ title?: string
32
+ value?: string
33
+ disabled?: boolean
34
+ border?: boolean
35
+ }
36
+
37
+ const props = withDefaults(defineProps<Props>(), {
38
+ name: '',
39
+ title: '',
40
+ value: '',
41
+ disabled: false,
42
+ border: true,
43
+ })
44
+
45
+ const ctx = inject<any>('ddCollapse', null)
46
+
47
+ const localActive = ref(false)
48
+ const identity = computed(() => (props.name !== '' ? props.name : '__dd_ci_self__'))
49
+
50
+ const expanded = computed(() => {
51
+ if (ctx) return ctx.isExpanded(identity.value)
52
+ return localActive.value
53
+ })
54
+
55
+ const contentHeight = ref(0)
56
+ const wrapStyle = computed(() => ({
57
+ maxHeight: expanded.value && contentHeight.value ? contentHeight.value + 'px' : '0px',
58
+ opacity: expanded.value ? '1' : '0',
59
+ }))
60
+
61
+ const instance = getCurrentInstance()
62
+
63
+ function measure() {
64
+ nextTick(() => {
65
+ const query = uni.createSelectorQuery().in(instance)
66
+ query
67
+ .select('.dd-collapse-item__content')
68
+ .boundingClientRect((rect) => {
69
+ const r = rect as UniApp.NodeInfo
70
+ if (r && r.height) contentHeight.value = r.height
71
+ })
72
+ .exec()
73
+ })
74
+ }
75
+
76
+ function onToggle() {
77
+ if (props.disabled) return
78
+ if (ctx) {
79
+ ctx.toggle(identity.value)
80
+ } else {
81
+ localActive.value = !localActive.value
82
+ }
83
+ }
84
+
85
+ watch(
86
+ () => expanded.value,
87
+ (val) => {
88
+ if (val) measure()
89
+ }
90
+ )
91
+
92
+ onMounted(() => {
93
+ if (expanded.value) measure()
94
+ })
95
+ </script>
96
+
97
+ <style lang="scss" scoped>
98
+ @import '../../scss/variables';
99
+ @import '../../scss/mixins';
100
+
101
+ .dd-collapse-item {
102
+ background: $dd-surface;
103
+
104
+ &--border {
105
+ @include dd-hairline-bottom($dd-border-subtle);
106
+ }
107
+
108
+ &--disabled {
109
+ opacity: 0.5;
110
+ @include dd-no-touch;
111
+ }
112
+
113
+ &__header {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: $dd-space-2;
117
+ padding: 24rpx 32rpx;
118
+ }
119
+
120
+ &__title {
121
+ flex: 1;
122
+ font-size: $dd-font-size-lead;
123
+ color: $dd-text-primary;
124
+ @include dd-ellipsis(1);
125
+ }
126
+
127
+ &__value {
128
+ font-size: $dd-font-size-body;
129
+ color: $dd-text-tertiary;
130
+ @include dd-ellipsis(1);
131
+ }
132
+
133
+ &__arrow {
134
+ font-size: 28rpx;
135
+ color: $dd-text-tertiary;
136
+ @include dd-transition(transform 0.3s);
137
+ }
138
+
139
+ &--expanded &__arrow {
140
+ transform: rotate(180deg);
141
+ }
142
+
143
+ &__wrap {
144
+ overflow: hidden;
145
+ @include dd-transition(max-height 0.3s ease, opacity 0.3s ease);
146
+ }
147
+
148
+ &__content {
149
+ padding: 0 32rpx 24rpx;
150
+ font-size: $dd-font-size-body;
151
+ color: $dd-text-secondary;
152
+ }
153
+ }
154
+ </style>
@@ -0,0 +1,163 @@
1
+ <template>
2
+ <view class="dd-count-down">
3
+ <slot
4
+ :days="timeData.days"
5
+ :hours="timeData.hours"
6
+ :minutes="timeData.minutes"
7
+ :seconds="timeData.seconds"
8
+ :milliseconds="timeData.milliseconds"
9
+ :total="timeData.total"
10
+ :formatted="formattedText"
11
+ >
12
+ <text class="dd-count-down__text">{{ formattedText }}</text>
13
+ </slot>
14
+ </view>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
19
+
20
+ interface Props {
21
+ /** 倒计时总时长 (ms) */
22
+ time?: number
23
+ /** 格式: HH:mm:ss | DD天 HH:mm:ss | mm:ss | HH:mm:ss:SSS */
24
+ format?: string
25
+ /** 是否自动开始 */
26
+ autoStart?: boolean
27
+ /** 毫秒级精度 (30ms 节流渲染) */
28
+ millisecond?: boolean
29
+ }
30
+
31
+ interface TimeData {
32
+ days: number
33
+ hours: number
34
+ minutes: number
35
+ seconds: number
36
+ milliseconds: number
37
+ total: number
38
+ }
39
+
40
+ const props = withDefaults(defineProps<Props>(), {
41
+ time: 0,
42
+ format: 'HH:mm:ss',
43
+ autoStart: true,
44
+ millisecond: false,
45
+ })
46
+
47
+ const emit = defineEmits<{
48
+ (e: 'change', data: TimeData): void
49
+ (e: 'finish'): void
50
+ (e: 'start'): void
51
+ (e: 'stop'): void
52
+ }>()
53
+
54
+ const remain = ref(props.time)
55
+ // ponytail: 用 endTime + Date.now() 差值而非递减计数,避免 setInterval 漂移累积;ms 模式渲染节流到 30ms
56
+ let endTime = 0
57
+ let timer: ReturnType<typeof setInterval> | null = null
58
+ let finished = false
59
+
60
+ function parseTime(total: number): TimeData {
61
+ const t = Math.max(0, total)
62
+ return {
63
+ days: Math.floor(t / 86400000),
64
+ hours: Math.floor((t % 86400000) / 3600000),
65
+ minutes: Math.floor((t % 3600000) / 60000),
66
+ seconds: Math.floor((t % 60000) / 1000),
67
+ milliseconds: t % 1000,
68
+ total: t,
69
+ }
70
+ }
71
+
72
+ const timeData = computed<TimeData>(() => parseTime(remain.value))
73
+
74
+ function pad(n: number, len = 2): string {
75
+ return String(n).padStart(len, '0')
76
+ }
77
+
78
+ const formattedText = computed(() => {
79
+ const d = timeData.value
80
+ return props.format
81
+ .replace('SSS', pad(d.milliseconds, 3))
82
+ .replace('DD', pad(d.days))
83
+ .replace('HH', pad(d.hours))
84
+ .replace('mm', pad(d.minutes))
85
+ .replace('ss', pad(d.seconds))
86
+ })
87
+
88
+ function tick() {
89
+ remain.value = Math.max(0, endTime - Date.now())
90
+ emit('change', timeData.value)
91
+ if (remain.value <= 0) {
92
+ if (!finished) {
93
+ finished = true
94
+ stop()
95
+ emit('finish')
96
+ }
97
+ }
98
+ }
99
+
100
+ function start() {
101
+ if (timer) return
102
+ if (remain.value <= 0) remain.value = props.time
103
+ endTime = Date.now() + remain.value
104
+ finished = false
105
+ emit('start')
106
+ tick()
107
+ const interval = props.millisecond ? 30 : 1000
108
+ timer = setInterval(tick, interval)
109
+ }
110
+
111
+ function stop() {
112
+ if (timer) {
113
+ clearInterval(timer)
114
+ timer = null
115
+ emit('stop')
116
+ }
117
+ }
118
+
119
+ function reset() {
120
+ stop()
121
+ remain.value = props.time
122
+ finished = false
123
+ }
124
+
125
+ defineExpose({ start, stop, reset })
126
+
127
+ watch(
128
+ () => props.time,
129
+ (val) => {
130
+ remain.value = val
131
+ finished = false
132
+ if (timer) {
133
+ endTime = Date.now() + val
134
+ }
135
+ }
136
+ )
137
+
138
+ onMounted(() => {
139
+ if (props.autoStart && props.time > 0) start()
140
+ })
141
+
142
+ onUnmounted(() => {
143
+ stop()
144
+ })
145
+ </script>
146
+
147
+ <style lang="scss" scoped>
148
+ @import '../../scss/variables';
149
+ @import '../../scss/mixins';
150
+
151
+ .dd-count-down {
152
+ display: inline-flex;
153
+ align-items: center;
154
+ }
155
+
156
+ .dd-count-down__text {
157
+ font-size: $dd-font-size-body;
158
+ color: $dd-text-primary;
159
+ font-variant-numeric: tabular-nums;
160
+ font-weight: $dd-font-weight-body;
161
+ line-height: 1;
162
+ }
163
+ </style>