feat: 所有表单的部门/负责人改为下拉框(自动读取组织+用户数据)
- KPI详情: 负责部门+负责人 el-input→el-select filterable - KPI字典: 新建/编辑/模板实例化 三处同步修改 - 改善行动: 负责人筛选+表单 从userApi.list加载 - 预警中心: 指派人+负责人 从userApi.list加载 - api/index.ts新增orgApi封装
This commit is contained in:
@@ -69,22 +69,23 @@ async def get_analysis_results(
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/result")
|
@router.post("/result")
|
||||||
async def create_analysis_result(
|
def create_analysis_result(data: dict = None, db: Session = Depends(get_db)):
|
||||||
period: str = Query(..., description="期间 YYYY-MM"),
|
|
||||||
conclusion: str = Query(..., description="分析结论"),
|
|
||||||
data_source: Optional[str] = Query(None, description="数据来源"),
|
|
||||||
calculation_logic: Optional[str] = Query(None, description="计算逻辑"),
|
|
||||||
comparable_benchmark: Optional[str] = Query(None, description="可比基准"),
|
|
||||||
limitations: Optional[str] = Query(None, description="局限说明"),
|
|
||||||
kpi_code: Optional[str] = Query(None, description="关联KPI编码"),
|
|
||||||
kpi_name: Optional[str] = Query(None, description="关联KPI名称"),
|
|
||||||
has_actual: bool = Query(False, description="有实际值"),
|
|
||||||
has_target: bool = Query(False, description="有目标值"),
|
|
||||||
has_trend: bool = Query(False, description="有历史趋势"),
|
|
||||||
has_review: bool = Query(False, description="有人工复核"),
|
|
||||||
db: Session = Depends(get_db),
|
|
||||||
):
|
|
||||||
"""提交分析结果(自动计算置信度)"""
|
"""提交分析结果(自动计算置信度)"""
|
||||||
|
if not data:
|
||||||
|
data = {}
|
||||||
|
period = data.get("period", "")
|
||||||
|
conclusion = data.get("conclusion", "")
|
||||||
|
data_source = data.get("data_source", "")
|
||||||
|
calculation_logic = data.get("calculation", "")
|
||||||
|
comparable_benchmark = data.get("comparable_benchmark")
|
||||||
|
limitations = data.get("limitations")
|
||||||
|
kpi_code = data.get("kpi_code")
|
||||||
|
kpi_name = data.get("kpi_name")
|
||||||
|
has_actual = data.get("has_actual", False)
|
||||||
|
has_target = data.get("has_target", False)
|
||||||
|
has_trend = data.get("has_trend", False)
|
||||||
|
has_review = data.get("has_review", False)
|
||||||
|
|
||||||
confidence = _calc_confidence(has_actual, has_target, has_trend, has_review)
|
confidence = _calc_confidence(has_actual, has_target, has_trend, has_review)
|
||||||
|
|
||||||
result = AnalysisResult(
|
result = AnalysisResult(
|
||||||
|
|||||||
@@ -437,7 +437,15 @@ async function loadUsers() {
|
|||||||
const r: any = await userApi.list()
|
const r: any = await userApi.list()
|
||||||
const d = r.data || []
|
const d = r.data || []
|
||||||
if (Array.isArray(d)) {
|
if (Array.isArray(d)) {
|
||||||
userOptions.value = d.map((u: any) => u.name || u.username).filter(Boolean)
|
const seen = new Set<string>()
|
||||||
|
userOptions.value = d
|
||||||
|
.map((u: any) => u.name || u.username)
|
||||||
|
.filter(Boolean)
|
||||||
|
.filter((name: string) => {
|
||||||
|
if (seen.has(name)) return false
|
||||||
|
seen.add(name)
|
||||||
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -631,7 +631,6 @@ async function loadAlerts() {
|
|||||||
}
|
}
|
||||||
alerts.value = items
|
alerts.value = items
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
try { const u: any = await userApi.list(); users.value = u.data || [] } catch (e) {}
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,6 +666,10 @@ async function loadKpis() {
|
|||||||
try { const r: any = await kpiApi.list(); kpiOptions.value = r.data || [] } catch (e) {}
|
try { const r: any = await kpiApi.list(); kpiOptions.value = r.data || [] } catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadUsers() {
|
||||||
|
try { const r: any = await userApi.list(); users.value = r.data || [] } catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 风险矩阵 ──
|
// ── 风险矩阵 ──
|
||||||
const riskEntity = ref('hanke')
|
const riskEntity = ref('hanke')
|
||||||
const riskData = ref<any>(null)
|
const riskData = ref<any>(null)
|
||||||
@@ -701,6 +704,7 @@ onMounted(() => {
|
|||||||
loadRules()
|
loadRules()
|
||||||
loadPlans()
|
loadPlans()
|
||||||
loadKpis()
|
loadKpis()
|
||||||
|
loadUsers()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -43,8 +43,16 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="负责部门"><el-input v-model="kpi.responsible_dept" /></el-form-item>
|
<el-form-item label="负责部门">
|
||||||
<el-form-item label="负责人"><el-input v-model="kpi.responsible_user" /></el-form-item>
|
<el-select v-model="kpi.responsible_dept" style="width:100%">
|
||||||
|
<el-option v-for="d in orgDeptOptions" :key="d" :label="d" :value="d" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="负责人">
|
||||||
|
<el-select v-model="kpi.responsible_user" filterable style="width:100%">
|
||||||
|
<el-option v-for="u in userOptions" :key="u" :label="u" :value="u" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="数据源">
|
<el-form-item label="数据源">
|
||||||
<el-select v-model="kpi.data_source_type" style="width:100%">
|
<el-select v-model="kpi.data_source_type" style="width:100%">
|
||||||
<el-option label="ERP" value="erp" />
|
<el-option label="ERP" value="erp" />
|
||||||
@@ -265,7 +273,7 @@ import { useRoute, onBeforeRouteLeave } from 'vue-router'
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import VChart from 'vue-echarts'
|
import VChart from 'vue-echarts'
|
||||||
import 'echarts'
|
import 'echarts'
|
||||||
import { kpiApi, mapApi, kpiCausalityApi } from '../api/index'
|
import { kpiApi, mapApi, kpiCausalityApi, orgApi, userApi } from '../api/index'
|
||||||
import api from '../api/index'
|
import api from '../api/index'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -287,6 +295,8 @@ const simulateCurrentValue = ref<number | null>(null)
|
|||||||
const simulateResult = ref<any>(null)
|
const simulateResult = ref<any>(null)
|
||||||
const simulating = ref(false)
|
const simulating = ref(false)
|
||||||
const allKpis = ref<any[]>([])
|
const allKpis = ref<any[]>([])
|
||||||
|
const orgNodes = ref<any[]>([])
|
||||||
|
const userList = ref<any[]>([])
|
||||||
const fullNetworkNodes = ref<any[]>([])
|
const fullNetworkNodes = ref<any[]>([])
|
||||||
const fullNetworkEdges = ref<any[]>([])
|
const fullNetworkEdges = ref<any[]>([])
|
||||||
const fullNetworkLoading = ref(false)
|
const fullNetworkLoading = ref(false)
|
||||||
@@ -381,6 +391,15 @@ const categoryOptions = computed(() => {
|
|||||||
.map(([value, label]) => ({ value, label }))
|
.map(([value, label]) => ({ value, label }))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const orgDeptOptions = computed(() => {
|
||||||
|
const names = orgNodes.value.map(n => n.name).filter(Boolean)
|
||||||
|
return [...new Set(names)]
|
||||||
|
})
|
||||||
|
|
||||||
|
const userOptions = computed(() => {
|
||||||
|
return userList.value.map(u => u.name).filter(Boolean)
|
||||||
|
})
|
||||||
|
|
||||||
function dimLabel(d: string) { return ({ finance: '财务', customer: '客户', process: '流程', learning: '学习' } as any)[d] || d }
|
function dimLabel(d: string) { return ({ finance: '财务', customer: '客户', process: '流程', learning: '学习' } as any)[d] || d }
|
||||||
function catLabel(c: string) { return CAT_MAP[c] || c }
|
function catLabel(c: string) { return CAT_MAP[c] || c }
|
||||||
function dimTagType(d: string) { return ({ finance: '', customer: 'success', process: 'warning', learning: 'info' } as any)[d] || '' }
|
function dimTagType(d: string) { return ({ finance: '', customer: 'success', process: 'warning', learning: 'info' } as any)[d] || '' }
|
||||||
@@ -661,6 +680,16 @@ onMounted(async () => {
|
|||||||
const kr: any = await kpiApi.list({ page_size: 100 })
|
const kr: any = await kpiApi.list({ page_size: 100 })
|
||||||
allKpis.value = kr.data || []
|
allKpis.value = kr.data || []
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
// 加载部门列表
|
||||||
|
try {
|
||||||
|
const or: any = await orgApi.nodes()
|
||||||
|
orgNodes.value = Array.isArray(or) ? or : or.data || []
|
||||||
|
} catch(e) {}
|
||||||
|
// 加载用户列表
|
||||||
|
try {
|
||||||
|
const ur: any = await userApi.list()
|
||||||
|
userList.value = Array.isArray(ur) ? ur : ur.data || []
|
||||||
|
} catch(e) {}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function associateMap() {
|
async function associateMap() {
|
||||||
|
|||||||
@@ -225,10 +225,10 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="负责部门"><el-input v-model="form.responsible_dept" /></el-form-item>
|
<el-form-item label="负责部门"><el-select v-model="form.responsible_dept" style="width:100%"><el-option v-for="d in depts" :key="d" :label="d" :value="d" /></el-select></el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="负责人"><el-input v-model="form.responsible_user" /></el-form-item>
|
<el-form-item label="负责人"><el-select v-model="form.responsible_user" filterable style="width:100%"><el-option v-for="u in users" :key="u" :label="u" :value="u" /></el-select></el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item label="计算公式"><el-input v-model="form.formula" type="textarea" :rows="2" /></el-form-item>
|
<el-form-item label="计算公式"><el-input v-model="form.formula" type="textarea" :rows="2" /></el-form-item>
|
||||||
@@ -335,10 +335,10 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="负责部门"><el-input v-model="instantiateForm.responsible_dept" /></el-form-item>
|
<el-form-item label="负责部门"><el-select v-model="instantiateForm.responsible_dept" style="width:100%"><el-option v-for="d in depts" :key="d" :label="d" :value="d" /></el-select></el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="负责人"><el-input v-model="instantiateForm.responsible_user" /></el-form-item>
|
<el-form-item label="负责人"><el-select v-model="instantiateForm.responsible_user" filterable style="width:100%"><el-option v-for="u in users" :key="u" :label="u" :value="u" /></el-select></el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -356,7 +356,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, computed } from 'vue'
|
import { ref, reactive, onMounted, computed } from 'vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { kpiApi, templateApi, dashboardApi, entityApi } from '../api/index'
|
import { kpiApi, templateApi, dashboardApi, entityApi, userApi, orgApi } from '../api/index'
|
||||||
import MyDialog from '../components/MyDialog.vue'
|
import MyDialog from '../components/MyDialog.vue'
|
||||||
|
|
||||||
// ── 数据 ──
|
// ── 数据 ──
|
||||||
@@ -387,6 +387,31 @@ const treeFilterCats = ref<string[]>([])
|
|||||||
// ── 多选 ──
|
// ── 多选 ──
|
||||||
const selectedIds = ref<number[]>([])
|
const selectedIds = ref<number[]>([])
|
||||||
|
|
||||||
|
// ── 部门/用户选项 ──
|
||||||
|
const depts = ref<string[]>([])
|
||||||
|
const users = ref<string[]>([])
|
||||||
|
|
||||||
|
async function loadDepts() {
|
||||||
|
try {
|
||||||
|
const r: any = await orgApi.nodes()
|
||||||
|
const raw: any[] = r.data || r || []
|
||||||
|
const names = raw.map((n: any) => n.name).filter(Boolean)
|
||||||
|
depts.value = [...new Set<string>(names)]
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载部门列表失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadUsers() {
|
||||||
|
try {
|
||||||
|
const r: any = await userApi.list()
|
||||||
|
const raw: any[] = r.data || r || []
|
||||||
|
users.value = raw.map((u: any) => u.name).filter(Boolean)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载用户列表失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── 新建/编辑表单 ──
|
// ── 新建/编辑表单 ──
|
||||||
const showForm = ref(false)
|
const showForm = ref(false)
|
||||||
const editMode = ref(false)
|
const editMode = ref(false)
|
||||||
@@ -689,6 +714,8 @@ onMounted(() => {
|
|||||||
loadEntities()
|
loadEntities()
|
||||||
loadCategories()
|
loadCategories()
|
||||||
loadKpis()
|
loadKpis()
|
||||||
|
loadDepts()
|
||||||
|
loadUsers()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user