feat(okr): Phase2 - 行业扩展包12个+OKR模板库页面
- 插入12个行业包模板:贸易经销(7)+IT服务(5) - 后端: name搜索+apply端点 - 前端: OKRTemplates.vue页面(搜索/筛选/三分区/应用弹窗) - 路由+菜单+权限配置
This commit is contained in:
@@ -6,22 +6,44 @@ from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
from app.auth_middleware import require_role
|
||||
from app.models import OKRTemplate
|
||||
from app.models import OKRTemplate, StrategicMap
|
||||
|
||||
router = APIRouter(prefix="/api/cma/okr-templates", tags=["OKR模板库"],
|
||||
dependencies=[Depends(require_role("ceo", "finance", "it"))],
|
||||
)
|
||||
|
||||
|
||||
DIMENSION_LAYER_NAMES = {
|
||||
"finance": "财务层",
|
||||
"customer": "客户层",
|
||||
"process": "内部流程层",
|
||||
"learning": "学习成长层",
|
||||
}
|
||||
|
||||
DIMENSION_LAYER_ICONS = {
|
||||
"finance": "💰",
|
||||
"customer": "👥",
|
||||
"process": "⚙️",
|
||||
"learning": "📚",
|
||||
}
|
||||
|
||||
DIMENSION_LAYER_COLORS = {
|
||||
"finance": "#F56C6C",
|
||||
"customer": "#409EFF",
|
||||
"process": "#67C23A",
|
||||
"learning": "#E6A23C",
|
||||
}
|
||||
|
||||
@router.get("")
|
||||
def list_okr_templates(
|
||||
dimension: Optional[str] = Query(None, description="按维度筛选: finance/customer/process/learning"),
|
||||
source: Optional[str] = Query(None, description="按来源筛选: system/user/industry_pack"),
|
||||
industry_tag: Optional[str] = Query(None, description="按行业标签筛选"),
|
||||
search: Optional[str] = Query(None, description="按O名称关键词搜索"),
|
||||
active_only: bool = Query(True, description="仅返回启用模板"),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""列出 OKR 模板,支持按维度筛选"""
|
||||
"""列出 OKR 模板,支持按维度/来源/行业/名称搜索"""
|
||||
q = db.query(OKRTemplate)
|
||||
if dimension:
|
||||
q = q.filter(OKRTemplate.dimension == dimension)
|
||||
@@ -29,6 +51,8 @@ def list_okr_templates(
|
||||
q = q.filter(OKRTemplate.source == source)
|
||||
if industry_tag:
|
||||
q = q.filter(OKRTemplate.industry_tag == industry_tag)
|
||||
if search:
|
||||
q = q.filter(OKRTemplate.name.like(f"%{search}%"))
|
||||
if active_only:
|
||||
q = q.filter(OKRTemplate.is_active == 1)
|
||||
templates = q.order_by(OKRTemplate.sort_order, OKRTemplate.id).all()
|
||||
@@ -113,3 +137,67 @@ def increment_use_count(template_id: int, db: Session = Depends(get_db)):
|
||||
t.use_count = (t.use_count or 0) + 1
|
||||
db.commit()
|
||||
return {"ok": True, "use_count": t.use_count}
|
||||
|
||||
|
||||
@router.post("/{template_id}/apply")
|
||||
def apply_okr_template(template_id: int, data: dict, db: Session = Depends(get_db)):
|
||||
"""应用 OKR 模板 — 创建战略地图并填入 O+KR"""
|
||||
t = db.query(OKRTemplate).filter(OKRTemplate.id == template_id).first()
|
||||
if not t:
|
||||
raise HTTPException(404, "模板不存在")
|
||||
|
||||
map_title = data.get("title", t.name)
|
||||
dim = t.dimension
|
||||
preset_krs = t.preset_krs or []
|
||||
|
||||
# 构建 dimensions: 仅包含模板所在的维度层
|
||||
dimensions = []
|
||||
for dk in ("finance", "customer", "process", "learning"):
|
||||
objectives = []
|
||||
if dk == dim:
|
||||
objectives.append({
|
||||
"name": t.name,
|
||||
"description": t.description or "",
|
||||
"kpis": [],
|
||||
"krs": [
|
||||
{
|
||||
"name": kr.get("name", ""),
|
||||
"target_value": kr.get("target_value", ""),
|
||||
"weight": kr.get("weight", 33),
|
||||
}
|
||||
for kr in preset_krs
|
||||
],
|
||||
})
|
||||
dimensions.append({
|
||||
"key": dk,
|
||||
"name": DIMENSION_LAYER_NAMES.get(dk, dk),
|
||||
"icon": DIMENSION_LAYER_ICONS.get(dk, "📌"),
|
||||
"color": DIMENSION_LAYER_COLORS.get(dk, "#909399"),
|
||||
"objectives": objectives,
|
||||
})
|
||||
|
||||
m = StrategicMap(
|
||||
title=map_title,
|
||||
version=data.get("version", "v1.0"),
|
||||
status="draft",
|
||||
dimensions=dimensions,
|
||||
canvas_data={"connections": []},
|
||||
)
|
||||
db.add(m)
|
||||
db.commit()
|
||||
db.refresh(m)
|
||||
|
||||
# 使用 maps API 的 _sync_map_objectives 同步到 map_objectives 表
|
||||
from app.api.maps import _sync_map_objectives
|
||||
_sync_map_objectives(m, db)
|
||||
|
||||
# 增加模板使用次数
|
||||
t.use_count = (t.use_count or 0) + 1
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"map_id": m.id,
|
||||
"title": m.title,
|
||||
"template_id": template_id,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user