包含前后端完整代码: - 前端:Vue3+Vite+ElementPlus - 后端:FastAPI+SQLAlchemy - 模块:驾驶舱/KPI/战略地图/预警/预算/成本/预测/改善行动 - 当前版本:v1.0.0
245 lines
10 KiB
Vue
245 lines
10 KiB
Vue
<template>
|
||
<div class="guide-page">
|
||
<div class="guide-header">
|
||
<h3>🚀 新手引导</h3>
|
||
<p class="guide-subtitle">5分钟快速上手管理会计OS,开始你的数据驱动管理之旅</p>
|
||
</div>
|
||
|
||
<!-- 快速开始引导 -->
|
||
<div class="quick-start-card" @click="startOnboarding">
|
||
<div class="qs-icon">🚀</div>
|
||
<div class="qs-body">
|
||
<div class="qs-title">快速开始引导</div>
|
||
<div class="qs-desc">按步骤带你完成系统配置和首次使用</div>
|
||
<el-tag size="small" type="danger">推荐新手</el-tag>
|
||
</div>
|
||
<el-button type="primary" size="large" class="qs-btn">开始引导</el-button>
|
||
</div>
|
||
|
||
<!-- 流程概览 -->
|
||
<div class="flow-section">
|
||
<h4>使用流程</h4>
|
||
<div class="flow-steps">
|
||
<div v-for="(step, i) in steps" :key="i" class="flow-step">
|
||
<div class="flow-num">{{ i + 1 }}</div>
|
||
<div class="flow-icon">{{ step.icon }}</div>
|
||
<div class="flow-title">{{ step.title }}</div>
|
||
<div class="flow-desc">{{ step.desc }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 视频教程 -->
|
||
<div class="video-section">
|
||
<h4>📹 视频教程</h4>
|
||
<div class="video-grid">
|
||
<div v-for="(v, i) in videos" :key="i" class="video-card" @click="openVideo(v)">
|
||
<div class="video-thumb">
|
||
<span class="play-icon">▶</span>
|
||
<span class="video-duration">{{ v.duration }}</span>
|
||
</div>
|
||
<div class="video-title">{{ v.title }}</div>
|
||
<div class="video-desc">{{ v.desc }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 引导遮罩弹窗 -->
|
||
<teleport to="body">
|
||
<div v-if="showWalkthrough" class="walkthrough-overlay" @click="skipWalkthrough">
|
||
<div class="walkthrough-box" @click.stop>
|
||
<div class="wt-header">
|
||
<span class="wt-step">{{ currentStep + 1 }} / {{ walkthroughSteps.length }}</span>
|
||
<el-button text size="small" @click="skipWalkthrough" style="color:#999;">跳过</el-button>
|
||
</div>
|
||
<div class="wt-body">
|
||
<div class="wt-icon">{{ walkthroughSteps[currentStep].icon }}</div>
|
||
<div class="wt-title">{{ walkthroughSteps[currentStep].title }}</div>
|
||
<div class="wt-desc">{{ walkthroughSteps[currentStep].desc }}</div>
|
||
<div class="wt-action" v-if="walkthroughSteps[currentStep].action">
|
||
<el-button type="primary" @click="goAction(walkthroughSteps[currentStep].action!)">前往{{ walkthroughSteps[currentStep].actionLabel }}</el-button>
|
||
</div>
|
||
</div>
|
||
<div class="wt-footer">
|
||
<div class="wt-dots">
|
||
<span v-for="(_, i) in walkthroughSteps" :key="i" class="wt-dot" :class="{ active: i === currentStep }"></span>
|
||
</div>
|
||
<div class="wt-btns">
|
||
<el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
|
||
<el-button v-if="currentStep < walkthroughSteps.length - 1" type="primary" @click="nextStep">下一步</el-button>
|
||
<el-button v-else type="success" @click="finishWalkthrough">🎉 完成引导</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</teleport>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from "vue"
|
||
import { useRouter } from "vue-router"
|
||
import { ElMessage } from "element-plus"
|
||
|
||
const router = useRouter()
|
||
|
||
// ── 流程步骤 ──
|
||
const steps = [
|
||
{ icon: "📊", title: "查看驾驶舱", desc: "总览企业KPI整体状况" },
|
||
{ icon: "🎯", title: "配置KPI", desc: "在KPI字典中设置关键指标" },
|
||
{ icon: "🗺️", title: "绘制战略地图", desc: "把你的战略目标画出来" },
|
||
{ icon: "⚡", title: "设定预警", desc: "配置红黄绿灯阈值" },
|
||
{ icon: "📋", title: "日常管理", desc: "通过工作台持续跟进" },
|
||
]
|
||
|
||
// ── 视频教程 ──
|
||
const videos = [
|
||
{ title: "如何设置KPI", desc: "KPI字典的创建、编辑和配置", duration: "3:20", url: "" },
|
||
{ title: "如何绘制战略地图", desc: "创建目标、关联KPI、画因果连线", duration: "4:15", url: "" },
|
||
{ title: "如何配置预警", desc: "设置红黄绿灯阈值和通知渠道", duration: "2:50", url: "" },
|
||
{ title: "如何进行差异分析", desc: "对比实际与预算,找出偏差原因", duration: "3:45", url: "" },
|
||
{ title: "如何使用战略回顾会", desc: "周会月会的复盘操作方法", duration: "3:00", url: "" },
|
||
{ title: "如何提交改善行动", desc: "从预警到行动计划的全流程", duration: "2:30", url: "" },
|
||
]
|
||
|
||
// ── 引导步骤 ──
|
||
const walkthroughSteps = [
|
||
{
|
||
icon: "👋",
|
||
title: "欢迎使用管理会计OS",
|
||
desc: "这是你的企业管理驾驶舱。我们将带你快速完成首次配置,让你在5分钟内上手。",
|
||
},
|
||
{
|
||
icon: "📊",
|
||
title: "Step 1: 先看驾驶舱",
|
||
desc: "驾驶舱是你了解企业整体状况的第一站。这里展示了所有KPI的红黄绿灯状态,以及你的个人KPI。",
|
||
action: "/dashboard",
|
||
actionLabel: "驾驶舱",
|
||
},
|
||
{
|
||
icon: "🎯",
|
||
title: "Step 2: 配置你的KPI",
|
||
desc: "在KPI字典中管理和配置你的关键绩效指标。你可以新建KPI、设置目标值和预警阈值。",
|
||
action: "/kpis",
|
||
actionLabel: "KPI字典",
|
||
},
|
||
{
|
||
icon: "🗺️",
|
||
title: "Step 3: 绘制战略地图",
|
||
desc: "使用BSC四维框架,把你的战略目标可视化。在画布上添加目标、关联KPI、画因果连线。",
|
||
action: "/maps",
|
||
actionLabel: "战略地图",
|
||
},
|
||
{
|
||
icon: "📋",
|
||
title: "Step 4: 开始日常管理",
|
||
desc: "通过「我的工作台」查看个人KPI和改善行动,通过「战略回顾会」进行周月复盘。",
|
||
action: "/my-dashboard",
|
||
actionLabel: "我的工作台",
|
||
},
|
||
]
|
||
|
||
const showWalkthrough = ref(false)
|
||
const currentStep = ref(0)
|
||
const FIRST_LOGIN_KEY = "cma_first_login_done"
|
||
|
||
// ── 方法 ──
|
||
function startOnboarding() {
|
||
currentStep.value = 0
|
||
showWalkthrough.value = true
|
||
}
|
||
|
||
function nextStep() {
|
||
if (currentStep.value < walkthroughSteps.length - 1) {
|
||
currentStep.value++
|
||
}
|
||
}
|
||
|
||
function prevStep() {
|
||
if (currentStep.value > 0) {
|
||
currentStep.value--
|
||
}
|
||
}
|
||
|
||
function skipWalkthrough() {
|
||
showWalkthrough.value = false
|
||
}
|
||
|
||
function finishWalkthrough() {
|
||
localStorage.setItem(FIRST_LOGIN_KEY, "true")
|
||
showWalkthrough.value = false
|
||
ElMessage.success("🎉 引导完成,开始你的管理会计之旅吧!")
|
||
}
|
||
|
||
function goAction(path: string) {
|
||
router.push(path)
|
||
showWalkthrough.value = false
|
||
}
|
||
|
||
function openVideo(v: any) {
|
||
if (v.url) {
|
||
window.open(v.url, "_blank")
|
||
} else {
|
||
ElMessage.info("视频教程正在制作中,敬请期待")
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
// 检查是否首次登录
|
||
const done = localStorage.getItem(FIRST_LOGIN_KEY)
|
||
if (!done) {
|
||
// 延迟1秒后再弹出,让页面先加载完
|
||
setTimeout(() => {
|
||
showWalkthrough.value = true
|
||
}, 1000)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.guide-page { padding: 20px; height: calc(100vh - 60px); overflow-y: auto; }
|
||
.guide-header { margin-bottom: 24px; }
|
||
.guide-header h3 { margin: 0; font-size: 20px; }
|
||
.guide-subtitle { color: #999; margin: 4px 0 0; font-size: 14px; }
|
||
.quick-start-card { display: flex; align-items: center; gap: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border-radius: 12px; padding: 20px; margin-bottom: 24px; cursor: pointer; }
|
||
.qs-icon { font-size: 40px; }
|
||
.qs-body { flex: 1; }
|
||
.qs-title { font-size: 18px; font-weight: 600; margin-bottom: 4px; }
|
||
.qs-desc { font-size: 13px; opacity: 0.8; margin-bottom: 6px; }
|
||
.qs-btn { flex-shrink: 0; }
|
||
.flow-section { margin-bottom: 24px; }
|
||
.flow-section h4 { margin: 0 0 16px; font-size: 16px; }
|
||
.flow-steps { display: flex; gap: 12px; }
|
||
.flow-step { flex: 1; text-align: center; padding: 16px; background: #fff; border-radius: 10px; box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||
.flow-num { width: 28px; height: 28px; border-radius: 50%; background: #409eff; color: #fff; display: flex; align-items: center; justify-content: center; margin: 0 auto 8px; font-size: 13px; font-weight: 600; }
|
||
.flow-icon { font-size: 28px; margin-bottom: 6px; }
|
||
.flow-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
|
||
.flow-desc { font-size: 12px; color: #999; }
|
||
.video-section { margin-bottom: 24px; }
|
||
.video-section h4 { margin: 0 0 16px; font-size: 16px; }
|
||
.video-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
|
||
.video-card { background: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.06); cursor: pointer; transition: transform 0.2s; }
|
||
.video-card:hover { transform: translateY(-2px); }
|
||
.video-thumb { position: relative; background: #304156; height: 120px; display: flex; align-items: center; justify-content: center; }
|
||
.play-icon { font-size: 40px; color: #fff; opacity: 0.8; }
|
||
.video-duration { position: absolute; bottom: 6px; right: 8px; background: rgba(0,0,0,0.6); color: #fff; padding: 2px 6px; border-radius: 4px; font-size: 11px; }
|
||
.video-title { padding: 10px 12px 2px; font-size: 14px; font-weight: 500; }
|
||
.video-desc { padding: 0 12px 10px; font-size: 12px; color: #999; }
|
||
|
||
/* 引导遮罩 */
|
||
.walkthrough-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999; display: flex; align-items: center; justify-content: center; }
|
||
.walkthrough-box { background: #fff; border-radius: 16px; width: 480px; max-width: 90vw; box-shadow: 0 8px 32px rgba(0,0,0,0.2); overflow: hidden; }
|
||
.wt-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 20px; border-bottom: 1px solid #f0f0f0; }
|
||
.wt-step { font-size: 12px; color: #999; }
|
||
.wt-body { padding: 32px 20px; text-align: center; }
|
||
.wt-icon { font-size: 56px; margin-bottom: 16px; }
|
||
.wt-title { font-size: 20px; font-weight: 600; margin-bottom: 12px; }
|
||
.wt-desc { font-size: 14px; color: #666; line-height: 1.6; margin-bottom: 20px; }
|
||
.wt-action { margin-top: 8px; }
|
||
.wt-footer { padding: 12px 20px; border-top: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: center; }
|
||
.wt-dots { display: flex; gap: 6px; }
|
||
.wt-dot { width: 8px; height: 8px; border-radius: 50%; background: #ddd; transition: background 0.3s; }
|
||
.wt-dot.active { background: #409eff; }
|
||
.wt-btns { display: flex; gap: 8px; }
|
||
</style>
|