@hlw-uni/mp-vue 2.1.71 → 2.1.98

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 (86) hide show
  1. package/README.md +230 -386
  2. package/dist/app.d.ts +1 -1
  3. package/dist/core/index.d.ts +3 -0
  4. package/dist/core/{theme.d.ts → theme/index.d.ts} +6 -1
  5. package/dist/hlw.d.ts +1 -10
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js +834 -804
  8. package/dist/index.mjs +836 -806
  9. package/dist/utils/ad/index.d.ts +61 -0
  10. package/dist/{composables/utils → utils/common}/index.d.ts +1 -24
  11. package/dist/{composables → utils}/device/index.d.ts +7 -8
  12. package/dist/utils/index.d.ts +5 -0
  13. package/dist/{composables → utils}/navigator/index.d.ts +14 -30
  14. package/dist/{composables → utils}/request/client.d.ts +2 -8
  15. package/dist/{composables → utils}/request/index.d.ts +1 -1
  16. package/dist/{composables → utils}/request/service.d.ts +2 -2
  17. package/package.json +1 -1
  18. package/src/app.ts +2 -2
  19. package/src/components/hlw-add-mini/README.md +56 -9
  20. package/src/components/hlw-add-mini/index.vue +12 -9
  21. package/src/components/hlw-avatar-upload/index.vue +103 -0
  22. package/src/components/hlw-back-top/index.vue +1 -1
  23. package/src/components/hlw-button/index.vue +5 -16
  24. package/src/components/hlw-canvas/index.vue +7 -7
  25. package/src/components/hlw-cell/index.vue +1 -1
  26. package/src/components/{hlw-custom/hlw-custom.vue → hlw-custom-service/hlw-custom-service.vue} +27 -15
  27. package/src/components/hlw-empty/index.vue +14 -0
  28. package/src/components/hlw-header/index.vue +1 -1
  29. package/src/components/hlw-menu/index.vue +40 -37
  30. package/src/components/hlw-nav-bar/index.vue +23 -8
  31. package/src/components/hlw-nickname/index.vue +185 -0
  32. package/src/components/hlw-notice-bar/index.vue +23 -0
  33. package/src/components/hlw-page/index.vue +27 -8
  34. package/src/components/hlw-paging/index.vue +36 -0
  35. package/src/components/hlw-popup/index.vue +23 -0
  36. package/src/components/hlw-reward-ad/index.vue +133 -0
  37. package/src/components/hlw-search/index.vue +25 -0
  38. package/src/components/hlw-sheet/index.vue +1 -1
  39. package/src/components/hlw-status-bar/index.vue +113 -0
  40. package/src/components/hlw-tag/index.vue +22 -0
  41. package/src/core/index.ts +3 -0
  42. package/src/{composables → core}/msg/index.ts +0 -1
  43. package/src/{composables → core}/refs/index.ts +0 -1
  44. package/src/core/theme/index.ts +115 -0
  45. package/src/hlw.ts +1 -21
  46. package/src/index.ts +3 -11
  47. package/src/utils/ad/README.md +114 -0
  48. package/src/utils/ad/index.ts +234 -0
  49. package/src/{composables/utils → utils/common}/index.ts +1 -27
  50. package/src/{composables → utils}/device/index.ts +13 -21
  51. package/src/utils/index.ts +5 -0
  52. package/src/{composables → utils}/navigator/index.ts +30 -31
  53. package/src/{composables → utils}/request/client.ts +3 -11
  54. package/src/{composables → utils}/request/index.ts +1 -1
  55. package/src/{composables → utils}/request/service.ts +5 -6
  56. package/src/{composables → utils}/request/types.ts +0 -1
  57. package/dist/composables/ad/index.d.ts +0 -42
  58. package/dist/composables/index.d.ts +0 -11
  59. package/src/composables/ad/README.md +0 -58
  60. package/src/composables/ad/index.ts +0 -227
  61. package/src/composables/device/README.md +0 -50
  62. package/src/composables/index.ts +0 -38
  63. package/src/composables/msg/README.md +0 -79
  64. package/src/composables/navigator/README.md +0 -71
  65. package/src/composables/refs/README.md +0 -40
  66. package/src/composables/request/README.md +0 -124
  67. package/src/composables/share/README.md +0 -53
  68. package/src/composables/utils/README.md +0 -81
  69. package/src/core/theme.ts +0 -62
  70. /package/dist/{composables → core}/msg/index.d.ts +0 -0
  71. /package/dist/{composables → core}/refs/index.d.ts +0 -0
  72. /package/dist/{composables → core}/share/index.d.ts +0 -0
  73. /package/dist/{composables → utils}/request/adapters/alist.d.ts +0 -0
  74. /package/dist/{composables → utils}/request/adapters/base.d.ts +0 -0
  75. /package/dist/{composables → utils}/request/adapters/cos.d.ts +0 -0
  76. /package/dist/{composables → utils}/request/adapters/index.d.ts +0 -0
  77. /package/dist/{composables → utils}/request/adapters/oss.d.ts +0 -0
  78. /package/dist/{composables → utils}/request/adapters/qiniu.d.ts +0 -0
  79. /package/dist/{composables → utils}/request/types.d.ts +0 -0
  80. /package/src/{composables → core}/share/index.ts +0 -0
  81. /package/src/{composables → utils}/request/adapters/alist.ts +0 -0
  82. /package/src/{composables → utils}/request/adapters/base.ts +0 -0
  83. /package/src/{composables → utils}/request/adapters/cos.ts +0 -0
  84. /package/src/{composables → utils}/request/adapters/index.ts +0 -0
  85. /package/src/{composables → utils}/request/adapters/oss.ts +0 -0
  86. /package/src/{composables → utils}/request/adapters/qiniu.ts +0 -0
@@ -4,15 +4,8 @@
4
4
  <text class="contact-title">{{ title }}</text>
5
5
  <text class="contact-desc">{{ desc }}</text>
6
6
  </view>
7
- <button
8
- class="contact-button"
9
- open-type="contact"
10
- :send-message-title="resolvedContact.title"
11
- :send-message-path="resolvedContact.path"
12
- :send-message-img="resolvedContact.img"
13
- :show-message-card="resolvedContact.show"
14
- >
15
- <text class="i-ri-customer-service-line contact-button-icon" />
7
+ <button class="contact-button" open-type="contact" :send-message-title="resolvedContact.title" :send-message-path="resolvedContact.path" :send-message-img="resolvedContact.img" :show-message-card="resolvedContact.show">
8
+ <span class="iconfont icon-service" />
16
9
  <text class="contact-button-text">{{ resolvedBtnTitle }}</text>
17
10
  </button>
18
11
  </view>
@@ -21,6 +14,30 @@
21
14
  <script setup lang="ts">
22
15
  import { computed } from "vue";
23
16
 
17
+ /**
18
+ * HlwCustomService — 客服卡片自定义组件
19
+ *
20
+ * 用于引导用户点击联系微信客服,支持配置标题、描述、按钮文案及原生客服卡片参数。
21
+ *
22
+ * @props
23
+ * title - 客服卡片主标题
24
+ * desc - 客服卡片描述/说明文字
25
+ * btn_title - 客服按钮文案,默认 "联系客服"
26
+ * contact - 微信原生客服卡片配置项
27
+ *
28
+ * @example
29
+ * ```vue
30
+ * <HlwCustomService
31
+ * title="专属客服"
32
+ * desc="遇到问题?点击咨询您的专属客服"
33
+ * btn_title="去咨询"
34
+ * :contact="{ send_message_title: '咨询标题' }"
35
+ * />
36
+ * ```
37
+ */
38
+
39
+ defineOptions({ name: "HlwCustomService" });
40
+
24
41
  interface ContactConfig {
25
42
  send_message_title?: string;
26
43
  send_message_path?: string;
@@ -38,7 +55,7 @@ const props = withDefaults(
38
55
  {
39
56
  btn_title: "",
40
57
  contact: () => ({}),
41
- }
58
+ },
42
59
  );
43
60
 
44
61
  const resolvedBtnTitle = computed(() => {
@@ -106,11 +123,6 @@ const resolvedContact = computed(() => {
106
123
  line-height: 68rpx;
107
124
  }
108
125
 
109
- .contact-button-icon {
110
- width: var(--font-sm);
111
- height: var(--font-sm);
112
- }
113
-
114
126
  .contact-button-text {
115
127
  color: inherit;
116
128
  font-size: var(--font-sm);
@@ -8,6 +8,20 @@
8
8
  </template>
9
9
 
10
10
  <script setup lang="ts">
11
+ /**
12
+ * HlwEmpty — 空状态组件
13
+ *
14
+ * 用于页面或区域无数据时的占位展示,支持自定义提示文本和占位图片。
15
+ *
16
+ * @props
17
+ * text - 占位提示文案,默认 "暂无数据"
18
+ * image - 占位图地址,未提供时默认渲染原生盒子 icon
19
+ *
20
+ * @example
21
+ * ```vue
22
+ * <HlwEmpty text="空空如也,快去添加内容吧" />
23
+ * ```
24
+ */
11
25
  defineProps<{
12
26
  text?: string;
13
27
  image?: string;
@@ -9,7 +9,7 @@
9
9
  <!-- 返回按钮 -->
10
10
  <view class="header-back" @click="handleBack">
11
11
  <slot name="back-icon">
12
- <text class="i-fa6-solid-chevron-left header-back-icon"></text>
12
+ <span class="i-fa6-solid-chevron-left header-back-icon"></span>
13
13
  </slot>
14
14
  </view>
15
15
 
@@ -9,17 +9,18 @@
9
9
  <!-- 列表模式 -->
10
10
  <template v-if="props.mode === 'list'">
11
11
  <template v-for="(item, index) in visibleItems" :key="index">
12
- <navigator v-if="item.url" :url="item.url" class="hlw-menu-item" hover-class="hlw-menu-item--active">
12
+ <navigator v-if="item.url" :url="item.url" animation-type="none" class="hlw-menu-item" hover-class="hlw-menu-item--active">
13
13
  <view class="hlw-menu-left">
14
14
  <view class="hlw-menu-icon" :class="`hlw-menu-icon--${item.iconTheme || 'slate'}`">
15
- <text :class="item.icon"></text>
15
+ <image v-if="isImageIcon(item.icon)" :src="item.icon" class="hlw-menu-icon-img" mode="aspectFit" />
16
+ <span v-else :class="item.icon"></span>
16
17
  </view>
17
18
  <text class="hlw-menu-label">{{ item.label }}</text>
18
19
  </view>
19
20
  <view class="hlw-menu-right">
20
21
  <text v-if="item.value" class="hlw-menu-value">{{ item.value }}</text>
21
22
  <text v-if="item.tag" class="hlw-menu-tag" :class="[`hlw-menu-tag--${item.tagTheme || 'rose'}`, item.tagPulse ? 'hlw-menu-tag-pulse' : '']">{{ item.tag }}</text>
22
- <text class="i-fa6-solid-chevron-right hlw-menu-arrow"></text>
23
+ <span class="i-fa6-solid-chevron-right hlw-menu-arrow"></span>
23
24
  </view>
24
25
  </navigator>
25
26
 
@@ -39,30 +40,32 @@
39
40
  >
40
41
  <view class="hlw-menu-left">
41
42
  <view class="hlw-menu-icon" :class="`hlw-menu-icon--${item.iconTheme || 'slate'}`">
42
- <text :class="item.icon"></text>
43
+ <image v-if="isImageIcon(item.icon)" :src="item.icon" class="hlw-menu-icon-img" mode="aspectFit" />
44
+ <span v-else :class="item.icon"></span>
43
45
  </view>
44
46
  <text class="hlw-menu-label">{{ item.label }}</text>
45
47
  </view>
46
48
  <view class="hlw-menu-right">
47
- <text v-if="item.loading" class="i-fa6-solid-circle-notch hlw-menu-spin hlw-menu-muted"></text>
49
+ <span v-if="item.loading" class="i-fa6-solid-circle-notch hlw-menu-spin hlw-menu-muted"></span>
48
50
  <text v-if="item.value" class="hlw-menu-value">{{ item.value }}</text>
49
51
  <text v-if="item.tag" class="hlw-menu-tag" :class="[`hlw-menu-tag--${item.tagTheme || 'rose'}`, item.tagPulse ? 'hlw-menu-tag-pulse' : '']">{{ item.tag }}</text>
50
- <text class="i-fa6-solid-chevron-right hlw-menu-arrow"></text>
52
+ <span class="i-fa6-solid-chevron-right hlw-menu-arrow"></span>
51
53
  </view>
52
54
  </button>
53
55
 
54
56
  <view v-else class="hlw-menu-item" hover-class="hlw-menu-item--active" @click="handleClick(item)">
55
57
  <view class="hlw-menu-left">
56
58
  <view class="hlw-menu-icon" :class="`hlw-menu-icon--${item.iconTheme || 'slate'}`">
57
- <text :class="item.icon"></text>
59
+ <image v-if="isImageIcon(item.icon)" :src="item.icon" class="hlw-menu-icon-img" mode="aspectFit" />
60
+ <span v-else :class="item.icon"></span>
58
61
  </view>
59
62
  <text class="hlw-menu-label">{{ item.label }}</text>
60
63
  </view>
61
64
  <view class="hlw-menu-right">
62
- <text v-if="item.loading" class="i-fa6-solid-circle-notch hlw-menu-spin hlw-menu-muted"></text>
65
+ <span v-if="item.loading" class="i-fa6-solid-circle-notch hlw-menu-spin hlw-menu-muted"></span>
63
66
  <text v-if="item.value" class="hlw-menu-value">{{ item.value }}</text>
64
67
  <text v-if="item.tag" class="hlw-menu-tag" :class="[`hlw-menu-tag--${item.tagTheme || 'rose'}`, item.tagPulse ? 'hlw-menu-tag-pulse' : '']">{{ item.tag }}</text>
65
- <text class="i-fa6-solid-chevron-right hlw-menu-arrow"></text>
68
+ <span class="i-fa6-solid-chevron-right hlw-menu-arrow"></span>
66
69
  </view>
67
70
  </view>
68
71
 
@@ -73,10 +76,11 @@
73
76
  <!-- 宫格模式 -->
74
77
  <view v-else class="hlw-menu-grid" :style="{ gridTemplateColumns: `repeat(${props.columns}, 1fr)` }">
75
78
  <template v-for="(item, index) in visibleItems" :key="index">
76
- <navigator v-if="item.url" :url="item.url" class="hlw-menu-grid-item" hover-class="hlw-menu-grid-item--active">
79
+ <navigator v-if="item.url" :url="item.url" animation-type="none" class="hlw-menu-grid-item" hover-class="hlw-menu-grid-item--active">
77
80
  <view class="hlw-menu-grid-icon-wrap">
78
81
  <view class="hlw-menu-icon hlw-menu-icon--grid" :class="`hlw-menu-icon--${item.iconTheme || 'slate'}`">
79
- <text :class="item.icon"></text>
82
+ <image v-if="isImageIcon(item.icon)" :src="item.icon" class="hlw-menu-icon-img" mode="aspectFit" />
83
+ <span v-else :class="item.icon"></span>
80
84
  </view>
81
85
  <text v-if="item.tag" class="hlw-menu-badge" :class="[`hlw-menu-tag--${item.tagTheme || 'rose'}`, item.tagPulse ? 'hlw-menu-tag-pulse' : '']">{{ item.tag }}</text>
82
86
  </view>
@@ -86,7 +90,8 @@
86
90
  <view v-else class="hlw-menu-grid-item" hover-class="hlw-menu-grid-item--active" @click="handleClick(item)">
87
91
  <view class="hlw-menu-grid-icon-wrap">
88
92
  <view class="hlw-menu-icon hlw-menu-icon--grid" :class="`hlw-menu-icon--${item.iconTheme || 'slate'}`">
89
- <text :class="item.icon"></text>
93
+ <image v-if="isImageIcon(item.icon)" :src="item.icon" class="hlw-menu-icon-img" mode="aspectFit" />
94
+ <span v-else :class="item.icon"></span>
90
95
  </view>
91
96
  <text v-if="item.tag" class="hlw-menu-badge" :class="[`hlw-menu-tag--${item.tagTheme || 'rose'}`, item.tagPulse ? 'hlw-menu-tag-pulse' : '']">{{ item.tag }}</text>
92
97
  </view>
@@ -179,6 +184,11 @@ const emit = defineEmits<{
179
184
  getphonenumber: [item: HlwMenuItem, event: unknown];
180
185
  }>();
181
186
 
187
+ const isImageIcon = (icon?: string) => {
188
+ if (!icon) return false;
189
+ return icon.startsWith("http") || icon.startsWith("/") || icon.startsWith(".") || icon.includes("/");
190
+ };
191
+
182
192
  const visibleItems = computed(() => props.items.filter((item) => item.visible !== false));
183
193
 
184
194
  const handleClick = (item: HlwMenuItem) => {
@@ -196,13 +206,13 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
196
206
 
197
207
  <style lang="scss" scoped>
198
208
  .hlw-menu {
199
- --hlw-menu-icon-size: var(--font-sm, 24rpx);
200
- --hlw-menu-icon-box-size: calc(var(--hlw-menu-icon-size) + 40rpx);
201
- --hlw-menu-grid-icon-size: var(--font-lg, 36rpx);
209
+ --hlw-menu-icon-size: var(--font-md, 32rpx);
210
+ --hlw-menu-icon-box-size: var(--hlw-menu-icon-size);
211
+ --hlw-menu-grid-icon-size: var(--font-xl, 40rpx);
202
212
  --hlw-menu-grid-icon-box-size: calc(var(--hlw-menu-grid-icon-size) + 52rpx);
203
213
  background: var(--surface-card, #ffffff);
204
214
  border-radius: var(--radius-lg, 24rpx);
205
- border: 1rpx solid var(--border-color, #e2e8f0);
215
+ border: 1rpx solid var(--border-color-light, #ececec);
206
216
  overflow: hidden;
207
217
  width: 100%;
208
218
 
@@ -224,7 +234,7 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
224
234
 
225
235
  .hlw-menu-title-divider {
226
236
  height: 0;
227
- border-bottom: 1rpx dashed var(--border-color, #e2e8f0);
237
+ border-bottom: 1rpx dashed var(--border-color-light, #ececec);
228
238
  }
229
239
 
230
240
  /* ========== 列表模式 ========== */
@@ -232,7 +242,7 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
232
242
  display: flex;
233
243
  align-items: center;
234
244
  justify-content: space-between;
235
- padding: 24rpx 32rpx;
245
+ padding: 32rpx 36rpx;
236
246
 
237
247
  &--active {
238
248
  background: var(--surface-card-muted, #f8fafc);
@@ -246,7 +256,7 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
246
256
  justify-content: space-between;
247
257
  width: 100%;
248
258
  margin: 0;
249
- padding: 24rpx 32rpx;
259
+ padding: 32rpx 36rpx;
250
260
  background: transparent;
251
261
  color: inherit;
252
262
  font-size: inherit;
@@ -264,15 +274,15 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
264
274
  }
265
275
 
266
276
  .hlw-menu-divider {
267
- margin: 0 32rpx;
277
+ margin: 0 36rpx;
268
278
  height: 0;
269
- border-bottom: 1rpx dashed var(--border-color, #e2e8f0);
279
+ border-bottom: 1rpx dashed var(--border-color-light, #ececec);
270
280
  }
271
281
 
272
282
  .hlw-menu-left {
273
283
  display: flex;
274
284
  align-items: center;
275
- gap: 24rpx;
285
+ gap: 20rpx;
276
286
  }
277
287
 
278
288
  .hlw-menu-right {
@@ -328,13 +338,14 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
328
338
  .hlw-menu-icon {
329
339
  width: var(--hlw-menu-icon-box-size);
330
340
  height: var(--hlw-menu-icon-box-size);
331
- border-radius: var(--radius-md, 16rpx);
332
341
  display: flex;
333
342
  align-items: center;
334
343
  justify-content: center;
335
344
  flex-shrink: 0;
336
345
 
337
- text {
346
+ text,
347
+ span,
348
+ .hlw-menu-icon-img {
338
349
  width: var(--hlw-menu-icon-size);
339
350
  height: var(--hlw-menu-icon-size);
340
351
  font-size: var(--hlw-menu-icon-size);
@@ -344,8 +355,9 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
344
355
  &--grid {
345
356
  width: var(--hlw-menu-grid-icon-box-size);
346
357
  height: var(--hlw-menu-grid-icon-box-size);
347
- border-radius: var(--radius-lg, 24rpx);
348
- text {
358
+ text,
359
+ span,
360
+ .hlw-menu-icon-img {
349
361
  width: var(--hlw-menu-grid-icon-size);
350
362
  height: var(--hlw-menu-grid-icon-size);
351
363
  font-size: var(--hlw-menu-grid-icon-size);
@@ -354,48 +366,39 @@ const handleGetPhoneNumber = (item: HlwMenuItem, event: unknown) => {
354
366
  }
355
367
 
356
368
  &--orange {
357
- background: #fff7ed;
358
369
  color: #f97316;
359
370
  }
360
371
  &--purple {
361
- background: #faf5ff;
362
372
  color: #a855f7;
363
373
  }
364
374
  &--wechat {
365
- background: #f0fdf4;
366
375
  color: #07c160;
367
376
  }
368
377
  &--cyan {
369
- background: #ecfeff;
370
378
  color: #06b6d4;
371
379
  }
372
380
  &--emerald {
373
- background: #ecfdf5;
374
381
  color: #10b981;
375
382
  }
376
383
  &--slate {
377
- background: #f1f5f9;
378
384
  color: #64748b;
379
385
  }
380
386
  &--rose {
381
- background: #fff1f2;
382
387
  color: #f43f5e;
383
388
  }
384
389
  &--blue {
385
- background: var(--primary-light, rgba(76, 68, 239, 0.12));
386
390
  color: var(--primary-color, #3b82f6);
387
391
  }
388
392
  &--red {
389
- background: #fef2f2;
390
393
  color: #ef4444;
391
394
  }
392
395
  }
393
396
 
394
397
  /* ========== 标签 / 角标 ========== */
395
398
  .hlw-menu-label {
396
- font-size: var(--font-base, 28rpx);
397
- font-weight: 500;
399
+ font-size: var(--font-26, 26rpx);
398
400
  color: var(--text-secondary, #334155);
401
+ letter-spacing: 1rpx;
399
402
  }
400
403
 
401
404
  .hlw-menu-value {
@@ -3,7 +3,7 @@
3
3
  <view :style="bar_style"></view>
4
4
  <view class="header" :style="{ height: header_height + 'px' }">
5
5
  <view @tap="tapBack" class="left" v-if="props.isBack">
6
- <text class="i-fa6-solid-chevron-left icon-left"></text>
6
+ <span class="i-fa6-solid-chevron-left icon-left"></span>
7
7
  </view>
8
8
  <text class="title">{{ title }}</text>
9
9
  </view>
@@ -15,6 +15,21 @@
15
15
  </template>
16
16
 
17
17
  <script lang="ts" setup>
18
+ /**
19
+ * HlwNavBar — 自定义导航栏组件
20
+ *
21
+ * 自适应状态栏高度与胶囊按钮,完美替代微信小程序原生导航栏。支持不同主题配色、返回按钮及自适应高度。
22
+ *
23
+ * @props
24
+ * title - 导航栏标题文字
25
+ * isBack - 是否显示返回按钮,默认 false;点击自动回退或回到首页
26
+ * isBar - 是否占用状态栏高度,默认 false
27
+ *
28
+ * @example
29
+ * ```vue
30
+ * <hlw-nav-bar title="设置中心" is-back />
31
+ * ```
32
+ */
18
33
  import { computed, ref } from "vue";
19
34
  import { useTheme } from "@/core";
20
35
 
@@ -82,11 +97,11 @@ function tapBack() {
82
97
  border-bottom: var(--navbar-border-bottom, 1rpx solid #e7e7e7);
83
98
 
84
99
  .title {
85
- color: var(--font-color, #303048);
100
+ color: var(--text-primary, #303048);
86
101
  }
87
102
 
88
103
  .icon-left {
89
- color: var(--font-color, #303048);
104
+ color: var(--text-primary, #303048);
90
105
  }
91
106
  }
92
107
 
@@ -96,11 +111,11 @@ function tapBack() {
96
111
  border-bottom: 1rpx solid rgba(226, 232, 240, 0);
97
112
 
98
113
  .title {
99
- color: var(--font-color, #303048);
114
+ color: var(--text-primary, #303048);
100
115
  }
101
116
 
102
117
  .icon-left {
103
- color: var(--font-color, #303048);
118
+ color: var(--text-primary, #303048);
104
119
  }
105
120
  }
106
121
 
@@ -154,9 +169,9 @@ function tapBack() {
154
169
  }
155
170
 
156
171
  .title {
157
- font-size: var(--navbar-font-size, 26rpx);
158
- letter-spacing: 1rpx;
159
- font-weight: normal;
172
+ font-size: var(--font-md, var(--navbar-font-size, 32rpx));
173
+ letter-spacing: 2rpx;
174
+ font-weight: 500;
160
175
  }
161
176
  }
162
177
 
@@ -0,0 +1,185 @@
1
+ <template>
2
+ <view class="hlw-nickname" @tap="openDialog">
3
+ <slot>
4
+ <text class="hlw-nickname__text">{{ props.text || '微信用户' }}</text>
5
+ </slot>
6
+
7
+ <!-- 修改昵称弹窗 -->
8
+ <root-portal v-if="showEditNicknameDialog">
9
+ <view class="nickname-modal" @tap="closeDialog">
10
+ <view class="nickname-dialog" @tap.stop>
11
+ <view class="dialog-title">修改昵称</view>
12
+ <view class="dialog-input-wrapper">
13
+ <input
14
+ class="dialog-input"
15
+ type="nickname"
16
+ v-model="tempNickname"
17
+ placeholder="请输入或获取微信昵称"
18
+ @blur="onNicknameBlur"
19
+ @input="onNicknameInput"
20
+ />
21
+ </view>
22
+ <view class="dialog-actions">
23
+ <button class="dialog-btn cancel-btn" @tap="closeDialog">取消</button>
24
+ <button class="dialog-btn confirm-btn" @tap="confirmNickname">确定</button>
25
+ </view>
26
+ </view>
27
+ </view>
28
+ </root-portal>
29
+ </view>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { ref } from "vue";
34
+
35
+ defineOptions({ name: "HlwNickname" });
36
+
37
+ interface Props {
38
+ /** 当前展示的昵称 */
39
+ text?: string;
40
+ }
41
+
42
+ const props = withDefaults(defineProps<Props>(), {
43
+ text: "",
44
+ });
45
+
46
+ const emit = defineEmits<{
47
+ (e: "onNick", nickname: string): void;
48
+ }>();
49
+
50
+ const showEditNicknameDialog = ref(false);
51
+ const tempNickname = ref("");
52
+
53
+ function openDialog() {
54
+ tempNickname.value = props.text || "";
55
+ showEditNicknameDialog.value = true;
56
+ }
57
+
58
+ function closeDialog() {
59
+ showEditNicknameDialog.value = false;
60
+ }
61
+
62
+ function onNicknameInput(e: any) {
63
+ tempNickname.value = e.detail?.value || "";
64
+ }
65
+
66
+ function onNicknameBlur(e: any) {
67
+ // 微信小程序特有:点击键盘上方微信昵称 suggestion 时,会触发 blur 并带上值
68
+ const value = e.detail?.value || "";
69
+ if (value) {
70
+ tempNickname.value = value;
71
+ }
72
+ }
73
+
74
+ function confirmNickname() {
75
+ const nickname = tempNickname.value?.trim();
76
+ if (!nickname) {
77
+ hlw.$msg.toast("昵称不能为空");
78
+ return;
79
+ }
80
+ if (nickname.length > 20) {
81
+ hlw.$msg.toast("昵称长度不能超过 20 个字符");
82
+ return;
83
+ }
84
+ emit("onNick", nickname);
85
+ closeDialog();
86
+ }
87
+ </script>
88
+
89
+ <style scoped>
90
+ .hlw-nickname {
91
+ display: inline-block;
92
+ }
93
+
94
+ /* 弹窗遮罩 */
95
+ .nickname-modal {
96
+ position: fixed;
97
+ left: 0;
98
+ top: 0;
99
+ right: 0;
100
+ bottom: 0;
101
+ z-index: 10000;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ background: rgba(0, 0, 0, 0.6);
106
+ backdrop-filter: blur(8rpx);
107
+ animation: fadeIn 0.2s ease-out;
108
+ }
109
+
110
+ /* 弹窗主体 */
111
+ .nickname-dialog {
112
+ width: 600rpx;
113
+ padding: 40rpx;
114
+ border-radius: 24rpx;
115
+ background: #ffffff;
116
+ box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.25);
117
+ animation: scaleIn 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
118
+ }
119
+
120
+ .dialog-title {
121
+ margin-bottom: 30rpx;
122
+ color: #1e293b;
123
+ text-align: center;
124
+ font-size: 32rpx;
125
+ font-weight: 600;
126
+ letter-spacing: 1rpx;
127
+ }
128
+
129
+ .dialog-input-wrapper {
130
+ margin-bottom: 40rpx;
131
+ padding: 20rpx 24rpx;
132
+ border: 2rpx solid #e2e8f0;
133
+ border-radius: 16rpx;
134
+ background: #f8fafc;
135
+ }
136
+
137
+ .dialog-input {
138
+ width: 100%;
139
+ height: 48rpx;
140
+ color: #1e293b;
141
+ font-size: 28rpx;
142
+ text-align: left;
143
+ }
144
+
145
+ .dialog-actions {
146
+ display: flex;
147
+ gap: 20rpx;
148
+ }
149
+
150
+ .dialog-btn {
151
+ flex: 1;
152
+ display: flex;
153
+ align-items: center;
154
+ justify-content: center;
155
+ height: 80rpx;
156
+ margin: 0;
157
+ font-size: 28rpx;
158
+ font-weight: 500;
159
+ border-radius: 16rpx;
160
+ }
161
+
162
+ .dialog-btn::after {
163
+ border: 0;
164
+ }
165
+
166
+ .cancel-btn {
167
+ background: #f1f5f9;
168
+ color: #64748b;
169
+ }
170
+
171
+ .confirm-btn {
172
+ background: #3b82f6;
173
+ color: #ffffff;
174
+ }
175
+
176
+ @keyframes fadeIn {
177
+ from { opacity: 0; }
178
+ to { opacity: 1; }
179
+ }
180
+
181
+ @keyframes scaleIn {
182
+ from { transform: scale(0.9); opacity: 0; }
183
+ to { transform: scale(1); opacity: 1; }
184
+ }
185
+ </style>
@@ -13,6 +13,29 @@
13
13
  </template>
14
14
 
15
15
  <script setup lang="ts">
16
+ /**
17
+ * HlwNoticeBar — 滚动通告栏
18
+ *
19
+ * 用于显示重要广播、紧急通告或系统消息。支持横向无缝滚动播放、可关闭状态及自定义图标/色彩。
20
+ *
21
+ * @props
22
+ * text - 通告文本内容
23
+ * scrollable - 是否开启滚动播放,默认 true
24
+ * closable - 是否显示关闭按钮,默认 false
25
+ * color - 文字颜色,默认 "#ed6a0c"
26
+ * background - 背景颜色,默认 "#fffbe8"
27
+ * speed - 滚动速度(每秒像素数),默认 60
28
+ * leftIcon - 左侧自定义图标 class,默认喇叭图标
29
+ *
30
+ * @events
31
+ * close - 点击关闭按钮时触发
32
+ * click - 点击通告内容区时触发
33
+ *
34
+ * @example
35
+ * ```vue
36
+ * <HlwNoticeBar text="系统将于今晚24点停机维护,给您带来的不便敬请谅解!" closable />
37
+ * ```
38
+ */
16
39
  import { computed, ref } from "vue";
17
40
 
18
41
  interface Props {