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
+169
View File
@@ -0,0 +1,169 @@
<template>
<div>
<h3>数据管理</h3>
<el-tabs v-model="activeTab" style="margin-top:16px;">
<el-tab-pane label="Excel导入" name="import">
<el-card>
<template #header>Excel导入</template>
<el-upload drag :auto-upload="false" :on-change="handleFile" accept=".xlsx,.xls" :limit="1">
<el-icon :size="32"><UploadFilled /></el-icon>
<div>拖拽或点击上传Excel文件</div>
<div style="color:#999;font-size:12px;">支持 .xlsx .xls需包含 kpi_code, period, actual_value 三列</div>
</el-upload>
<el-button v-if="file" type="primary" style="margin-top:12px;" :loading="uploading" @click="doImport">导入数据</el-button>
<div v-if="result" style="margin-top:12px;"><el-alert :title="result" type="success" show-icon /></div>
</el-card>
</el-tab-pane>
<el-tab-pane label="数据源管理" name="sources">
<el-card>
<template #header>
<div style="display:flex;justify-content:space-between;align-items:center;">
<span>数据源配置</span>
<el-button type="primary" size="small" @click="openSourceForm()">新增数据源</el-button>
</div>
</template>
<el-table :data="sources" v-loading="sourcesLoading" style="width:100%">
<el-table-column prop="name" label="名称" min-width="140" />
<el-table-column prop="source_type" label="类型" width="80">
<template #default="{ row }">
<el-tag :type="sourceTypeTag(row.source_type)" size="small">{{ row.source_type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="api_endpoint" label="API地址" min-width="200" show-overflow-tooltip />
<el-table-column prop="sync_type" label="同步方式" width="90">
<template #default="{ row }">
<el-tag size="small" :type="row.sync_type==='realtime'?'success':'info'">{{ row.sync_type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="70">
<template #default="{ row }">
<el-tag :type="row.status==='active'?'success':'danger'" size="small">{{ row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="last_sync_at" label="上次同步" width="160" />
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button size="small" @click="openSourceForm(row)">编辑</el-button>
<el-button size="small" type="danger" @click="doDeleteSource(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</el-tab-pane>
</el-tabs>
<MyDialog v-model="showSourceForm" :title="sourceForm.id ? '编辑数据源' : '新增数据源'" :width="550">
<el-form :model="sourceForm" label-width="120px">
<el-form-item label="名称"><el-input v-model="sourceForm.name" /></el-form-item>
<el-form-item label="类型">
<el-select v-model="sourceForm.source_type">
<el-option label="ERP" value="erp" />
<el-option label="业务系统" value="business" />
<el-option label="Excel" value="excel" />
<el-option label="手工" value="manual" />
</el-select>
</el-form-item>
<el-form-item label="API地址"><el-input v-model="sourceForm.api_endpoint" placeholder="https://..." /></el-form-item>
<el-form-item label="API Key"><el-input v-model="sourceForm.api_key" type="password" show-password /></el-form-item>
<el-form-item label="SQL查询"><el-input v-model="sourceForm.query_sql" type="textarea" :rows="3" placeholder="可选,用于查询型数据源" /></el-form-item>
<el-form-item label="同步方式">
<el-select v-model="sourceForm.sync_type">
<el-option label="实时" value="realtime" />
<el-option label="定时批量" value="batch" />
<el-option label="手动" value="manual" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showSourceForm=false">取消</el-button>
<el-button type="primary" @click="saveSource" :loading="sourceSaving">保存</el-button>
</template>
</MyDialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { UploadFilled } from '@element-plus/icons-vue'
import { dataApi } from '../api/index'
import MyDialog from '../components/MyDialog.vue'
const activeTab = ref('import')
// === Excel导入 ===
const file = ref<any>(null)
const uploading = ref(false)
const result = ref('')
function handleFile(f: any) { file.value = f.raw }
async function doImport() {
if (!file.value) return
uploading.value = true
try {
const r: any = await dataApi.importExcel(file.value)
result.value = r.message || '导入成功'
file.value = null
} catch (e) { ElMessage.error('导入失败') }
uploading.value = false
}
// === 数据源管理 ===
const sources = ref<any[]>([])
const sourcesLoading = ref(false)
const showSourceForm = ref(false)
const sourceSaving = ref(false)
const sourceForm = ref<any>({ source_type: 'manual', sync_type: 'manual' })
function sourceTypeTag(t: string) {
return ({ erp: 'primary', business: 'warning', excel: 'success', manual: 'info' } as any)[t] || 'info'
}
async function loadSources() {
sourcesLoading.value = true
try {
const r: any = await dataApi.listSources()
sources.value = r.data || []
} catch (e) { ElMessage.error('加载数据源失败') }
sourcesLoading.value = false
}
function openSourceForm(row?: any) {
if (row) {
sourceForm.value = { ...row }
} else {
sourceForm.value = { source_type: 'manual', sync_type: 'manual' }
}
showSourceForm.value = true
}
async function saveSource() {
sourceSaving.value = true
try {
if (sourceForm.value.id) {
await dataApi.updateSource(sourceForm.value.id, sourceForm.value)
ElMessage.success('已更新')
} else {
await dataApi.createSource(sourceForm.value)
ElMessage.success('已创建')
}
showSourceForm.value = false
loadSources()
} catch (e) { ElMessage.error('保存失败') }
sourceSaving.value = false
}
async function doDeleteSource(row: any) {
try {
await ElMessageBox.confirm(`确认删除数据源「${row.name}」?`, '确认')
await dataApi.deleteSource(row.id)
ElMessage.success('已删除')
loadSources()
} catch (e: any) {
if (e !== 'cancel') ElMessage.error('删除失败')
}
}
onMounted(loadSources)
</script>