add
This commit is contained in:
parent
79d1bbb43f
commit
25681c16a4
71
Pipfile
Normal file
71
Pipfile
Normal file
@ -0,0 +1,71 @@
|
||||
[[source]]
|
||||
url = "https://pypi.doubanio.com/simple"
|
||||
verify_ssl = true
|
||||
name = "pip_conf_index_global"
|
||||
|
||||
[packages]
|
||||
aerich = "==0.8.1"
|
||||
aiosqlite = "==0.20.0"
|
||||
annotated-types = "==0.7.0"
|
||||
anyio = "==4.8.0"
|
||||
argon2-cffi = "==23.1.0"
|
||||
argon2-cffi-bindings = "==21.2.0"
|
||||
asyncclick = "==8.1.8"
|
||||
black = "==24.10.0"
|
||||
certifi = "==2024.12.14"
|
||||
cffi = "==1.17.1"
|
||||
click = "==8.1.8"
|
||||
dictdiffer = "==0.9.0"
|
||||
dnspython = "==2.7.0"
|
||||
email-validator = "==2.2.0"
|
||||
fastapi = "==0.111.0"
|
||||
fastapi-cli = "==0.0.7"
|
||||
h11 = "==0.14.0"
|
||||
httpcore = "==1.0.7"
|
||||
httptools = "==0.6.4"
|
||||
httpx = "==0.28.1"
|
||||
idna = "==3.10"
|
||||
iso8601 = "==2.1.0"
|
||||
isort = "==5.13.2"
|
||||
jinja2 = "==3.1.5"
|
||||
loguru = "==0.7.3"
|
||||
markdown-it-py = "==3.0.0"
|
||||
markupsafe = "==3.0.2"
|
||||
mdurl = "==0.1.2"
|
||||
mypy-extensions = "==1.0.0"
|
||||
orjson = "==3.10.14"
|
||||
packaging = "==24.2"
|
||||
passlib = "==1.7.4"
|
||||
pathspec = "==0.12.1"
|
||||
platformdirs = "==4.3.6"
|
||||
pycparser = "==2.22"
|
||||
pydantic = "==2.10.5"
|
||||
pydantic-core = "==2.27.2"
|
||||
pydantic-settings = "==2.7.1"
|
||||
pygments = "==2.19.1"
|
||||
pyjwt = "==2.10.1"
|
||||
pypika-tortoise = "==0.3.2"
|
||||
python-dotenv = "==1.0.1"
|
||||
python-multipart = "==0.0.20"
|
||||
pytz = "==2024.2"
|
||||
pyyaml = "==6.0.2"
|
||||
rich = "==13.9.4"
|
||||
rich-toolkit = "==0.13.2"
|
||||
ruff = "==0.9.1"
|
||||
shellingham = "==1.5.4"
|
||||
sniffio = "==1.3.1"
|
||||
starlette = "==0.37.2"
|
||||
tortoise-orm = "==0.23.0"
|
||||
typer = "==0.15.1"
|
||||
typing-extensions = "==4.12.2"
|
||||
ujson = "==5.10.0"
|
||||
uvicorn = "==0.34.0"
|
||||
uvloop = "==0.21.0"
|
||||
watchfiles = "==1.0.4"
|
||||
websockets = "==14.1"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.9"
|
||||
python_full_version = "3.9.6"
|
||||
@ -6,7 +6,11 @@ from .apis import apis_router
|
||||
from .auditlog import auditlog_router
|
||||
from .base import base_router
|
||||
from .depts import depts_router
|
||||
from .esg.esg import router as esg_router
|
||||
from .index.index import router as index_router
|
||||
from .industry.industry import router as industry_router
|
||||
from .menus import menus_router
|
||||
from .policy.policy import router as policy_router
|
||||
from .roles import roles_router
|
||||
from .users import users_router
|
||||
|
||||
@ -19,3 +23,7 @@ v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependPermi
|
||||
v1_router.include_router(apis_router, prefix="/api", dependencies=[DependPermission])
|
||||
v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependPermission])
|
||||
v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependPermission])
|
||||
v1_router.include_router(esg_router, prefix="/esg", dependencies=[DependPermission])
|
||||
v1_router.include_router(index_router, prefix="/index", dependencies=[DependPermission])
|
||||
v1_router.include_router(industry_router, prefix="/industry", dependencies=[DependPermission])
|
||||
v1_router.include_router(policy_router, prefix="/policy", dependencies=[DependPermission])
|
||||
|
||||
0
app/api/v1/esg/__init__.py
Normal file
0
app/api/v1/esg/__init__.py
Normal file
71
app/api/v1/esg/esg.py
Normal file
71
app/api/v1/esg/esg.py
Normal file
@ -0,0 +1,71 @@
|
||||
from fastapi import APIRouter, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.esg import esg_controller
|
||||
from app.schemas import Success, SuccessExtra
|
||||
from app.schemas.esg import ESGCreate, ESGUpdate, ESGResponse
|
||||
|
||||
router = APIRouter(tags=["ESG管理"])
|
||||
|
||||
|
||||
@router.get("/list", summary="查看ESG列表")
|
||||
async def list_esg(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
code: str = Query(None, description="ESG代码"),
|
||||
name: str = Query(None, description="ESG名称"),
|
||||
level: str = Query(None, description="ESG级别"),
|
||||
):
|
||||
q = Q()
|
||||
if code:
|
||||
q &= Q(code__contains=code)
|
||||
if name:
|
||||
q &= Q(name__contains=name)
|
||||
if level:
|
||||
q &= Q(level__contains=level)
|
||||
total, esg_objs = await esg_controller.list(page=page, page_size=page_size, search=q, order=["id"])
|
||||
data = [await obj.to_dict() for obj in esg_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看ESG详情")
|
||||
async def get_esg(
|
||||
id: int = Query(..., description="ESG ID"),
|
||||
):
|
||||
esg_obj = await esg_controller.get(id=id)
|
||||
data = await esg_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建ESG")
|
||||
async def create_esg(
|
||||
esg_in: ESGCreate,
|
||||
):
|
||||
# 检查代码是否已存在
|
||||
if await esg_controller.is_exist(esg_in.code):
|
||||
return Success(code=400, msg="ESG代码已存在")
|
||||
|
||||
await esg_controller.create(obj_in=esg_in)
|
||||
return Success(msg="创建成功")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新ESG")
|
||||
async def update_esg(
|
||||
esg_in: ESGUpdate,
|
||||
):
|
||||
# 检查代码是否已存在(排除当前记录)
|
||||
if esg_in.code:
|
||||
existing_obj = await esg_controller.model.filter(code=esg_in.code).exclude(id=esg_in.id).first()
|
||||
if existing_obj:
|
||||
return Success(code=400, msg="ESG代码已存在")
|
||||
|
||||
await esg_controller.update(id=esg_in.id, obj_in=esg_in)
|
||||
return Success(msg="更新成功")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除ESG")
|
||||
async def delete_esg(
|
||||
esg_id: int = Query(..., description="ESG ID"),
|
||||
):
|
||||
await esg_controller.remove(id=esg_id)
|
||||
return Success(msg="删除成功")
|
||||
0
app/api/v1/index/__init__.py
Normal file
0
app/api/v1/index/__init__.py
Normal file
68
app/api/v1/index/index.py
Normal file
68
app/api/v1/index/index.py
Normal file
@ -0,0 +1,68 @@
|
||||
from fastapi import APIRouter, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.index import index_controller
|
||||
from app.schemas import Success, SuccessExtra
|
||||
from app.schemas.index import IndexCreate, IndexUpdate, IndexResponse
|
||||
|
||||
router = APIRouter(tags=["指数管理"])
|
||||
|
||||
|
||||
@router.get("/list", summary="查看指数列表")
|
||||
async def list_index(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
code: str = Query(None, description="指数代码"),
|
||||
name: str = Query(None, description="指数名称"),
|
||||
):
|
||||
q = Q()
|
||||
if code:
|
||||
q &= Q(code__contains=code)
|
||||
if name:
|
||||
q &= Q(name__contains=name)
|
||||
total, index_objs = await index_controller.list(page=page, page_size=page_size, search=q, order=["id"])
|
||||
data = [await obj.to_dict() for obj in index_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看指数详情")
|
||||
async def get_index(
|
||||
id: int = Query(..., description="指数 ID"),
|
||||
):
|
||||
index_obj = await index_controller.get(id=id)
|
||||
data = await index_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建指数")
|
||||
async def create_index(
|
||||
index_in: IndexCreate,
|
||||
):
|
||||
# 检查代码是否已存在
|
||||
if await index_controller.is_exist(index_in.code):
|
||||
return Success(code=400, msg="指数代码已存在")
|
||||
|
||||
await index_controller.create(obj_in=index_in)
|
||||
return Success(msg="创建成功")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新指数")
|
||||
async def update_index(
|
||||
index_in: IndexUpdate,
|
||||
):
|
||||
# 检查代码是否已存在(排除当前记录)
|
||||
if index_in.code:
|
||||
existing_obj = await index_controller.model.filter(code=index_in.code).exclude(id=index_in.id).first()
|
||||
if existing_obj:
|
||||
return Success(code=400, msg="指数代码已存在")
|
||||
|
||||
await index_controller.update(id=index_in.id, obj_in=index_in)
|
||||
return Success(msg="更新成功")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除指数")
|
||||
async def delete_index(
|
||||
index_id: int = Query(..., description="指数 ID"),
|
||||
):
|
||||
await index_controller.remove(id=index_id)
|
||||
return Success(msg="删除成功")
|
||||
0
app/api/v1/industry/__init__.py
Normal file
0
app/api/v1/industry/__init__.py
Normal file
68
app/api/v1/industry/industry.py
Normal file
68
app/api/v1/industry/industry.py
Normal file
@ -0,0 +1,68 @@
|
||||
from fastapi import APIRouter, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.industry import industry_controller
|
||||
from app.schemas import Success, SuccessExtra
|
||||
from app.schemas.industry import IndustryCreate, IndustryUpdate, IndustryResponse
|
||||
|
||||
router = APIRouter(tags=["行业管理"])
|
||||
|
||||
|
||||
@router.get("/list", summary="查看行业列表")
|
||||
async def list_industry(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
code: str = Query(None, description="行业代码"),
|
||||
name: str = Query(None, description="行业名称"),
|
||||
):
|
||||
q = Q()
|
||||
if code:
|
||||
q &= Q(code__contains=code)
|
||||
if name:
|
||||
q &= Q(name__contains=name)
|
||||
total, industry_objs = await industry_controller.list(page=page, page_size=page_size, search=q, order=["id"])
|
||||
data = [await obj.to_dict() for obj in industry_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看行业详情")
|
||||
async def get_industry(
|
||||
id: int = Query(..., description="行业 ID"),
|
||||
):
|
||||
industry_obj = await industry_controller.get(id=id)
|
||||
data = await industry_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建行业")
|
||||
async def create_industry(
|
||||
industry_in: IndustryCreate,
|
||||
):
|
||||
# 检查代码是否已存在
|
||||
if await industry_controller.is_exist(industry_in.code):
|
||||
return Success(code=400, msg="行业代码已存在")
|
||||
|
||||
await industry_controller.create(obj_in=industry_in)
|
||||
return Success(msg="创建成功")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新行业")
|
||||
async def update_industry(
|
||||
industry_in: IndustryUpdate,
|
||||
):
|
||||
# 检查代码是否已存在(排除当前记录)
|
||||
if industry_in.code:
|
||||
existing_obj = await industry_controller.model.filter(code=industry_in.code).exclude(id=industry_in.id).first()
|
||||
if existing_obj:
|
||||
return Success(code=400, msg="行业代码已存在")
|
||||
|
||||
await industry_controller.update(id=industry_in.id, obj_in=industry_in)
|
||||
return Success(msg="更新成功")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除行业")
|
||||
async def delete_industry(
|
||||
industry_id: int = Query(..., description="行业 ID"),
|
||||
):
|
||||
await industry_controller.remove(id=industry_id)
|
||||
return Success(msg="删除成功")
|
||||
0
app/api/v1/policy/__init__.py
Normal file
0
app/api/v1/policy/__init__.py
Normal file
71
app/api/v1/policy/policy.py
Normal file
71
app/api/v1/policy/policy.py
Normal file
@ -0,0 +1,71 @@
|
||||
from fastapi import APIRouter, Query
|
||||
from tortoise.expressions import Q
|
||||
|
||||
from app.controllers.policy import policy_controller
|
||||
from app.schemas import Success, SuccessExtra
|
||||
from app.schemas.policy import PolicyCreate, PolicyUpdate, PolicyResponse
|
||||
|
||||
router = APIRouter(tags=["政策管理"])
|
||||
|
||||
|
||||
@router.get("/list", summary="查看政策列表")
|
||||
async def list_policy(
|
||||
page: int = Query(1, description="页码"),
|
||||
page_size: int = Query(10, description="每页数量"),
|
||||
code: str = Query(None, description="政策代码"),
|
||||
name: str = Query(None, description="政策名称"),
|
||||
level: str = Query(None, description="政策级别"),
|
||||
):
|
||||
q = Q()
|
||||
if code:
|
||||
q &= Q(code__contains=code)
|
||||
if name:
|
||||
q &= Q(name__contains=name)
|
||||
if level:
|
||||
q &= Q(level__contains=level)
|
||||
total, policy_objs = await policy_controller.list(page=page, page_size=page_size, search=q, order=["id"])
|
||||
data = [await obj.to_dict() for obj in policy_objs]
|
||||
return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
|
||||
|
||||
|
||||
@router.get("/get", summary="查看政策详情")
|
||||
async def get_policy(
|
||||
id: int = Query(..., description="政策 ID"),
|
||||
):
|
||||
policy_obj = await policy_controller.get(id=id)
|
||||
data = await policy_obj.to_dict()
|
||||
return Success(data=data)
|
||||
|
||||
|
||||
@router.post("/create", summary="创建政策")
|
||||
async def create_policy(
|
||||
policy_in: PolicyCreate,
|
||||
):
|
||||
# 检查代码是否已存在
|
||||
if await policy_controller.is_exist(policy_in.code):
|
||||
return Success(code=400, msg="政策代码已存在")
|
||||
|
||||
await policy_controller.create(obj_in=policy_in)
|
||||
return Success(msg="创建成功")
|
||||
|
||||
|
||||
@router.post("/update", summary="更新政策")
|
||||
async def update_policy(
|
||||
policy_in: PolicyUpdate,
|
||||
):
|
||||
# 检查代码是否已存在(排除当前记录)
|
||||
if policy_in.code:
|
||||
existing_obj = await policy_controller.model.filter(code=policy_in.code).exclude(id=policy_in.id).first()
|
||||
if existing_obj:
|
||||
return Success(code=400, msg="政策代码已存在")
|
||||
|
||||
await policy_controller.update(id=policy_in.id, obj_in=policy_in)
|
||||
return Success(msg="更新成功")
|
||||
|
||||
|
||||
@router.delete("/delete", summary="删除政策")
|
||||
async def delete_policy(
|
||||
policy_id: int = Query(..., description="政策 ID"),
|
||||
):
|
||||
await policy_controller.remove(id=policy_id)
|
||||
return Success(msg="删除成功")
|
||||
@ -33,7 +33,7 @@ class ApiController(CRUDBase[Api, ApiCreate, ApiUpdate]):
|
||||
method = list(route.methods)[0]
|
||||
path = route.path_format
|
||||
summary = route.summary
|
||||
tags = list(route.tags)[0]
|
||||
tags = list(route.tags)[0] if route.tags else "default"
|
||||
api_obj = await Api.filter(method=method, path=path).first()
|
||||
if api_obj:
|
||||
await api_obj.update_from_dict(dict(method=method, path=path, summary=summary, tags=tags)).save()
|
||||
|
||||
15
app/controllers/esg.py
Normal file
15
app/controllers/esg.py
Normal file
@ -0,0 +1,15 @@
|
||||
from app.core.crud import CRUDBase
|
||||
from app.models.esg import ESG
|
||||
from app.schemas.esg import ESGCreate, ESGUpdate
|
||||
|
||||
|
||||
class ESGController(CRUDBase[ESG, ESGCreate, ESGUpdate]):
|
||||
def __init__(self):
|
||||
super().__init__(model=ESG)
|
||||
|
||||
async def is_exist(self, code: str) -> bool:
|
||||
"""检查行业代码是否已存在"""
|
||||
return await self.model.filter(code=code).exists()
|
||||
|
||||
|
||||
esg_controller = ESGController()
|
||||
15
app/controllers/index.py
Normal file
15
app/controllers/index.py
Normal file
@ -0,0 +1,15 @@
|
||||
from app.core.crud import CRUDBase
|
||||
from app.models.index import Index
|
||||
from app.schemas.index import IndexCreate, IndexUpdate
|
||||
|
||||
|
||||
class IndexController(CRUDBase[Index, IndexCreate, IndexUpdate]):
|
||||
def __init__(self):
|
||||
super().__init__(model=Index)
|
||||
|
||||
async def is_exist(self, code: str) -> bool:
|
||||
"""检查行业代码是否已存在"""
|
||||
return await self.model.filter(code=code).exists()
|
||||
|
||||
|
||||
index_controller = IndexController()
|
||||
15
app/controllers/industry.py
Normal file
15
app/controllers/industry.py
Normal file
@ -0,0 +1,15 @@
|
||||
from app.core.crud import CRUDBase
|
||||
from app.models.industry import Industry
|
||||
from app.schemas.industry import IndustryCreate, IndustryUpdate
|
||||
|
||||
|
||||
class IndustryController(CRUDBase[Industry, IndustryCreate, IndustryUpdate]):
|
||||
def __init__(self):
|
||||
super().__init__(model=Industry)
|
||||
|
||||
async def is_exist(self, code: str) -> bool:
|
||||
"""检查行业代码是否已存在"""
|
||||
return await self.model.filter(code=code).exists()
|
||||
|
||||
|
||||
industry_controller = IndustryController()
|
||||
15
app/controllers/policy.py
Normal file
15
app/controllers/policy.py
Normal file
@ -0,0 +1,15 @@
|
||||
from app.core.crud import CRUDBase
|
||||
from app.models.policy import Policy
|
||||
from app.schemas.policy import PolicyCreate, PolicyUpdate
|
||||
|
||||
|
||||
class PolicyController(CRUDBase[Policy, PolicyCreate, PolicyUpdate]):
|
||||
def __init__(self):
|
||||
super().__init__(model=Policy)
|
||||
|
||||
async def is_exist(self, code: str) -> bool:
|
||||
"""检查行业代码是否已存在"""
|
||||
return await self.model.filter(code=code).exists()
|
||||
|
||||
|
||||
policy_controller = PolicyController()
|
||||
@ -162,6 +162,67 @@ async def init_menus():
|
||||
),
|
||||
]
|
||||
await Menu.bulk_create(children_menu)
|
||||
|
||||
# 创建系统数据管理菜单
|
||||
data_menu = await Menu.create(
|
||||
menu_type=MenuType.CATALOG,
|
||||
name="系统数据",
|
||||
path="/data",
|
||||
order=2,
|
||||
parent_id=0,
|
||||
icon="carbon:data-base",
|
||||
is_hidden=False,
|
||||
component="Layout",
|
||||
keepalive=False,
|
||||
redirect="/data/industry",
|
||||
)
|
||||
data_children_menu = [
|
||||
Menu(
|
||||
menu_type=MenuType.MENU,
|
||||
name="行业修正",
|
||||
path="industry",
|
||||
order=1,
|
||||
parent_id=data_menu.id,
|
||||
icon="carbon:industry",
|
||||
is_hidden=False,
|
||||
component="/data/industry",
|
||||
keepalive=False,
|
||||
),
|
||||
Menu(
|
||||
menu_type=MenuType.MENU,
|
||||
name="政策匹配",
|
||||
path="policy",
|
||||
order=2,
|
||||
parent_id=data_menu.id,
|
||||
icon="carbon:policy",
|
||||
is_hidden=False,
|
||||
component="/data/policy",
|
||||
keepalive=False,
|
||||
),
|
||||
Menu(
|
||||
menu_type=MenuType.MENU,
|
||||
name="ESG关联",
|
||||
path="esg",
|
||||
order=3,
|
||||
parent_id=data_menu.id,
|
||||
icon="carbon:earth-southeast-asia",
|
||||
is_hidden=False,
|
||||
component="/data/esg",
|
||||
keepalive=False,
|
||||
),
|
||||
Menu(
|
||||
menu_type=MenuType.MENU,
|
||||
name="行业基准",
|
||||
path="index",
|
||||
order=4,
|
||||
parent_id=data_menu.id,
|
||||
icon="carbon:chart-line",
|
||||
is_hidden=False,
|
||||
component="/data/index",
|
||||
keepalive=False,
|
||||
),
|
||||
]
|
||||
await Menu.bulk_create(data_children_menu)
|
||||
await Menu.create(
|
||||
menu_type=MenuType.MENU,
|
||||
name="一级菜单",
|
||||
|
||||
@ -1,2 +1,6 @@
|
||||
# 新增model需要在这里导入
|
||||
from .admin import *
|
||||
from .esg import *
|
||||
from .index import *
|
||||
from .industry import *
|
||||
from .policy import *
|
||||
|
||||
@ -17,3 +17,18 @@ class MethodType(StrEnum):
|
||||
PUT = "PUT"
|
||||
DELETE = "DELETE"
|
||||
PATCH = "PATCH"
|
||||
|
||||
class PolicyType(StrEnum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "C"
|
||||
D = "D"
|
||||
E = "E"
|
||||
|
||||
|
||||
class ESGType(StrEnum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "C"
|
||||
D = "D"
|
||||
E = "E"
|
||||
15
app/models/esg.py
Normal file
15
app/models/esg.py
Normal file
@ -0,0 +1,15 @@
|
||||
from tortoise import fields
|
||||
|
||||
from .base import BaseModel, TimestampMixin
|
||||
from .enums import ESGType
|
||||
|
||||
# ESG关联价值对应表
|
||||
class ESG(BaseModel, TimestampMixin):
|
||||
code = fields.CharField(max_length=20, unique=True, description="行业代码", index=True)
|
||||
name = fields.CharField(max_length=20, description="行业名称")
|
||||
level = fields.CharEnumField(ESGType, description="ESG价值等级", index=True)
|
||||
number = fields.IntField(default=0, description="ESG基准分")
|
||||
remark = fields.TextField(default="简要说明(ESG视角)")
|
||||
|
||||
class Meta:
|
||||
table = "esg"
|
||||
16
app/models/index.py
Normal file
16
app/models/index.py
Normal file
@ -0,0 +1,16 @@
|
||||
from tortoise import fields
|
||||
|
||||
from app.schemas.menus import MenuType
|
||||
|
||||
from .base import BaseModel, TimestampMixin
|
||||
from .enums import MethodType
|
||||
|
||||
# 行业基准搜索指数表
|
||||
class Index(BaseModel, TimestampMixin):
|
||||
code = fields.CharField(max_length=20, unique=True, description="行业代码", index=True)
|
||||
name = fields.CharField(max_length=20, description="行业名称")
|
||||
search_num = fields.FloatField(description="行业基准搜索指数", default=0)
|
||||
remark = fields.TextField(default="备注")
|
||||
|
||||
class Meta:
|
||||
table = "index"
|
||||
14
app/models/industry.py
Normal file
14
app/models/industry.py
Normal file
@ -0,0 +1,14 @@
|
||||
from tortoise import fields
|
||||
|
||||
|
||||
from .base import BaseModel, TimestampMixin
|
||||
|
||||
# 行业修正系数与ROE
|
||||
class Industry(BaseModel, TimestampMixin):
|
||||
code = fields.CharField(max_length=20, unique=True, description="行业代码", index=True)
|
||||
name = fields.CharField(max_length=20, description="行业名称")
|
||||
roe = fields.FloatField(description="平均ROE",default=0)
|
||||
fix_num = fields.FloatField(description="行业修正系数", default=0)
|
||||
remark = fields.TextField(default="备注")
|
||||
class Meta:
|
||||
table = "industry"
|
||||
16
app/models/policy.py
Normal file
16
app/models/policy.py
Normal file
@ -0,0 +1,16 @@
|
||||
from tortoise import fields
|
||||
|
||||
from app.schemas.menus import MenuType
|
||||
|
||||
from .base import BaseModel, TimestampMixin
|
||||
from .enums import PolicyType
|
||||
|
||||
# 政策匹配度对应表
|
||||
class Policy(BaseModel, TimestampMixin):
|
||||
code = fields.CharField(max_length=20, unique=True, description="行业代码", index=True)
|
||||
name = fields.CharField(max_length=20, description="行业名称")
|
||||
level = fields.CharEnumField(PolicyType, description="政策扶持等级")
|
||||
score = fields.IntField( description="政策匹配度基准分",default=0)
|
||||
|
||||
class Meta:
|
||||
table = "policy"
|
||||
28
app/schemas/esg.py
Normal file
28
app/schemas/esg.py
Normal file
@ -0,0 +1,28 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.models.enums import ESGType
|
||||
|
||||
|
||||
class BaseESG(BaseModel):
|
||||
code: str = Field(..., description="行业代码", example="001")
|
||||
name: str = Field(..., description="行业名称", example="农业")
|
||||
level: ESGType = Field(..., description="ESG价值等级", example=ESGType.A)
|
||||
number: int = Field(0, description="ESG基准分", example=85)
|
||||
remark: str = Field("简要说明(ESG视角)", description="备注", example="环保友好型行业")
|
||||
|
||||
|
||||
class ESGCreate(BaseESG):
|
||||
pass
|
||||
|
||||
|
||||
class ESGUpdate(BaseESG):
|
||||
id: int
|
||||
|
||||
|
||||
class ESGResponse(BaseESG):
|
||||
id: int
|
||||
created_at: Optional[datetime]
|
||||
updated_at: Optional[datetime]
|
||||
25
app/schemas/index.py
Normal file
25
app/schemas/index.py
Normal file
@ -0,0 +1,25 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class BaseIndex(BaseModel):
|
||||
code: str = Field(..., description="行业代码", example="001")
|
||||
name: str = Field(..., description="行业名称", example="农业")
|
||||
search_num: float = Field(0, description="行业基准搜索指数", example=1.25)
|
||||
remark: str = Field("备注", description="备注", example="基准搜索指数说明")
|
||||
|
||||
|
||||
class IndexCreate(BaseIndex):
|
||||
pass
|
||||
|
||||
|
||||
class IndexUpdate(BaseIndex):
|
||||
id: int
|
||||
|
||||
|
||||
class IndexResponse(BaseIndex):
|
||||
id: int
|
||||
created_at: Optional[datetime]
|
||||
updated_at: Optional[datetime]
|
||||
26
app/schemas/industry.py
Normal file
26
app/schemas/industry.py
Normal file
@ -0,0 +1,26 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class BaseIndustry(BaseModel):
|
||||
code: str = Field(..., description="行业代码", example="001")
|
||||
name: str = Field(..., description="行业名称", example="农业")
|
||||
roe: float = Field(0, description="平均ROE", example=0.15)
|
||||
fix_num: float = Field(0, description="行业修正系数", example=1.2)
|
||||
remark: str = Field("备注", description="备注", example="行业修正系数说明")
|
||||
|
||||
|
||||
class IndustryCreate(BaseIndustry):
|
||||
pass
|
||||
|
||||
|
||||
class IndustryUpdate(BaseIndustry):
|
||||
id: int
|
||||
|
||||
|
||||
class IndustryResponse(BaseIndustry):
|
||||
id: int
|
||||
created_at: Optional[datetime]
|
||||
updated_at: Optional[datetime]
|
||||
27
app/schemas/policy.py
Normal file
27
app/schemas/policy.py
Normal file
@ -0,0 +1,27 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.models.enums import PolicyType
|
||||
|
||||
|
||||
class BasePolicy(BaseModel):
|
||||
code: str = Field(..., description="行业代码", example="001")
|
||||
name: str = Field(..., description="行业名称", example="农业")
|
||||
level: PolicyType = Field(..., description="政策扶持等级", example=PolicyType.A)
|
||||
score: int = Field(0, description="政策匹配度基准分", example=90)
|
||||
|
||||
|
||||
class PolicyCreate(BasePolicy):
|
||||
pass
|
||||
|
||||
|
||||
class PolicyUpdate(BasePolicy):
|
||||
id: int
|
||||
|
||||
|
||||
class PolicyResponse(BasePolicy):
|
||||
id: int
|
||||
created_at: Optional[datetime]
|
||||
updated_at: Optional[datetime]
|
||||
0
app/utils/api_utils.py
Normal file
0
app/utils/api_utils.py
Normal file
8415
web/package-lock.json
generated
Normal file
8415
web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -28,9 +28,9 @@
|
||||
"sass": "^1.65.1",
|
||||
"typescript": "^5.1.6",
|
||||
"unocss": "^0.55.0",
|
||||
"unplugin-auto-import": "^0.16.6",
|
||||
"unplugin-auto-import": "^0.15.3",
|
||||
"unplugin-icons": "^0.16.5",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
|
||||
2458
web/pnpm-lock.yaml
generated
2458
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -39,4 +39,28 @@ export default {
|
||||
deleteDept: (params = {}) => request.delete('/dept/delete', { params }),
|
||||
// auditlog
|
||||
getAuditLogList: (params = {}) => request.get('/auditlog/list', { params }),
|
||||
// esg
|
||||
getESGList: (params = {}) => request.get('/esg/list', { params }),
|
||||
getESGById: (params = {}) => request.get('/esg/get', { params }),
|
||||
createESG: (data = {}) => request.post('/esg/create', data),
|
||||
updateESG: (data = {}) => request.post('/esg/update', data),
|
||||
deleteESG: (params = {}) => request.delete('/esg/delete', { params }),
|
||||
// index
|
||||
getIndexList: (params = {}) => request.get('/index/list', { params }),
|
||||
getIndexById: (params = {}) => request.get('/index/get', { params }),
|
||||
createIndex: (data = {}) => request.post('/index/create', data),
|
||||
updateIndex: (data = {}) => request.post('/index/update', data),
|
||||
deleteIndex: (params = {}) => request.delete('/index/delete', { params }),
|
||||
// industry
|
||||
getIndustryList: (params = {}) => request.get('/industry/list', { params }),
|
||||
getIndustryById: (params = {}) => request.get('/industry/get', { params }),
|
||||
createIndustry: (data = {}) => request.post('/industry/create', data),
|
||||
updateIndustry: (data = {}) => request.post('/industry/update', data),
|
||||
deleteIndustry: (params = {}) => request.delete('/industry/delete', { params }),
|
||||
// policy
|
||||
getPolicyList: (params = {}) => request.get('/policy/list', { params }),
|
||||
getPolicyById: (params = {}) => request.get('/policy/get', { params }),
|
||||
createPolicy: (data = {}) => request.post('/policy/create', data),
|
||||
updatePolicy: (data = {}) => request.post('/policy/update', data),
|
||||
deletePolicy: (params = {}) => request.delete('/policy/delete', { params }),
|
||||
}
|
||||
|
||||
282
web/src/views/data/esg/index.vue
Normal file
282
web/src/views/data/esg/index.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<script setup>
|
||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NSpace,
|
||||
NTag,
|
||||
NPopconfirm,
|
||||
} from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||
import CrudModal from '@/components/table/CrudModal.vue'
|
||||
import CrudTable from '@/components/table/CrudTable.vue'
|
||||
|
||||
import { formatDate, renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from '@/api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
defineOptions({ name: 'ESG关联' })
|
||||
|
||||
const $table = ref(null)
|
||||
const queryItems = ref({})
|
||||
const vPermission = resolveDirective('permission')
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
modalAction,
|
||||
modalLoading,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleAdd,
|
||||
} = useCRUD({
|
||||
name: 'ESG',
|
||||
initForm: { code: '', name: '', level: '', number: '', remark: '' },
|
||||
doCreate: api.createESG,
|
||||
doUpdate: api.updateESG,
|
||||
doDelete: api.deleteESG,
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ESG代码',
|
||||
key: 'code',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: 'ESG名称',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: 'ESG级别',
|
||||
key: 'level',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
const levelMap = {
|
||||
'E': { type: 'success', text: '环境(E)' },
|
||||
'S': { type: 'info', text: '社会(S)' },
|
||||
'G': { type: 'warning', text: '治理(G)' },
|
||||
'ESG': { type: 'error', text: '综合ESG' }
|
||||
}
|
||||
const level = levelMap[row.level] || { type: 'default', text: row.level }
|
||||
return h(
|
||||
NTag,
|
||||
{ type: level.type },
|
||||
{ default: () => level.text }
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'ESG编号',
|
||||
key: 'number',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'created_at',
|
||||
align: 'center',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'text', ghost: true },
|
||||
{
|
||||
default: () => formatDate(row.created_at),
|
||||
icon: renderIcon('mdi:update', { size: 16 }),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
secondary: true,
|
||||
onClick: () => handleEdit(row),
|
||||
},
|
||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
||||
),
|
||||
[[vPermission, 'post/api/v1/esg/update']]
|
||||
),
|
||||
|
||||
withDirectives(
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDelete([row.id], false),
|
||||
},
|
||||
{
|
||||
default: () => '确认删除',
|
||||
trigger: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
||||
),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'delete/api/v1/esg/delete']]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const modalRules = {
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入ESG代码',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入ESG名称',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
level: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入ESG级别',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
number: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入ESG编号',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer title="ESG关联">
|
||||
<template #action>
|
||||
<div>
|
||||
<NButton
|
||||
v-permission="'post/api/v1/esg/create'"
|
||||
class="float-right mb-8"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建ESG
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:extra-params="{ ordering: 'id' }"
|
||||
:scroll-x="1200"
|
||||
:columns="columns"
|
||||
:get-data="api.getESGList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="ESG代码" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.code"
|
||||
type="text"
|
||||
placeholder="请输入ESG代码"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="ESG名称" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.name"
|
||||
type="text"
|
||||
placeholder="请输入ESG名称"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="ESG级别" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.level"
|
||||
type="text"
|
||||
placeholder="请输入ESG级别"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
:show-footer="true"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="modalRules"
|
||||
>
|
||||
<NFormItem label="ESG代码" path="code">
|
||||
<NInput v-model:value="modalForm.code" placeholder="请输入ESG代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="ESG名称" path="name">
|
||||
<NInput v-model:value="modalForm.name" placeholder="请输入ESG名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="ESG级别" path="level">
|
||||
<NInput v-model:value="modalForm.level" placeholder="请输入ESG级别(如:E, S, G, ESG)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="ESG编号" path="number">
|
||||
<NInput v-model:value="modalForm.number" placeholder="请输入ESG编号" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="modalForm.remark" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
257
web/src/views/data/index/index.vue
Normal file
257
web/src/views/data/index/index.vue
Normal file
@ -0,0 +1,257 @@
|
||||
<script setup>
|
||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NSpace,
|
||||
NTag,
|
||||
NPopconfirm,
|
||||
} from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||
import CrudModal from '@/components/table/CrudModal.vue'
|
||||
import CrudTable from '@/components/table/CrudTable.vue'
|
||||
|
||||
import { formatDate, renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from '@/api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
defineOptions({ name: '行业基准' })
|
||||
|
||||
const $table = ref(null)
|
||||
const queryItems = ref({})
|
||||
const vPermission = resolveDirective('permission')
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
modalAction,
|
||||
modalLoading,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleAdd,
|
||||
} = useCRUD({
|
||||
name: '指数',
|
||||
initForm: { code: '', name: '', search_num: 0, remark: '' },
|
||||
doCreate: api.createIndex,
|
||||
doUpdate: api.updateIndex,
|
||||
doDelete: api.deleteIndex,
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '指数代码',
|
||||
key: 'code',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '指数名称',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '搜索次数',
|
||||
key: 'search_num',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
const getSearchType = (num) => {
|
||||
if (num >= 1000) return 'error'
|
||||
if (num >= 500) return 'warning'
|
||||
if (num >= 100) return 'info'
|
||||
return 'default'
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{ type: getSearchType(row.search_num) },
|
||||
{ default: () => `${row.search_num}次` }
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'created_at',
|
||||
align: 'center',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'text', ghost: true },
|
||||
{
|
||||
default: () => formatDate(row.created_at),
|
||||
icon: renderIcon('mdi:update', { size: 16 }),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
secondary: true,
|
||||
onClick: () => handleEdit(row),
|
||||
},
|
||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
||||
),
|
||||
[[vPermission, 'post/api/v1/index/update']]
|
||||
),
|
||||
|
||||
withDirectives(
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDelete([row.id], false),
|
||||
},
|
||||
{
|
||||
default: () => '确认删除',
|
||||
trigger: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
||||
),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'delete/api/v1/index/delete']]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const modalRules = {
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入指数代码',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入指数名称',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
search_num: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入搜索次数',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer title="行业基准">
|
||||
<template #action>
|
||||
<div>
|
||||
<NButton
|
||||
v-permission="'post/api/v1/index/create'"
|
||||
class="float-right mb-8"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建指数
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:extra-params="{ ordering: 'id' }"
|
||||
:scroll-x="1200"
|
||||
:columns="columns"
|
||||
:get-data="api.getIndexList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="指数代码" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.code"
|
||||
type="text"
|
||||
placeholder="请输入指数代码"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="指数名称" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.name"
|
||||
type="text"
|
||||
placeholder="请输入指数名称"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
:show-footer="true"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="modalRules"
|
||||
>
|
||||
<NFormItem label="指数代码" path="code">
|
||||
<NInput v-model:value="modalForm.code" placeholder="请输入指数代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="指数名称" path="name">
|
||||
<NInput v-model:value="modalForm.name" placeholder="请输入指数名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="搜索次数" path="search_num">
|
||||
<NInputNumber v-model:value="modalForm.search_num" placeholder="请输入搜索次数" :min="0" :precision="0" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="modalForm.remark" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
275
web/src/views/data/industry/index.vue
Normal file
275
web/src/views/data/industry/index.vue
Normal file
@ -0,0 +1,275 @@
|
||||
<script setup>
|
||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NSpace,
|
||||
NTag,
|
||||
NPopconfirm,
|
||||
} from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||
import CrudModal from '@/components/table/CrudModal.vue'
|
||||
import CrudTable from '@/components/table/CrudTable.vue'
|
||||
|
||||
import { formatDate, renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from '@/api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
defineOptions({ name: '行业修正' })
|
||||
|
||||
const $table = ref(null)
|
||||
const queryItems = ref({})
|
||||
const vPermission = resolveDirective('permission')
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
modalAction,
|
||||
modalLoading,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleAdd,
|
||||
} = useCRUD({
|
||||
name: '行业',
|
||||
initForm: { code: '', name: '', roe: 0, fix_num: 0, remark: '' },
|
||||
doCreate: api.createIndustry,
|
||||
doUpdate: api.updateIndustry,
|
||||
doDelete: api.deleteIndustry,
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '行业代码',
|
||||
key: 'code',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '行业名称',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: 'ROE',
|
||||
key: 'roe',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{ type: 'info' },
|
||||
{ default: () => `${row.roe}%` }
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '修正数值',
|
||||
key: 'fix_num',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(
|
||||
NTag,
|
||||
{ type: row.fix_num > 0 ? 'success' : row.fix_num < 0 ? 'error' : 'default' },
|
||||
{ default: () => row.fix_num }
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'created_at',
|
||||
align: 'center',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'text', ghost: true },
|
||||
{
|
||||
default: () => formatDate(row.created_at),
|
||||
icon: renderIcon('mdi:update', { size: 16 }),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
secondary: true,
|
||||
onClick: () => handleEdit(row),
|
||||
},
|
||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
||||
),
|
||||
[[vPermission, 'post/api/v1/industry/update']]
|
||||
),
|
||||
|
||||
withDirectives(
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDelete([row.id], false),
|
||||
},
|
||||
{
|
||||
default: () => '确认删除',
|
||||
trigger: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
||||
),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'delete/api/v1/industry/delete']]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const modalRules = {
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入行业代码',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入行业名称',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
roe: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入ROE值',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
fix_num: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入修正数值',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer title="行业修正">
|
||||
<template #action>
|
||||
<div>
|
||||
<NButton
|
||||
v-permission="'post/api/v1/industry/create'"
|
||||
class="float-right mb-8"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建行业
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:extra-params="{ ordering: 'id' }"
|
||||
:scroll-x="1200"
|
||||
:columns="columns"
|
||||
:get-data="api.getIndustryList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="行业代码" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.code"
|
||||
type="text"
|
||||
placeholder="请输入行业代码"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="行业名称" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.name"
|
||||
type="text"
|
||||
placeholder="请输入行业名称"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
:show-footer="true"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="modalRules"
|
||||
>
|
||||
<NFormItem label="行业代码" path="code">
|
||||
<NInput v-model:value="modalForm.code" placeholder="请输入行业代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="行业名称" path="name">
|
||||
<NInput v-model:value="modalForm.name" placeholder="请输入行业名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="ROE" path="roe">
|
||||
<NInputNumber v-model:value="modalForm.roe" placeholder="请输入ROE值" :precision="2" :step="0.01" />
|
||||
</NFormItem>
|
||||
<NFormItem label="修正数值" path="fix_num">
|
||||
<NInputNumber v-model:value="modalForm.fix_num" placeholder="请输入修正数值" :precision="2" :step="0.01" />
|
||||
</NFormItem>
|
||||
<NFormItem label="备注" path="remark">
|
||||
<NInput v-model:value="modalForm.remark" type="textarea" placeholder="请输入备注" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
284
web/src/views/data/policy/index.vue
Normal file
284
web/src/views/data/policy/index.vue
Normal file
@ -0,0 +1,284 @@
|
||||
<script setup>
|
||||
import { h, onMounted, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NForm,
|
||||
NFormItem,
|
||||
NInput,
|
||||
NInputNumber,
|
||||
NSpace,
|
||||
NTag,
|
||||
NPopconfirm,
|
||||
} from 'naive-ui'
|
||||
|
||||
import CommonPage from '@/components/page/CommonPage.vue'
|
||||
import QueryBarItem from '@/components/query-bar/QueryBarItem.vue'
|
||||
import CrudModal from '@/components/table/CrudModal.vue'
|
||||
import CrudTable from '@/components/table/CrudTable.vue'
|
||||
|
||||
import { formatDate, renderIcon } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from '@/api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
defineOptions({ name: '政策匹配' })
|
||||
|
||||
const $table = ref(null)
|
||||
const queryItems = ref({})
|
||||
const vPermission = resolveDirective('permission')
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalTitle,
|
||||
modalAction,
|
||||
modalLoading,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
handleEdit,
|
||||
handleDelete,
|
||||
handleAdd,
|
||||
} = useCRUD({
|
||||
name: '政策',
|
||||
initForm: { code: '', name: '', level: '', score: 0 },
|
||||
doCreate: api.createPolicy,
|
||||
doUpdate: api.updatePolicy,
|
||||
doDelete: api.deletePolicy,
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
$table.value?.handleSearch()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '政策代码',
|
||||
key: 'code',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '政策名称',
|
||||
key: 'name',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: { tooltip: true },
|
||||
},
|
||||
{
|
||||
title: '政策级别',
|
||||
key: 'level',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
const levelMap = {
|
||||
'national': { type: 'error', text: '国家级' },
|
||||
'provincial': { type: 'warning', text: '省级' },
|
||||
'municipal': { type: 'info', text: '市级' },
|
||||
'county': { type: 'default', text: '县级' }
|
||||
}
|
||||
const level = levelMap[row.level] || { type: 'default', text: row.level }
|
||||
return h(
|
||||
NTag,
|
||||
{ type: level.type },
|
||||
{ default: () => level.text }
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '政策评分',
|
||||
key: 'score',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
render(row) {
|
||||
const getScoreType = (score) => {
|
||||
if (score >= 80) return 'success'
|
||||
if (score >= 60) return 'warning'
|
||||
return 'error'
|
||||
}
|
||||
return h(
|
||||
NTag,
|
||||
{ type: getScoreType(row.score) },
|
||||
{ default: () => `${row.score}分` }
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'created_at',
|
||||
align: 'center',
|
||||
width: 180,
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'text', ghost: true },
|
||||
{
|
||||
default: () => formatDate(row.created_at),
|
||||
icon: renderIcon('mdi:update', { size: 16 }),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
secondary: true,
|
||||
onClick: () => handleEdit(row),
|
||||
},
|
||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 16 }) }
|
||||
),
|
||||
[[vPermission, 'post/api/v1/policy/update']]
|
||||
),
|
||||
|
||||
withDirectives(
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: () => handleDelete([row.id], false),
|
||||
},
|
||||
{
|
||||
default: () => '确认删除',
|
||||
trigger: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ size: 'small', type: 'error', style: 'margin-left: 12px;' },
|
||||
{ default: () => '删除', icon: renderIcon('material-symbols:delete-outline', { size: 16 }) }
|
||||
),
|
||||
}
|
||||
),
|
||||
[[vPermission, 'delete/api/v1/policy/delete']]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const modalRules = {
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入政策代码',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入政策名称',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
level: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入政策级别',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
score: [
|
||||
{
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入政策评分',
|
||||
trigger: ['input', 'blur'],
|
||||
},
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer title="政策匹配">
|
||||
<template #action>
|
||||
<div>
|
||||
<NButton
|
||||
v-permission="'post/api/v1/policy/create'"
|
||||
class="float-right mb-8"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />新建政策
|
||||
</NButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:extra-params="{ ordering: 'id' }"
|
||||
:scroll-x="1200"
|
||||
:columns="columns"
|
||||
:get-data="api.getPolicyList"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="政策代码" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.code"
|
||||
type="text"
|
||||
placeholder="请输入政策代码"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="政策名称" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.name"
|
||||
type="text"
|
||||
placeholder="请输入政策名称"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
<QueryBarItem label="政策级别" :label-width="80">
|
||||
<NInput
|
||||
v-model:value="queryItems.level"
|
||||
type="text"
|
||||
placeholder="请输入政策级别"
|
||||
@keydown.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
:show-footer="true"
|
||||
@save="handleSave"
|
||||
>
|
||||
<NForm
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:rules="modalRules"
|
||||
>
|
||||
<NFormItem label="政策代码" path="code">
|
||||
<NInput v-model:value="modalForm.code" placeholder="请输入政策代码" />
|
||||
</NFormItem>
|
||||
<NFormItem label="政策名称" path="name">
|
||||
<NInput v-model:value="modalForm.name" placeholder="请输入政策名称" />
|
||||
</NFormItem>
|
||||
<NFormItem label="政策级别" path="level">
|
||||
<NInput v-model:value="modalForm.level" placeholder="请输入政策级别(如:national, provincial, municipal, county)" />
|
||||
</NFormItem>
|
||||
<NFormItem label="政策评分" path="score">
|
||||
<NInputNumber v-model:value="modalForm.score" placeholder="请输入政策评分" :min="0" :max="100" :precision="0" />
|
||||
</NFormItem>
|
||||
</NForm>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
Loading…
x
Reference in New Issue
Block a user