@didaoktv/didaoui-uniapp 1.3.9 → 1.4.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.
- package/README.md +5 -5
- package/components/dd-price/dd-price.vue +84 -0
- package/components/dd-sidebar/dd-sidebar.vue +54 -0
- package/components/dd-sidebar-item/dd-sidebar-item.vue +129 -0
- package/components/dd-submit-bar/dd-submit-bar.vue +194 -0
- package/components/dd-tree-select/dd-tree-select.vue +208 -0
- package/index.ts +17 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- **帝王级配色** — 帝王金 `#F5A623` + 皇家蓝 `#2D4BA0` 双主色,暗色主题默认
|
|
8
8
|
- **玻璃拟态设计** — 多层级玻璃拟态效果,打造高端夜店沉浸感
|
|
9
|
-
- **
|
|
9
|
+
- **95 个精心打磨的组件** — 覆盖表单、导航、布局卡片、数据展示、浮层反馈、交互、画布七大类,含 KTV 业务组件(房态卡、账单明细、工单卡、日历、二维码)
|
|
10
10
|
- **Vue3 + TypeScript** — 完整类型定义,Composition API
|
|
11
11
|
- **多端兼容** — H5 / 微信小程序 / 抖音小程序 / Android / iOS
|
|
12
12
|
|
|
@@ -50,10 +50,10 @@ import { DdButton, DdInput } from '@didaoktv/didaoui-uniapp'
|
|
|
50
50
|
|
|
51
51
|
| 分类 | 数量 | 组件 |
|
|
52
52
|
| ----- | -- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
-
| 表单输入 |
|
|
54
|
-
| 导航
|
|
55
|
-
| 布局/卡片 |
|
|
56
|
-
| 数据展示 |
|
|
53
|
+
| 表单输入 | 24 | Button, CodeButton, Input, Form, Field, Switch, Checkbox, CheckboxGroup, Radio, RadioGroup, Stepper, SearchBar, Slider, DatePicker, Picker, Rate, Upload, Icon, Keyboard, NumberKeyboard, CarKeyboard, Cascader, GoodsSku, TreeSelect |
|
|
54
|
+
| 导航 | 14 | Navigation, TopNavbar, SegmentedTab, SwipeableTab, Drawer, Tabbar, TabbarItem, Backtop, Collapse, CollapseItem, DropdownMenu, DropdownItem, Sidebar, SidebarItem | |
|
|
55
|
+
| 布局/卡片 | 9 | Card, RoomCard, FeatureGrid, Sticky, BillDetail, WorkorderCard, Calendar, Qrcode, SubmitBar |
|
|
56
|
+
| 数据展示 | 25 | Tag, StatCard, ChampionCard, Avatar, AvatarGroup, Badge, ListCell, Cell, CellGroup, CountDown, Divider, Image, Progress, Skeleton, EmptyState, Step, Steps, Text, Link, Copy, Album, Coupon, Parse, Markdown, Price |
|
|
57
57
|
| 浮层反馈 | 11 | Modal, ActionSheet, Toast, Alert, Loading, Dialog, Overlay, Popup, Popover, PopoverItem, Portal |
|
|
58
58
|
| 交互 | 5 | SwipeAction, Swipe, SwipeItem, PullRefresh, Loadmore |
|
|
59
59
|
| 小程序专属 | 2 | CapsuleButton, MiniProgramNavbar |
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view v-if="pricePair" class="dd-price">
|
|
3
|
+
<text v-if="prefix" class="dd-price__prefix">{{ prefix }}</text>
|
|
4
|
+
<text class="dd-price__currency">{{ currency }}</text>
|
|
5
|
+
<text class="dd-price__integer">{{ pricePair[0] }}</text>
|
|
6
|
+
<text v-if="pricePair[1]" class="dd-price__decimal">{{ mark }}{{ pricePair[1] }}</text>
|
|
7
|
+
<text v-if="suffix" class="dd-price__suffix">{{ suffix }}</text>
|
|
8
|
+
</view>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import { computed } from 'vue'
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
/** 金额(元,直接展示不除以 100);不传不渲染 */
|
|
16
|
+
rate?: number | string
|
|
17
|
+
/** 小数位数(rate 为 number 时生效,0 隐藏小数) */
|
|
18
|
+
decimalLength?: string | number
|
|
19
|
+
/** 货币符号 */
|
|
20
|
+
currency?: string
|
|
21
|
+
/** 整数/小数分隔符 */
|
|
22
|
+
mark?: string
|
|
23
|
+
/** 额外前缀文案(货币符之前) */
|
|
24
|
+
prefix?: string
|
|
25
|
+
/** 额外后缀文案(如「起」) */
|
|
26
|
+
suffix?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
30
|
+
rate: undefined,
|
|
31
|
+
decimalLength: 2,
|
|
32
|
+
currency: '¥',
|
|
33
|
+
mark: '.',
|
|
34
|
+
prefix: '',
|
|
35
|
+
suffix: '',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const pricePair = computed<string[] | null>(() => {
|
|
39
|
+
if (props.rate === undefined || props.rate === null || props.rate === '') return null
|
|
40
|
+
if (typeof props.rate === 'number') {
|
|
41
|
+
const len = Math.max(0, Number(props.decimalLength) || 0)
|
|
42
|
+
return props.rate.toFixed(len).split('.')
|
|
43
|
+
}
|
|
44
|
+
return props.rate.split('.')
|
|
45
|
+
})
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<style lang="scss" scoped>
|
|
49
|
+
@import '../../scss/variables';
|
|
50
|
+
@import '../../scss/mixins';
|
|
51
|
+
|
|
52
|
+
.dd-price {
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
align-items: baseline;
|
|
55
|
+
// ponytail: 颜色跟随使用方(inherit),Vant 亦由上下文(如 submit-bar 红价)控制
|
|
56
|
+
color: inherit;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
line-height: 1;
|
|
59
|
+
|
|
60
|
+
&__prefix,
|
|
61
|
+
&__suffix {
|
|
62
|
+
color: var(--dd-text-secondary, #{$dd-text-secondary});
|
|
63
|
+
font-size: 24rpx;
|
|
64
|
+
font-weight: 400;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
&__prefix {
|
|
68
|
+
margin-right: $dd-space-1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
&__suffix {
|
|
72
|
+
margin-left: $dd-space-1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
&__currency,
|
|
76
|
+
&__decimal {
|
|
77
|
+
font-size: 24rpx;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&__integer {
|
|
81
|
+
font-size: 36rpx;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="dd-sidebar">
|
|
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
|
+
/** 当前选中项的索引(v-model) */
|
|
12
|
+
modelValue?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
16
|
+
modelValue: 0,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const emit = defineEmits<{
|
|
20
|
+
(e: 'update:modelValue', val: number): void
|
|
21
|
+
(e: 'change', val: number): void
|
|
22
|
+
}>()
|
|
23
|
+
|
|
24
|
+
const counter = ref(0)
|
|
25
|
+
|
|
26
|
+
function register(): number {
|
|
27
|
+
const idx = counter.value
|
|
28
|
+
counter.value += 1
|
|
29
|
+
return idx
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function onItemClick(idx: number) {
|
|
33
|
+
if (idx === props.modelValue) return
|
|
34
|
+
emit('update:modelValue', idx)
|
|
35
|
+
emit('change', idx)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
provide('ddSidebar', {
|
|
39
|
+
active: computed(() => props.modelValue),
|
|
40
|
+
register,
|
|
41
|
+
onItemClick,
|
|
42
|
+
})
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<style lang="scss" scoped>
|
|
46
|
+
@import '../../scss/variables';
|
|
47
|
+
@import '../../scss/mixins';
|
|
48
|
+
|
|
49
|
+
.dd-sidebar {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
background: var(--dd-bg-section, #{$dd-bg-section});
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="dd-sidebar-item"
|
|
4
|
+
:class="[
|
|
5
|
+
customClass,
|
|
6
|
+
{
|
|
7
|
+
'dd-sidebar-item--active': isActive,
|
|
8
|
+
'dd-sidebar-item--disabled': disabled,
|
|
9
|
+
},
|
|
10
|
+
]"
|
|
11
|
+
@click="onClick"
|
|
12
|
+
>
|
|
13
|
+
<view class="dd-sidebar-item__text">
|
|
14
|
+
<slot name="title">{{ title }}</slot>
|
|
15
|
+
</view>
|
|
16
|
+
<view v-if="badge" class="dd-sidebar-item__badge">
|
|
17
|
+
<text class="dd-sidebar-item__badge-text">{{ badge }}</text>
|
|
18
|
+
</view>
|
|
19
|
+
<view v-else-if="dot" class="dd-sidebar-item__dot"></view>
|
|
20
|
+
</view>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import { computed, inject, ref } from 'vue'
|
|
25
|
+
|
|
26
|
+
interface Props {
|
|
27
|
+
/** 导航项文案 */
|
|
28
|
+
title?: string
|
|
29
|
+
/** 右上角红点 */
|
|
30
|
+
dot?: boolean
|
|
31
|
+
/** 右上角徽标数 */
|
|
32
|
+
badge?: string | number
|
|
33
|
+
/** 禁用(点击不切换、不触发 click) */
|
|
34
|
+
disabled?: boolean
|
|
35
|
+
/** 额外类名 */
|
|
36
|
+
customClass?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
40
|
+
title: '',
|
|
41
|
+
dot: false,
|
|
42
|
+
badge: '',
|
|
43
|
+
disabled: false,
|
|
44
|
+
customClass: '',
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const emit = defineEmits<{
|
|
48
|
+
(e: 'click', index: number): void
|
|
49
|
+
}>()
|
|
50
|
+
|
|
51
|
+
const ctx = inject<any>('ddSidebar', null)
|
|
52
|
+
const fallbackIdx = ref(ctx ? ctx.register() : 0)
|
|
53
|
+
|
|
54
|
+
const isActive = computed(() => (ctx ? ctx.active.value === fallbackIdx.value : false))
|
|
55
|
+
|
|
56
|
+
function onClick() {
|
|
57
|
+
if (props.disabled) return
|
|
58
|
+
ctx?.onItemClick(fallbackIdx.value)
|
|
59
|
+
emit('click', fallbackIdx.value)
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<style lang="scss" scoped>
|
|
64
|
+
@import '../../scss/variables';
|
|
65
|
+
@import '../../scss/mixins';
|
|
66
|
+
|
|
67
|
+
.dd-sidebar-item {
|
|
68
|
+
position: relative;
|
|
69
|
+
padding: $dd-space-4 $dd-space-3;
|
|
70
|
+
color: var(--dd-text-secondary, #{$dd-text-secondary});
|
|
71
|
+
font-size: $dd-font-size-body;
|
|
72
|
+
line-height: $dd-line-height-body;
|
|
73
|
+
|
|
74
|
+
&--active {
|
|
75
|
+
background: var(--dd-bg-card, #{$dd-bg-card});
|
|
76
|
+
color: var(--dd-text-primary, #{$dd-text-primary});
|
|
77
|
+
font-weight: 600;
|
|
78
|
+
|
|
79
|
+
// 左侧金色选中条
|
|
80
|
+
&::before {
|
|
81
|
+
content: '';
|
|
82
|
+
position: absolute;
|
|
83
|
+
left: 0;
|
|
84
|
+
top: 50%;
|
|
85
|
+
transform: translateY(-50%);
|
|
86
|
+
width: 6rpx;
|
|
87
|
+
height: 32rpx;
|
|
88
|
+
border-radius: 0 $dd-radius-full $dd-radius-full 0;
|
|
89
|
+
background: var(--dd-primary, #{$dd-primary});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
&--disabled {
|
|
94
|
+
color: var(--dd-text-tertiary, #{$dd-text-tertiary});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
&__text {
|
|
98
|
+
@include dd-ellipsis(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
&__badge {
|
|
102
|
+
position: absolute;
|
|
103
|
+
top: $dd-space-2;
|
|
104
|
+
right: $dd-space-2;
|
|
105
|
+
min-width: $dd-space-4;
|
|
106
|
+
height: $dd-space-4;
|
|
107
|
+
padding: 0 $dd-space-1;
|
|
108
|
+
border-radius: $dd-radius-full;
|
|
109
|
+
background: var(--dd-error, #{$dd-error});
|
|
110
|
+
@include dd-flex-center;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&__badge-text {
|
|
114
|
+
color: $dd-color-white;
|
|
115
|
+
font-size: 20rpx;
|
|
116
|
+
line-height: 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&__dot {
|
|
120
|
+
position: absolute;
|
|
121
|
+
top: $dd-space-2;
|
|
122
|
+
right: $dd-space-2;
|
|
123
|
+
width: $dd-space-2;
|
|
124
|
+
height: $dd-space-2;
|
|
125
|
+
border-radius: 50%;
|
|
126
|
+
background: var(--dd-error, #{$dd-error});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
v-if="placeholder"
|
|
4
|
+
class="dd-submit-bar__placeholder"
|
|
5
|
+
:class="{ 'dd-submit-bar__placeholder--safe': safeAreaInsetBottom }"
|
|
6
|
+
></view>
|
|
7
|
+
<view class="dd-submit-bar" :class="{ 'dd-submit-bar--safe': safeAreaInsetBottom }">
|
|
8
|
+
<slot name="top"></slot>
|
|
9
|
+
<view v-if="hasTip" class="dd-submit-bar__tip">
|
|
10
|
+
<dd-icon v-if="tipIcon" :name="tipIcon" class="dd-submit-bar__tip-icon" />
|
|
11
|
+
<text v-if="tip" class="dd-submit-bar__tip-text">{{ tip }}</text>
|
|
12
|
+
<slot name="tip"></slot>
|
|
13
|
+
</view>
|
|
14
|
+
<view class="dd-submit-bar__bar">
|
|
15
|
+
<slot></slot>
|
|
16
|
+
<view v-if="hasPrice" class="dd-submit-bar__text" :style="textStyle">
|
|
17
|
+
<text class="dd-submit-bar__label">{{ label }}</text>
|
|
18
|
+
<dd-price
|
|
19
|
+
class="dd-submit-bar__price"
|
|
20
|
+
:rate="priceYuan"
|
|
21
|
+
:currency="currency"
|
|
22
|
+
:decimal-length="decimalLength"
|
|
23
|
+
/>
|
|
24
|
+
<text v-if="suffixLabel" class="dd-submit-bar__suffix-label">{{ suffixLabel }}</text>
|
|
25
|
+
</view>
|
|
26
|
+
<slot name="button">
|
|
27
|
+
<dd-button
|
|
28
|
+
class="dd-submit-bar__button"
|
|
29
|
+
round
|
|
30
|
+
:type="buttonType"
|
|
31
|
+
:style="buttonStyle"
|
|
32
|
+
:loading="loading"
|
|
33
|
+
:disabled="disabled"
|
|
34
|
+
@click="onSubmit"
|
|
35
|
+
>{{ buttonText }}</dd-button>
|
|
36
|
+
</slot>
|
|
37
|
+
</view>
|
|
38
|
+
</view>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
import { computed, useSlots } from 'vue'
|
|
43
|
+
import type { CSSProperties } from 'vue'
|
|
44
|
+
import DdButton from '../dd-button/dd-button.vue'
|
|
45
|
+
import DdIcon from '../dd-icon/dd-icon.vue'
|
|
46
|
+
import DdPrice from '../dd-price/dd-price.vue'
|
|
47
|
+
|
|
48
|
+
interface Props {
|
|
49
|
+
/** 提示文案(tip 区域,如「小计不含服务费」) */
|
|
50
|
+
tip?: string
|
|
51
|
+
/** 价格左侧文案 */
|
|
52
|
+
label?: string
|
|
53
|
+
/** 价格(单位:分),传 number 才渲染价格区 */
|
|
54
|
+
price?: number
|
|
55
|
+
/** 提示区图标名(dd-icon name) */
|
|
56
|
+
tipIcon?: string
|
|
57
|
+
/** 按钮加载态 */
|
|
58
|
+
loading?: boolean
|
|
59
|
+
/** 货币符号 */
|
|
60
|
+
currency?: string
|
|
61
|
+
/** 按钮禁用态 */
|
|
62
|
+
disabled?: boolean
|
|
63
|
+
/** 价格区对齐方式(left/center/right) */
|
|
64
|
+
textAlign?: string
|
|
65
|
+
/** 按钮文案 */
|
|
66
|
+
buttonText?: string
|
|
67
|
+
/** 按钮类型(dd-button type) */
|
|
68
|
+
buttonType?: 'default' | 'primary' | 'secondary' | 'ghost' | 'text' | 'success' | 'warning' | 'danger'
|
|
69
|
+
/** 按钮自定义背景色(透传覆盖 dd-button 背景) */
|
|
70
|
+
buttonColor?: string
|
|
71
|
+
/** 价格右侧附加文案 */
|
|
72
|
+
suffixLabel?: string
|
|
73
|
+
/** 固定在底部时是否生成占位视图 */
|
|
74
|
+
placeholder?: boolean
|
|
75
|
+
/** 价格小数位数 */
|
|
76
|
+
decimalLength?: string | number
|
|
77
|
+
/** 是否预留底部安全区 */
|
|
78
|
+
safeAreaInsetBottom?: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
82
|
+
tip: '',
|
|
83
|
+
label: '合计:',
|
|
84
|
+
tipIcon: '',
|
|
85
|
+
loading: false,
|
|
86
|
+
currency: '¥',
|
|
87
|
+
disabled: false,
|
|
88
|
+
textAlign: '',
|
|
89
|
+
buttonText: '',
|
|
90
|
+
buttonType: 'danger',
|
|
91
|
+
buttonColor: '',
|
|
92
|
+
suffixLabel: '',
|
|
93
|
+
placeholder: false,
|
|
94
|
+
decimalLength: 2,
|
|
95
|
+
safeAreaInsetBottom: true,
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const emit = defineEmits<{
|
|
99
|
+
(e: 'submit'): void
|
|
100
|
+
}>()
|
|
101
|
+
|
|
102
|
+
const slots = useSlots()
|
|
103
|
+
|
|
104
|
+
const hasTip = computed(() => !!props.tip || !!slots.tip)
|
|
105
|
+
|
|
106
|
+
// Vant 口径:price 单位为分,除以 100 转元交给 dd-price 渲染
|
|
107
|
+
const priceYuan = computed(() => (typeof props.price === 'number' ? props.price / 100 : undefined))
|
|
108
|
+
const hasPrice = computed(() => priceYuan.value !== undefined)
|
|
109
|
+
|
|
110
|
+
const textStyle = computed<CSSProperties | undefined>(() =>
|
|
111
|
+
props.textAlign ? { textAlign: props.textAlign as CSSProperties['textAlign'] } : undefined,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const buttonStyle = computed(() => (props.buttonColor ? { background: props.buttonColor } : undefined))
|
|
115
|
+
|
|
116
|
+
function onSubmit() {
|
|
117
|
+
if (props.disabled || props.loading) return
|
|
118
|
+
emit('submit')
|
|
119
|
+
}
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<style lang="scss" scoped>
|
|
123
|
+
@import '../../scss/variables';
|
|
124
|
+
@import '../../scss/mixins';
|
|
125
|
+
|
|
126
|
+
.dd-submit-bar {
|
|
127
|
+
position: fixed;
|
|
128
|
+
left: 0;
|
|
129
|
+
right: 0;
|
|
130
|
+
bottom: 0;
|
|
131
|
+
z-index: $dd-z-index-sticky;
|
|
132
|
+
background: var(--dd-bg-card, #{$dd-bg-card});
|
|
133
|
+
|
|
134
|
+
&--safe {
|
|
135
|
+
@include dd-safe-area-bottom;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.dd-submit-bar__placeholder {
|
|
140
|
+
width: 100%;
|
|
141
|
+
height: 100rpx;
|
|
142
|
+
box-sizing: content-box;
|
|
143
|
+
|
|
144
|
+
&--safe {
|
|
145
|
+
@include dd-safe-area-bottom;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.dd-submit-bar__tip {
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
padding: $dd-space-2 $dd-space-3;
|
|
153
|
+
background: var(--dd-warning, #{$dd-warning});
|
|
154
|
+
color: $dd-color-white;
|
|
155
|
+
font-size: $dd-font-size-caption;
|
|
156
|
+
line-height: $dd-line-height-caption;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.dd-submit-bar__tip-icon {
|
|
160
|
+
margin-right: $dd-space-1;
|
|
161
|
+
font-size: 28rpx;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.dd-submit-bar__bar {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
min-height: 100rpx;
|
|
168
|
+
padding: $dd-space-2 $dd-space-3;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.dd-submit-bar__text {
|
|
172
|
+
flex: 1;
|
|
173
|
+
padding-right: $dd-space-3;
|
|
174
|
+
color: var(--dd-text-primary, #{$dd-text-primary});
|
|
175
|
+
font-size: $dd-font-size-body;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.dd-submit-bar__price {
|
|
179
|
+
margin-left: $dd-space-1;
|
|
180
|
+
color: var(--dd-error, #{$dd-error});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.dd-submit-bar__suffix-label {
|
|
184
|
+
margin-left: $dd-space-1;
|
|
185
|
+
color: var(--dd-text-secondary, #{$dd-text-secondary});
|
|
186
|
+
font-size: $dd-font-size-caption;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.dd-submit-bar__button {
|
|
190
|
+
flex-shrink: 0;
|
|
191
|
+
min-width: 200rpx;
|
|
192
|
+
margin-left: $dd-space-2;
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="dd-tree-select" :style="rootStyle">
|
|
3
|
+
<!-- 左侧分类导航(dd-sidebar) -->
|
|
4
|
+
<scroll-view class="dd-tree-select__nav" :scroll-y="true">
|
|
5
|
+
<dd-sidebar :model-value="mainActiveIndex" @update:model-value="onNavChange">
|
|
6
|
+
<dd-sidebar-item
|
|
7
|
+
v-for="(item, index) in items"
|
|
8
|
+
:key="index"
|
|
9
|
+
:title="item.text"
|
|
10
|
+
:dot="item.dot"
|
|
11
|
+
:badge="item.badge"
|
|
12
|
+
:disabled="item.disabled"
|
|
13
|
+
:custom-class="item.className"
|
|
14
|
+
>
|
|
15
|
+
<template #title>
|
|
16
|
+
<slot name="nav-text" :item="item">{{ item.text }}</slot>
|
|
17
|
+
</template>
|
|
18
|
+
</dd-sidebar-item>
|
|
19
|
+
</dd-sidebar>
|
|
20
|
+
</scroll-view>
|
|
21
|
+
<!-- 右侧子项区(content 插槽可整体自定义) -->
|
|
22
|
+
<scroll-view class="dd-tree-select__content" :scroll-y="true">
|
|
23
|
+
<slot name="content">
|
|
24
|
+
<view
|
|
25
|
+
v-for="child in activeChildren"
|
|
26
|
+
:key="child.id"
|
|
27
|
+
class="dd-tree-select__item"
|
|
28
|
+
:class="{
|
|
29
|
+
'dd-tree-select__item--active': isActive(child.id),
|
|
30
|
+
'dd-tree-select__item--disabled': child.disabled,
|
|
31
|
+
}"
|
|
32
|
+
@click="onItemClick(child)"
|
|
33
|
+
>
|
|
34
|
+
<text class="dd-tree-select__item-text">{{ child.text }}</text>
|
|
35
|
+
<dd-icon
|
|
36
|
+
v-if="isActive(child.id)"
|
|
37
|
+
:name="selectedIcon"
|
|
38
|
+
class="dd-tree-select__selected"
|
|
39
|
+
/>
|
|
40
|
+
</view>
|
|
41
|
+
</slot>
|
|
42
|
+
</scroll-view>
|
|
43
|
+
</view>
|
|
44
|
+
</template>
|
|
45
|
+
|
|
46
|
+
<script setup lang="ts">
|
|
47
|
+
import { computed } from 'vue'
|
|
48
|
+
import DdIcon from '../dd-icon/dd-icon.vue'
|
|
49
|
+
import DdSidebar from '../dd-sidebar/dd-sidebar.vue'
|
|
50
|
+
import DdSidebarItem from '../dd-sidebar-item/dd-sidebar-item.vue'
|
|
51
|
+
|
|
52
|
+
/** 右侧子选项 */
|
|
53
|
+
interface TreeSelectChild {
|
|
54
|
+
/** 选项唯一 id */
|
|
55
|
+
id: string | number
|
|
56
|
+
/** 选项文案 */
|
|
57
|
+
text: string
|
|
58
|
+
disabled?: boolean
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 左侧导航分类项 */
|
|
62
|
+
interface TreeSelectItem {
|
|
63
|
+
/** 导航文案 */
|
|
64
|
+
text?: string
|
|
65
|
+
disabled?: boolean
|
|
66
|
+
/** 导航项右上角红点 */
|
|
67
|
+
dot?: boolean
|
|
68
|
+
/** 导航项右上角徽标数 */
|
|
69
|
+
badge?: string | number
|
|
70
|
+
/** 额外类名 */
|
|
71
|
+
className?: string
|
|
72
|
+
/** 该分类下的子选项 */
|
|
73
|
+
children?: TreeSelectChild[]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface Props {
|
|
77
|
+
/** 分类数据(左侧导航 + 右侧子项) */
|
|
78
|
+
items?: TreeSelectItem[]
|
|
79
|
+
/** 左侧导航选中索引(v-model:main-active-index) */
|
|
80
|
+
mainActiveIndex?: number
|
|
81
|
+
/** 右侧选中项 id,传数组开启多选(v-model:active-id) */
|
|
82
|
+
activeId?: string | number | Array<string | number>
|
|
83
|
+
/** 多选时最多可选数量(activeId 为数组时生效) */
|
|
84
|
+
max?: number
|
|
85
|
+
/** 组件高度,数字按 px,字符串原样输出(支持 rpx) */
|
|
86
|
+
height?: string | number
|
|
87
|
+
/** 选中项右侧图标名(dd-icon name) */
|
|
88
|
+
selectedIcon?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
92
|
+
items: () => [],
|
|
93
|
+
mainActiveIndex: 0,
|
|
94
|
+
activeId: 0,
|
|
95
|
+
max: Number.POSITIVE_INFINITY,
|
|
96
|
+
height: 300,
|
|
97
|
+
selectedIcon: 'success',
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const emit = defineEmits<{
|
|
101
|
+
(e: 'update:mainActiveIndex', index: number): void
|
|
102
|
+
(e: 'update:activeId', id: string | number | Array<string | number>): void
|
|
103
|
+
(e: 'clickNav', index: number): void
|
|
104
|
+
(e: 'clickItem', item: TreeSelectChild): void
|
|
105
|
+
}>()
|
|
106
|
+
|
|
107
|
+
const rootStyle = computed(() => ({
|
|
108
|
+
height: typeof props.height === 'number' ? `${props.height}px` : props.height,
|
|
109
|
+
}))
|
|
110
|
+
|
|
111
|
+
/** 当前选中分类的子选项 */
|
|
112
|
+
const activeChildren = computed<TreeSelectChild[]>(
|
|
113
|
+
() => props.items[props.mainActiveIndex]?.children ?? [],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
function isActive(id: string | number) {
|
|
117
|
+
return Array.isArray(props.activeId) ? props.activeId.includes(id) : props.activeId === id
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function onNavChange(index: number) {
|
|
121
|
+
emit('update:mainActiveIndex', index)
|
|
122
|
+
emit('clickNav', index)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function onItemClick(child: TreeSelectChild) {
|
|
126
|
+
if (child.disabled) return
|
|
127
|
+
let activeId: string | number | Array<string | number>
|
|
128
|
+
if (Array.isArray(props.activeId)) {
|
|
129
|
+
activeId = props.activeId.slice()
|
|
130
|
+
const index = activeId.indexOf(child.id)
|
|
131
|
+
if (index !== -1) {
|
|
132
|
+
activeId.splice(index, 1)
|
|
133
|
+
} else if (activeId.length < props.max) {
|
|
134
|
+
activeId.push(child.id)
|
|
135
|
+
} else {
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
activeId = child.id
|
|
140
|
+
}
|
|
141
|
+
emit('update:activeId', activeId)
|
|
142
|
+
emit('clickItem', child)
|
|
143
|
+
}
|
|
144
|
+
</script>
|
|
145
|
+
|
|
146
|
+
<style lang="scss" scoped>
|
|
147
|
+
@import '../../scss/variables';
|
|
148
|
+
@import '../../scss/mixins';
|
|
149
|
+
|
|
150
|
+
.dd-tree-select {
|
|
151
|
+
display: flex;
|
|
152
|
+
font-size: $dd-font-size-body;
|
|
153
|
+
|
|
154
|
+
&__nav {
|
|
155
|
+
flex: 1;
|
|
156
|
+
height: 100%;
|
|
157
|
+
background: var(--dd-bg-section, #{$dd-bg-section});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
&__content {
|
|
161
|
+
flex: 2;
|
|
162
|
+
height: 100%;
|
|
163
|
+
background: var(--dd-bg-card, #{$dd-bg-card});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&__item {
|
|
167
|
+
position: relative;
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
justify-content: space-between;
|
|
171
|
+
min-height: 96rpx;
|
|
172
|
+
padding: 0 88rpx 0 $dd-space-4;
|
|
173
|
+
font-weight: 600;
|
|
174
|
+
color: var(--dd-text-primary, #{$dd-text-primary});
|
|
175
|
+
|
|
176
|
+
&:active {
|
|
177
|
+
background: var(--dd-interactive-hover, #{$dd-interactive-hover});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&--active {
|
|
181
|
+
color: var(--dd-primary, #{$dd-primary});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
&--disabled {
|
|
185
|
+
color: var(--dd-text-tertiary, #{$dd-text-tertiary});
|
|
186
|
+
|
|
187
|
+
&:active {
|
|
188
|
+
background: transparent;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
&__item-text {
|
|
194
|
+
overflow: hidden;
|
|
195
|
+
white-space: nowrap;
|
|
196
|
+
text-overflow: ellipsis;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
&__selected {
|
|
200
|
+
position: absolute;
|
|
201
|
+
top: 50%;
|
|
202
|
+
right: $dd-space-4;
|
|
203
|
+
transform: translateY(-50%);
|
|
204
|
+
font-size: 32rpx;
|
|
205
|
+
color: var(--dd-primary, #{$dd-primary});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
</style>
|
package/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { App } from 'vue'
|
|
2
2
|
|
|
3
|
-
// === Input (
|
|
3
|
+
// === Input (24) ===
|
|
4
4
|
import DdButton from './components/dd-button/dd-button.vue'
|
|
5
5
|
import DdCodeButton from './components/dd-code-button/dd-code-button.vue'
|
|
6
6
|
import DdInput from './components/dd-input/dd-input.vue'
|
|
@@ -24,8 +24,9 @@ import DdNumberKeyboard from './components/dd-number-keyboard/dd-number-keyboard
|
|
|
24
24
|
import DdCarKeyboard from './components/dd-car-keyboard/dd-car-keyboard.vue'
|
|
25
25
|
import DdCascader from './components/dd-cascader/dd-cascader.vue'
|
|
26
26
|
import DdGoodsSku from './components/dd-goods-sku/dd-goods-sku.vue'
|
|
27
|
+
import DdTreeSelect from './components/dd-tree-select/dd-tree-select.vue'
|
|
27
28
|
|
|
28
|
-
// === Navigation (
|
|
29
|
+
// === Navigation (14) ===
|
|
29
30
|
import DdNavigation from './components/dd-navigation/dd-navigation.vue'
|
|
30
31
|
import DdTopNavbar from './components/dd-top-navbar/dd-top-navbar.vue'
|
|
31
32
|
import DdSegmentedTab from './components/dd-segmented-tab/dd-segmented-tab.vue'
|
|
@@ -38,8 +39,10 @@ import DdCollapse from './components/dd-collapse/dd-collapse.vue'
|
|
|
38
39
|
import DdCollapseItem from './components/dd-collapse-item/dd-collapse-item.vue'
|
|
39
40
|
import DdDropdownMenu from './components/dd-dropdown-menu/dd-dropdown-menu.vue'
|
|
40
41
|
import DdDropdownItem from './components/dd-dropdown-item/dd-dropdown-item.vue'
|
|
42
|
+
import DdSidebar from './components/dd-sidebar/dd-sidebar.vue'
|
|
43
|
+
import DdSidebarItem from './components/dd-sidebar-item/dd-sidebar-item.vue'
|
|
41
44
|
|
|
42
|
-
// === Layout (
|
|
45
|
+
// === Layout (9) ===
|
|
43
46
|
import DdCard from './components/dd-card/dd-card.vue'
|
|
44
47
|
import DdRoomCard from './components/dd-room-card/dd-room-card.vue'
|
|
45
48
|
import DdFeatureGrid from './components/dd-feature-grid/dd-feature-grid.vue'
|
|
@@ -48,8 +51,9 @@ import DdBillDetail from './components/dd-bill-detail/dd-bill-detail.vue'
|
|
|
48
51
|
import DdWorkorderCard from './components/dd-workorder-card/dd-workorder-card.vue'
|
|
49
52
|
import DdCalendar from './components/dd-calendar/dd-calendar.vue'
|
|
50
53
|
import DdQrcode from './components/dd-qrcode/dd-qrcode.vue'
|
|
54
|
+
import DdSubmitBar from './components/dd-submit-bar/dd-submit-bar.vue'
|
|
51
55
|
|
|
52
|
-
// === Display (
|
|
56
|
+
// === Display (25) ===
|
|
53
57
|
import DdTag from './components/dd-tag/dd-tag.vue'
|
|
54
58
|
import DdStatCard from './components/dd-stat-card/dd-stat-card.vue'
|
|
55
59
|
import DdChampionCard from './components/dd-champion-card/dd-champion-card.vue'
|
|
@@ -74,6 +78,7 @@ import DdAlbum from './components/dd-album/dd-album.vue'
|
|
|
74
78
|
import DdCoupon from './components/dd-coupon/dd-coupon.vue'
|
|
75
79
|
import DdParse from './components/dd-parse/dd-parse.vue'
|
|
76
80
|
import DdMarkdown from './components/dd-markdown/dd-markdown.vue'
|
|
81
|
+
import DdPrice from './components/dd-price/dd-price.vue'
|
|
77
82
|
|
|
78
83
|
// === Overlay (11) ===
|
|
79
84
|
import DdPortal from './components/dd-portal/dd-portal.vue'
|
|
@@ -110,15 +115,17 @@ const components = {
|
|
|
110
115
|
DdButton, DdCodeButton, DdInput, DdSwitch, DdCheckbox, DdCheckboxGroup, DdRadio, DdRadioGroup, DdForm,
|
|
111
116
|
DdSearchBar, DdSlider, DdStepper, DdDatePicker, DdField,
|
|
112
117
|
DdPicker, DdRate, DdUpload, DdIcon,
|
|
113
|
-
DdKeyboard, DdNumberKeyboard, DdCarKeyboard, DdCascader, DdGoodsSku,
|
|
118
|
+
DdKeyboard, DdNumberKeyboard, DdCarKeyboard, DdCascader, DdGoodsSku, DdTreeSelect,
|
|
114
119
|
DdNavigation, DdTopNavbar, DdSegmentedTab, DdSwipeableTab,
|
|
115
120
|
DdDrawer, DdTabbar, DdTabbarItem, DdBacktop,
|
|
116
121
|
DdCollapse, DdCollapseItem, DdDropdownMenu, DdDropdownItem,
|
|
122
|
+
DdSidebar, DdSidebarItem,
|
|
117
123
|
DdCard, DdRoomCard, DdFeatureGrid, DdSticky, DdBillDetail, DdWorkorderCard, DdCalendar, DdQrcode,
|
|
124
|
+
DdSubmitBar,
|
|
118
125
|
DdTag, DdStatCard, DdChampionCard, DdAvatar, DdAvatarGroup, DdBadge,
|
|
119
126
|
DdListCell, DdCell, DdCellGroup, DdCountDown, DdDivider,
|
|
120
127
|
DdImage, DdProgress, DdSkeleton, DdEmptyState, DdStep, DdSteps,
|
|
121
|
-
DdText, DdLink, DdCopy, DdAlbum, DdCoupon, DdParse, DdMarkdown,
|
|
128
|
+
DdText, DdLink, DdCopy, DdAlbum, DdCoupon, DdParse, DdMarkdown, DdPrice,
|
|
122
129
|
DdModal, DdActionSheet, DdToast, DdAlert, DdLoading,
|
|
123
130
|
DdPortal, DdDialog, DdOverlay, DdPopup, DdPopover, DdPopoverItem,
|
|
124
131
|
DdSwipeAction, DdSwipe, DdSwipeItem, DdPullRefresh, DdLoadmore,
|
|
@@ -140,15 +147,17 @@ export {
|
|
|
140
147
|
DdButton, DdCodeButton, DdInput, DdSwitch, DdCheckbox, DdCheckboxGroup, DdRadio, DdRadioGroup, DdForm,
|
|
141
148
|
DdSearchBar, DdSlider, DdStepper, DdDatePicker, DdField,
|
|
142
149
|
DdPicker, DdRate, DdUpload, DdIcon,
|
|
143
|
-
DdKeyboard, DdNumberKeyboard, DdCarKeyboard, DdCascader, DdGoodsSku,
|
|
150
|
+
DdKeyboard, DdNumberKeyboard, DdCarKeyboard, DdCascader, DdGoodsSku, DdTreeSelect,
|
|
144
151
|
DdNavigation, DdTopNavbar, DdSegmentedTab, DdSwipeableTab,
|
|
145
152
|
DdDrawer, DdTabbar, DdTabbarItem, DdBacktop,
|
|
146
153
|
DdCollapse, DdCollapseItem, DdDropdownMenu, DdDropdownItem,
|
|
154
|
+
DdSidebar, DdSidebarItem,
|
|
147
155
|
DdCard, DdRoomCard, DdFeatureGrid, DdSticky, DdBillDetail, DdWorkorderCard, DdCalendar, DdQrcode,
|
|
156
|
+
DdSubmitBar,
|
|
148
157
|
DdTag, DdStatCard, DdChampionCard, DdAvatar, DdAvatarGroup, DdBadge,
|
|
149
158
|
DdListCell, DdCell, DdCellGroup, DdCountDown, DdDivider,
|
|
150
159
|
DdImage, DdProgress, DdSkeleton, DdEmptyState, DdStep, DdSteps,
|
|
151
|
-
DdText, DdLink, DdCopy, DdAlbum, DdCoupon, DdParse, DdMarkdown,
|
|
160
|
+
DdText, DdLink, DdCopy, DdAlbum, DdCoupon, DdParse, DdMarkdown, DdPrice,
|
|
152
161
|
DdModal, DdActionSheet, DdToast, DdAlert, DdLoading,
|
|
153
162
|
DdPortal, DdDialog, DdOverlay, DdPopup, DdPopover, DdPopoverItem,
|
|
154
163
|
DdSwipeAction, DdSwipe, DdSwipeItem, DdPullRefresh, DdLoadmore,
|