feat: 合同金额权限控制 + 内置角色权限自动同步
- 新增 project:view_contract 权限,仅超级管理员可查看合同金额 - 项目详情、仪表盘、结算页、创建项目表单均受权限保护 - 启动时自动同步内置角色权限,新增权限无需手动更新数据库 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
db56ea1f99
commit
2b990f06fb
@ -12,6 +12,7 @@ from models import (
|
||||
Project, ProjectMilestone, DEFAULT_MILESTONES
|
||||
)
|
||||
from auth import hash_password
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
import os
|
||||
import logging
|
||||
|
||||
@ -108,7 +109,7 @@ def init_roles_and_admin():
|
||||
from database import SessionLocal
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# 初始化内置角色
|
||||
# 初始化 / 同步内置角色权限
|
||||
for role_name, role_def in BUILTIN_ROLES.items():
|
||||
existing = db.query(Role).filter(Role.name == role_name).first()
|
||||
if not existing:
|
||||
@ -120,6 +121,15 @@ def init_roles_and_admin():
|
||||
)
|
||||
db.add(role)
|
||||
print(f"[OK] created role: {role_name}")
|
||||
elif existing.is_system:
|
||||
# 同步内置角色:补充代码中新增的权限
|
||||
current = set(existing.permissions or [])
|
||||
defined = set(role_def["permissions"])
|
||||
missing = defined - current
|
||||
if missing:
|
||||
existing.permissions = list(current | missing)
|
||||
flag_modified(existing, "permissions")
|
||||
print(f"[SYNC] added permissions to {role_name}: {missing}")
|
||||
db.commit()
|
||||
|
||||
# 迁移旧成本权限 → 细分权限
|
||||
|
||||
@ -20,6 +20,7 @@ ALL_PERMISSIONS = [
|
||||
("project:edit", "编辑项目", "项目管理"),
|
||||
("project:delete", "删除项目", "项目管理"),
|
||||
("project:complete", "确认完成项目", "项目管理"),
|
||||
("project:view_contract", "查看合同金额", "项目管理"),
|
||||
# 内容提交
|
||||
("submission:view", "查看提交记录", "内容提交"),
|
||||
("submission:create", "新增提交", "内容提交"),
|
||||
|
||||
@ -161,7 +161,7 @@
|
||||
<div class="card-body">
|
||||
<el-table :data="data.settled_projects" size="small">
|
||||
<el-table-column prop="project_name" label="项目" />
|
||||
<el-table-column label="合同金额" align="right" width="120">
|
||||
<el-table-column v-if="authStore.hasPermission('project:view_contract')" label="合同金额" align="right" width="120">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.contract_amount">¥{{ formatNum(row.contract_amount) }}</span>
|
||||
<span v-else class="text-muted">—</span>
|
||||
@ -187,8 +187,11 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { dashboardApi } from '../api'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const data = ref({})
|
||||
const typeTagMap = { '客户正式项目': 'success', '客户测试项目': 'warning', '内部原创项目': '', '内部测试项目': 'info' }
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<!-- 项目信息 -->
|
||||
<div class="card info-card">
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-item" v-if="authStore.hasPermission('project:view_contract')">
|
||||
<span class="info-label">合同金额</span>
|
||||
<span class="info-value price">{{ project.contract_amount ? '¥' + project.contract_amount.toLocaleString() : '未设置' }}</span>
|
||||
</div>
|
||||
@ -355,7 +355,7 @@
|
||||
<el-form-item label="预估完成日期">
|
||||
<el-date-picker v-model="editForm.estimated_completion_date" value-format="YYYY-MM-DD" placeholder="选择预计交付日期" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="editForm.project_type === '客户正式项目'" label="合同金额">
|
||||
<el-form-item v-if="editForm.project_type === '客户正式项目' && authStore.hasPermission('project:view_contract')" label="合同金额">
|
||||
<el-input-number v-model="editForm.contract_amount" :min="0" :step="10000" :controls="false" placeholder="甲方合同总金额(元)" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
<el-form-item label="预估完成日期">
|
||||
<el-date-picker v-model="form.estimated_completion_date" value-format="YYYY-MM-DD" placeholder="选择预计交付日期" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.project_type === '客户正式项目'" label="合同金额">
|
||||
<el-form-item v-if="form.project_type === '客户正式项目' && authStore.hasPermission('project:view_contract')" label="合同金额">
|
||||
<el-input-number v-model="form.contract_amount" :min="0" :step="10000" :controls="false" placeholder="甲方合同总金额(元)" style="width:100%" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
</el-row>
|
||||
|
||||
<!-- 盈亏(仅客户正式项目) -->
|
||||
<el-card v-if="data.contract_amount != null" class="section-card">
|
||||
<el-card v-if="data.contract_amount != null && authStore.hasPermission('project:view_contract')" class="section-card">
|
||||
<template #header><span class="section-title">项目盈亏</span></template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
@ -102,6 +102,9 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { projectApi } from '../api'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const route = useRoute()
|
||||
const loading = ref(false)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user