Files
cma-management/frontend/src/views/DataManage.vue
T
Hermes CI Fix 3dddd36866 init: 管理会计OS初始代码
包含前后端完整代码:
- 前端:Vue3+Vite+ElementPlus
- 后端:FastAPI+SQLAlchemy
- 模块:驾驶舱/KPI/战略地图/预警/预算/成本/预测/改善行动
- 当前版本:v1.0.0
2026-05-28 17:32:22 +08:00

170 lines
6.7 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>
<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>