chore: initialize wygj monorepo

This commit is contained in:
2026-07-03 01:25:10 +08:00
commit 62ffcb6e99
1248 changed files with 137340 additions and 0 deletions
+723
View File
@@ -0,0 +1,723 @@
<template>
<div class="demo-home">
<section class="toolbar-meta">
<span class="snapshot-time">数据截至:2026-07-02 10:00:00</span>
<el-button text :icon="Refresh">刷新</el-button>
</section>
<section class="hero-grid">
<article v-for="item in heroEntries" :key="item.title" class="entry-card">
<div class="entry-main">
<div class="entry-icon">
<el-icon><component :is="item.icon" /></el-icon>
</div>
<div>
<h2>{{ item.title }}</h2>
<p>{{ item.desc }}</p>
<span class="status-pill" :class="`pill-${item.type}`">{{ item.status }}</span>
</div>
</div>
<div class="entry-actions">
<el-button type="danger" :disabled="!item.path" @click="goFeature(item.path)">进入功能</el-button>
<el-button>查看样例</el-button>
</div>
</article>
</section>
<section class="content-grid">
<article class="panel progress-panel">
<div class="panel-title">
<h2>选 用 育 留 案例沉淀 流程进度</h2>
</div>
<div class="stage-track">
<div v-for="stage in stages" :key="stage.name" class="stage-item" :class="stage.state">
<div class="stage-node">
<el-icon v-if="stage.state === 'done'"><Check /></el-icon>
<span v-else>{{ stage.step }}</span>
</div>
<div class="stage-copy">
<strong>{{ stage.name }}</strong>
<span>{{ stage.desc }}</span>
<span class="status-pill" :class="`pill-${stage.tagType}`">{{ stage.label }}</span>
</div>
</div>
</div>
</article>
<article class="panel risk-panel">
<div class="panel-title">
<h2>演示待办与风险</h2>
</div>
<ul class="risk-list">
<li v-for="risk in risks" :key="risk.name">
<div class="risk-icon">
<el-icon><component :is="risk.icon" /></el-icon>
</div>
<span class="risk-name">{{ risk.name }}</span>
<span class="risk-owner">负责人:{{ risk.owner }}</span>
<span class="status-pill risk-status" :class="`pill-${risk.type}`">{{ risk.status }}</span>
</li>
</ul>
</article>
</section>
<section class="bottom-grid">
<article class="panel record-panel">
<div class="panel-title compact">
<h2>最近训练记录</h2>
</div>
<el-table :data="records" size="small" class="record-table">
<el-table-column prop="name" label="人员" min-width="92" />
<el-table-column prop="scene" label="场景" min-width="118" />
<el-table-column prop="score" label="得分" width="68" />
<el-table-column label="状态" width="88">
<template #default="{ row }">
<span class="status-dot" :class="row.state"></span>
{{ row.status }}
</template>
</el-table-column>
<el-table-column prop="time" label="时间" width="112" />
</el-table>
<div class="panel-link">
查看更多 <el-icon><ArrowRight /></el-icon>
</div>
</article>
<article class="panel pipeline-panel">
<div class="panel-title compact">
<h2>案例沉淀 Pipeline</h2>
</div>
<div class="pipeline-steps">
<div v-for="(step, index) in pipeline" :key="step.name" class="pipeline-step">
<div class="pipeline-icon">
<el-icon><component :is="step.icon" /></el-icon>
</div>
<strong>{{ step.name }}</strong>
<span>{{ step.count }}</span>
<el-icon v-if="index < pipeline.length - 1" class="pipeline-arrow"><ArrowRight /></el-icon>
</div>
</div>
<div class="pipeline-summary">
<span>今日新增:12 条</span>
<span>待审核:2 条</span>
<span>已入库:128 条</span>
</div>
<div class="panel-link">
进入案例沉淀 <el-icon><ArrowRight /></el-icon>
</div>
</article>
<article class="panel metrics-panel">
<div class="panel-title compact">
<h2>验收指标</h2>
</div>
<div class="metric-grid">
<div v-for="metric in metrics" :key="metric.label" class="metric-card">
<el-icon><component :is="metric.icon" /></el-icon>
<div>
<span>{{ metric.label }}</span>
<strong>{{ metric.value }}</strong>
<small>{{ metric.target }}</small>
</div>
</div>
</div>
<div class="panel-link">
查看指标详情 <el-icon><ArrowRight /></el-icon>
</div>
</article>
</section>
</div>
</template>
<script setup name="Index" lang="ts">
import {
ArrowRight,
Check,
Cpu,
DataLine,
DocumentChecked,
Finished,
FolderChecked,
Headset,
Histogram,
Microphone,
Notebook,
Operation,
Reading,
Refresh,
Switch,
Tickets,
TrendCharts,
UserFilled,
VideoCamera
} from '@element-plus/icons-vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const heroEntries = [
{
title: 'AI面试出题',
desc: '基于岗位画像与 JD,一键生成面试题目',
status: '可演示',
type: 'success',
icon: DocumentChecked,
path: '/recruit/interview'
},
{
title: '三角色对练',
desc: '简历生成 + 面试对象 + 结果评估闭环',
status: '可演示',
type: 'success',
icon: UserFilled,
path: '/train/practice'
},
{
title: '案例语音整理',
desc: '语音上传,AI 整理为结构化案例',
status: '样片兜底',
type: 'warning',
icon: Microphone,
path: '/knowledge/cases'
},
{
title: 'SOP知识库',
desc: '知识库检索 + RAG问答,支持员工赋能',
status: '可演示',
type: 'success',
icon: Reading,
path: '/knowledge/sop'
}
] as const;
const goFeature = (path?: string) => {
if (!path) return;
router.push(path);
};
const stages = [
{ step: 1, name: '选:AI面试', desc: '题目与评估', label: '已完成', state: 'done', tagType: 'success' },
{ step: 2, name: '用:岗位适配', desc: '与录用评估', label: '已完成', state: 'done', tagType: 'success' },
{ step: 3, name: '育:三角色对练', desc: '实战陪练', label: '进行中', state: 'active', tagType: 'danger' },
{ step: 4, name: '留:成长追踪', desc: '与能力提升', label: '待启动', state: 'idle', tagType: 'info' },
{ step: 5, name: '案例沉淀', desc: '知识与复用', label: '待启动', state: 'idle', tagType: 'info' }
] as const;
const risks = [
{ name: '住宅 SOP 素材', owner: '培训组', status: '待确认', type: 'danger', icon: Tickets },
{ name: '方言 ASR', owner: '技术组', status: '样片兜底', type: 'warning', icon: Headset },
{ name: 'PII 出境评估', owner: '合规组', status: '评估中', type: 'warning', icon: Finished },
{ name: '录屏兜底方案', owner: '产品组', status: '已准备', type: 'primary', icon: VideoCamera }
] as const;
const records = [
{ name: '张三', scene: '客服应对投诉', score: 86, status: '已完成', state: 'done', time: '07-02 09:32' },
{ name: '李四', scene: '催缴沟通技巧', score: 78, status: '已完成', state: 'done', time: '07-02 09:12' },
{ name: '王五', scene: '突发事件处理', score: 72, status: '进行中', state: 'active', time: '07-02 08:47' },
{ name: '赵六', scene: '业主沟通谈判', score: 90, status: '已完成', state: 'done', time: '07-02 08:15' },
{ name: '孙七', scene: '服务礼仪规范', score: 65, status: '未达标', state: 'danger', time: '07-01 18:33' }
];
const pipeline = [
{ name: '语音上传', count: '12 条', icon: FolderChecked },
{ name: 'ASR 转写', count: '12 条', icon: Histogram },
{ name: 'AI 整理', count: '10 条', icon: Cpu },
{ name: '审核入库', count: '8 条', icon: Tickets },
{ name: '样片展示', count: '5 条', icon: VideoCamera }
] as const;
const metrics = [
{ label: '端到端路径', value: '1 条', target: '可完整演示', icon: Switch },
{ label: 'RAG 可用率', value: '95%', target: '目标 > 90%', icon: Operation },
{ label: '评分一致率', value: '92%', target: '目标 > 85%', icon: TrendCharts },
{ label: '试点人数', value: '120 人', target: '目标 > 100 人', icon: DataLine }
] as const;
</script>
<style lang="scss" scoped>
.demo-home {
min-height: calc(100vh - 84px);
padding: 22px;
background: #f5f7fb;
color: #162033;
}
.toolbar-meta {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
margin-bottom: 12px;
color: #657184;
font-size: 13px;
}
.status-pill {
display: inline-flex;
align-items: center;
justify-content: center;
width: fit-content;
min-height: 24px;
padding: 0 10px;
border-radius: 6px;
font-size: 12px;
line-height: 24px;
font-weight: 700;
white-space: nowrap;
}
.pill-danger {
color: #c80f1f;
background: #ffe8e9;
}
.pill-success {
color: #15844a;
background: #e5f8ee;
}
.pill-warning {
color: #c46b00;
background: #fff2dc;
}
.pill-primary {
color: #1467bd;
background: #e7f2ff;
}
.pill-info {
color: #64748b;
background: #eef2f7;
}
.hero-grid,
.content-grid,
.bottom-grid {
display: grid;
gap: 12px;
}
.hero-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
margin-bottom: 12px;
}
.content-grid {
grid-template-columns: minmax(0, 1.25fr) minmax(360px, 0.75fr);
margin-bottom: 12px;
}
.bottom-grid {
grid-template-columns: minmax(350px, 1fr) minmax(420px, 1.1fr) minmax(330px, 0.9fr);
}
.panel,
.entry-card {
border: 1px solid #e4e9f2;
border-radius: 8px;
background: #fff;
box-shadow: 0 10px 24px rgba(21, 30, 45, 0.04);
}
.entry-card {
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 188px;
padding: 22px 22px 20px;
}
.entry-main {
display: flex;
gap: 16px;
align-items: flex-start;
h2 {
margin: 2px 0 8px;
font-size: 18px;
line-height: 1.25;
font-weight: 700;
}
p {
min-height: 40px;
margin: 0 0 12px;
color: #3f4b5f;
font-size: 13px;
line-height: 1.55;
}
}
.entry-icon {
display: grid;
width: 58px;
height: 58px;
flex: 0 0 58px;
place-items: center;
border-radius: 8px;
color: #fff;
background: linear-gradient(135deg, #ff6b72, #d90f20);
font-size: 32px;
box-shadow: 0 10px 20px rgba(216, 15, 32, 0.22);
}
.entry-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 22px;
.el-button {
margin: 0;
}
}
.panel {
padding: 18px;
}
.panel-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 18px;
&.compact {
margin-bottom: 12px;
}
h2 {
margin: 0;
font-size: 17px;
line-height: 1.35;
font-weight: 700;
}
}
.stage-track {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
}
.stage-item {
position: relative;
display: grid;
justify-items: center;
gap: 12px;
min-height: 150px;
text-align: center;
&:not(:last-child)::after {
position: absolute;
top: 22px;
right: -50%;
width: 100%;
height: 0;
border-top: 2px dashed #c7ced8;
content: '';
}
&.done:not(:last-child)::after {
border-color: #31b367;
}
&.active:not(:last-child)::after {
border-color: #d70f20;
}
}
.stage-node {
z-index: 1;
display: grid;
width: 48px;
height: 48px;
place-items: center;
border: 2px solid #d3d9e3;
border-radius: 50%;
background: #fff;
color: #69758a;
font-size: 18px;
font-weight: 700;
}
.stage-item.done .stage-node {
border-color: #31b367;
color: #fff;
background: #31b367;
}
.stage-item.active .stage-node {
border-color: #d70f20;
color: #fff;
background: #d70f20;
}
.stage-copy {
display: grid;
justify-items: center;
gap: 8px;
strong {
font-size: 14px;
}
span {
min-height: 34px;
color: #303b4c;
font-size: 13px;
line-height: 1.35;
}
}
.risk-list {
display: grid;
gap: 0;
padding: 0;
margin: 0;
list-style: none;
li {
display: grid;
grid-template-columns: 34px minmax(110px, 1fr) minmax(120px, 0.8fr) 82px;
align-items: center;
gap: 12px;
min-height: 43px;
border-bottom: 1px solid #edf0f5;
&:last-child {
border-bottom: 0;
}
}
}
.risk-icon {
color: #5d6878;
font-size: 22px;
}
.risk-name {
font-weight: 700;
}
.risk-owner {
color: #637085;
font-size: 13px;
}
.record-panel {
:deep(.el-table__inner-wrapper::before) {
display: none;
}
}
.record-table {
border: 1px solid #edf0f5;
border-radius: 8px;
overflow: hidden;
}
.status-dot {
display: inline-block;
width: 6px;
height: 6px;
margin-right: 6px;
border-radius: 50%;
vertical-align: middle;
&.done {
background: #31b367;
}
&.active {
background: #e6a23c;
}
&.danger {
background: #d70f20;
}
}
.pipeline-steps {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 12px;
padding: 8px 0 18px;
}
.pipeline-step {
position: relative;
display: grid;
justify-items: center;
gap: 8px;
text-align: center;
strong {
font-size: 13px;
}
span {
color: #0f7be8;
font-size: 13px;
font-weight: 700;
}
}
.pipeline-icon {
display: grid;
width: 56px;
height: 56px;
place-items: center;
border: 1px solid #dce2ec;
border-radius: 50%;
background: #f8fafc;
color: #1d2b3f;
font-size: 30px;
}
.pipeline-arrow {
position: absolute;
top: 22px;
right: -18px;
color: #aab3c2;
}
.pipeline-summary {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
padding-top: 14px;
border-top: 1px solid #edf0f5;
color: #526178;
span {
position: relative;
&:not(:last-child)::after {
position: absolute;
top: 3px;
right: -9px;
width: 1px;
height: 12px;
background: #cbd3df;
content: '';
}
}
}
.metric-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.metric-card {
display: grid;
grid-template-columns: 44px minmax(0, 1fr);
align-items: center;
gap: 10px;
min-height: 86px;
padding: 12px;
border: 1px solid #e5eaf3;
border-radius: 8px;
.el-icon {
color: #d70f20;
font-size: 34px;
}
span,
small {
display: block;
color: #5f6c80;
font-size: 12px;
}
strong {
display: block;
margin: 2px 0;
color: #142033;
font-size: 24px;
line-height: 1.1;
}
}
.panel-link {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 4px;
margin-top: 14px;
color: #d70f20;
font-size: 13px;
font-weight: 700;
}
@media (max-width: 1360px) {
.hero-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.content-grid,
.bottom-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.demo-home {
padding: 14px;
}
.page-heading {
align-items: flex-start;
flex-direction: column;
}
.hero-grid,
.stage-track,
.pipeline-steps,
.metric-grid {
grid-template-columns: 1fr;
}
.stage-item {
justify-items: start;
grid-template-columns: 48px minmax(0, 1fr);
min-height: auto;
text-align: left;
&::after {
display: none;
}
}
.stage-copy {
justify-items: start;
}
.risk-list li {
grid-template-columns: 28px minmax(0, 1fr);
}
.risk-owner,
.risk-status {
grid-column: 2;
}
.pipeline-step {
grid-template-columns: 56px minmax(0, 1fr);
justify-items: start;
text-align: left;
}
.pipeline-arrow {
display: none;
}
.pipeline-summary span::after {
display: none;
}
}
</style>