create-weapp-vite 1.3.2 → 1.3.3
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/{chunk-CP2T5O3U.js → chunk-KDUDSVZ5.js} +2 -2
- package/dist/cli.cjs +2 -2
- package/dist/cli.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/wevu-tdesign/README.md +5 -0
- package/templates/wevu-tdesign/components.d.ts +1 -1
- package/templates/wevu-tdesign/public/tabbar/ability-active.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/ability.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/data-active.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/data.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/form-active.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/form.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/home-active.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/home.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/list-active.png +0 -0
- package/templates/wevu-tdesign/public/tabbar/list.png +0 -0
- package/templates/wevu-tdesign/src/app.vue +60 -2
- package/templates/wevu-tdesign/src/components/EmptyState/index.vue +36 -0
- package/templates/wevu-tdesign/src/components/FilterBar/index.vue +63 -0
- package/templates/wevu-tdesign/src/components/FormRow/index.vue +43 -0
- package/templates/wevu-tdesign/src/components/FormStep/index.vue +48 -0
- package/templates/wevu-tdesign/src/components/KpiBoard/index.vue +146 -0
- package/templates/wevu-tdesign/src/components/QuickActionGrid/index.vue +90 -0
- package/templates/wevu-tdesign/src/components/ResultCard/index.vue +51 -0
- package/templates/wevu-tdesign/src/components/SectionTitle/index.vue +34 -0
- package/templates/wevu-tdesign/src/components/TrendCard/index.vue +101 -0
- package/templates/wevu-tdesign/src/pages/ability/index.vue +177 -0
- package/templates/wevu-tdesign/src/pages/data/index.vue +188 -0
- package/templates/wevu-tdesign/src/pages/form/index.vue +249 -0
- package/templates/wevu-tdesign/src/pages/index/index.vue +228 -126
- package/templates/wevu-tdesign/src/pages/list/index.vue +178 -0
- package/templates/wevu-tdesign/src/subpackages/ability/index.vue +106 -0
- package/templates/wevu-tdesign/src/subpackages/lab/index.vue +136 -0
- package/templates/wevu-tdesign/src/vite-env.d.ts +4 -0
- package/templates/wevu-tdesign/tsconfig.app.json +6 -1
- package/templates/wevu-tdesign/vite.config.ts +11 -1
- package/templates/wevu-tdesign/src/components/HelloWorld/index.vue +0 -23
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineOptions({
|
|
3
|
+
options: {
|
|
4
|
+
multipleSlots: true,
|
|
5
|
+
},
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
withDefaults(
|
|
9
|
+
defineProps<{
|
|
10
|
+
title: string
|
|
11
|
+
subtitle?: string
|
|
12
|
+
}>(),
|
|
13
|
+
{
|
|
14
|
+
subtitle: '',
|
|
15
|
+
},
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
defineComponentJson({
|
|
19
|
+
styleIsolation: 'apply-shared',
|
|
20
|
+
})
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<view class="flex items-end justify-between">
|
|
25
|
+
<view>
|
|
26
|
+
<text class="text-[30rpx] font-semibold text-[#1f1a3f]">
|
|
27
|
+
{{ title }}
|
|
28
|
+
</text>
|
|
29
|
+
<text v-if="subtitle" class="mt-[6rpx] block text-[22rpx] text-[#6f6b8a]">
|
|
30
|
+
{{ subtitle }}
|
|
31
|
+
</text>
|
|
32
|
+
</view>
|
|
33
|
+
</view>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'wevu'
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(
|
|
5
|
+
defineProps<{
|
|
6
|
+
title: string
|
|
7
|
+
value: number
|
|
8
|
+
unit?: string
|
|
9
|
+
delta?: number
|
|
10
|
+
progress?: number
|
|
11
|
+
}>(),
|
|
12
|
+
{
|
|
13
|
+
unit: '',
|
|
14
|
+
delta: undefined,
|
|
15
|
+
progress: undefined,
|
|
16
|
+
},
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
defineComponentJson({
|
|
20
|
+
styleIsolation: 'apply-shared',
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
type TrendTone = 'positive' | 'negative' | 'neutral'
|
|
24
|
+
|
|
25
|
+
const tone = computed<TrendTone>(() => {
|
|
26
|
+
if (props.delta === undefined || Number.isNaN(props.delta)) {
|
|
27
|
+
return 'neutral'
|
|
28
|
+
}
|
|
29
|
+
if (props.delta > 0) {
|
|
30
|
+
return 'positive'
|
|
31
|
+
}
|
|
32
|
+
if (props.delta < 0) {
|
|
33
|
+
return 'negative'
|
|
34
|
+
}
|
|
35
|
+
return 'neutral'
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const hasProgress = computed(() => {
|
|
39
|
+
if (props.progress === undefined || props.progress === null) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
return Number.isFinite(Number(props.progress))
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const progressValue = computed(() => {
|
|
46
|
+
if (!hasProgress.value) {
|
|
47
|
+
return 0
|
|
48
|
+
}
|
|
49
|
+
const raw = Number(props.progress)
|
|
50
|
+
return Math.min(Math.max(raw, 0), 100)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
function toneText(toneValue: TrendTone) {
|
|
54
|
+
if (toneValue === 'positive') {
|
|
55
|
+
return '↑'
|
|
56
|
+
}
|
|
57
|
+
if (toneValue === 'negative') {
|
|
58
|
+
return '↓'
|
|
59
|
+
}
|
|
60
|
+
return '→'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function toneClass(toneValue: TrendTone) {
|
|
64
|
+
if (toneValue === 'positive') {
|
|
65
|
+
return 'text-[#1b7a3a]'
|
|
66
|
+
}
|
|
67
|
+
if (toneValue === 'negative') {
|
|
68
|
+
return 'text-[#b42318]'
|
|
69
|
+
}
|
|
70
|
+
return 'text-[#64748b]'
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<view class="rounded-[20rpx] bg-white p-[18rpx] shadow-[0_12rpx_28rpx_rgba(17,24,39,0.08)]">
|
|
76
|
+
<view class="flex items-center justify-between">
|
|
77
|
+
<text class="text-[24rpx] text-[#5b5876]">
|
|
78
|
+
{{ title }}
|
|
79
|
+
</text>
|
|
80
|
+
<text class="text-[20rpx]" :class="toneClass(tone)">
|
|
81
|
+
{{ toneText(tone) }}
|
|
82
|
+
{{ delta === undefined ? '--' : delta }}
|
|
83
|
+
</text>
|
|
84
|
+
</view>
|
|
85
|
+
<view class="mt-[12rpx] flex items-baseline gap-[6rpx]">
|
|
86
|
+
<text class="text-[36rpx] font-bold text-[#1f1a3f]">
|
|
87
|
+
{{ value }}
|
|
88
|
+
</text>
|
|
89
|
+
<text v-if="unit" class="text-[20rpx] text-[#7a7aa0]">
|
|
90
|
+
{{ unit }}
|
|
91
|
+
</text>
|
|
92
|
+
</view>
|
|
93
|
+
<t-progress
|
|
94
|
+
v-if="hasProgress"
|
|
95
|
+
class="mt-[12rpx]"
|
|
96
|
+
:percentage="progressValue"
|
|
97
|
+
status="active"
|
|
98
|
+
stroke-width="6"
|
|
99
|
+
/>
|
|
100
|
+
</view>
|
|
101
|
+
</template>
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Dialog from 'tdesign-miniprogram/dialog/index'
|
|
3
|
+
import Toast from 'tdesign-miniprogram/toast/index'
|
|
4
|
+
|
|
5
|
+
import { getCurrentInstance, ref } from 'wevu'
|
|
6
|
+
|
|
7
|
+
import SectionTitle from '@/components/SectionTitle/index.vue'
|
|
8
|
+
|
|
9
|
+
definePageJson({
|
|
10
|
+
navigationBarTitleText: '能力',
|
|
11
|
+
backgroundColor: '#f6f7fb',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const mpContext = getCurrentInstance()
|
|
15
|
+
|
|
16
|
+
const capabilityCards = ref([
|
|
17
|
+
{
|
|
18
|
+
key: 'scan',
|
|
19
|
+
title: '扫一扫',
|
|
20
|
+
desc: '识别条码或二维码',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'location',
|
|
24
|
+
title: '定位',
|
|
25
|
+
desc: '获取当前坐标',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key: 'clipboard',
|
|
29
|
+
title: '剪贴板',
|
|
30
|
+
desc: '写入演示文本',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
key: 'share',
|
|
34
|
+
title: '分享',
|
|
35
|
+
desc: '唤起分享菜单',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
key: 'image',
|
|
39
|
+
title: '选择图片',
|
|
40
|
+
desc: '上传素材预览',
|
|
41
|
+
},
|
|
42
|
+
])
|
|
43
|
+
|
|
44
|
+
const subscribeTemplateId = ''
|
|
45
|
+
|
|
46
|
+
function showToast(message: string, theme: 'success' | 'warning' = 'success') {
|
|
47
|
+
if (!mpContext) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
Toast({
|
|
51
|
+
selector: '#t-toast',
|
|
52
|
+
context: mpContext as any,
|
|
53
|
+
message,
|
|
54
|
+
theme,
|
|
55
|
+
duration: 1200,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function handleCapability(key: string) {
|
|
60
|
+
switch (key) {
|
|
61
|
+
case 'scan':
|
|
62
|
+
wx.scanCode({
|
|
63
|
+
success: (result) => {
|
|
64
|
+
showToast(`识别结果:${result.result || '已完成'}`)
|
|
65
|
+
},
|
|
66
|
+
fail: () => {
|
|
67
|
+
showToast('扫码失败,请重试', 'warning')
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
break
|
|
71
|
+
case 'location':
|
|
72
|
+
wx.getLocation({
|
|
73
|
+
type: 'wgs84',
|
|
74
|
+
success: (result) => {
|
|
75
|
+
showToast(`定位成功:${result.latitude.toFixed(2)}, ${result.longitude.toFixed(2)}`)
|
|
76
|
+
},
|
|
77
|
+
fail: () => {
|
|
78
|
+
showToast('未获取定位权限', 'warning')
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
break
|
|
82
|
+
case 'clipboard':
|
|
83
|
+
wx.setClipboardData({
|
|
84
|
+
data: 'weapp-vite + wevu + TDesign',
|
|
85
|
+
success: () => showToast('已写入剪贴板'),
|
|
86
|
+
})
|
|
87
|
+
break
|
|
88
|
+
case 'share':
|
|
89
|
+
wx.showShareMenu({
|
|
90
|
+
withShareTicket: true,
|
|
91
|
+
success: () => showToast('已开启分享菜单'),
|
|
92
|
+
fail: () => showToast('分享菜单开启失败', 'warning'),
|
|
93
|
+
})
|
|
94
|
+
break
|
|
95
|
+
case 'image':
|
|
96
|
+
wx.chooseImage({
|
|
97
|
+
count: 3,
|
|
98
|
+
success: (result) => {
|
|
99
|
+
showToast(`已选择 ${result.tempFilePaths.length} 张图片`)
|
|
100
|
+
},
|
|
101
|
+
fail: () => showToast('未选择图片', 'warning'),
|
|
102
|
+
})
|
|
103
|
+
break
|
|
104
|
+
default:
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function requestSubscribe() {
|
|
110
|
+
if (!subscribeTemplateId) {
|
|
111
|
+
Dialog.alert({
|
|
112
|
+
selector: '#t-dialog',
|
|
113
|
+
title: '订阅消息',
|
|
114
|
+
message: '请在 ability 页面配置订阅模板 ID 后再试。',
|
|
115
|
+
context: mpContext as any,
|
|
116
|
+
confirmBtn: '知道了',
|
|
117
|
+
})
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
wx.requestSubscribeMessage({
|
|
121
|
+
tmplIds: [subscribeTemplateId],
|
|
122
|
+
success: () => showToast('订阅成功'),
|
|
123
|
+
fail: () => showToast('订阅失败', 'warning'),
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function navigateTo(url: string) {
|
|
128
|
+
wx.navigateTo({
|
|
129
|
+
url,
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
</script>
|
|
133
|
+
|
|
134
|
+
<template>
|
|
135
|
+
<view class="min-h-screen bg-[#f6f7fb] px-[28rpx] pb-[88rpx] pt-[24rpx] text-[#1c1c3c]">
|
|
136
|
+
<view class="rounded-[28rpx] bg-gradient-to-br from-[#ecfeff] via-[#ffffff] to-[#e0f2fe] p-[20rpx]">
|
|
137
|
+
<SectionTitle title="小程序能力" subtitle="本页展示原生 API 与分包导航" />
|
|
138
|
+
</view>
|
|
139
|
+
|
|
140
|
+
<view class="mt-[18rpx] rounded-[24rpx] bg-white p-[20rpx] shadow-[0_18rpx_40rpx_rgba(17,24,39,0.08)]">
|
|
141
|
+
<SectionTitle title="常用能力" subtitle="点击体验微信原生 API" />
|
|
142
|
+
<view class="mt-[12rpx] grid grid-cols-2 gap-[12rpx]">
|
|
143
|
+
<view
|
|
144
|
+
v-for="item in capabilityCards"
|
|
145
|
+
:key="item.key"
|
|
146
|
+
class="rounded-[18rpx] bg-[#f0f9ff] p-[16rpx]"
|
|
147
|
+
@tap="handleCapability(item.key)"
|
|
148
|
+
>
|
|
149
|
+
<text class="text-[24rpx] font-semibold text-[#1f1a3f]">
|
|
150
|
+
{{ item.title }}
|
|
151
|
+
</text>
|
|
152
|
+
<text class="mt-[6rpx] block text-[20rpx] text-[#6f6b8a]">
|
|
153
|
+
{{ item.desc }}
|
|
154
|
+
</text>
|
|
155
|
+
</view>
|
|
156
|
+
</view>
|
|
157
|
+
<view class="mt-[16rpx]">
|
|
158
|
+
<t-button block theme="primary" variant="outline" @tap="requestSubscribe">
|
|
159
|
+
订阅消息提醒
|
|
160
|
+
</t-button>
|
|
161
|
+
</view>
|
|
162
|
+
</view>
|
|
163
|
+
|
|
164
|
+
<view class="mt-[18rpx] rounded-[24rpx] bg-white p-[20rpx] shadow-[0_18rpx_40rpx_rgba(17,24,39,0.08)]">
|
|
165
|
+
<SectionTitle title="分包演示" subtitle="weapp-vite 子包页面" />
|
|
166
|
+
<view class="mt-[12rpx]">
|
|
167
|
+
<t-cell-group>
|
|
168
|
+
<t-cell title="组件实验室" note="subpackages/lab" arrow @tap="navigateTo('/subpackages/lab/index')" />
|
|
169
|
+
<t-cell title="API 场景页" note="subpackages/ability" arrow @tap="navigateTo('/subpackages/ability/index')" />
|
|
170
|
+
</t-cell-group>
|
|
171
|
+
</view>
|
|
172
|
+
</view>
|
|
173
|
+
|
|
174
|
+
<t-toast id="t-toast" />
|
|
175
|
+
<t-dialog id="t-dialog" />
|
|
176
|
+
</view>
|
|
177
|
+
</template>
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from 'wevu'
|
|
3
|
+
|
|
4
|
+
import KpiBoard from '@/components/KpiBoard/index.vue'
|
|
5
|
+
import SectionTitle from '@/components/SectionTitle/index.vue'
|
|
6
|
+
import TrendCard from '@/components/TrendCard/index.vue'
|
|
7
|
+
|
|
8
|
+
definePageJson({
|
|
9
|
+
navigationBarTitleText: '数据',
|
|
10
|
+
backgroundColor: '#f6f7fb',
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const ranges = [
|
|
14
|
+
{ value: 'today', label: '今日' },
|
|
15
|
+
{ value: 'week', label: '本周' },
|
|
16
|
+
{ value: 'month', label: '本月' },
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
const activeRange = ref('week')
|
|
20
|
+
const refreshTick = ref(0)
|
|
21
|
+
|
|
22
|
+
const kpiItems = computed(() => {
|
|
23
|
+
const scale = activeRange.value === 'today' ? 1 : activeRange.value === 'month' ? 4 : 2
|
|
24
|
+
const drift = refreshTick.value
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
key: 'orders',
|
|
28
|
+
label: '订单量',
|
|
29
|
+
value: 268 * scale + drift,
|
|
30
|
+
unit: '单',
|
|
31
|
+
delta: 12 + drift,
|
|
32
|
+
footnote: '核心目标',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: 'gmv',
|
|
36
|
+
label: '成交额',
|
|
37
|
+
value: 42 * scale + drift,
|
|
38
|
+
unit: '万',
|
|
39
|
+
delta: 3 + drift,
|
|
40
|
+
footnote: 'GMV',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: 'retention',
|
|
44
|
+
label: '留存',
|
|
45
|
+
value: 62 + drift,
|
|
46
|
+
unit: '%',
|
|
47
|
+
delta: 4,
|
|
48
|
+
footnote: '用户粘性',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'nps',
|
|
52
|
+
label: 'NPS',
|
|
53
|
+
value: 48 + drift,
|
|
54
|
+
unit: '分',
|
|
55
|
+
delta: 2,
|
|
56
|
+
footnote: '满意度',
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const trends = computed(() => [
|
|
62
|
+
{
|
|
63
|
+
key: 'active',
|
|
64
|
+
title: '活跃用户',
|
|
65
|
+
value: activeRange.value === 'today' ? 980 : activeRange.value === 'month' ? 8820 : 3560,
|
|
66
|
+
unit: '人',
|
|
67
|
+
delta: 12 + refreshTick.value,
|
|
68
|
+
progress: 78,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'conversion',
|
|
72
|
+
title: '转化漏斗',
|
|
73
|
+
value: activeRange.value === 'today' ? 21 : activeRange.value === 'month' ? 25 : 23,
|
|
74
|
+
unit: '%',
|
|
75
|
+
delta: 1,
|
|
76
|
+
progress: 56,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'response',
|
|
80
|
+
title: '响应时长',
|
|
81
|
+
value: activeRange.value === 'today' ? 1.6 : 1.8,
|
|
82
|
+
unit: 's',
|
|
83
|
+
delta: -0.2,
|
|
84
|
+
progress: 68,
|
|
85
|
+
},
|
|
86
|
+
])
|
|
87
|
+
|
|
88
|
+
const reportLines = computed(() => [
|
|
89
|
+
`分组:${ranges.find(range => range.value === activeRange.value)?.label ?? '本周'}`,
|
|
90
|
+
'渠道贡献 Top3:直播、社群、搜索',
|
|
91
|
+
'重点事项:提升转化 > 优化留存',
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
function onRangeChange(e: WechatMiniprogram.CustomEvent<{ value: string }>) {
|
|
95
|
+
activeRange.value = e.detail.value
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
watch(activeRange, () => {
|
|
99
|
+
refreshTick.value = Math.floor(Math.random() * 6)
|
|
100
|
+
})
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<template>
|
|
104
|
+
<view class="min-h-screen bg-[#f6f7fb] px-[28rpx] pb-[88rpx] pt-[24rpx] text-[#1c1c3c]">
|
|
105
|
+
<view class="rounded-[28rpx] bg-gradient-to-br from-[#f1f5ff] via-[#eef2ff] to-[#ffffff] p-[20rpx]">
|
|
106
|
+
<SectionTitle title="经营仪表盘" subtitle="聚焦关键指标与趋势" />
|
|
107
|
+
<view class="mt-[12rpx]">
|
|
108
|
+
<t-tabs :value="activeRange" @change="onRangeChange">
|
|
109
|
+
<t-tab-panel v-for="range in ranges" :key="range.value" :value="range.value" :label="range.label" />
|
|
110
|
+
</t-tabs>
|
|
111
|
+
</view>
|
|
112
|
+
</view>
|
|
113
|
+
|
|
114
|
+
<view class="mt-[18rpx]">
|
|
115
|
+
<KpiBoard title="核心 KPI" subtitle="趋势随区间自动刷新" :items="kpiItems">
|
|
116
|
+
<template #action />
|
|
117
|
+
<template #items="{ items }">
|
|
118
|
+
<view v-for="card in items" :key="card.key">
|
|
119
|
+
<view class="rounded-[18rpx] bg-[#f7f7fb] p-[16rpx]">
|
|
120
|
+
<view class="flex items-center justify-between">
|
|
121
|
+
<view class="flex items-center gap-[8rpx]">
|
|
122
|
+
<view class="h-[8rpx] w-[8rpx] rounded-full" :class="card.tone === 'positive' ? 'bg-[#22c55e]' : card.tone === 'negative' ? 'bg-[#ef4444]' : 'bg-[#94a3b8]'" />
|
|
123
|
+
<text class="text-[22rpx] text-[#61618a]">
|
|
124
|
+
{{ card.item.label }}
|
|
125
|
+
</text>
|
|
126
|
+
</view>
|
|
127
|
+
<view v-if="card.isLeading" class="rounded-full bg-[#fff3c2] px-[10rpx] py-[4rpx]">
|
|
128
|
+
<text class="text-[18rpx] font-semibold text-[#8a5200]">
|
|
129
|
+
HOT
|
|
130
|
+
</text>
|
|
131
|
+
</view>
|
|
132
|
+
</view>
|
|
133
|
+
<view class="mt-[10rpx] flex items-end justify-between">
|
|
134
|
+
<view class="flex items-baseline gap-[6rpx]">
|
|
135
|
+
<text class="text-[32rpx] font-bold text-[#1c1c3c]">
|
|
136
|
+
{{ card.item.value }}
|
|
137
|
+
</text>
|
|
138
|
+
<text v-if="card.item.unit" class="text-[20rpx] text-[#7a7aa0]">
|
|
139
|
+
{{ card.item.unit }}
|
|
140
|
+
</text>
|
|
141
|
+
</view>
|
|
142
|
+
<view
|
|
143
|
+
class="rounded-full px-[10rpx] py-[4rpx]"
|
|
144
|
+
:class="card.tone === 'positive' ? 'bg-[#e7f7ee] text-[#1b7a3a]' : card.tone === 'negative' ? 'bg-[#ffe9e9] text-[#b42318]' : 'bg-[#edf1f7] text-[#64748b]'"
|
|
145
|
+
>
|
|
146
|
+
<text class="text-[20rpx] font-semibold">
|
|
147
|
+
{{ card.tone === 'positive' ? '↑' : card.tone === 'negative' ? '↓' : '→' }}
|
|
148
|
+
{{ card.item.delta == null ? '--' : card.item.delta + (card.item.unit ? card.item.unit : '') }}
|
|
149
|
+
</text>
|
|
150
|
+
</view>
|
|
151
|
+
</view>
|
|
152
|
+
<text v-if="card.item.footnote" class="mt-[6rpx] block text-[20rpx] text-[#8a8aa5]">
|
|
153
|
+
{{ card.item.footnote }}
|
|
154
|
+
</text>
|
|
155
|
+
</view>
|
|
156
|
+
</view>
|
|
157
|
+
</template>
|
|
158
|
+
</KpiBoard>
|
|
159
|
+
</view>
|
|
160
|
+
|
|
161
|
+
<view class="mt-[18rpx]">
|
|
162
|
+
<SectionTitle title="趋势追踪" subtitle="转化与体验指标" />
|
|
163
|
+
<view class="mt-[12rpx] grid gap-[12rpx]">
|
|
164
|
+
<TrendCard
|
|
165
|
+
v-for="card in trends"
|
|
166
|
+
:key="card.key"
|
|
167
|
+
:title="card.title"
|
|
168
|
+
:value="card.value"
|
|
169
|
+
:unit="card.unit"
|
|
170
|
+
:delta="card.delta"
|
|
171
|
+
:progress="card.progress"
|
|
172
|
+
/>
|
|
173
|
+
</view>
|
|
174
|
+
</view>
|
|
175
|
+
|
|
176
|
+
<view class="mt-[18rpx] rounded-[24rpx] bg-white p-[20rpx] shadow-[0_18rpx_40rpx_rgba(17,24,39,0.08)]">
|
|
177
|
+
<SectionTitle title="经营洞察" subtitle="跟进重点事项" />
|
|
178
|
+
<view class="mt-[12rpx] space-y-[10rpx]">
|
|
179
|
+
<view v-for="line in reportLines" :key="line" class="flex items-center gap-[8rpx]">
|
|
180
|
+
<view class="h-[8rpx] w-[8rpx] rounded-full bg-[#5a48c5]" />
|
|
181
|
+
<text class="text-[22rpx] text-[#4c4b68]">
|
|
182
|
+
{{ line }}
|
|
183
|
+
</text>
|
|
184
|
+
</view>
|
|
185
|
+
</view>
|
|
186
|
+
</view>
|
|
187
|
+
</view>
|
|
188
|
+
</template>
|