Files
erp-ass/frontend/src/views/CreateFunction.vue
dazhuang acd73431ae 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>
2026-03-21 14:23:20 +00:00

380 lines
9.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>