init: 管理会计OS初始代码

包含前后端完整代码:
- 前端:Vue3+Vite+ElementPlus
- 后端:FastAPI+SQLAlchemy
- 模块:驾驶舱/KPI/战略地图/预警/预算/成本/预测/改善行动
- 当前版本:v1.0.0
This commit is contained in:
Hermes CI Fix
2026-05-28 17:32:22 +08:00
commit 3dddd36866
142 changed files with 18533 additions and 0 deletions
+244
View File
@@ -0,0 +1,244 @@
<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>