create-weapp-vite 2.0.23 → 2.0.25

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.
@@ -5,10 +5,10 @@ import fs2 from "fs-extra";
5
5
  import path2 from "pathe";
6
6
 
7
7
  // ../weapp-vite/package.json
8
- var version = "6.5.3";
8
+ var version = "6.6.0";
9
9
 
10
10
  // ../wevu/package.json
11
- var version2 = "2.1.7";
11
+ var version2 = "2.1.9";
12
12
 
13
13
  // src/enums.ts
14
14
  var TemplateName = /* @__PURE__ */ ((TemplateName2) => {
package/dist/cli.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  createProject
3
- } from "./chunk-VQI7VSSU.js";
3
+ } from "./chunk-HATWO324.js";
4
4
 
5
5
  // src/cli.ts
6
6
  import path from "path";
7
7
  import process from "process";
8
8
  import { confirm, input, select } from "@inquirer/prompts";
9
+ import logger from "@weapp-core/logger";
9
10
  import fs from "fs-extra";
10
11
  var cwd = process.cwd();
11
12
  async function run() {
@@ -58,8 +59,12 @@ async function run() {
58
59
  }
59
60
  var runPromise = run().catch(
60
61
  (err) => {
61
- console.error("\u2717 \u521B\u5EFA\u5931\u8D25:", err.message || err);
62
- console.log("\u2717 \u53D6\u6D88\u521B\u5EFA");
62
+ const message = err instanceof Error ? err.message : String(err);
63
+ if (message.toLowerCase().includes("cancel")) {
64
+ logger.warn("\u2717 \u5DF2\u53D6\u6D88\u521B\u5EFA");
65
+ return;
66
+ }
67
+ logger.error("\u2717 \u521B\u5EFA\u5931\u8D25:", message);
63
68
  }
64
69
  );
65
70
  export {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TemplateName,
3
3
  createProject
4
- } from "./chunk-VQI7VSSU.js";
4
+ } from "./chunk-HATWO324.js";
5
5
  export {
6
6
  TemplateName,
7
7
  createProject
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-weapp-vite",
3
3
  "type": "module",
4
- "version": "2.0.23",
4
+ "version": "2.0.25",
5
5
  "description": "create-weapp-vite",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  "fs-extra": "^11.3.3",
40
40
  "pathe": "^2.0.3",
41
41
  "pkg-types": "^2.3.0",
42
- "@weapp-core/logger": "^3.0.3"
42
+ "@weapp-core/logger": "^3.1.0"
43
43
  },
44
44
  "scripts": {
45
45
  "dev": "tsup --watch --sourcemap",
@@ -1,3 +1,4 @@
1
1
  {
2
- "HelloWorld": "/components/HelloWorld/index"
2
+ "HelloWorld": "/components/HelloWorld/index",
3
+ "InfoBanner": "/components/InfoBanner/index"
3
4
  }
@@ -14,10 +14,12 @@ type __WeappComponentImport<TModule, Fallback = {}> = 0 extends 1 & TModule ? Fa
14
14
  declare module 'wevu' {
15
15
  export interface GlobalComponents {
16
16
  HelloWorld: typeof import("./src/components/HelloWorld/index.vue")['default'];
17
+ InfoBanner: typeof import("./src/components/InfoBanner/index.vue")['default'];
17
18
  }
18
19
  }
19
20
 
20
21
  // 用于 TSX 支持
21
22
  declare global {
22
23
  const HelloWorld: typeof import("./src/components/HelloWorld/index.vue")['default']
24
+ const InfoBanner: typeof import("./src/components/InfoBanner/index.vue")['default']
23
25
  }
@@ -1,45 +1,473 @@
1
1
  <script setup lang="ts">
2
+ import { computed, ref, watch } from 'wevu'
3
+
4
+ type HighlightTone = 'up' | 'down' | 'flat'
5
+
6
+ interface HighlightItem {
7
+ key: string
8
+ label: string
9
+ value: string | number
10
+ tone?: HighlightTone
11
+ note?: string
12
+ }
13
+
14
+ interface FeatureItem {
15
+ id: string
16
+ title: string
17
+ group: 'core' | 'template' | 'engineering'
18
+ done?: boolean
19
+ level?: 'base' | 'advanced'
20
+ }
21
+
2
22
  const props = withDefaults(
3
23
  defineProps<{
4
24
  title?: string
5
25
  subtitle?: string
26
+ highlights?: HighlightItem[]
27
+ features?: FeatureItem[]
28
+ compact?: boolean
6
29
  }>(),
7
30
  {
8
31
  title: 'Hello WeVU',
9
32
  subtitle: '',
33
+ highlights: () => [],
34
+ features: () => [],
35
+ compact: false,
36
+ },
37
+ )
38
+
39
+ const emit = defineEmits<{
40
+ (e: 'action', payload: { type: 'toggle' | 'copy' | 'select' | 'stats', value?: string }): void
41
+ }>()
42
+
43
+ const expanded = ref(true)
44
+ const keyword = ref('')
45
+ const activeGroup = ref<'all' | FeatureItem['group']>('all')
46
+ const selectedFeatureId = ref('')
47
+ const searchHit = ref(0)
48
+ const searchMiss = ref(0)
49
+
50
+ watch(
51
+ () => props.features,
52
+ (nextFeatures) => {
53
+ if (!nextFeatures.length) {
54
+ selectedFeatureId.value = ''
55
+ return
56
+ }
57
+
58
+ const exists = nextFeatures.some(feature => feature.id === selectedFeatureId.value)
59
+ if (!selectedFeatureId.value || !exists) {
60
+ selectedFeatureId.value = nextFeatures[0]?.id ?? ''
61
+ }
62
+ },
63
+ { immediate: true },
64
+ )
65
+
66
+ const panelClass = computed(() => {
67
+ return props.compact ? 'hello-panel hello-panel-compact' : 'hello-panel'
68
+ })
69
+
70
+ const summaryText = computed(() => {
71
+ return `${props.highlights.length} 个指标 · ${props.features.length} 条能力`
72
+ })
73
+
74
+ const filteredFeatures = computed(() => {
75
+ const normalized = keyword.value.trim().toLowerCase()
76
+ return props.features.filter((feature) => {
77
+ const matchGroup = activeGroup.value === 'all' || feature.group === activeGroup.value
78
+ if (!matchGroup) {
79
+ return false
80
+ }
81
+ if (!normalized) {
82
+ return true
83
+ }
84
+ return feature.title.toLowerCase().includes(normalized)
85
+ })
86
+ })
87
+
88
+ const selectedFeature = computed(() => {
89
+ return props.features.find(feature => feature.id === selectedFeatureId.value)
90
+ })
91
+
92
+ const stats = computed(() => {
93
+ const total = props.features.length
94
+ const done = props.features.filter(feature => feature.done).length
95
+ const advanced = props.features.filter(feature => feature.level === 'advanced').length
96
+ const progress = total > 0 ? Math.round((done / total) * 100) : 0
97
+ return {
98
+ total,
99
+ done,
100
+ advanced,
101
+ progress,
102
+ }
103
+ })
104
+
105
+ const completionText = computed(() => {
106
+ return `${stats.value.done}/${stats.value.total} · ${stats.value.progress}%`
107
+ })
108
+
109
+ const groupTabs = computed(() => {
110
+ const base = [
111
+ { key: 'all', label: '全部' },
112
+ { key: 'core', label: '核心' },
113
+ { key: 'template', label: '模板' },
114
+ { key: 'engineering', label: '工程化' },
115
+ ] as const
116
+ return base
117
+ })
118
+
119
+ watch(
120
+ [keyword, filteredFeatures],
121
+ () => {
122
+ if (!keyword.value.trim()) {
123
+ return
124
+ }
125
+ if (filteredFeatures.value.length > 0) {
126
+ searchHit.value += 1
127
+ return
128
+ }
129
+ searchMiss.value += 1
130
+ },
131
+ )
132
+
133
+ watch(
134
+ () => stats.value.progress,
135
+ (progress) => {
136
+ emit('action', { type: 'stats', value: String(progress) })
10
137
  },
138
+ { immediate: true },
11
139
  )
140
+
141
+ function toneClass(tone?: HighlightTone) {
142
+ if (tone === 'up') {
143
+ return 'metric-value metric-up'
144
+ }
145
+ if (tone === 'down') {
146
+ return 'metric-value metric-down'
147
+ }
148
+ return 'metric-value metric-flat'
149
+ }
150
+
151
+ function toggleExpand() {
152
+ expanded.value = !expanded.value
153
+ emit('action', { type: 'toggle', value: String(expanded.value) })
154
+ }
155
+
156
+ function selectGroup(group: 'all' | FeatureItem['group']) {
157
+ activeGroup.value = group
158
+ }
159
+
160
+ function selectFeature(feature: FeatureItem) {
161
+ selectedFeatureId.value = feature.id
162
+ emit('action', { type: 'select', value: feature.title })
163
+ }
164
+
165
+ function copySelected() {
166
+ if (!selectedFeature.value) {
167
+ return
168
+ }
169
+ emit('action', { type: 'copy', value: selectedFeature.value.title })
170
+ }
12
171
  </script>
13
172
 
14
173
  <template>
15
- <view class="header">
16
- <text class="title">
17
- {{ props.title }}
18
- </text>
19
- <text v-if="props.subtitle" class="subtitle">
20
- {{ props.subtitle }}
21
- </text>
174
+ <view :class="panelClass">
175
+ <view class="head-row">
176
+ <view class="head-main">
177
+ <text class="title">
178
+ {{ props.title }}
179
+ </text>
180
+ <text v-if="props.subtitle" class="subtitle">
181
+ {{ props.subtitle }}
182
+ </text>
183
+ <text class="summary">
184
+ {{ summaryText }}
185
+ </text>
186
+ <text class="summary">
187
+ 完成度:{{ completionText }}
188
+ </text>
189
+ </view>
190
+ <button class="tiny-btn" @tap.stop="toggleExpand">
191
+ {{ expanded ? '收起' : '展开' }}
192
+ </button>
193
+ </view>
194
+
195
+ <view v-if="expanded" class="panel-body">
196
+ <view v-if="props.highlights.length" class="metrics">
197
+ <view v-for="item in props.highlights" :key="item.key" class="metric-item">
198
+ <text class="metric-label">
199
+ {{ item.label }}
200
+ </text>
201
+ <text :class="toneClass(item.tone)">
202
+ {{ item.value }}
203
+ </text>
204
+ <text v-if="item.note" class="metric-note">
205
+ {{ item.note }}
206
+ </text>
207
+ </view>
208
+ </view>
209
+
210
+ <view class="feature-toolbar">
211
+ <input v-model="keyword" class="search-input" placeholder="筛选能力关键词…">
212
+ <button class="tiny-btn tiny-btn-light" @tap.catch="copySelected">
213
+ 复制当前
214
+ </button>
215
+ </view>
216
+
217
+ <view class="group-tabs">
218
+ <view
219
+ v-for="tab in groupTabs"
220
+ :key="tab.key"
221
+ class="group-tab"
222
+ :class="tab.key === activeGroup ? 'group-tab-active' : ''"
223
+ @tap="selectGroup(tab.key)"
224
+ >
225
+ {{ tab.label }}
226
+ </view>
227
+ </view>
228
+
229
+ <view class="chips">
230
+ <view
231
+ v-for="feature in filteredFeatures"
232
+ :key="feature.id"
233
+ class="chip"
234
+ :class="feature.id === selectedFeatureId ? 'chip-active' : ''"
235
+ @tap="selectFeature(feature)"
236
+ >
237
+ {{ feature.title }}
238
+ </view>
239
+ </view>
240
+
241
+ <view class="search-stat">
242
+ <text>
243
+ 搜索命中:{{ searchHit }}
244
+ </text>
245
+ <text>
246
+ 未命中:{{ searchMiss }}
247
+ </text>
248
+ </view>
249
+
250
+ <view v-if="selectedFeature" class="selected-card">
251
+ <text class="selected-label">
252
+ 当前焦点
253
+ </text>
254
+ <text class="selected-value">
255
+ {{ selectedFeature.title }}
256
+ </text>
257
+ <text class="selected-meta">
258
+ 分组:{{ selectedFeature.group }} | 等级:{{ selectedFeature.level ?? 'base' }}
259
+ </text>
260
+ </view>
261
+
262
+ <slot name="footer" />
263
+ </view>
22
264
  </view>
23
265
  </template>
24
266
 
25
267
  <style>
26
- .header {
268
+ .hello-panel {
27
269
  padding: 24rpx;
28
- background: linear-gradient(135deg, #4c6ef5, #7048e8);
270
+ margin-top: 20rpx;
271
+ background: linear-gradient(180deg, #fff, #f8faff);
29
272
  border-radius: 24rpx;
273
+ box-shadow: 0 10rpx 28rpx rgb(76 110 245 / 12%);
274
+ }
275
+
276
+ .hello-panel-compact {
277
+ padding: 18rpx;
278
+ }
279
+
280
+ .head-row {
281
+ display: flex;
282
+ align-items: flex-start;
283
+ justify-content: space-between;
284
+ }
285
+
286
+ .head-main {
287
+ display: flex;
288
+ flex-direction: column;
289
+ gap: 8rpx;
30
290
  }
31
291
 
32
292
  .title {
33
293
  display: block;
34
- font-size: 40rpx;
294
+ font-size: 36rpx;
35
295
  font-weight: 700;
36
- color: #fff;
296
+ color: #2d2f6b;
37
297
  }
38
298
 
39
299
  .subtitle {
40
300
  display: block;
41
- margin-top: 8rpx;
42
- font-size: 26rpx;
43
- color: rgb(255 255 255 / 85%);
301
+ font-size: 24rpx;
302
+ color: #5b5f93;
303
+ }
304
+
305
+ .summary {
306
+ display: block;
307
+ font-size: 22rpx;
308
+ color: #7b80af;
309
+ }
310
+
311
+ .panel-body {
312
+ margin-top: 20rpx;
313
+ }
314
+
315
+ .metrics {
316
+ display: grid;
317
+ grid-template-columns: repeat(2, minmax(0, 1fr));
318
+ gap: 12rpx;
319
+ }
320
+
321
+ .metric-item {
322
+ display: flex;
323
+ flex-direction: column;
324
+ gap: 6rpx;
325
+ padding: 14rpx;
326
+ background: #fff;
327
+ border: 2rpx solid #eef1ff;
328
+ border-radius: 16rpx;
329
+ }
330
+
331
+ .metric-label {
332
+ font-size: 22rpx;
333
+ color: #6870a2;
334
+ }
335
+
336
+ .metric-value {
337
+ font-size: 30rpx;
338
+ font-weight: 700;
339
+ }
340
+
341
+ .metric-up {
342
+ color: #1b7a3a;
343
+ }
344
+
345
+ .metric-down {
346
+ color: #c92a2a;
347
+ }
348
+
349
+ .metric-flat {
350
+ color: #3b3f73;
351
+ }
352
+
353
+ .metric-note {
354
+ font-size: 20rpx;
355
+ color: #8a90bd;
356
+ }
357
+
358
+ .feature-toolbar {
359
+ display: flex;
360
+ gap: 12rpx;
361
+ align-items: center;
362
+ margin-top: 16rpx;
363
+ }
364
+
365
+ .search-input {
366
+ box-sizing: border-box;
367
+ flex: 1;
368
+ height: 68rpx;
369
+ padding: 0 18rpx;
370
+ margin: 0;
371
+ font-size: 24rpx;
372
+ background: #fff;
373
+ border: 2rpx solid #e6e9f7;
374
+ border-radius: 12rpx;
375
+ }
376
+
377
+ .chips {
378
+ display: flex;
379
+ flex-wrap: wrap;
380
+ gap: 10rpx;
381
+ margin-top: 14rpx;
382
+ }
383
+
384
+ .group-tabs {
385
+ display: flex;
386
+ gap: 10rpx;
387
+ margin-top: 14rpx;
388
+ }
389
+
390
+ .group-tab {
391
+ padding: 8rpx 14rpx;
392
+ font-size: 22rpx;
393
+ color: #596090;
394
+ background: #f0f3ff;
395
+ border-radius: 999rpx;
396
+ }
397
+
398
+ .group-tab-active {
399
+ color: #fff;
400
+ background: #4f5ee3;
401
+ }
402
+
403
+ .chip {
404
+ padding: 10rpx 16rpx;
405
+ font-size: 22rpx;
406
+ color: #4f5685;
407
+ background: #edf1ff;
408
+ border-radius: 999rpx;
409
+ }
410
+
411
+ .chip-active {
412
+ color: #fff;
413
+ background: #5b5ce2;
414
+ }
415
+
416
+ .selected-card {
417
+ padding: 14rpx 16rpx;
418
+ margin-top: 14rpx;
419
+ background: #fff;
420
+ border: 2rpx dashed #ccd4ff;
421
+ border-radius: 14rpx;
422
+ }
423
+
424
+ .selected-label {
425
+ display: block;
426
+ font-size: 20rpx;
427
+ color: #7c84b0;
428
+ }
429
+
430
+ .selected-value {
431
+ display: block;
432
+ margin-top: 4rpx;
433
+ font-size: 24rpx;
434
+ font-weight: 600;
435
+ color: #36407a;
436
+ }
437
+
438
+ .selected-meta {
439
+ display: block;
440
+ margin-top: 4rpx;
441
+ font-size: 20rpx;
442
+ color: #7b82b1;
443
+ }
444
+
445
+ .search-stat {
446
+ display: flex;
447
+ gap: 16rpx;
448
+ margin-top: 12rpx;
449
+ font-size: 20rpx;
450
+ color: #6d74a5;
451
+ }
452
+
453
+ .tiny-btn {
454
+ min-width: 108rpx;
455
+ height: 56rpx;
456
+ padding: 0 16rpx;
457
+ margin: 0;
458
+ font-size: 22rpx;
459
+ line-height: 56rpx;
460
+ color: #fff;
461
+ background: #5b5ce2;
462
+ border-radius: 12rpx;
463
+ }
464
+
465
+ .tiny-btn-light {
466
+ color: #4f5ee3;
467
+ background: #edf0ff;
468
+ }
469
+
470
+ .tiny-btn::after {
471
+ border: 0;
44
472
  }
45
473
  </style>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ const props = withDefaults(
3
+ defineProps<{
4
+ title?: string
5
+ description?: string
6
+ badge?: string
7
+ }>(),
8
+ {
9
+ title: 'WeVU Starter',
10
+ description: '',
11
+ badge: 'Template',
12
+ },
13
+ )
14
+ </script>
15
+
16
+ <template>
17
+ <view class="banner">
18
+ <view class="main">
19
+ <text class="title">
20
+ {{ props.title }}
21
+ </text>
22
+ <text v-if="props.description" class="description">
23
+ {{ props.description }}
24
+ </text>
25
+ </view>
26
+ <text class="badge">
27
+ {{ props.badge }}
28
+ </text>
29
+ </view>
30
+ </template>
31
+
32
+ <style>
33
+ .banner {
34
+ display: flex;
35
+ gap: 20rpx;
36
+ align-items: center;
37
+ justify-content: space-between;
38
+ padding: 26rpx 28rpx;
39
+ margin: 16rpx 0 24rpx;
40
+ background: linear-gradient(120deg, #4c6ef5, #5f3dc4);
41
+ border-radius: 24rpx;
42
+ }
43
+
44
+ .main {
45
+ display: flex;
46
+ flex: 1;
47
+ flex-direction: column;
48
+ gap: 8rpx;
49
+ min-width: 0;
50
+ }
51
+
52
+ .title {
53
+ display: block;
54
+ overflow: hidden;
55
+ text-overflow: ellipsis;
56
+ font-size: 34rpx;
57
+ font-weight: 700;
58
+ color: #fff;
59
+ white-space: nowrap;
60
+ }
61
+
62
+ .description {
63
+ display: block;
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ font-size: 24rpx;
67
+ color: rgb(255 255 255 / 86%);
68
+ white-space: nowrap;
69
+ }
70
+
71
+ .badge {
72
+ flex: none;
73
+ padding: 8rpx 16rpx;
74
+ font-size: 22rpx;
75
+ color: #4c2fa6;
76
+ background: #fff;
77
+ border-radius: 999rpx;
78
+ }
79
+ </style>
@@ -1,26 +1,201 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, watch } from 'wevu'
3
3
 
4
+ interface HelloActionPayload {
5
+ type: 'toggle' | 'copy' | 'select' | 'stats'
6
+ value?: string
7
+ }
8
+
9
+ type HighlightTone = 'up' | 'down' | 'flat'
10
+
11
+ interface HighlightItem {
12
+ key: string
13
+ label: string
14
+ value: string | number
15
+ tone?: HighlightTone
16
+ note?: string
17
+ }
18
+
4
19
  definePageJson({
5
- navigationBarTitleText: '首页',
20
+ navigationBarTitleText: '首页示例',
6
21
  })
7
22
 
8
23
  const count = ref(0)
9
24
  const message = ref('Hello WeVU!')
25
+ const activeGroup = ref('概览')
26
+
10
27
  const todos = ref([
11
- '用 Vue SFC 写页面/组件',
12
- '用 wevu API(ref/computed/watch)写逻辑',
13
- ' v-for / v-if / @tap / v-model 写模板',
28
+ {
29
+ id: 'sfc',
30
+ title: '自动编译 SFC 到小程序四件套',
31
+ group: 'template' as const,
32
+ done: true,
33
+ level: 'advanced' as const,
34
+ },
35
+ {
36
+ id: 'components',
37
+ title: '自动注入 usingComponents 与组件类型提示',
38
+ group: 'template' as const,
39
+ done: true,
40
+ level: 'base' as const,
41
+ },
42
+ {
43
+ id: 'pipeline',
44
+ title: 'WXML/WXSS/WXS 全链路处理与平台适配',
45
+ group: 'engineering' as const,
46
+ done: true,
47
+ level: 'advanced' as const,
48
+ },
49
+ {
50
+ id: 'events',
51
+ title: '支持 @tap.catch / @tap.stop 等事件语义',
52
+ group: 'core' as const,
53
+ done: true,
54
+ level: 'base' as const,
55
+ },
56
+ {
57
+ id: 'reactivity',
58
+ title: '通过 wevu 使用 ref/computed/watch 写业务逻辑',
59
+ group: 'core' as const,
60
+ done: true,
61
+ level: 'advanced' as const,
62
+ },
14
63
  ])
15
64
 
16
65
  const doubled = computed(() => count.value * 2)
17
66
 
67
+ const pageClass = computed(() => {
68
+ return {
69
+ 'page-empty': count.value === 0,
70
+ 'page-energetic': count.value > 0,
71
+ }
72
+ })
73
+
74
+ const pageStyle = computed(() => {
75
+ return [
76
+ {
77
+ background: count.value > 0 ? '#eaf0ff' : '#f6f7ff',
78
+ },
79
+ {
80
+ borderTop: count.value > 0 ? '4rpx solid #4c6ef5' : '4rpx solid transparent',
81
+ },
82
+ ]
83
+ })
84
+
85
+ const cardClass = computed(() => {
86
+ return [
87
+ {
88
+ 'card-active': count.value > 0,
89
+ },
90
+ count.value >= 3 ? 'card-boost' : '',
91
+ ]
92
+ })
93
+
94
+ const cardStyle = computed(() => {
95
+ return [
96
+ {
97
+ borderColor: count.value > 0 ? '#4c6ef5' : 'transparent',
98
+ },
99
+ {
100
+ borderWidth: count.value > 0 ? '2rpx' : '1rpx',
101
+ },
102
+ ]
103
+ })
104
+
105
+ const primaryBtnClass = computed(() => {
106
+ return [
107
+ {
108
+ 'btn-boost': count.value > 0,
109
+ },
110
+ count.value >= 3 ? 'btn-boost-strong' : '',
111
+ ]
112
+ })
113
+
114
+ const primaryBtnStyle = computed(() => {
115
+ return [
116
+ {
117
+ opacity: count.value > 0 ? 0.88 : 1,
118
+ },
119
+ count.value > 0
120
+ ? {
121
+ boxShadow: '0 8rpx 20rpx rgba(76, 110, 245, 0.28)',
122
+ }
123
+ : null,
124
+ ]
125
+ })
126
+
127
+ const helloHighlights = computed<HighlightItem[]>(() => {
128
+ return [
129
+ {
130
+ key: 'count',
131
+ label: '当前计数',
132
+ value: count.value,
133
+ tone: 'up' as const,
134
+ note: '来自 ref 状态',
135
+ },
136
+ {
137
+ key: 'double',
138
+ label: '双倍值',
139
+ value: doubled.value,
140
+ tone: 'flat' as const,
141
+ note: '来自 computed',
142
+ },
143
+ {
144
+ key: 'feature',
145
+ label: '能力条目',
146
+ value: todos.value.length,
147
+ tone: 'flat' as const,
148
+ note: '模板清单',
149
+ },
150
+ {
151
+ key: 'title',
152
+ label: '标题长度',
153
+ value: message.value.length,
154
+ tone: message.value.length > 10 ? 'up' : 'down',
155
+ note: '来自 v-model',
156
+ },
157
+ ]
158
+ })
159
+
160
+ function showToast(title: string) {
161
+ wx.showToast({
162
+ title,
163
+ icon: 'none',
164
+ duration: 1200,
165
+ })
166
+ }
167
+
18
168
  function increment() {
19
169
  count.value += 1
20
170
  }
21
171
 
22
172
  function reset() {
23
173
  count.value = 0
174
+ showToast('计数已重置')
175
+ }
176
+
177
+ function handleHelloAction(payload: HelloActionPayload) {
178
+ if (payload.type === 'copy' && payload.value) {
179
+ wx.setClipboardData({
180
+ data: payload.value,
181
+ })
182
+ return
183
+ }
184
+
185
+ if (payload.type === 'toggle') {
186
+ showToast(`面板状态:${payload.value === 'true' ? '展开' : '收起'}`)
187
+ return
188
+ }
189
+
190
+ if (payload.type === 'select' && payload.value) {
191
+ activeGroup.value = payload.value
192
+ showToast(`当前焦点:${payload.value}`)
193
+ return
194
+ }
195
+
196
+ if (payload.type === 'stats' && payload.value) {
197
+ console.log(`[wevu] hello progress: ${payload.value}%`)
198
+ }
24
199
  }
25
200
 
26
201
  watch(count, (newValue, oldValue) => {
@@ -29,10 +204,30 @@ watch(count, (newValue, oldValue) => {
29
204
  </script>
30
205
 
31
206
  <template>
32
- <view class="page">
33
- <HelloWorld :title="message" :subtitle="`count=${count}, doubled=${doubled}`" />
207
+ <view class="page" :class="pageClass" :style="pageStyle">
208
+ <InfoBanner
209
+ :title="message"
210
+ :description="`group=${activeGroup}, count=${count}, doubled=${doubled}`"
211
+ badge="Demo"
212
+ />
213
+
214
+ <HelloWorld
215
+ :title="`欢迎,${message}`"
216
+ :subtitle="`HelloWorld 示例面板(当前分组:${activeGroup})`"
217
+ :highlights="helloHighlights"
218
+ :features="todos"
219
+ @action="handleHelloAction"
220
+ >
221
+ <template #footer>
222
+ <view class="hello-footer">
223
+ <text class="hello-footer-text">
224
+ 此区域来自父组件 slot,展示 wevu + weapp-vite 组合能力。
225
+ </text>
226
+ </view>
227
+ </template>
228
+ </HelloWorld>
34
229
 
35
- <view class="card">
230
+ <view class="card" :class="cardClass" :style="cardStyle">
36
231
  <view class="row">
37
232
  <text class="label">
38
233
  当前计数:{{ count }}
@@ -43,10 +238,10 @@ watch(count, (newValue, oldValue) => {
43
238
  </view>
44
239
 
45
240
  <view class="row actions">
46
- <button class="btn primary" @tap="increment">
241
+ <button class="btn primary" :class="primaryBtnClass" :style="primaryBtnStyle" @tap.catch="increment">
47
242
  +1
48
243
  </button>
49
- <button class="btn danger" @tap="reset">
244
+ <button class="btn danger" @tap.stop="reset">
50
245
  重置
51
246
  </button>
52
247
  </view>
@@ -65,7 +260,7 @@ watch(count, (newValue, oldValue) => {
65
260
  </view>
66
261
  <view class="todo">
67
262
  <view v-for="(todo, index) in todos" :key="index" class="todo-item">
68
- <text>• {{ todo }}</text>
263
+ <text>• {{ todo.title }}</text>
69
264
  </view>
70
265
  </view>
71
266
  </view>
@@ -75,17 +270,36 @@ watch(count, (newValue, oldValue) => {
75
270
  <style>
76
271
  .page {
77
272
  box-sizing: border-box;
78
- padding: 48rpx 32rpx 64rpx;
273
+ min-height: 100vh;
274
+ padding: 0 32rpx 64rpx;
275
+ background: #f6f7ff;
276
+ }
277
+
278
+ .page-empty {
279
+ opacity: 0.98;
280
+ }
281
+
282
+ .page-energetic {
283
+ background: #f1f4ff;
79
284
  }
80
285
 
81
286
  .card {
82
287
  padding: 32rpx;
83
288
  margin-top: 24rpx;
84
289
  background: #fff;
290
+ border: 2rpx solid transparent;
85
291
  border-radius: 24rpx;
86
292
  box-shadow: 0 12rpx 32rpx rgb(44 44 84 / 10%);
87
293
  }
88
294
 
295
+ .card-active {
296
+ box-shadow: 0 14rpx 36rpx rgb(76 110 245 / 14%);
297
+ }
298
+
299
+ .card-boost {
300
+ transform: translateY(-2rpx);
301
+ }
302
+
89
303
  .row {
90
304
  display: flex;
91
305
  gap: 16rpx;
@@ -114,6 +328,14 @@ watch(count, (newValue, oldValue) => {
114
328
  background: #4c6ef5;
115
329
  }
116
330
 
331
+ .btn-boost {
332
+ transform: scale(1.02);
333
+ }
334
+
335
+ .btn-boost-strong {
336
+ transform: scale(1.04);
337
+ }
338
+
117
339
  .btn.danger {
118
340
  background: #f03e3e;
119
341
  }
@@ -133,4 +355,16 @@ watch(count, (newValue, oldValue) => {
133
355
  font-size: 26rpx;
134
356
  color: #4f4f7a;
135
357
  }
358
+
359
+ .hello-footer {
360
+ padding: 12rpx 14rpx;
361
+ margin-top: 16rpx;
362
+ background: #eef2ff;
363
+ border-radius: 12rpx;
364
+ }
365
+
366
+ .hello-footer-text {
367
+ font-size: 22rpx;
368
+ color: #4f5ea0;
369
+ }
136
370
  </style>
@@ -7,9 +7,17 @@
7
7
  declare module 'weapp-vite/typed-components' {
8
8
  export interface ComponentProps {
9
9
  HelloWorld: {
10
+ readonly compact?: boolean;
11
+ readonly features?: any[];
12
+ readonly highlights?: any[];
10
13
  readonly subtitle?: string;
11
14
  readonly title?: string;
12
15
  };
16
+ InfoBanner: {
17
+ readonly badge?: string;
18
+ readonly description?: string;
19
+ readonly title?: string;
20
+ };
13
21
  }
14
22
  export type ComponentPropName = keyof ComponentProps;
15
23
  export type ComponentProp<Name extends string> = Name extends ComponentPropName ? ComponentProps[Name] : Record<string, any>;