feat: 账套模式全量接入 — 12个API entity_id统一走token解析链 + 前端登录选公司/切换器重签token

- 后端: kpis/bsc_layers/cash/predict/growth_quality/tax_compliance/data 的 entity_id 参数统一改为 Depends(get_entity_id)
- 前端: 拦截器删除自动附加X-Entity-Id/entity_id; 登录页公司选择器(按用户名授权过滤); 切换器改POST /auth/switch-entity重新签发token+整页刷新; ReportCenter同步改造
- 修复前后端不匹配: login-entities路由→/auth/entities; login响应entity_id取user.entity_id
This commit is contained in:
Hermes CI Fix
2026-08-11 11:29:39 +08:00
parent 12f9ae520b
commit 9986f38faa
11 changed files with 116 additions and 58 deletions
+2 -1
View File
@@ -4,6 +4,7 @@ from sqlalchemy.orm import Session
from typing import List from typing import List
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth from app.auth_middleware import require_auth
from app.models import BscLayerConfig, Entity from app.models import BscLayerConfig, Entity
@@ -13,7 +14,7 @@ router = APIRouter(prefix="/api/cma/bsc-layers", tags=["BSC层配置"],
@router.get("") @router.get("")
def list_bsc_layers(entity_id: int = Query(1, description="企业ID"), db: Session = Depends(get_db)): def list_bsc_layers(entity_id: int = Depends(get_entity_id), db: Session = Depends(get_db)):
"""获取某企业的BSC四层权重配置""" """获取某企业的BSC四层权重配置"""
# 验证企业存在 # 验证企业存在
entity = db.query(Entity).filter(Entity.id == entity_id).first() entity = db.query(Entity).filter(Entity.id == entity_id).first()
+8 -7
View File
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta
from fastapi import APIRouter, HTTPException, Depends, Query from fastapi import APIRouter, HTTPException, Depends, Query
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_role from app.auth_middleware import require_role
from app.models import CashPlan from app.models import CashPlan
from app.utils.cash_forecast_engine import ( from app.utils.cash_forecast_engine import (
@@ -45,7 +46,7 @@ def _plan_dict(p: CashPlan) -> dict:
@router.get("/gap-forecast") @router.get("/gap-forecast")
def api_gap_forecast( def api_gap_forecast(
entity_id: int = Query(1, description="企业ID"), entity_id: int = Depends(get_entity_id),
days: int = Query(30, ge=1, le=90, description="预测天数"), days: int = Query(30, ge=1, le=90, description="预测天数"),
current_cash: float = Query(None, description="当前现金余额(万元),不传则自动获取"), current_cash: float = Query(None, description="当前现金余额(万元),不传则自动获取"),
db: Session = Depends(get_db), db: Session = Depends(get_db),
@@ -60,7 +61,7 @@ def api_gap_forecast(
@router.get("/balance") @router.get("/balance")
def api_get_balance( def api_get_balance(
entity_id: int = Query(1), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
"""获取当前现金余额(预测基线)""" """获取当前现金余额(预测基线)"""
@@ -89,7 +90,7 @@ def api_set_balance(
@router.get("/plans") @router.get("/plans")
def api_list_plans( def api_list_plans(
entity_id: int = Query(1), entity_id: int = Depends(get_entity_id),
plan_type: str = Query(None, description="receive/pay"), plan_type: str = Query(None, description="receive/pay"),
status: str = Query(None, description="pending/completed/cancelled"), status: str = Query(None, description="pending/completed/cancelled"),
month: str = Query(None, description="YYYY-MM 按计划月份过滤"), month: str = Query(None, description="YYYY-MM 按计划月份过滤"),
@@ -215,7 +216,7 @@ def api_complete_plan(plan_id: int, db: Session = Depends(get_db)):
@router.get("/upcoming") @router.get("/upcoming")
def api_upcoming( def api_upcoming(
entity_id: int = Query(1), entity_id: int = Depends(get_entity_id),
days: int = Query(7, ge=1, le=30), days: int = Query(7, ge=1, le=30),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
@@ -243,7 +244,7 @@ def api_upcoming(
@router.get("/dashboard") @router.get("/dashboard")
def api_cash_dashboard( def api_cash_dashboard(
entity_id: int = Query(1), entity_id: int = Depends(get_entity_id),
month: str = Query(None, description="YYYY-MM 默认本月"), month: str = Query(None, description="YYYY-MM 默认本月"),
days: int = Query(30), days: int = Query(30),
current_cash: float = Query(None, description="当前现金余额(万元)"), current_cash: float = Query(None, description="当前现金余额(万元)"),
@@ -303,7 +304,7 @@ def api_cash_dashboard(
# ══════════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════════
@router.post("/check-alerts") @router.post("/check-alerts")
def api_check_cash_alerts(entity_id: int = 1, db: Session = Depends(get_db)): def api_check_cash_alerts(entity_id: int = Depends(get_entity_id), db: Session = Depends(get_db)):
"""手动触发资金预警检查(写入预警中心kpi_alerts)""" """手动触发资金预警检查(写入预警中心kpi_alerts)"""
try: try:
return check_cash_alerts(db, entity_id=entity_id) return check_cash_alerts(db, entity_id=entity_id)
@@ -313,7 +314,7 @@ def api_check_cash_alerts(entity_id: int = 1, db: Session = Depends(get_db)):
@router.get("/alerts/status") @router.get("/alerts/status")
def api_cash_alert_status(entity_id: int = Query(1), db: Session = Depends(get_db)): def api_cash_alert_status(entity_id: int = Depends(get_entity_id), db: Session = Depends(get_db)):
"""资金预警状态概览 — 当前缺口/逾期情况(不写库,只读)""" """资金预警状态概览 — 当前缺口/逾期情况(不写库,只读)"""
result = forecast_cash_flow_with_plans(entity_id, db, days=30) result = forecast_cash_flow_with_plans(entity_id, db, days=30)
return { return {
+2 -1
View File
@@ -7,6 +7,7 @@ from sqlalchemy.orm import Session
from sqlalchemy import func from sqlalchemy import func
from typing import Optional from typing import Optional
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role from app.auth_middleware import require_auth, require_role
from app.models import KPIValue, DataSourceConfig, OperationLog, KPIDefinition from app.models import KPIValue, DataSourceConfig, OperationLog, KPIDefinition
@@ -165,7 +166,7 @@ def _smart_detect_statement_type(filename: str) -> str | None:
@router.post("/import-excel-smart") @router.post("/import-excel-smart")
async def import_excel_smart( async def import_excel_smart(
file: UploadFile = File(...), file: UploadFile = File(...),
entity_id: int = Form(1), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
"""智能导入 — BOT自动识别列名/期间/报表类型,无需手动映射""" """智能导入 — BOT自动识别列名/期间/报表类型,无需手动映射"""
+3 -2
View File
@@ -11,6 +11,7 @@ from datetime import datetime
import re import re
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role from app.auth_middleware import require_auth, require_role
from app.models import KPIDefinition, KPIValue, Entity from app.models import KPIDefinition, KPIValue, Entity
@@ -453,7 +454,7 @@ def _diagnose(db: Session, entity_id: int, period: str, history: List[dict]):
@router.get("/periods") @router.get("/periods")
def list_periods(entity_id: int = Query(1), db: Session = Depends(get_db)): def list_periods(entity_id: int = Depends(get_entity_id), db: Session = Depends(get_db)):
"""列出某实体有KPI数据的期间(按月,含数据覆盖度,用于前端默认期间选择)""" """列出某实体有KPI数据的期间(按月,含数据覆盖度,用于前端默认期间选择)"""
rows = (db.query(KPIValue.period, KPIValue.kpi_id) rows = (db.query(KPIValue.period, KPIValue.kpi_id)
.join(KPIDefinition, KPIDefinition.id == KPIValue.kpi_id) .join(KPIDefinition, KPIDefinition.id == KPIValue.kpi_id)
@@ -470,7 +471,7 @@ def list_periods(entity_id: int = Query(1), db: Session = Depends(get_db)):
@router.get("/diagnosis") @router.get("/diagnosis")
def growth_quality_diagnosis( def growth_quality_diagnosis(
entity_id: int = Query(1, ge=1), entity_id: int = Depends(get_entity_id),
period: Optional[str] = Query(None, description="期间 YYYY-MM,默认最近有数据期间"), period: Optional[str] = Query(None, description="期间 YYYY-MM,默认最近有数据期间"),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
+5 -4
View File
@@ -7,6 +7,7 @@ from datetime import datetime
import json import json
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role, filter_kpis_by_role, kpi_visible_dims from app.auth_middleware import require_auth, require_role, filter_kpis_by_role, kpi_visible_dims
from app.models import StrategicMap, MapObjective, KPIDefinition, KPIValue, KPIAlert, OperationLog, Entity, KPICausality, KPIHierarchy from app.models import StrategicMap, MapObjective, KPIDefinition, KPIValue, KPIAlert, OperationLog, Entity, KPICausality, KPIHierarchy
from app.api.kpi_governance import validate_kpi_payload, kpi_issues_message from app.api.kpi_governance import validate_kpi_payload, kpi_issues_message
@@ -27,7 +28,7 @@ def list_kpis(
keyword: Optional[str] = None, keyword: Optional[str] = None,
epic: Optional[str] = None, epic: Optional[str] = None,
category: Optional[str] = None, category: Optional[str] = None,
entity_id: Optional[int] = None, entity_id: int = Depends(get_entity_id),
kpi_level: Optional[str] = None, kpi_level: Optional[str] = None,
db: Session = Depends(get_db), db: Session = Depends(get_db),
current_user = Depends(require_auth), current_user = Depends(require_auth),
@@ -141,7 +142,7 @@ def _calc_five_tier_score(current_value, target_value, is_reverse=False):
@router.get("/score") @router.get("/score")
def get_kpi_score( def get_kpi_score(
entity_id: int = Query(1, ge=1), entity_id: int = Depends(get_entity_id),
period: Optional[str] = None, period: Optional[str] = None,
db: Session = Depends(get_db), db: Session = Depends(get_db),
current_user = Depends(require_auth), current_user = Depends(require_auth),
@@ -243,7 +244,7 @@ def get_kpi_score(
@router.get("/glossary") @router.get("/glossary")
def get_kpi_glossary( def get_kpi_glossary(
entity_id: int = Query(1, ge=1), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
current_user = Depends(require_auth), current_user = Depends(require_auth),
): ):
@@ -314,7 +315,7 @@ def get_kpi_glossary(
@router.get("/hierarchy") @router.get("/hierarchy")
def get_kpi_hierarchy( def get_kpi_hierarchy(
entity_id: int = Query(1, ge=1), entity_id: int = Depends(get_entity_id),
kpi_id: Optional[int] = None, kpi_id: Optional[int] = None,
db: Session = Depends(get_db), db: Session = Depends(get_db),
current_user = Depends(require_auth), current_user = Depends(require_auth),
+3 -2
View File
@@ -10,6 +10,7 @@ from app.utils.cash_forecast_engine import (
calculate_accuracy, generate_scenario_suggestion, calculate_accuracy, generate_scenario_suggestion,
) )
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
logger = logging.getLogger("cma.predict") logger = logging.getLogger("cma.predict")
@@ -183,7 +184,7 @@ def api_cash_forecast(data: dict, db: Session = Depends(get_db)):
@router.get("/cash-forecast/history") @router.get("/cash-forecast/history")
def api_cash_forecast_history( def api_cash_forecast_history(
entity_id: int = 1, entity_id: int = Depends(get_entity_id),
days: int = 30, days: int = 30,
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
@@ -206,7 +207,7 @@ def api_cash_forecast_history(
@router.get("/accuracy") @router.get("/accuracy")
def api_forecast_accuracy( def api_forecast_accuracy(
entity_id: int = 1, entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
"""预测准确率报表 — 上期预测 vs 本期实际""" """预测准确率报表 — 上期预测 vs 本期实际"""
+12 -11
View File
@@ -11,6 +11,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.database import get_db from app.database import get_db
from app.deps import get_entity_id
from app.auth_middleware import require_auth, require_role from app.auth_middleware import require_auth, require_role
from app.models import TaxRecord, InvoiceCheck, SocialSecurity, ExpenseReimbursement from app.models import TaxRecord, InvoiceCheck, SocialSecurity, ExpenseReimbursement
@@ -140,7 +141,7 @@ def _apply_burden_to_all(db: Session, entity_id: int):
def list_tax_records( def list_tax_records(
period: str = Query(None), period: str = Query(None),
tax_type: str = Query(None), tax_type: str = Query(None),
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
): ):
@@ -236,7 +237,7 @@ def delete_tax_record(
@router.get("/burden") @router.get("/burden")
def burden_analysis( def burden_analysis(
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
tax_type: str = Query(None), tax_type: str = Query(None),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
@@ -274,7 +275,7 @@ def burden_analysis(
@router.post("/check") @router.post("/check")
def run_tax_check( def run_tax_check(
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
): ):
@@ -358,7 +359,7 @@ def _check_invoice(db: Session, inv: InvoiceCheck):
def list_invoices( def list_invoices(
status: str = Query(None), status: str = Query(None),
keyword: str = Query(None), keyword: str = Query(None),
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
): ):
@@ -457,7 +458,7 @@ def delete_invoice(
@router.post("/invoices/check") @router.post("/invoices/check")
def batch_check_invoices( def batch_check_invoices(
invoice_id: int = Query(None), invoice_id: int = Query(None),
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
): ):
@@ -481,7 +482,7 @@ def batch_check_invoices(
@router.get("/invoices/abnormal") @router.get("/invoices/abnormal")
def list_abnormal_invoices( def list_abnormal_invoices(
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
limit: int = Query(50), limit: int = Query(50),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
@@ -556,7 +557,7 @@ def list_ss_records(
period: str = Query(None), period: str = Query(None),
employee: str = Query(None), employee: str = Query(None),
status: str = Query(None), status: str = Query(None),
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
): ):
@@ -652,7 +653,7 @@ def delete_ss_record(
@router.post("/ss/check") @router.post("/ss/check")
def batch_check_ss( def batch_check_ss(
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
): ):
@@ -674,7 +675,7 @@ def batch_check_ss(
@router.get("/ss/abnormal") @router.get("/ss/abnormal")
def list_abnormal_ss( def list_abnormal_ss(
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
limit: int = Query(50), limit: int = Query(50),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
@@ -693,7 +694,7 @@ def list_abnormal_ss(
@router.get("/dashboard") @router.get("/dashboard")
def tax_dashboard( def tax_dashboard(
entity_id: int = Query(None), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_auth), _=Depends(require_auth),
): ):
@@ -771,7 +772,7 @@ def tax_dashboard(
@router.post("/demo-data") @router.post("/demo-data")
def seed_demo_data( def seed_demo_data(
entity_id: int = Query(1), entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db), db: Session = Depends(get_db),
_=Depends(require_role("ceo", "finance")), _=Depends(require_role("ceo", "finance")),
): ):
+4 -6
View File
@@ -8,12 +8,7 @@ const api = axios.create({
api.interceptors.request.use((config) => { api.interceptors.request.use((config) => {
const token = localStorage.getItem('cma_token') const token = localStorage.getItem('cma_token')
if (token) config.headers.Authorization = `Bearer ${token}` if (token) config.headers.Authorization = `Bearer ${token}`
// 多租户实时切换:统一附加当前企业ID(解决29个页面未传entity_id的问题) // 账套模式:entity_id 由 token 绑定,前端不再自动附加 X-Entity-Id / entity_id
const entityId = Number(localStorage.getItem('cma_entity_id') || 1)
config.headers['X-Entity-Id'] = String(entityId)
if (config.method === 'get' || config.method === 'GET') {
config.params = { ...(config.params || {}), entity_id: entityId }
}
return config return config
}) })
@@ -32,6 +27,9 @@ api.interceptors.response.use(
export const authApi = { export const authApi = {
login: (data: any) => api.post('/auth/login', data), login: (data: any) => api.post('/auth/login', data),
register: (data: any) => api.post('/auth/register', data), register: (data: any) => api.post('/auth/register', data),
switchEntity: (data: any) => api.post('/auth/switch-entity', data),
myEntities: () => api.get('/auth/my-entities'),
loginEntities: (username: string) => api.get('/auth/login-entities', { params: { username } }),
} }
export const kpiApi = { export const kpiApi = {
+23 -12
View File
@@ -48,6 +48,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import api from '../api/index' import api from '../api/index'
import { import {
User, Monitor, Document, TrendCharts, WarningFilled, Connection, User, Monitor, Document, TrendCharts, WarningFilled, Connection,
@@ -61,25 +62,35 @@ const router = useRouter()
const isCollapse = ref(false) const isCollapse = ref(false)
const mobileMenuOpen = ref(false) const mobileMenuOpen = ref(false)
// // switch-entity token +
const entities = ref<any[]>([]) const entities = ref<any[]>([])
const currentEntityId = ref(Number(localStorage.getItem('cma_entity_id') || 1)) const currentEntityId = ref(Number(localStorage.getItem('cma_entity_id') || 1))
const currentEntityName = ref(localStorage.getItem('cma_entity_name') || '酣客') const currentEntityName = ref(localStorage.getItem('cma_entity_name') || '酣客')
function onEntityChange(id: number) { async function onEntityChange(id: number) {
currentEntityId.value = id if (id === Number(localStorage.getItem('cma_entity_id'))) return
const e = entities.value.find((x: any) => x.id === id)
currentEntityName.value = e?.short_name || e?.name || ''
localStorage.setItem('cma_entity_id', String(id))
localStorage.setItem('cma_entity_name', currentEntityName.value)
// tenantBot A2A
try { try {
api.post('/tenant/switch', { entity_id: id, source: 'cma-frontend' }) const r: any = await api.post('/auth/switch-entity', {
} catch (_) {} entity_id: id,
location.reload() from_entity_id: Number(localStorage.getItem('cma_entity_id') || 1),
})
localStorage.setItem('cma_token', r.token)
localStorage.setItem('cma_entity_id', String(r.entity_id))
localStorage.setItem('cma_entity_name', r.entity_short_name || r.entity_name || '')
// tenantBot A2A
try {
api.post('/tenant/switch', { entity_id: r.entity_id, source: 'cma-frontend' })
} catch (_) {}
location.reload()
} catch (e: any) {
currentEntityId.value = Number(localStorage.getItem('cma_entity_id') || 1)
const msg = e?.response?.data?.detail || '切换失败'
ElMessage.error(msg)
}
} }
onMounted(async () => { onMounted(async () => {
//
try { try {
const r: any = await api.get('/entities') const r: any = await api.get('/auth/my-entities')
entities.value = r.data || r || [] entities.value = r.data || r || []
} catch (_) {} } catch (_) {}
}) })
+40 -4
View File
@@ -4,7 +4,17 @@
<h1>管理会计OS</h1> <h1>管理会计OS</h1>
<p class="subtitle">博海网络科技</p> <p class="subtitle">博海网络科技</p>
<el-form ref="formRef" :model="form" label-width="0" @keyup.enter="handleLogin"> <el-form ref="formRef" :model="form" label-width="0" @keyup.enter="handleLogin">
<el-form-item><el-input v-model="form.username" placeholder="用户名" size="large" /></el-form-item> <el-form-item>
<el-input v-model="form.username" placeholder="用户名" size="large" @input="onUsernameChange" />
</el-form-item>
<el-form-item>
<el-select v-model="form.entity_id" placeholder="选择登录公司" size="large" style="width:100%" :disabled="entities.length === 0">
<el-option v-for="e in entities" :key="e.id" :label="e.short_name || e.name" :value="e.id">
<span>{{ e.short_name || e.name }}</span>
<span style="float:right;color:#999;font-size:12px;">{{ e.name }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item><el-input v-model="form.password" type="password" placeholder="密码" size="large" show-password /></el-form-item> <el-form-item><el-input v-model="form.password" type="password" placeholder="密码" size="large" show-password /></el-form-item>
<el-form-item><el-button type="primary" size="large" style="width:100%" :loading="loading" @click="handleLogin"> </el-button></el-form-item> <el-form-item><el-button type="primary" size="large" style="width:100%" :loading="loading" @click="handleLogin"> </el-button></el-form-item>
</el-form> </el-form>
@@ -13,27 +23,53 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive } from 'vue' import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { authApi } from '../api/index' import { authApi } from '../api/index'
const router = useRouter() const router = useRouter()
const loading = ref(false) const loading = ref(false)
const form = reactive({ username: 'admin', password: 'admin123' }) const entities = ref<any[]>([])
const form = reactive({ username: 'admin', password: 'admin123', entity_id: undefined as number | undefined })
async function loadEntities(username: string) {
try {
const r: any = await authApi.loginEntities(username)
entities.value = r.data || []
//
if (entities.value.length > 0 && (form.entity_id === undefined || !entities.value.some((e: any) => e.id === form.entity_id))) {
form.entity_id = entities.value[0].id
}
} catch (_) {
entities.value = []
}
}
function onUsernameChange(val: string) {
if (val && val.trim()) loadEntities(val.trim())
}
async function handleLogin() { async function handleLogin() {
if (form.entity_id === undefined) {
ElMessage.warning('请选择登录公司')
return
}
loading.value = true loading.value = true
try { try {
const res: any = await authApi.login(form) const res: any = await authApi.login({ ...form, entity_id: Number(form.entity_id) })
localStorage.setItem('cma_token', res.token) localStorage.setItem('cma_token', res.token)
localStorage.setItem('cma_user', JSON.stringify(res.user)) localStorage.setItem('cma_user', JSON.stringify(res.user))
localStorage.setItem('cma_entity_id', String(res.user?.entity_id ?? 1))
localStorage.setItem('cma_entity_name', res.user?.entity_short_name || res.user?.entity_name || '')
router.push('/dashboard') router.push('/dashboard')
} catch (e: any) { } catch (e: any) {
ElMessage.error(e?.response?.data?.detail || '登录失败') ElMessage.error(e?.response?.data?.detail || '登录失败')
} }
loading.value = false loading.value = false
} }
onMounted(() => loadEntities(form.username))
</script> </script>
<style scoped> <style scoped>
+14 -8
View File
@@ -587,7 +587,7 @@ function getEntityId() {
async function loadEntities() { async function loadEntities() {
try { try {
const r: any = await api.get('/entities') const r: any = await api.get('/auth/my-entities')
entities.value = r.data || r || [] entities.value = r.data || r || []
const e = entities.value.find((x: any) => x.id === currentEntityId.value) const e = entities.value.find((x: any) => x.id === currentEntityId.value)
if (e) localStorage.setItem('cma_entity_name', e.short_name || e.name || '') if (e) localStorage.setItem('cma_entity_name', e.short_name || e.name || '')
@@ -596,13 +596,19 @@ async function loadEntities() {
} }
} }
function onEntityChange(id: number) { async function onEntityChange(id: number) {
localStorage.setItem('cma_entity_id', String(id)) // switch-entity token +
const e = entities.value.find((x: any) => x.id === id) if (id === Number(localStorage.getItem('cma_entity_id'))) return
if (e) localStorage.setItem('cma_entity_name', e.short_name || e.name || '') try {
ElMessage.success(`已切换企业:${e ? e.short_name + ' / ' + e.name : id}`) const r: any = await api.post('/auth/switch-entity', { entity_id: id })
loadAll() localStorage.setItem('cma_token', r.token)
loadStatutory() localStorage.setItem('cma_entity_id', String(r.entity_id))
localStorage.setItem('cma_entity_name', r.entity_short_name || r.entity_name || '')
ElMessage.success(`已切换企业:${r.entity_short_name || r.entity_name || id}`)
location.reload()
} catch (e: any) {
ElMessage.error(e?.response?.data?.detail || '切换失败')
}
} }
// //