feat: implement ERP AI Assistant Phase 1
Backend (FastAPI + SQLAlchemy + Claude API + RAG): - Config management with Pydantic v2 - Database engine with connection pooling and SQL injection prevention - AI engine with Claude API integration (support custom base URL) - RAG engine with ChromaDB and sentence-transformers - Requirement analysis service - Config generation service - Executor engine with SQL validation - REST API endpoints: /analyze, /generate, /execute Frontend (Vue 3 + Element Plus + Pinia): - Complete 3-step workflow: analyze → generate → execute - Step indicator with progress visualization - Analysis result display with field table - SQL preview with monospace font - Execute confirmation dialog with safety warning - Execution result display - State management with Pinia - API service integration Security: - SQL injection prevention with parameterized queries - Dangerous SQL operation blocking - Database password URL encoding - Transaction auto-rollback - Pydantic config validation Features: - Natural language requirement analysis - Automated SQL configuration generation - Safe execution with human review - LAN access support - Custom Claude API endpoint support Documentation: - README with quick start guide - Quick start guide - LAN access configuration - Dependency fixes guide - Claude API configuration - Git operation guide - Implementation report Dependencies fixed: - numpy<2.0.0 for chromadb compatibility - sentence-transformers==2.7.0 for huggingface_hub compatibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
380
frontend/src/views/CreateFunction.vue
Normal file
380
frontend/src/views/CreateFunction.vue
Normal file
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<div class="create-function">
|
||||
<!-- 步骤指示器 -->
|
||||
<el-steps :active="currentStep" finish-status="success" simple class="steps">
|
||||
<el-step title="需求分析" />
|
||||
<el-step title="配置生成" />
|
||||
<el-step title="执行配置" />
|
||||
</el-steps>
|
||||
|
||||
<!-- 步骤 1: 需求输入 -->
|
||||
<el-card v-if="currentStep === 0" class="section-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>步骤 1: 输入需求</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="需求描述">
|
||||
<el-input
|
||||
v-model="form.requirement"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入功能需求,例如:创建一个销售订单管理页面,包含订单号、客户、订单日期、金额等字段"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleAnalyze"
|
||||
:loading="functionStore.loading.analyzing"
|
||||
:disabled="!form.requirement.trim()"
|
||||
>
|
||||
开始分析需求
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 步骤 2: 分析结果 -->
|
||||
<el-card v-if="functionStore.analysisResult" class="section-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>需求分析结果</span>
|
||||
<el-tag type="success">已完成</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="功能名称">
|
||||
{{ functionStore.analysisResult.功能名称 || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="功能类型">
|
||||
{{ functionStore.analysisResult.功能类型 || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="功能号建议">
|
||||
{{ functionStore.analysisResult.功能号建议 || '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="窗体类型">
|
||||
{{ functionStore.analysisResult.窗体类型 || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div v-if="functionStore.analysisResult.主表字段" class="field-list">
|
||||
<h4>主表字段:</h4>
|
||||
<el-table :data="functionStore.analysisResult.主表字段" border size="small">
|
||||
<el-table-column prop="字段名" label="字段名" />
|
||||
<el-table-column prop="字段类型" label="字段类型" />
|
||||
<el-table-column prop="必填" label="必填">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.必填 ? 'danger' : 'info'" size="small">
|
||||
{{ row.必填 ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<el-button @click="resetAll">重新分析</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleGenerate"
|
||||
:loading="functionStore.loading.generating"
|
||||
>
|
||||
生成配置方案
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 步骤 3: 配置方案 -->
|
||||
<el-card v-if="functionStore.configResult" class="section-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>配置方案</span>
|
||||
<el-tag type="success">已生成</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-alert
|
||||
title="请仔细检查以下 SQL 配置,确认无误后执行"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="alert-box"
|
||||
/>
|
||||
|
||||
<div v-if="functionStore.configResult.配置方案" class="config-section">
|
||||
<h4>SQL 配置语句:</h4>
|
||||
<el-input
|
||||
v-model="sqlDisplay"
|
||||
type="textarea"
|
||||
:rows="15"
|
||||
readonly
|
||||
class="sql-textarea"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<el-button @click="backToAnalysis">返回修改</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
@click="showExecuteConfirm"
|
||||
:loading="functionStore.loading.executing"
|
||||
>
|
||||
确认并执行
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 执行结果 -->
|
||||
<el-card v-if="functionStore.executeResult" class="section-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>执行结果</span>
|
||||
<el-tag :type="functionStore.executeResult.status === 'success' ? 'success' : 'danger'">
|
||||
{{ functionStore.executeResult.status === 'success' ? '执行成功' : '执行失败' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-result
|
||||
:icon="functionStore.executeResult.status === 'success' ? 'success' : 'error'"
|
||||
:title="functionStore.executeResult.message"
|
||||
>
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="resetAll">创建新功能</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</el-card>
|
||||
|
||||
<!-- 执行确认对话框 -->
|
||||
<el-dialog
|
||||
v-model="executeDialogVisible"
|
||||
title="确认执行"
|
||||
width="500px"
|
||||
>
|
||||
<el-alert
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<template #title>
|
||||
<strong>警告:此操作将直接修改数据库</strong>
|
||||
</template>
|
||||
<div style="margin-top: 10px;">
|
||||
<p>• 请确认已仔细检查所有 SQL 语句</p>
|
||||
<p>• 建议在生产环境执行前先备份数据库</p>
|
||||
<p>• 执行后可通过历史记录查看详情</p>
|
||||
</div>
|
||||
</el-alert>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="executeDialogVisible = false">取消</el-button>
|
||||
<el-button type="danger" @click="handleExecute">
|
||||
确认执行
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useFunctionStore } from '@/stores/function'
|
||||
import { analyzeRequirement, generateConfig, executeConfig } from '@/api'
|
||||
|
||||
const functionStore = useFunctionStore()
|
||||
|
||||
const form = ref({
|
||||
requirement: ''
|
||||
})
|
||||
|
||||
const executeDialogVisible = ref(false)
|
||||
|
||||
// 当前步骤
|
||||
const currentStep = computed(() => {
|
||||
if (functionStore.executeResult) return 3
|
||||
if (functionStore.configResult) return 2
|
||||
if (functionStore.analysisResult) return 1
|
||||
return 0
|
||||
})
|
||||
|
||||
// SQL 显示
|
||||
const sqlDisplay = computed(() => {
|
||||
if (!functionStore.configResult?.配置方案?.sql_list) return ''
|
||||
return functionStore.configResult.配置方案.sql_list.join('\n\n')
|
||||
})
|
||||
|
||||
// 分析需求
|
||||
const handleAnalyze = async () => {
|
||||
if (!form.value.requirement.trim()) {
|
||||
ElMessage.warning('请输入需求描述')
|
||||
return
|
||||
}
|
||||
|
||||
functionStore.loading.analyzing = true
|
||||
|
||||
try {
|
||||
const result = await analyzeRequirement({
|
||||
content: form.value.requirement,
|
||||
session_id: null
|
||||
})
|
||||
|
||||
functionStore.setSession(result.session_id)
|
||||
functionStore.setAnalysisResult(result.data)
|
||||
|
||||
ElMessage.success('需求分析成功')
|
||||
} catch (error) {
|
||||
console.error('Analysis failed:', error)
|
||||
ElMessage.error(
|
||||
'需求分析失败: ' + (error.response?.data?.detail?.message || error.message)
|
||||
)
|
||||
} finally {
|
||||
functionStore.loading.analyzing = false
|
||||
}
|
||||
}
|
||||
|
||||
// 生成配置
|
||||
const handleGenerate = async () => {
|
||||
if (!functionStore.currentSession || !functionStore.analysisResult) {
|
||||
ElMessage.warning('请先完成需求分析')
|
||||
return
|
||||
}
|
||||
|
||||
functionStore.loading.generating = true
|
||||
|
||||
try {
|
||||
const result = await generateConfig({
|
||||
session_id: functionStore.currentSession,
|
||||
requirements: functionStore.analysisResult
|
||||
})
|
||||
|
||||
functionStore.setConfigResult(result.data)
|
||||
|
||||
ElMessage.success('配置生成成功')
|
||||
} catch (error) {
|
||||
console.error('Generation failed:', error)
|
||||
ElMessage.error(
|
||||
'配置生成失败: ' + (error.response?.data?.detail?.message || error.message)
|
||||
)
|
||||
} finally {
|
||||
functionStore.loading.generating = false
|
||||
}
|
||||
}
|
||||
|
||||
// 显示执行确认对话框
|
||||
const showExecuteConfirm = () => {
|
||||
executeDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 执行配置
|
||||
const handleExecute = async () => {
|
||||
if (!functionStore.currentSession) {
|
||||
ElMessage.warning('会话信息丢失,请重新开始')
|
||||
return
|
||||
}
|
||||
|
||||
executeDialogVisible.value = false
|
||||
functionStore.loading.executing = true
|
||||
|
||||
try {
|
||||
const result = await executeConfig({
|
||||
session_id: functionStore.currentSession,
|
||||
confirmed: true,
|
||||
backup_enabled: true
|
||||
})
|
||||
|
||||
functionStore.setExecuteResult(result)
|
||||
|
||||
if (result.status === 'success') {
|
||||
ElMessage.success('配置执行成功')
|
||||
} else {
|
||||
ElMessage.error('配置执行失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Execution failed:', error)
|
||||
ElMessage.error(
|
||||
'配置执行失败: ' + (error.response?.data?.detail?.message || error.message)
|
||||
)
|
||||
} finally {
|
||||
functionStore.loading.executing = false
|
||||
}
|
||||
}
|
||||
|
||||
// 返回分析步骤
|
||||
const backToAnalysis = () => {
|
||||
functionStore.setConfigResult(null)
|
||||
}
|
||||
|
||||
// 重置所有状态
|
||||
const resetAll = () => {
|
||||
functionStore.reset()
|
||||
form.value.requirement = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.create-function {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.steps {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.field-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.field-list h4 {
|
||||
margin-bottom: 10px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.alert-box {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.config-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.config-section h4 {
|
||||
margin-bottom: 10px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.sql-textarea {
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.sql-textarea :deep(textarea) {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user