@hlw-uni/mp-vue 1.0.35 → 1.1.1
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/dist/index.d.ts +11 -0
- package/dist/index.js +619 -80
- package/dist/index.mjs +620 -81
- package/dist/style.css +582 -35
- package/package.json +1 -1
- package/src/components/hlw-avatar/index.vue +15 -0
- package/src/components/hlw-button/index.vue +141 -0
- package/src/components/hlw-cell/index.vue +143 -0
- package/src/components/hlw-divider/index.vue +72 -0
- package/src/components/hlw-empty/index.vue +3 -2
- package/src/components/hlw-header/index.vue +28 -0
- package/src/components/hlw-loading/index.vue +13 -0
- package/src/components/hlw-modal/index.vue +157 -0
- package/src/components/hlw-notice-bar/index.vue +99 -0
- package/src/components/hlw-page/index.vue +24 -0
- package/src/components/hlw-popup/index.vue +108 -0
- package/src/components/hlw-search/index.vue +95 -0
- package/src/components/hlw-skeleton/index.vue +105 -0
- package/src/components/hlw-tabs/index.vue +126 -0
- package/src/components/hlw-tag/index.vue +76 -0
- package/src/index.ts +11 -0
package/package.json
CHANGED
|
@@ -14,6 +14,21 @@
|
|
|
14
14
|
</template>
|
|
15
15
|
|
|
16
16
|
<script setup lang="ts">
|
|
17
|
+
/**
|
|
18
|
+
* HlwAvatar — 头像组件
|
|
19
|
+
*
|
|
20
|
+
* 显示用户头像,图片加载失败时自动回退到姓名首字母占位。
|
|
21
|
+
*
|
|
22
|
+
* @props
|
|
23
|
+
* src - 头像图片地址
|
|
24
|
+
* name - 用户名称,用于提取首字母(图片缺失时显示)
|
|
25
|
+
* size - 尺寸:small(56rpx) / medium(80rpx) / large(120rpx),默认 medium
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```vue
|
|
29
|
+
* <HlwAvatar src="/avatar.png" name="张三" size="large" />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
17
32
|
import { ref, computed } from 'vue';
|
|
18
33
|
|
|
19
34
|
const props = defineProps<{
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
class="hlw-btn"
|
|
4
|
+
:class="[
|
|
5
|
+
`hlw-btn--${type}`,
|
|
6
|
+
`hlw-btn--${size}`,
|
|
7
|
+
{ 'hlw-btn--block': block, 'hlw-btn--round': round, 'hlw-btn--disabled': disabled, 'hlw-btn--loading': loading },
|
|
8
|
+
]"
|
|
9
|
+
:disabled="disabled || loading"
|
|
10
|
+
:open-type="openType"
|
|
11
|
+
@tap="$emit('click')"
|
|
12
|
+
>
|
|
13
|
+
<view v-if="loading" class="hlw-btn-spinner" />
|
|
14
|
+
<view v-else-if="icon" :class="icon" class="hlw-btn-icon" />
|
|
15
|
+
<slot />
|
|
16
|
+
</button>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* HlwButton — 主题按钮
|
|
22
|
+
*
|
|
23
|
+
* 跟随 --primary-color 主题色,支持多种类型、尺寸和状态。
|
|
24
|
+
*
|
|
25
|
+
* @props
|
|
26
|
+
* type - 按钮类型:primary / outline / text / ghost,默认 primary
|
|
27
|
+
* size - 尺寸:small / medium / large,默认 medium
|
|
28
|
+
* loading - 加载状态(显示 spinner 并禁止点击),默认 false
|
|
29
|
+
* disabled - 禁用状态,默认 false
|
|
30
|
+
* block - 块级按钮(占满父容器宽度),默认 false
|
|
31
|
+
* round - 圆角药丸形状,默认 false
|
|
32
|
+
* icon - 左侧图标 class(如 i-fa6-solid-plus)
|
|
33
|
+
* openType - 微信原生 open-type(如 share、getPhoneNumber)
|
|
34
|
+
*
|
|
35
|
+
* @events
|
|
36
|
+
* click - 点击事件
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```vue
|
|
40
|
+
* <HlwButton type="primary" @click="submit">提交</HlwButton>
|
|
41
|
+
* <HlwButton type="outline" loading>加载中</HlwButton>
|
|
42
|
+
* <HlwButton type="text" icon="i-fa6-solid-plus">新增</HlwButton>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
interface Props {
|
|
46
|
+
type?: "primary" | "outline" | "text" | "ghost";
|
|
47
|
+
size?: "small" | "medium" | "large";
|
|
48
|
+
loading?: boolean;
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
block?: boolean;
|
|
51
|
+
round?: boolean;
|
|
52
|
+
icon?: string;
|
|
53
|
+
openType?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
withDefaults(defineProps<Props>(), {
|
|
57
|
+
type: "primary",
|
|
58
|
+
size: "medium",
|
|
59
|
+
loading: false,
|
|
60
|
+
disabled: false,
|
|
61
|
+
block: false,
|
|
62
|
+
round: false,
|
|
63
|
+
icon: "",
|
|
64
|
+
openType: "",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
defineEmits<{ click: [] }>();
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style lang="scss" scoped>
|
|
71
|
+
.hlw-btn {
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
gap: 8rpx;
|
|
76
|
+
border: none;
|
|
77
|
+
font-weight: 500;
|
|
78
|
+
line-height: 1;
|
|
79
|
+
transition: opacity 0.2s;
|
|
80
|
+
|
|
81
|
+
&::after { display: none; }
|
|
82
|
+
&:active { opacity: 0.7; }
|
|
83
|
+
|
|
84
|
+
&--primary {
|
|
85
|
+
background: var(--primary-color);
|
|
86
|
+
color: #fff;
|
|
87
|
+
}
|
|
88
|
+
&--outline {
|
|
89
|
+
background: transparent;
|
|
90
|
+
color: var(--primary-color);
|
|
91
|
+
border: 2rpx solid var(--primary-color);
|
|
92
|
+
}
|
|
93
|
+
&--text {
|
|
94
|
+
background: transparent;
|
|
95
|
+
color: var(--primary-color);
|
|
96
|
+
}
|
|
97
|
+
&--ghost {
|
|
98
|
+
background: transparent;
|
|
99
|
+
color: #fff;
|
|
100
|
+
border: 2rpx solid rgba(255, 255, 255, 0.6);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
&--small {
|
|
104
|
+
padding: 8rpx 20rpx;
|
|
105
|
+
font-size: var(--font-xs, 20rpx);
|
|
106
|
+
border-radius: var(--radius-sm, 8rpx);
|
|
107
|
+
}
|
|
108
|
+
&--medium {
|
|
109
|
+
padding: 16rpx 32rpx;
|
|
110
|
+
font-size: var(--font-sm, 24rpx);
|
|
111
|
+
border-radius: var(--radius-md, 16rpx);
|
|
112
|
+
}
|
|
113
|
+
&--large {
|
|
114
|
+
padding: 24rpx 48rpx;
|
|
115
|
+
font-size: var(--font-base, 28rpx);
|
|
116
|
+
border-radius: var(--radius-md, 16rpx);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&--block { display: flex; width: 100%; }
|
|
120
|
+
&--round { border-radius: 999rpx; }
|
|
121
|
+
&--disabled { opacity: 0.4; &:active { opacity: 0.4; } }
|
|
122
|
+
&--loading { opacity: 0.7; }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.hlw-btn-icon {
|
|
126
|
+
font-size: 1.1em;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.hlw-btn-spinner {
|
|
130
|
+
width: 28rpx;
|
|
131
|
+
height: 28rpx;
|
|
132
|
+
border: 3rpx solid currentColor;
|
|
133
|
+
border-top-color: transparent;
|
|
134
|
+
border-radius: 50%;
|
|
135
|
+
animation: hlw-spin 0.6s linear infinite;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes hlw-spin {
|
|
139
|
+
to { transform: rotate(360deg); }
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<navigator v-if="url" :url="url" class="hlw-cell" :class="{ 'hlw-cell--border': border }" hover-class="hlw-cell--hover">
|
|
3
|
+
<view v-if="icon || $slots.icon" class="hlw-cell-icon">
|
|
4
|
+
<slot name="icon"><view :class="icon" /></slot>
|
|
5
|
+
</view>
|
|
6
|
+
<view class="hlw-cell-body">
|
|
7
|
+
<view class="hlw-cell-title">
|
|
8
|
+
<slot name="title">{{ title }}</slot>
|
|
9
|
+
<view v-if="label" class="hlw-cell-label">{{ label }}</view>
|
|
10
|
+
</view>
|
|
11
|
+
<view class="hlw-cell-value">
|
|
12
|
+
<slot name="value">{{ value }}</slot>
|
|
13
|
+
</view>
|
|
14
|
+
</view>
|
|
15
|
+
<view class="hlw-cell-arrow" />
|
|
16
|
+
</navigator>
|
|
17
|
+
<view v-else class="hlw-cell" :class="{ 'hlw-cell--border': border, 'hlw-cell--link': isLink }" :hover-class="isLink ? 'hlw-cell--hover' : ''" @tap="$emit('click')">
|
|
18
|
+
<view v-if="icon || $slots.icon" class="hlw-cell-icon">
|
|
19
|
+
<slot name="icon"><view :class="icon" /></slot>
|
|
20
|
+
</view>
|
|
21
|
+
<view class="hlw-cell-body">
|
|
22
|
+
<view class="hlw-cell-title">
|
|
23
|
+
<slot name="title">{{ title }}</slot>
|
|
24
|
+
<view v-if="label" class="hlw-cell-label">{{ label }}</view>
|
|
25
|
+
</view>
|
|
26
|
+
<view class="hlw-cell-value">
|
|
27
|
+
<slot name="value">{{ value }}</slot>
|
|
28
|
+
</view>
|
|
29
|
+
</view>
|
|
30
|
+
<view v-if="isLink" class="hlw-cell-arrow" />
|
|
31
|
+
<slot name="right" />
|
|
32
|
+
</view>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
/**
|
|
37
|
+
* HlwCell — 列表单元格
|
|
38
|
+
*
|
|
39
|
+
* 通用列表项:左侧 icon + 标题/描述 + 右侧内容/箭头。
|
|
40
|
+
* 传入 url 自动渲染为 navigator 组件。
|
|
41
|
+
*
|
|
42
|
+
* @props
|
|
43
|
+
* title - 标题文字
|
|
44
|
+
* label - 描述文字(标题下方灰色小字)
|
|
45
|
+
* value - 右侧内容文字
|
|
46
|
+
* icon - 左侧图标 class(如 i-fa6-solid-gear)
|
|
47
|
+
* isLink - 是否显示右箭头,默认 false
|
|
48
|
+
* url - 跳转地址(有值则渲染为 navigator)
|
|
49
|
+
* border - 是否显示底部分割线,默认 true
|
|
50
|
+
*
|
|
51
|
+
* @events
|
|
52
|
+
* click - 点击事件(url 模式下由 navigator 处理跳转)
|
|
53
|
+
*
|
|
54
|
+
* @slots
|
|
55
|
+
* icon - 自定义左侧图标
|
|
56
|
+
* title - 自定义标题区域
|
|
57
|
+
* value - 自定义右侧内容
|
|
58
|
+
* right - 自定义最右侧区域
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```vue
|
|
62
|
+
* <HlwCell title="设置" icon="i-fa6-solid-gear" is-link url="/pages/settings/index" />
|
|
63
|
+
* <HlwCell title="版本号" value="1.0.0" />
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
interface Props {
|
|
67
|
+
title?: string;
|
|
68
|
+
label?: string;
|
|
69
|
+
value?: string;
|
|
70
|
+
icon?: string;
|
|
71
|
+
isLink?: boolean;
|
|
72
|
+
url?: string;
|
|
73
|
+
border?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
withDefaults(defineProps<Props>(), {
|
|
77
|
+
title: "",
|
|
78
|
+
label: "",
|
|
79
|
+
value: "",
|
|
80
|
+
icon: "",
|
|
81
|
+
isLink: false,
|
|
82
|
+
url: "",
|
|
83
|
+
border: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
defineEmits<{ click: [] }>();
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<style lang="scss" scoped>
|
|
90
|
+
.hlw-cell {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
padding: 24rpx 32rpx;
|
|
94
|
+
background: #fff;
|
|
95
|
+
gap: 20rpx;
|
|
96
|
+
|
|
97
|
+
&--border {
|
|
98
|
+
border-bottom: 1rpx solid var(--border-color-light, #f1f5f9);
|
|
99
|
+
&:last-child { border-bottom: none; }
|
|
100
|
+
}
|
|
101
|
+
&--hover { background: #f8fafc; }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.hlw-cell-icon {
|
|
105
|
+
font-size: var(--font-lg, 36rpx);
|
|
106
|
+
color: var(--primary-color, #3b82f6);
|
|
107
|
+
flex-shrink: 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.hlw-cell-body {
|
|
111
|
+
flex: 1;
|
|
112
|
+
min-width: 0;
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: space-between;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.hlw-cell-title {
|
|
119
|
+
font-size: var(--font-base, 28rpx);
|
|
120
|
+
color: #1e293b;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.hlw-cell-label {
|
|
124
|
+
font-size: var(--font-xs, 20rpx);
|
|
125
|
+
color: #94a3b8;
|
|
126
|
+
margin-top: 4rpx;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.hlw-cell-value {
|
|
130
|
+
font-size: var(--font-sm, 24rpx);
|
|
131
|
+
color: #94a3b8;
|
|
132
|
+
flex-shrink: 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.hlw-cell-arrow {
|
|
136
|
+
width: 16rpx;
|
|
137
|
+
height: 16rpx;
|
|
138
|
+
border-top: 3rpx solid #c0c4cc;
|
|
139
|
+
border-right: 3rpx solid #c0c4cc;
|
|
140
|
+
transform: rotate(45deg);
|
|
141
|
+
flex-shrink: 0;
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="hlw-divider" :class="[`hlw-divider--${position}`, { 'hlw-divider--dashed': dashed }]">
|
|
3
|
+
<view class="hlw-divider-line" />
|
|
4
|
+
<view v-if="text || $slots.default" class="hlw-divider-text">
|
|
5
|
+
<slot>{{ text }}</slot>
|
|
6
|
+
</view>
|
|
7
|
+
<view class="hlw-divider-line" />
|
|
8
|
+
</view>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
/**
|
|
13
|
+
* HlwDivider — 分割线
|
|
14
|
+
*
|
|
15
|
+
* 水平分割线,可带文字说明,支持虚线和文字位置调整。
|
|
16
|
+
*
|
|
17
|
+
* @props
|
|
18
|
+
* text - 分割线中间文字
|
|
19
|
+
* position - 文字位置:left / center / right,默认 center
|
|
20
|
+
* dashed - 是否虚线,默认 false
|
|
21
|
+
*
|
|
22
|
+
* @slots
|
|
23
|
+
* default - 自定义分割线中间内容(覆盖 text)
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```vue
|
|
27
|
+
* <HlwDivider />
|
|
28
|
+
* <HlwDivider text="或" />
|
|
29
|
+
* <HlwDivider text="更多" position="left" dashed />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
interface Props {
|
|
33
|
+
text?: string;
|
|
34
|
+
position?: "left" | "center" | "right";
|
|
35
|
+
dashed?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
withDefaults(defineProps<Props>(), {
|
|
39
|
+
text: "",
|
|
40
|
+
position: "center",
|
|
41
|
+
dashed: false,
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<style lang="scss" scoped>
|
|
46
|
+
.hlw-divider {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
padding: 24rpx 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.hlw-divider-line {
|
|
53
|
+
flex: 1;
|
|
54
|
+
height: 1rpx;
|
|
55
|
+
background: var(--border-color, #e2e8f0);
|
|
56
|
+
|
|
57
|
+
.hlw-divider--dashed & {
|
|
58
|
+
background: none;
|
|
59
|
+
border-top: 1rpx dashed var(--border-color, #e2e8f0);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.hlw-divider-text {
|
|
64
|
+
padding: 0 24rpx;
|
|
65
|
+
font-size: var(--font-xs, 20rpx);
|
|
66
|
+
color: #94a3b8;
|
|
67
|
+
white-space: nowrap;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.hlw-divider--left .hlw-divider-line:first-child { max-width: 60rpx; }
|
|
71
|
+
.hlw-divider--right .hlw-divider-line:last-child { max-width: 60rpx; }
|
|
72
|
+
</style>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view class="hlw-empty">
|
|
3
3
|
<image v-if="image" class="hlw-empty__image" :src="image" mode="aspectFit" />
|
|
4
|
-
<
|
|
5
|
-
<text class="hlw-empty__text">{{ text ||
|
|
4
|
+
<view v-else class="hlw-empty__icon i-fa6-solid-box-open" />
|
|
5
|
+
<text class="hlw-empty__text">{{ text || "暂无数据" }}</text>
|
|
6
6
|
<slot />
|
|
7
7
|
</view>
|
|
8
8
|
</template>
|
|
@@ -32,6 +32,7 @@ defineProps<{
|
|
|
32
32
|
.hlw-empty__icon {
|
|
33
33
|
font-size: 100rpx;
|
|
34
34
|
margin-bottom: 20rpx;
|
|
35
|
+
color: #cbd5e1;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
.hlw-empty__text {
|
|
@@ -34,6 +34,34 @@
|
|
|
34
34
|
</template>
|
|
35
35
|
|
|
36
36
|
<script setup lang="ts">
|
|
37
|
+
/**
|
|
38
|
+
* HlwHeader — 页面顶部导航栏
|
|
39
|
+
*
|
|
40
|
+
* 自动适配状态栏高度和胶囊按钮位置(微信小程序),支持返回按钮、自定义标题和背景。
|
|
41
|
+
*
|
|
42
|
+
* @props
|
|
43
|
+
* title - 标题文字
|
|
44
|
+
* titleAlign - 标题对齐:center / left,默认 center
|
|
45
|
+
* titleSize - 标题字号,默认 var(--font-base)
|
|
46
|
+
* titleWeight - 标题字重,默认 500
|
|
47
|
+
* titleColor - 标题颜色
|
|
48
|
+
* isBack - 是否显示返回按钮,默认 false
|
|
49
|
+
* bgClass - 自定义背景 CSS class
|
|
50
|
+
* extraHeight - 额外高度(rpx),默认 0
|
|
51
|
+
*
|
|
52
|
+
* @events
|
|
53
|
+
* back - 点击返回按钮
|
|
54
|
+
*
|
|
55
|
+
* @slots
|
|
56
|
+
* bg - 自定义背景层
|
|
57
|
+
* title - 自定义标题区域
|
|
58
|
+
* back-icon - 自定义返回图标
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```vue
|
|
62
|
+
* <HlwHeader title="我的" is-back @back="goBack" />
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
37
65
|
import { ref, computed, useSlots } from "vue";
|
|
38
66
|
|
|
39
67
|
const getNavBarContentHeight = (): number => {
|
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
</template>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
|
+
/**
|
|
10
|
+
* HlwLoading — 加载动画组件
|
|
11
|
+
*
|
|
12
|
+
* 圆形旋转 spinner,可附带文字提示。
|
|
13
|
+
*
|
|
14
|
+
* @props
|
|
15
|
+
* text - 加载提示文字(可选)
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```vue
|
|
19
|
+
* <HlwLoading text="加载中..." />
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
9
22
|
defineProps<{
|
|
10
23
|
text?: string;
|
|
11
24
|
}>();
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view v-if="show" class="hlw-modal-mask" @tap.self="onMask">
|
|
3
|
+
<view class="hlw-modal" :class="{ 'hlw-modal--show': show }">
|
|
4
|
+
<view v-if="title" class="hlw-modal-title">{{ title }}</view>
|
|
5
|
+
<view class="hlw-modal-body">
|
|
6
|
+
<slot />
|
|
7
|
+
</view>
|
|
8
|
+
<slot name="footer">
|
|
9
|
+
<view class="hlw-modal-footer">
|
|
10
|
+
<view v-if="showCancel" class="hlw-modal-btn hlw-modal-btn--cancel" @tap="onCancel">{{ cancelText }}</view>
|
|
11
|
+
<view class="hlw-modal-btn hlw-modal-btn--confirm" @tap="onConfirm">{{ confirmText }}</view>
|
|
12
|
+
</view>
|
|
13
|
+
</slot>
|
|
14
|
+
</view>
|
|
15
|
+
</view>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
/**
|
|
20
|
+
* HlwModal — 模态弹窗
|
|
21
|
+
*
|
|
22
|
+
* 比 uni.showModal 更灵活:支持插槽自定义内容和底部按钮。
|
|
23
|
+
* 使用 v-model:show 控制显隐。
|
|
24
|
+
*
|
|
25
|
+
* @props
|
|
26
|
+
* show - 是否显示,支持 v-model:show
|
|
27
|
+
* title - 标题文字
|
|
28
|
+
* showCancel - 是否显示取消按钮,默认 true
|
|
29
|
+
* confirmText - 确认按钮文字,默认 "确定"
|
|
30
|
+
* cancelText - 取消按钮文字,默认 "取消"
|
|
31
|
+
* closeOnMask - 点击蒙层是否关闭,默认 true
|
|
32
|
+
*
|
|
33
|
+
* @events
|
|
34
|
+
* update:show - 显隐状态变更
|
|
35
|
+
* confirm - 点击确认
|
|
36
|
+
* cancel - 点击取消
|
|
37
|
+
*
|
|
38
|
+
* @slots
|
|
39
|
+
* default - 弹窗内容
|
|
40
|
+
* footer - 自定义底部按钮区域(覆盖默认确认/取消)
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```vue
|
|
44
|
+
* <HlwModal v-model:show="visible" title="提示" @confirm="onOk">
|
|
45
|
+
* <text>确定要删除吗?</text>
|
|
46
|
+
* </HlwModal>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
interface Props {
|
|
50
|
+
show?: boolean;
|
|
51
|
+
title?: string;
|
|
52
|
+
showCancel?: boolean;
|
|
53
|
+
confirmText?: string;
|
|
54
|
+
cancelText?: string;
|
|
55
|
+
closeOnMask?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
withDefaults(defineProps<Props>(), {
|
|
59
|
+
show: false,
|
|
60
|
+
title: "",
|
|
61
|
+
showCancel: true,
|
|
62
|
+
confirmText: "确定",
|
|
63
|
+
cancelText: "取消",
|
|
64
|
+
closeOnMask: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const emit = defineEmits<{ "update:show": [value: boolean]; confirm: []; cancel: [] }>();
|
|
68
|
+
|
|
69
|
+
function close() {
|
|
70
|
+
emit("update:show", false);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function onMask() {
|
|
74
|
+
close();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function onConfirm() {
|
|
78
|
+
emit("confirm");
|
|
79
|
+
close();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function onCancel() {
|
|
83
|
+
emit("cancel");
|
|
84
|
+
close();
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<style lang="scss" scoped>
|
|
89
|
+
.hlw-modal-mask {
|
|
90
|
+
position: fixed;
|
|
91
|
+
inset: 0;
|
|
92
|
+
display: flex;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: center;
|
|
95
|
+
background: rgba(0, 0, 0, 0.5);
|
|
96
|
+
z-index: 1000;
|
|
97
|
+
animation: hlw-fade-in 0.2s;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.hlw-modal {
|
|
101
|
+
width: 80%;
|
|
102
|
+
max-width: 600rpx;
|
|
103
|
+
background: #fff;
|
|
104
|
+
border-radius: var(--radius-xl, 32rpx);
|
|
105
|
+
overflow: hidden;
|
|
106
|
+
animation: hlw-scale-in 0.25s ease;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.hlw-modal-title {
|
|
110
|
+
padding: 40rpx 32rpx 0;
|
|
111
|
+
text-align: center;
|
|
112
|
+
font-size: var(--font-md, 32rpx);
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
color: #1e293b;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.hlw-modal-body {
|
|
118
|
+
padding: 32rpx;
|
|
119
|
+
font-size: var(--font-base, 28rpx);
|
|
120
|
+
color: #475569;
|
|
121
|
+
text-align: center;
|
|
122
|
+
line-height: 1.6;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.hlw-modal-footer {
|
|
126
|
+
display: flex;
|
|
127
|
+
border-top: 1rpx solid var(--border-color-light, #f1f5f9);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.hlw-modal-btn {
|
|
131
|
+
flex: 1;
|
|
132
|
+
padding: 24rpx 0;
|
|
133
|
+
text-align: center;
|
|
134
|
+
font-size: var(--font-base, 28rpx);
|
|
135
|
+
font-weight: 500;
|
|
136
|
+
|
|
137
|
+
&:active { background: #f8fafc; }
|
|
138
|
+
|
|
139
|
+
&--cancel {
|
|
140
|
+
color: #64748b;
|
|
141
|
+
border-right: 1rpx solid var(--border-color-light, #f1f5f9);
|
|
142
|
+
}
|
|
143
|
+
&--confirm {
|
|
144
|
+
color: var(--primary-color, #3b82f6);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@keyframes hlw-fade-in {
|
|
149
|
+
from { opacity: 0; }
|
|
150
|
+
to { opacity: 1; }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@keyframes hlw-scale-in {
|
|
154
|
+
from { transform: scale(0.9); opacity: 0; }
|
|
155
|
+
to { transform: scale(1); opacity: 1; }
|
|
156
|
+
}
|
|
157
|
+
</style>
|