新增: 初始化 DevFlow 项目仓库

Tauri 2 + Vue 3 + Vite 6 桌面应用,Rust workspace 含 13 个 crate
(df-ai / df-storage / df-workflow / df-core / df-execute 等)。
核心能力:AI 聊天 agentic 循环(工具调用+人工审批)、工作流引擎、
任务/想法/项目/阶段管理、可追溯性,及配套前端组件。
This commit is contained in:
2026-06-12 01:31:05 +08:00
commit 98393b4908
178 changed files with 27859 additions and 0 deletions

338
src/views/Knowledge.vue Normal file
View File

@@ -0,0 +1,338 @@
<template>
<div class="knowledge">
<!-- 页面头部 -->
<header class="page-header">
<h1>📚 知识库</h1>
<div class="header-actions">
<button class="btn btn-primary" @click="addKnowledge">+ 新增知识</button>
</div>
</header>
<!-- 搜索栏 -->
<div class="search-bar">
<input
v-model="searchQuery"
type="text"
class="search-input"
placeholder="搜索知识标题、标签、内容..."
/>
</div>
<!-- 分类标签页 -->
<div class="category-tabs">
<button
v-for="cat in categories"
:key="cat.key"
class="tab-btn"
:class="{ 'tab-active': activeCategory === cat.key }"
@click="activeCategory = cat.key"
>
<span class="tab-icon">{{ cat.icon }}</span>
<span class="tab-label">{{ cat.label }}</span>
<span class="tab-count">{{ getCategoryCount(cat.key) }}</span>
</button>
</div>
<!-- 知识卡片列表 -->
<div class="knowledge-grid">
<div class="knowledge-card" v-for="item in filteredItems" :key="item.id">
<div class="card-header">
<span class="card-title">{{ item.title }}</span>
<span class="card-score" :class="scoreClass(item.score)">{{ item.score }}</span>
</div>
<div class="card-desc">{{ item.description }}</div>
<div class="card-tags">
<span class="tag" v-for="tag in item.tags" :key="tag">{{ tag }}</span>
</div>
<div class="card-footer">
<span class="card-reuse">🔄 复用 {{ item.reuseCount }} </span>
<span class="card-category">{{ getCategoryLabel(item.category) }}</span>
<span class="card-time">{{ item.updatedAt }}</span>
<button class="btn btn-ghost" @click="deleteKnowledge(item.id)">删除</button>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-if="filteredItems.length === 0" class="empty-state">
<div class="empty-icon">📭</div>
<div class="empty-text">没有找到匹配的知识条目</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
interface KnowledgeItem {
id: number
title: string
description: string
category: string
tags: string[]
reuseCount: number
score: number
updatedAt: string
}
const categories = [
{ key: 'all', label: '全部', icon: '📦' },
{ key: 'review', label: '审查规则', icon: '🔍' },
{ key: 'prompt', label: 'Prompt模板', icon: '💬' },
{ key: 'pitfall', label: '踩坑经验', icon: '⚠️' },
{ key: 'diagnosis', label: '诊断知识', icon: '🩺' },
{ key: 'deploy', label: '部署经验', icon: '🚀' },
]
const activeCategory = ref('all')
const searchQuery = ref('')
const knowledgeItems = ref<KnowledgeItem[]>(loadKnowledgeItems())
// 从 localStorage 加载知识数据
function loadKnowledgeItems(): KnowledgeItem[] {
const saved = localStorage.getItem('devflow-knowledge')
if (saved) {
try {
return JSON.parse(saved)
} catch {
// 解析失败,使用默认数据
}
}
return getDefaultData()
}
// 获取默认数据
function getDefaultData(): KnowledgeItem[] {
return [
// 审查规则
{ id: 1, title: 'SQL 注入防护检查', description: '所有 SQL 拼接必须使用参数化查询,禁止字符串拼接用户输入', category: 'review', tags: ['安全', 'SQL', 'Go'], reuseCount: 23, score: 95, updatedAt: '3 天前' },
{ id: 2, title: '错误处理规范', description: '禁止吞掉 error必须向上传播或记录日志', category: 'review', tags: ['Go', '规范', '错误处理'], reuseCount: 18, score: 88, updatedAt: '1 周前' },
{ id: 3, title: 'API 响应格式一致性', description: '统一使用 { code, message, data } 结构, HTTP 状态码语义正确', category: 'review', tags: ['API', '规范'], reuseCount: 15, score: 82, updatedAt: '5 天前' },
// Prompt 模板
{ id: 4, title: 'SQL 查询生成 Prompt', description: '根据自然语言生成 SQL包含表结构上下文和示例输出格式', category: 'prompt', tags: ['SQL', 'LLM', '模板'], reuseCount: 142, score: 91, updatedAt: '昨天' },
{ id: 5, title: '代码审查 Prompt', description: 'AI 代码审查专用 Prompt涵盖安全、性能、可维护性维度', category: 'prompt', tags: ['代码审查', 'LLM'], reuseCount: 87, score: 85, updatedAt: '3 天前' },
{ id: 6, title: 'API 文档生成 Prompt', description: '从 Handler 代码自动生成 API 文档的 Prompt 模板', category: 'prompt', tags: ['文档', 'LLM', '自动生成'], reuseCount: 34, score: 78, updatedAt: '1 周前' },
// 踩坑经验
{ id: 7, title: 'Go context 传递陷阱', description: 'goroutine 中必须传递 context 而非创建新的,否则超时控制失效', category: 'pitfall', tags: ['Go', '并发', 'context'], reuseCount: 12, score: 92, updatedAt: '2 天前' },
{ id: 8, title: 'Vue 3 作用域坑', description: 'v-for 内 ref 绑定不会自动响应式,需使用数组形式', category: 'pitfall', tags: ['Vue', '前端', '响应式'], reuseCount: 8, score: 75, updatedAt: '1 周前' },
{ id: 9, title: 'Docker 网络模式选择', description: 'host 模式在 Mac/Win 上无效,必须用端口映射', category: 'pitfall', tags: ['Docker', '网络'], reuseCount: 6, score: 70, updatedAt: '2 周前' },
// 诊断知识
{ id: 10, title: 'MySQL 慢查询诊断流程', description: 'EXPLAIN → 索引检查 → 慢查询日志分析 → 优化建议', category: 'diagnosis', tags: ['MySQL', '性能', '诊断'], reuseCount: 31, score: 89, updatedAt: '4 天前' },
{ id: 11, title: 'Go 内存泄漏排查', description: 'pprof heap 分析 → goroutine 泄漏检查 → GC 调优', category: 'diagnosis', tags: ['Go', '内存', 'pprof'], reuseCount: 9, score: 83, updatedAt: '1 周前' },
{ id: 12, title: '前端白屏诊断', description: 'Console 错误 → 网络请求 → 路由配置 → 构建产物检查', category: 'diagnosis', tags: ['前端', '调试', 'Vue'], reuseCount: 14, score: 80, updatedAt: '5 天前' },
// 部署经验
{ id: 13, title: 'Go 二进制热更新', description: 'kill + mv + nohup 启动,无需 systemd 的轻量部署方案', category: 'deploy', tags: ['Go', '部署', 'Linux'], reuseCount: 19, score: 86, updatedAt: '昨天' },
{ id: 14, title: 'SCP 上传最佳实践', description: '使用 ssh-proxy upload 代替 scp支持配置化管理', category: 'deploy', tags: ['SCP', '部署', '工具'], reuseCount: 11, score: 72, updatedAt: '3 天前' },
{ id: 15, title: 'Nginx 反向代理配置', description: 'u-ask 的 Nginx 配置模板,含 WebSocket 支持和 gzip', category: 'deploy', tags: ['Nginx', '配置', '反向代理'], reuseCount: 7, score: 77, updatedAt: '1 周前' },
]
}
// 保存到 localStorage
function saveToLocalStorage() {
localStorage.setItem('devflow-knowledge', JSON.stringify(knowledgeItems.value))
}
// 添加知识
function addKnowledge() {
// TODO: 实现添加知识的对话框逻辑
console.log('添加知识')
}
// 删除知识
function deleteKnowledge(id: number) {
const index = knowledgeItems.value.findIndex(item => item.id === id)
if (index !== -1) {
knowledgeItems.value.splice(index, 1)
saveToLocalStorage()
}
}
const filteredItems = computed(() => {
let items = knowledgeItems.value
if (activeCategory.value !== 'all') {
items = items.filter(i => i.category === activeCategory.value)
}
if (searchQuery.value.trim()) {
const q = searchQuery.value.toLowerCase()
items = items.filter(i =>
i.title.toLowerCase().includes(q) ||
i.tags.some(t => t.toLowerCase().includes(q)) ||
i.description.toLowerCase().includes(q)
)
}
return items
})
function getCategoryCount(key: string): number {
if (key === 'all') return knowledgeItems.value.length
return knowledgeItems.value.filter(i => i.category === key).length
}
function getCategoryLabel(key: string): string {
const cat = categories.find(c => c.key === key)
return cat ? cat.label : key
}
function scoreClass(score: number): string {
if (score >= 90) return 'score-high'
if (score >= 75) return 'score-mid'
return 'score-low'
}
</script>
<style scoped>
.knowledge { padding: 16px 20px 20px; }
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--df-gap-page);
}
.page-header h1 { font-size: 24px; font-weight: 500; color: var(--df-text); }
.header-actions { display: flex; gap: 8px; }
/* ===== 按钮 ===== */
.btn {
padding: 8px 16px;
border: none;
border-radius: var(--df-radius-sm);
font-size: 13px;
cursor: pointer;
transition: all 0.15s;
}
.btn-primary { background: var(--df-accent); color: #fff; }
.btn-primary:hover { background: var(--df-accent-hover); }
.btn-accent { background: rgba(100,255,218,0.15); color: var(--df-success); border: 0.5px solid rgba(100,255,218,0.3); }
.btn-accent:hover { background: rgba(100,255,218,0.25); }
.btn-ghost { background: transparent; color: var(--df-text-secondary); border: 0.5px solid var(--df-border); }
.btn-ghost:hover { background: var(--df-bg-card); color: var(--df-text); }
/* ===== 搜索栏 ===== */
.search-bar { margin-bottom: var(--df-gap-page); }
.search-input {
width: 100%;
max-width: 480px;
padding: 10px 16px;
background: var(--df-bg-card);
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius);
color: var(--df-text);
font-size: 14px;
outline: none;
transition: border-color 0.2s;
}
.search-input::placeholder { color: var(--df-text-dim); }
.search-input:focus { border-color: var(--df-accent); }
/* ===== 分类标签 ===== */
.category-tabs {
display: flex;
gap: 8px;
margin-bottom: var(--df-gap-page);
flex-wrap: wrap;
}
.tab-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius);
background: transparent;
color: var(--df-text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.15s;
}
.tab-btn:hover { background: var(--df-bg-card); color: var(--df-text); }
.tab-active {
background: var(--df-accent) !important;
color: #fff !important;
border-color: var(--df-accent) !important;
}
.tab-icon { font-size: 14px; }
.tab-count {
font-size: 11px;
background: rgba(255,255,255,0.15);
padding: 1px 6px;
border-radius: var(--df-radius);
min-width: 18px;
text-align: center;
}
.tab-active .tab-count { background: rgba(255,255,255,0.25); }
/* ===== 知识卡片网格 ===== */
.knowledge-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
gap: var(--df-gap-grid);
}
.knowledge-card {
background: var(--df-bg-card);
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius-lg);
padding: var(--df-pad-panel);
transition: border-color 0.15s, transform 0.15s;
}
.knowledge-card:hover {
border-color: var(--df-accent);
transform: translateY(-1px);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.card-title { font-size: 14px; font-weight: 500; color: var(--df-text); }
.card-score {
font-size: 12px;
font-weight: 500;
padding: 2px 8px;
border-radius: var(--df-radius-xs);
}
.score-high { background: rgba(100,255,218,0.15); color: var(--df-success); }
.score-mid { background: rgba(255,217,61,0.15); color: var(--df-warning); }
.score-low { background: rgba(255,107,107,0.15); color: var(--df-danger); }
.card-desc {
font-size: 13px;
color: var(--df-text-secondary);
line-height: 1.5;
margin-bottom: 12px;
}
.card-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.tag {
font-size: 11px;
padding: 2px 8px;
border-radius: var(--df-radius-xs);
background: rgba(108,99,255,0.1);
color: var(--df-accent);
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 11px;
color: var(--df-text-dim);
}
.card-reuse { color: var(--df-info); }
.card-category {
background: rgba(90,99,128,0.2);
padding: 2px 6px;
border-radius: var(--df-radius-sm);
}
/* ===== 空状态 ===== */
.empty-state {
text-align: center;
padding: 60px 0;
}
.empty-icon { font-size: 48px; margin-bottom: 12px; }
.empty-text { font-size: 14px; color: var(--df-text-dim); }
</style>