277 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import {
BookOpenCheck,
Bot,
Box,
Building2,
Camera,
Mic,
Scale,
Search,
} from "@lucide/vue";
import { computed } from "vue";
import { featureFlags } from "@/config/features";
const emit = defineEmits<{
select: [prompt: string];
tool: [value: "assessment" | "ocr"];
}>();
const props = defineProps<{ canUpload: boolean }>();
const actions = [
{
title: "评估新品风险",
description: "含品类预警 + 标准判定",
prompt: "帮我评估一款500ml PET瓶洗发水,泵头用祥印的,加工厂博元",
icon: Search,
tone: "blue",
tool: "assessment" as const,
},
{
title: "查供应商",
description: "含合作频次 + 分层",
prompt: "佳亿的风险怎么样?",
icon: Building2,
tone: "green",
},
{
title: "查物料风险",
description: "品类常见问题",
prompt: "软管类物料有什么常见问题?",
icon: Box,
tone: "amber",
},
{
title: "AI方案生成",
description: "无历史时按标准兜底",
prompt: "我要做一款次氯酸消毒喷雾,50ml,铝瓶加喷头",
icon: Bot,
tone: "purple",
isNew: true,
},
{
title: "标准判定",
description: "检测数值对标标准库",
prompt: "洗发水 pH 值检测报告 4.8,请判定",
icon: BookOpenCheck,
tone: "sky",
isNew: true,
},
{
title: "法规合规",
description: "法规标准快速核验",
prompt: "新产品宣称能祛痘,帮我查下合规风险",
icon: Scale,
tone: "rose",
isNew: true,
},
{
title: "OCR识别录入",
description: "拍照自动填表 + 数值判定",
prompt: "请帮我识别检测报告并进行风险判定",
icon: Camera,
tone: "sky",
requiresUpload: true,
tool: "ocr" as const,
},
...(featureFlags.voiceEntry
? [
{
title: "语音录入异常",
description: "口述信息自动结构化",
prompt: "记录一条新的质量异常",
icon: Mic,
tone: "purple",
},
]
: []),
];
const visibleActions = computed(() =>
actions.filter(
(action) =>
!("requiresUpload" in action) ||
!action.requiresUpload ||
props.canUpload,
),
);
function handleAction(action: (typeof actions)[number]) {
if ("tool" in action && action.tool) {
emit("tool", action.tool);
return;
}
emit("select", action.prompt);
}
</script>
<template>
<section class="welcome">
<div class="welcome__mark">蝶</div>
<h1>你好,我是风险预警助手</h1>
<p>
双引擎风险预警:历史异常经验引擎 +
判定标准规则引擎,帮助你在立项、开发和量产阶段提前识别风险。
</p>
<div class="welcome-grid">
<button
v-for="action in visibleActions"
:key="action.title"
class="welcome-action"
type="button"
@click="handleAction(action)"
>
<span class="welcome-action__icon" :class="`tone-${action.tone}`">
<component :is="action.icon" :size="16" />
</span>
<span class="welcome-action__copy">
<strong>{{ action.title }}</strong>
<small>{{ action.description }}</small>
</span>
<span v-if="action.isNew" class="welcome-action__new">新</span>
</button>
</div>
</section>
</template>
<style scoped>
.welcome {
padding: 42px 20px 26px;
text-align: center;
}
.welcome__mark {
display: inline-grid;
width: 48px;
height: 48px;
place-items: center;
border-radius: 14px;
margin-bottom: 18px;
background: var(--color-text);
color: white;
font-size: 20px;
font-weight: 650;
}
h1 {
margin: 0 0 6px;
font-size: 20px;
font-weight: 650;
}
p {
max-width: 440px;
margin: 0 auto 26px;
color: var(--color-text-muted);
font-size: 12px;
line-height: 1.75;
}
.welcome-grid {
display: grid;
max-width: 520px;
margin: 0 auto;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.welcome-action {
display: flex;
min-width: 0;
min-height: 62px;
align-items: center;
gap: 10px;
border: 1px solid var(--color-border);
border-radius: 10px;
padding: 10px 13px;
background: var(--color-surface);
color: var(--color-text);
text-align: left;
transition:
border-color 0.15s,
box-shadow 0.15s,
transform 0.15s;
}
.welcome-action:hover {
border-color: var(--color-border-strong);
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.welcome-action__icon {
display: grid;
width: 32px;
height: 32px;
flex: 0 0 32px;
place-items: center;
border-radius: 6px;
}
.tone-blue,
.tone-sky {
background: var(--color-accent-subtle);
color: var(--color-accent);
}
.tone-green {
background: var(--color-success-soft);
color: var(--color-success);
}
.tone-amber {
background: var(--color-warning-soft);
color: var(--color-warning);
}
.tone-purple {
background: var(--color-purple-soft);
color: var(--color-purple);
}
.tone-rose {
background: #fce7f3;
color: #be185d;
}
.welcome-action__copy {
display: grid;
min-width: 0;
gap: 1px;
}
.welcome-action strong {
font-size: 13px;
font-weight: 600;
}
.welcome-action small {
overflow: hidden;
color: var(--color-text-faint);
font-size: 10px;
text-overflow: ellipsis;
white-space: nowrap;
}
.welcome-action__new {
margin-left: auto;
border-radius: 3px;
padding: 0 4px;
background: var(--color-accent-subtle);
color: var(--color-accent);
font-size: 8px;
font-weight: 700;
}
@media (max-width: 640px) {
.welcome {
padding-inline: 10px;
}
.welcome-grid {
grid-template-columns: 1fr;
}
}
</style>