@hlw-uni/mp-vue 2.1.14 → 2.1.15
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/package.json
CHANGED
|
@@ -3,35 +3,21 @@
|
|
|
3
3
|
class="hlw-card"
|
|
4
4
|
:class="[
|
|
5
5
|
`hlw-card--radius-${radius}`,
|
|
6
|
-
|
|
6
|
+
...borderClasses,
|
|
7
7
|
]"
|
|
8
|
+
:style="rootStyle"
|
|
8
9
|
>
|
|
9
|
-
<!-- 头部 -->
|
|
10
|
+
<!-- 头部 — 用 #header slot 自定义;常规场景请用 <hlw-card-header> -->
|
|
10
11
|
<view v-if="hasHeader" class="hlw-card-header">
|
|
11
|
-
<slot name="header"
|
|
12
|
-
<view class="hlw-card-header-inner">
|
|
13
|
-
<!-- 头部左侧 -->
|
|
14
|
-
<view class="hlw-card-header-left">
|
|
15
|
-
<slot name="header-left">
|
|
16
|
-
<text v-if="title" class="hlw-card-title">{{ title }}</text>
|
|
17
|
-
</slot>
|
|
18
|
-
</view>
|
|
19
|
-
<!-- 头部右侧 -->
|
|
20
|
-
<view v-if="$slots['header-right'] || extra" class="hlw-card-header-right">
|
|
21
|
-
<slot name="header-right">
|
|
22
|
-
<text v-if="extra" class="hlw-card-extra">{{ extra }}</text>
|
|
23
|
-
</slot>
|
|
24
|
-
</view>
|
|
25
|
-
</view>
|
|
26
|
-
</slot>
|
|
12
|
+
<slot name="header" />
|
|
27
13
|
</view>
|
|
28
14
|
|
|
29
|
-
<!--
|
|
30
|
-
<view v-if="showDivider" class="hlw-card-divider"
|
|
15
|
+
<!-- 头部虚线分隔(有 #header slot 且 divider != false 时显示) -->
|
|
16
|
+
<view v-if="showDivider" class="hlw-card-divider" />
|
|
31
17
|
|
|
32
18
|
<!-- 内容区 -->
|
|
33
19
|
<view class="hlw-card-body" :class="{ 'hlw-card-body--padded': padding }">
|
|
34
|
-
<slot
|
|
20
|
+
<slot />
|
|
35
21
|
</view>
|
|
36
22
|
|
|
37
23
|
<!-- 底部 -->
|
|
@@ -39,10 +25,10 @@
|
|
|
39
25
|
<slot name="footer">
|
|
40
26
|
<view class="hlw-card-footer-inner">
|
|
41
27
|
<view class="hlw-card-footer-left">
|
|
42
|
-
<slot name="footer-left"
|
|
28
|
+
<slot name="footer-left" />
|
|
43
29
|
</view>
|
|
44
30
|
<view v-if="$slots['footer-right']" class="hlw-card-footer-right">
|
|
45
|
-
<slot name="footer-right"
|
|
31
|
+
<slot name="footer-right" />
|
|
46
32
|
</view>
|
|
47
33
|
</view>
|
|
48
34
|
</slot>
|
|
@@ -56,68 +42,106 @@ import { computed, useSlots } from "vue";
|
|
|
56
42
|
/**
|
|
57
43
|
* hlw-card 卡片容器
|
|
58
44
|
*
|
|
59
|
-
*
|
|
45
|
+
* 头部用法已迁出:常规标题 + 副标题请用 <hlw-card-header>,写在 default slot 里
|
|
46
|
+
* (记得 :padding="false" 避免 body padding 把 header 顶出来)。
|
|
47
|
+
*
|
|
48
|
+
* @example 标准头部 + body
|
|
60
49
|
* ```vue
|
|
61
|
-
* <hlw-card
|
|
62
|
-
* <
|
|
50
|
+
* <hlw-card :padding="false">
|
|
51
|
+
* <hlw-card-header title="标题" icon="i-fa6-solid-star" extra="副标题" />
|
|
52
|
+
* <view style="padding: 24rpx 28rpx">content</view>
|
|
63
53
|
* </hlw-card>
|
|
64
54
|
* ```
|
|
65
55
|
*
|
|
66
|
-
* @example
|
|
56
|
+
* @example 完全自定义头部
|
|
67
57
|
* ```vue
|
|
68
58
|
* <hlw-card>
|
|
69
|
-
* <template #header
|
|
70
|
-
* <text>自定义左侧</text>
|
|
71
|
-
* </template>
|
|
72
|
-
* <template #header-right>
|
|
73
|
-
* <button>操作</button>
|
|
74
|
-
* </template>
|
|
59
|
+
* <template #header>...</template>
|
|
75
60
|
* <text>内容</text>
|
|
76
61
|
* </hlw-card>
|
|
77
62
|
* ```
|
|
78
63
|
*
|
|
79
64
|
* @example 自定义底部
|
|
80
65
|
* ```vue
|
|
81
|
-
* <hlw-card
|
|
66
|
+
* <hlw-card>
|
|
82
67
|
* <text>内容</text>
|
|
83
|
-
* <template #footer-left>
|
|
84
|
-
*
|
|
85
|
-
* </template>
|
|
86
|
-
* <template #footer-right>
|
|
87
|
-
* <button>确认</button>
|
|
88
|
-
* </template>
|
|
68
|
+
* <template #footer-left><text>左侧说明</text></template>
|
|
69
|
+
* <template #footer-right><button>确认</button></template>
|
|
89
70
|
* </hlw-card>
|
|
90
71
|
* ```
|
|
91
72
|
*/
|
|
73
|
+
type BorderValue = boolean | string | string[];
|
|
74
|
+
|
|
92
75
|
interface Props {
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 边框,支持三种形式:
|
|
78
|
+
* - `true`(默认)/`false` —— 四边全开 / 全关
|
|
79
|
+
* - 字符串:"t r b l" 或 "top right bottom left",空格分隔,如 `"t b"` = 仅上下
|
|
80
|
+
* - 数组:`['t','l']` 同上
|
|
81
|
+
*/
|
|
82
|
+
border?: BorderValue;
|
|
83
|
+
/** 边框颜色,任意 CSS color:`#f00`、`rgb(...)`、`var(--xxx)`;默认走主题 var(--border-color) */
|
|
84
|
+
borderColor?: string;
|
|
85
|
+
/** 边框线型:solid(默认)/ dashed / dotted / double */
|
|
86
|
+
borderStyle?: "solid" | "dashed" | "dotted" | "double";
|
|
87
|
+
/** 边框宽度,CSS 长度值,默认 `1rpx` */
|
|
88
|
+
borderWidth?: string;
|
|
99
89
|
/** 圆角大小,对应 CSS 变量体系 */
|
|
100
90
|
radius?: "none" | "sm" | "md" | "lg" | "xl";
|
|
101
|
-
/**
|
|
91
|
+
/** 头部与内容之间是否显示虚线分隔,有 #header slot 时默认 true */
|
|
102
92
|
divider?: boolean;
|
|
103
93
|
/** body 是否有内边距,默认 true */
|
|
104
94
|
padding?: boolean;
|
|
105
95
|
}
|
|
106
96
|
|
|
107
97
|
const props = withDefaults(defineProps<Props>(), {
|
|
108
|
-
title: "",
|
|
109
|
-
extra: "",
|
|
110
98
|
border: true,
|
|
99
|
+
borderColor: "",
|
|
100
|
+
borderStyle: "solid",
|
|
101
|
+
borderWidth: "",
|
|
111
102
|
radius: "xl",
|
|
112
103
|
divider: undefined,
|
|
113
104
|
padding: true,
|
|
114
105
|
});
|
|
115
106
|
|
|
107
|
+
const rootStyle = computed<Record<string, string>>(() => {
|
|
108
|
+
const s: Record<string, string> = {};
|
|
109
|
+
if (props.borderColor) s["--card-border-color"] = props.borderColor;
|
|
110
|
+
if (props.borderStyle && props.borderStyle !== "solid") s["--card-border-style"] = props.borderStyle;
|
|
111
|
+
if (props.borderWidth) s["--card-border-width"] = props.borderWidth;
|
|
112
|
+
return s;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const SIDE_MAP: Record<string, string> = {
|
|
116
|
+
t: "top", top: "top",
|
|
117
|
+
r: "right", right: "right",
|
|
118
|
+
b: "bottom", bottom: "bottom",
|
|
119
|
+
l: "left", left: "left",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const borderClasses = computed<string[]>(() => {
|
|
123
|
+
if (props.border === false) return [];
|
|
124
|
+
if (props.border === true) return ["hlw-card--bordered"];
|
|
125
|
+
|
|
126
|
+
const sides = Array.isArray(props.border)
|
|
127
|
+
? props.border
|
|
128
|
+
: String(props.border).trim().split(/\s+/).filter(Boolean);
|
|
129
|
+
|
|
130
|
+
const seen = new Set<string>();
|
|
131
|
+
const classes: string[] = [];
|
|
132
|
+
for (const s of sides) {
|
|
133
|
+
const side = SIDE_MAP[s.toLowerCase()];
|
|
134
|
+
if (side && !seen.has(side)) {
|
|
135
|
+
seen.add(side);
|
|
136
|
+
classes.push(`hlw-card--border-${side}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return classes.length === 4 ? ["hlw-card--bordered"] : classes;
|
|
140
|
+
});
|
|
141
|
+
|
|
116
142
|
const slots = useSlots();
|
|
117
143
|
|
|
118
|
-
const hasHeader = computed(
|
|
119
|
-
() => !!(props.title || props.extra || slots.header || slots["header-left"] || slots["header-right"]),
|
|
120
|
-
);
|
|
144
|
+
const hasHeader = computed(() => !!slots.header);
|
|
121
145
|
|
|
122
146
|
const hasFooter = computed(
|
|
123
147
|
() => !!(slots.footer || slots["footer-left"] || slots["footer-right"]),
|
|
@@ -142,46 +166,46 @@ const showDivider = computed(() => {
|
|
|
142
166
|
&--radius-lg { border-radius: var(--radius-lg, 24rpx); }
|
|
143
167
|
&--radius-xl { border-radius: var(--radius-xl, 32rpx); }
|
|
144
168
|
|
|
145
|
-
/* 边框 */
|
|
169
|
+
/* 边框 — width / style / color 全部走 CSS 变量,未设置时回落 */
|
|
146
170
|
&--bordered {
|
|
147
|
-
border:
|
|
171
|
+
border:
|
|
172
|
+
var(--card-border-width, 1rpx)
|
|
173
|
+
var(--card-border-style, solid)
|
|
174
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* 边框 — 单边 */
|
|
178
|
+
&--border-top {
|
|
179
|
+
border-top:
|
|
180
|
+
var(--card-border-width, 1rpx)
|
|
181
|
+
var(--card-border-style, solid)
|
|
182
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
183
|
+
}
|
|
184
|
+
&--border-right {
|
|
185
|
+
border-right:
|
|
186
|
+
var(--card-border-width, 1rpx)
|
|
187
|
+
var(--card-border-style, solid)
|
|
188
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
189
|
+
}
|
|
190
|
+
&--border-bottom {
|
|
191
|
+
border-bottom:
|
|
192
|
+
var(--card-border-width, 1rpx)
|
|
193
|
+
var(--card-border-style, solid)
|
|
194
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
195
|
+
}
|
|
196
|
+
&--border-left {
|
|
197
|
+
border-left:
|
|
198
|
+
var(--card-border-width, 1rpx)
|
|
199
|
+
var(--card-border-style, solid)
|
|
200
|
+
var(--card-border-color, var(--border-color, #e2e8f0));
|
|
148
201
|
}
|
|
149
202
|
}
|
|
150
203
|
|
|
151
|
-
/* 头部 */
|
|
204
|
+
/* 头部 wrapper(#header slot) */
|
|
152
205
|
.hlw-card-header {
|
|
153
206
|
width: 100%;
|
|
154
207
|
}
|
|
155
208
|
|
|
156
|
-
.hlw-card-header-inner {
|
|
157
|
-
display: flex;
|
|
158
|
-
align-items: center;
|
|
159
|
-
justify-content: space-between;
|
|
160
|
-
padding: 24rpx 28rpx;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.hlw-card-header-left {
|
|
164
|
-
flex: 1;
|
|
165
|
-
min-width: 0;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.hlw-card-header-right {
|
|
169
|
-
flex-shrink: 0;
|
|
170
|
-
margin-left: 16rpx;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.hlw-card-title {
|
|
174
|
-
font-size: var(--font-sm, 24rpx);
|
|
175
|
-
font-weight: 700;
|
|
176
|
-
color: #1e293b;
|
|
177
|
-
letter-spacing: 0.02em;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.hlw-card-extra {
|
|
181
|
-
font-size: var(--font-xs, 20rpx);
|
|
182
|
-
color: #94a3b8;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
209
|
/* 虚线分隔 */
|
|
186
210
|
.hlw-card-divider {
|
|
187
211
|
width: 100%;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="hlw-card-header">
|
|
3
|
+
<view class="hlw-card-header__left">
|
|
4
|
+
<slot name="left">
|
|
5
|
+
<text v-if="icon" class="hlw-card-header__icon" :class="icon" />
|
|
6
|
+
<text v-if="title" class="hlw-card-header__title">{{ title }}</text>
|
|
7
|
+
</slot>
|
|
8
|
+
</view>
|
|
9
|
+
<view v-if="hasRight" class="hlw-card-header__right">
|
|
10
|
+
<slot name="right">
|
|
11
|
+
<text v-if="extra" class="hlw-card-header__extra">{{ extra }}</text>
|
|
12
|
+
</slot>
|
|
13
|
+
</view>
|
|
14
|
+
</view>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
import { computed, useSlots } from "vue";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* hlw-card-header — 卡片头部独立组件
|
|
22
|
+
*
|
|
23
|
+
* 三种用法:
|
|
24
|
+
*
|
|
25
|
+
* @example A. 直接当 <hlw-card> 的 default slot 子元素(注意把 hlw-card 的 padding 关掉,否则会双重 padding)
|
|
26
|
+
* ```vue
|
|
27
|
+
* <hlw-card :padding="false">
|
|
28
|
+
* <hlw-card-header title="标题" icon="i-fa6-solid-heart-pulse text-rose-500" extra="副标题" />
|
|
29
|
+
* <view style="padding: 24rpx 28rpx">body</view>
|
|
30
|
+
* </hlw-card>
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example B. 放进 <hlw-card> 的 #header slot(保留 body 默认 padding)
|
|
34
|
+
* ```vue
|
|
35
|
+
* <hlw-card>
|
|
36
|
+
* <template #header>
|
|
37
|
+
* <hlw-card-header title="标题" icon="i-fa6-solid-star" extra="副标题" />
|
|
38
|
+
* </template>
|
|
39
|
+
* body
|
|
40
|
+
* </hlw-card>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example C. 完全独立(不必嵌在 hlw-card 里)
|
|
44
|
+
* ```vue
|
|
45
|
+
* <hlw-card-header title="独立标题">
|
|
46
|
+
* <template #right><button>操作</button></template>
|
|
47
|
+
* </hlw-card-header>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
interface Props {
|
|
51
|
+
/** 标题文字 */
|
|
52
|
+
title?: string;
|
|
53
|
+
/** 图标 class(iconify 或自定义),如 `"i-fa6-solid-heart-pulse text-rose-500"` */
|
|
54
|
+
icon?: string;
|
|
55
|
+
/** 右侧附加文字(无 right slot 时显示) */
|
|
56
|
+
extra?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
60
|
+
title: "",
|
|
61
|
+
icon: "",
|
|
62
|
+
extra: "",
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const slots = useSlots();
|
|
66
|
+
|
|
67
|
+
const hasRight = computed(() => !!(slots.right || props.extra));
|
|
68
|
+
|
|
69
|
+
defineOptions({
|
|
70
|
+
name: "HlwCardHeader",
|
|
71
|
+
});
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<style lang="scss" scoped>
|
|
75
|
+
.hlw-card-header {
|
|
76
|
+
width: 100%;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: space-between;
|
|
80
|
+
padding: 24rpx 28rpx;
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.hlw-card-header__left {
|
|
85
|
+
flex: 1;
|
|
86
|
+
min-width: 0;
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
gap: 12rpx;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.hlw-card-header__right {
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
margin-left: 16rpx;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.hlw-card-header__icon {
|
|
98
|
+
font-size: var(--font-base, 28rpx);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.hlw-card-header__title {
|
|
102
|
+
font-size: var(--font-sm, 24rpx);
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
color: var(--text-primary, #1e293b);
|
|
105
|
+
letter-spacing: 0.02em;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.hlw-card-header__extra {
|
|
109
|
+
font-size: var(--font-xs, 20rpx);
|
|
110
|
+
color: var(--text-subtle, #94a3b8);
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
<scroll-view
|
|
15
15
|
class="hlw-page-content"
|
|
16
|
+
:class="bodyClass"
|
|
17
|
+
:style="bodyStyle"
|
|
16
18
|
:scroll-y="true"
|
|
17
19
|
:enable-flex="true"
|
|
18
20
|
:enhanced="true"
|
|
@@ -39,16 +41,25 @@ defineOptions({
|
|
|
39
41
|
inheritAttrs: false,
|
|
40
42
|
});
|
|
41
43
|
|
|
44
|
+
type ClassValue = string | Record<string, boolean> | Array<string | Record<string, boolean>>;
|
|
45
|
+
type StyleValue = string | Record<string, string | number>;
|
|
46
|
+
|
|
42
47
|
interface Props {
|
|
43
48
|
title?: string;
|
|
44
49
|
isBack?: boolean;
|
|
45
50
|
bgClass?: string;
|
|
51
|
+
/** 透传到 scroll-view 内容区的 class,常用于直接挂 .container */
|
|
52
|
+
bodyClass?: ClassValue;
|
|
53
|
+
/** 透传到 scroll-view 内容区的 style */
|
|
54
|
+
bodyStyle?: StyleValue;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
const props = withDefaults(defineProps<Props>(), {
|
|
49
58
|
title: "",
|
|
50
59
|
isBack: false,
|
|
51
60
|
bgClass: "",
|
|
61
|
+
bodyClass: "",
|
|
62
|
+
bodyStyle: "",
|
|
52
63
|
});
|
|
53
64
|
</script>
|
|
54
65
|
|