@didaoktv/didaoui-uniapp 1.3.2 → 1.3.3

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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @didaoktv/didaoui-uniapp
1
+ # @didaoktv/didaoui-uniapp
2
2
 
3
3
  专为 KTV 场景打造的 UniApp 组件库。
4
4
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  - **帝王级配色** — 帝王金 `#F5A623` + 皇家蓝 `#2D4BA0` 双主色,暗色主题默认
8
8
  - **玻璃拟态设计** — 多层级玻璃拟态效果,打造高端夜店沉浸感
9
- - **67 个精心打磨的组件** — 覆盖表单、导航、布局卡片、数据展示、浮层反馈、交互六大类,含 KTV 业务组件(房态卡、账单明细、工单卡、日历、二维码)
9
+ - **89 个精心打磨的组件** — 覆盖表单、导航、布局卡片、数据展示、浮层反馈、交互、画布七大类,含 KTV 业务组件(房态卡、账单明细、工单卡、日历、二维码)
10
10
  - **Vue3 + TypeScript** — 完整类型定义,Composition API
11
11
  - **多端兼容** — H5 / 微信小程序 / 抖音小程序 / Android / iOS
12
12
 
@@ -50,13 +50,14 @@ import { DdButton, DdInput } from '@didaoktv/didaoui-uniapp'
50
50
 
51
51
  | 分类 | 数量 | 组件 |
52
52
  | ----- | -- | --------------------------------------------------------------------------------------------------------------------------------------------- |
53
- | 表单输入 | 17 | Button, Input, Form, Field, Switch, Checkbox, CheckboxGroup, Radio, RadioGroup, Stepper, SearchBar, Slider, DatePicker, Picker, Rate, Upload, Icon |
53
+ | 表单输入 | 23 | Button, CodeButton, Input, Form, Field, Switch, Checkbox, CheckboxGroup, Radio, RadioGroup, Stepper, SearchBar, Slider, DatePicker, Picker, Rate, Upload, Icon, Keyboard, NumberKeyboard, CarKeyboard, Cascader, GoodsSku |
54
54
  | 导航 | 12 | Navigation, TopNavbar, SwipeableTab, SegmentedTab, Drawer, Tabbar, TabbarItem, Backtop, Collapse, CollapseItem, DropdownMenu, DropdownItem |
55
55
  | 布局/卡片 | 8 | Card, RoomCard, FeatureGrid, Sticky, BillDetail, WorkorderCard, Calendar, Qrcode |
56
- | 数据展示 | 16 | Tag, StatCard, ChampionCard, Avatar, Badge, ListCell, Cell, CellGroup, CountDown, Divider, Image, Progress, Skeleton, EmptyState, Step, Steps |
56
+ | 数据展示 | 24 | Tag, StatCard, ChampionCard, Avatar, AvatarGroup, Badge, ListCell, Cell, CellGroup, CountDown, Divider, Image, Progress, Skeleton, EmptyState, Step, Steps, Text, Link, Copy, Album, Coupon, Parse, Markdown |
57
57
  | 浮层反馈 | 10 | Modal, ActionSheet, Toast, Alert, Loading, Dialog, Overlay, Popup, Popover, PopoverItem |
58
58
  | 交互 | 5 | SwipeAction, Swipe, SwipeItem, PullRefresh, Loadmore |
59
- | 小程序专属 | 2 | CapsuleButton, MiniProgramNavbar |
59
+ | 小程序专属 | 2 | CapsuleButton, MiniProgramNavbar |
60
+ | 画布 | 5 | Canvas, Barcode, Cropper, Signature, Poster |
60
61
 
61
62
  ## 设计规范
62
63
 
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <!-- class/style 经单根节点 fallthrough 合并到 dd-button 根元素 -->
3
+ <dd-button
4
+ :type="type"
5
+ :size="size"
6
+ :disabled="!canGetCode"
7
+ :class="customClass"
8
+ :style="customStyle"
9
+ @click="handleClick"
10
+ >
11
+ {{ showText }}
12
+ </dd-button>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { computed, onMounted, onUnmounted, ref } from 'vue'
17
+ import DdButton from '../dd-button/dd-button.vue'
18
+
19
+ interface Props {
20
+ /** 倒计时秒数 */
21
+ time?: number | string
22
+ /** 初始文本 */
23
+ startText?: string
24
+ /** 倒计时文本,X/x 占位剩余秒数 */
25
+ changeText?: string
26
+ /** 倒计时结束文本 */
27
+ endText?: string
28
+ /** H5刷新/各端离开再进入时是否继续倒计时 */
29
+ keepRunning?: boolean
30
+ /** 多个组件间的区分 key */
31
+ uniqueKey?: string
32
+ type?: 'primary' | 'secondary' | 'ghost' | 'text' | 'success' | 'warning' | 'danger'
33
+ size?: 'sm' | 'md' | 'lg'
34
+ customClass?: string
35
+ customStyle?: Record<string, string>
36
+ }
37
+
38
+ const props = withDefaults(defineProps<Props>(), {
39
+ time: 60,
40
+ startText: '获取验证码',
41
+ changeText: 'X秒重新获取',
42
+ endText: '重新获取',
43
+ keepRunning: false,
44
+ uniqueKey: 'dd-code-button',
45
+ type: 'primary',
46
+ size: 'sm',
47
+ customClass: '',
48
+ customStyle: () => ({}),
49
+ })
50
+
51
+ const emit = defineEmits<{
52
+ (e: 'click'): void
53
+ (e: 'start'): void
54
+ (e: 'change', seconds: number): void
55
+ (e: 'finish'): void
56
+ }>()
57
+
58
+ const totalSeconds = computed(() => Math.max(1, Math.floor(Number(props.time) || 60)))
59
+
60
+ const storageKey = computed(() => `dd-code-btn:${props.uniqueKey}`)
61
+
62
+ const remainMs = ref(totalSeconds.value * 1000)
63
+ // 是否点过(用于区分初始文本与结束文本)
64
+ const isClick = ref(false)
65
+ const isRunning = ref(false)
66
+ const canGetCode = ref(true)
67
+
68
+ // ponytail: endTime + Date.now() 差值计时而非递减计数,避免 setInterval 漂移;小程序退后台定时器冻结,回前台下一个 tick 自动对齐
69
+ let endTime = 0
70
+ let timer: ReturnType<typeof setInterval> | null = null
71
+
72
+ const showText = computed(() => {
73
+ if (!isClick.value) return props.startText
74
+ if (!isRunning.value) return props.endText
75
+ const seconds = Math.ceil(remainMs.value / 1000)
76
+ return props.changeText.replace(/X|x/g, String(seconds))
77
+ })
78
+
79
+ function tick() {
80
+ remainMs.value = Math.max(0, endTime - Date.now())
81
+ if (remainMs.value <= 0) {
82
+ finish()
83
+ return
84
+ }
85
+ emit('change', Math.ceil(remainMs.value / 1000))
86
+ }
87
+
88
+ function finish() {
89
+ stopTimer()
90
+ isRunning.value = false
91
+ canGetCode.value = true
92
+ remainMs.value = totalSeconds.value * 1000
93
+ if (props.keepRunning) uni.removeStorageSync(storageKey.value)
94
+ emit('finish')
95
+ }
96
+
97
+ function stopTimer() {
98
+ if (timer) {
99
+ clearInterval(timer)
100
+ timer = null
101
+ }
102
+ }
103
+
104
+ /** 开始倒计时(外部发验证码成功后调用),可传入剩余毫秒用于恢复 */
105
+ function start(remainOverride?: number) {
106
+ stopTimer()
107
+ if (!canGetCode.value && isClick.value) return
108
+ emit('start')
109
+ canGetCode.value = false
110
+ isClick.value = true
111
+ isRunning.value = true
112
+ remainMs.value = remainOverride ?? totalSeconds.value * 1000
113
+ endTime = Date.now() + remainMs.value
114
+ // 开始即落盘结束时间戳:H5 刷新/进程被杀时 onUnmounted 不可靠,写一次比每秒写便宜
115
+ if (props.keepRunning) {
116
+ uni.setStorageSync(storageKey.value, Math.floor(endTime / 1000))
117
+ }
118
+ tick()
119
+ timer = setInterval(tick, 1000)
120
+ }
121
+
122
+ function reset() {
123
+ stopTimer()
124
+ canGetCode.value = true
125
+ isRunning.value = false
126
+ remainMs.value = totalSeconds.value * 1000
127
+ if (props.keepRunning) uni.removeStorageSync(storageKey.value)
128
+ }
129
+
130
+ function handleClick() {
131
+ if (!canGetCode.value) return
132
+ emit('click')
133
+ }
134
+
135
+ /** keepRunning: 挂载时检查本地记录的结束时间戳,未到期则续跑 */
136
+ function resume() {
137
+ if (!props.keepRunning) return
138
+ const endTs = Number(uni.getStorageSync(storageKey.value))
139
+ if (!endTs) return
140
+ uni.removeStorageSync(storageKey.value)
141
+ const remain = (endTs - Math.floor(Date.now() / 1000)) * 1000
142
+ if (remain > 0) start(remain)
143
+ }
144
+
145
+ onMounted(resume)
146
+
147
+ onUnmounted(stopTimer)
148
+
149
+ defineExpose({ canGetCode, start, reset })
150
+ </script>
@@ -69,11 +69,8 @@ const emit = defineEmits<{
69
69
  const isFocused = ref(false)
70
70
  const visible = ref(false)
71
71
 
72
- // ponytail: search/number 在小程序降级处理;password 用 uni 原生 password 布尔属性跨端
73
- const inputType = computed<'text' | 'number'>(() => {
74
- if (props.type === 'password' || props.type === 'search') return 'text'
75
- return 'number'
76
- })
72
+ // ponytail: search 在小程序降级为 text;password 用 uni 原生 password 布尔属性跨端
73
+ const inputType = computed<'text' | 'number'>(() => (props.type === 'number' ? 'number' : 'text'))
77
74
  const isPassword = computed(() => props.type === 'password' && !visible.value)
78
75
  const confirmType = computed(() => (props.type === 'search' ? 'search' : 'done'))
79
76
  const showClear = computed(() => props.clearable && !!props.modelValue && !props.disabled)
package/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { App } from 'vue'
2
2
 
3
- // === Input (21) ===
3
+ // === Input (23) ===
4
4
  import DdButton from './components/dd-button/dd-button.vue'
5
+ import DdCodeButton from './components/dd-code-button/dd-code-button.vue'
5
6
  import DdInput from './components/dd-input/dd-input.vue'
6
7
  import DdSwitch from './components/dd-switch/dd-switch.vue'
7
8
  import DdCheckbox from './components/dd-checkbox/dd-checkbox.vue'
@@ -38,7 +39,7 @@ import DdCollapseItem from './components/dd-collapse-item/dd-collapse-item.vue'
38
39
  import DdDropdownMenu from './components/dd-dropdown-menu/dd-dropdown-menu.vue'
39
40
  import DdDropdownItem from './components/dd-dropdown-item/dd-dropdown-item.vue'
40
41
 
41
- // === Layout (4) ===
42
+ // === Layout (8) ===
42
43
  import DdCard from './components/dd-card/dd-card.vue'
43
44
  import DdRoomCard from './components/dd-room-card/dd-room-card.vue'
44
45
  import DdFeatureGrid from './components/dd-feature-grid/dd-feature-grid.vue'
@@ -105,7 +106,7 @@ import DdSignature from './components/dd-signature/dd-signature.vue'
105
106
  import DdPoster from './components/dd-poster/dd-poster.vue'
106
107
 
107
108
  const components = {
108
- DdButton, DdInput, DdSwitch, DdCheckbox, DdCheckboxGroup, DdRadio, DdRadioGroup, DdForm,
109
+ DdButton, DdCodeButton, DdInput, DdSwitch, DdCheckbox, DdCheckboxGroup, DdRadio, DdRadioGroup, DdForm,
109
110
  DdSearchBar, DdSlider, DdStepper, DdDatePicker, DdField,
110
111
  DdPicker, DdRate, DdUpload, DdIcon,
111
112
  DdKeyboard, DdNumberKeyboard, DdCarKeyboard, DdCascader, DdGoodsSku,
@@ -135,7 +136,7 @@ export default { install }
135
136
  // 全量具名导出
136
137
  export {
137
138
  components,
138
- DdButton, DdInput, DdSwitch, DdCheckbox, DdCheckboxGroup, DdRadio, DdRadioGroup, DdForm,
139
+ DdButton, DdCodeButton, DdInput, DdSwitch, DdCheckbox, DdCheckboxGroup, DdRadio, DdRadioGroup, DdForm,
139
140
  DdSearchBar, DdSlider, DdStepper, DdDatePicker, DdField,
140
141
  DdPicker, DdRate, DdUpload, DdIcon,
141
142
  DdKeyboard, DdNumberKeyboard, DdCarKeyboard, DdCascader, DdGoodsSku,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@didaoktv/didaoui-uniapp",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "帝到KTV UniApp 组件库",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",