diff --git a/backend/app/api/v1/admin/admin.py b/backend/app/api/v1/admin/admin.py index db69142..edf6780 100644 --- a/backend/app/api/v1/admin/admin.py +++ b/backend/app/api/v1/admin/admin.py @@ -42,11 +42,12 @@ async def set_db_conn(data: DbInfo, request: Request): @router.post("/dutyLogs/search", summary="查询操作日志") async def search_operation_logs( data: dict, - depart: str = Query("glb", description="部门"), + area: str = Query("glb", description="区域"), + metaType: str = Query("tool", description="工具类型"), page: int = Query(1, description="页码"), pageSize: int = Query(10, description="每页数量"), ): - q = Q(depart__contains=depart) + q = Q(Q(area__contains=area), Q(type__contains=metaType)) status = data.get("status") operatingTime: list = data.get("operatingTime") if status: diff --git a/backend/app/api/v1/base/base.py b/backend/app/api/v1/base/base.py index a7f4b87..a8b95a7 100644 --- a/backend/app/api/v1/base/base.py +++ b/backend/app/api/v1/base/base.py @@ -65,6 +65,8 @@ async def login_access_token(credentials: CredentialsSchema): async def refresh_token(refreshToken: refreshTokenSchema): try: payload = decode_access_token(refreshToken.refreshToken) + if payload.exp.timestamp() < datetime.now().timestamp(): + raise ExpiredSignatureError except ExpiredSignatureError: return FailAuth(msg="refreshToken已过期") access_token_expires = timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES) @@ -130,31 +132,43 @@ async def get_user_menu(): parent_menu_dict["meta"]["showLink"] = parent_menu_dict["showLink"] parent_menu_dict["meta"]["rank"] = parent_menu_dict["rank"] - for menu in menus: - if menu.parentId == parent_menu.id: - # roles = await menu.roles.all().values_list("code", flat=True) - children_menu = await menu.to_dict() - children_menu["meta"] = {} - children_menu["meta"]["title"] = children_menu["title"] - children_menu["meta"]["icon"] = children_menu["icon"] - children_menu["meta"]["extraIcon"] = children_menu["extraIcon"] - children_menu["meta"]["showLink"] = children_menu["showLink"] - children_menu["meta"]["showParent"] = children_menu["showParent"] - # children_menu["meta"]["roles"] = roles - children_menu["meta"]["auths"] = children_menu["auths"] - children_menu["meta"]["keepAlive"] = children_menu["keepAlive"] - children_menu["meta"]["frameSrc"] = children_menu["frameSrc"] - children_menu["meta"]["frameLoading"] = children_menu["frameLoading"] - children_menu["meta"]["frameLoading"] = children_menu["frameLoading"] - children_menu["meta"]["hiddenTag"] = children_menu["hiddenTag"] - children_menu["meta"]["dynamicLevel"] = children_menu["dynamicLevel"] - children_menu["meta"]["activePath"] = children_menu["activePath"] - children_menu["meta"]["transition"] = {} - children_menu["meta"]["transition"]["name"] = children_menu["transitionName"] - children_menu["meta"]["transition"]["enterTransition"] = children_menu["enterTransition"] - children_menu["meta"]["transition"]["leaveTransition"] = children_menu["leaveTransition"] - - parent_menu_dict["children"].append(children_menu) + async def menuTree(parent_menu_dict: dict) -> dict: + for menu in menus: + if menu.parentId == parent_menu_dict["id"]: + # roles = await menu.roles.all().values_list("code", flat=True) + children_menu = await menu.to_dict() + children_menu["children"] = [] + children_menu["meta"] = {} + children_menu["meta"]["title"] = children_menu["title"] + children_menu["meta"]["icon"] = children_menu["icon"] + children_menu["meta"]["extraIcon"] = children_menu["extraIcon"] + children_menu["meta"]["showLink"] = children_menu["showLink"] + children_menu["meta"]["showParent"] = children_menu["showParent"] + # children_menu["meta"]["roles"] = roles + children_menu["meta"]["auths"] = children_menu["auths"] + children_menu["meta"]["keepAlive"] = children_menu["keepAlive"] + children_menu["meta"]["frameSrc"] = children_menu["frameSrc"] + children_menu["meta"]["frameLoading"] = children_menu["frameLoading"] + children_menu["meta"]["frameLoading"] = children_menu["frameLoading"] + children_menu["meta"]["hiddenTag"] = children_menu["hiddenTag"] + children_menu["meta"]["dynamicLevel"] = children_menu["dynamicLevel"] + children_menu["meta"]["activePath"] = children_menu["activePath"] + children_menu["meta"]["transition"] = {} + children_menu["meta"]["transition"]["name"] = children_menu["transitionName"] + children_menu["meta"]["transition"]["enterTransition"] = children_menu["enterTransition"] + children_menu["meta"]["transition"]["leaveTransition"] = children_menu["leaveTransition"] + + parent_menu_dict["children"].append(children_menu) + + parent_menu_dict["children"].sort(key=lambda x: x["rank"]) + if len(parent_menu_dict["children"]) == 0: + del parent_menu_dict["children"] + return parent_menu_dict + for i, v in enumerate(parent_menu_dict["children"]): + parent_menu_dict["children"][i] = await menuTree(v) + return parent_menu_dict + + parent_menu_dict = await menuTree(parent_menu_dict) res.append(parent_menu_dict) return Success(data=res) diff --git a/backend/app/api/v1/home/home.py b/backend/app/api/v1/home/home.py index 19b3439..e0738a9 100644 --- a/backend/app/api/v1/home/home.py +++ b/backend/app/api/v1/home/home.py @@ -27,11 +27,11 @@ async def get_home_list( returnStatus: Union[bool, None] = Query(None, description="归还批准状态") ): if returnStatus is None: - q = Q(Q(material__depart=area), Q(borrowApproveStatus=borrowedStatus)) + q = Q(Q(material__area=area), Q(borrowApproveStatus=borrowedStatus)) else: - q = Q(Q(material__depart=area), Q(borrowApproveWhether=borrowWhether), Q(returnApproveStatus=returnStatus)) + q = Q(Q(material__area=area), Q(borrowApproveWhether=borrowWhether), Q(returnApproveStatus=returnStatus)) total, objs = await borrowedController.list(page=page, page_size=pageSize, search=q) - data = [await obj.to_dict(m2m=True) for obj in objs] + data = [await obj.to_dict(m2m=True, exclude_fields=["password"]) for obj in objs] return SuccessExtra(data=data, total=total, currentPage=page, pageSize=pageSize) @@ -45,6 +45,7 @@ async def create_borrowed(data: CreateBorrowedInfo): item["phone"] = data.phone item["userDepart"] = data.depart item["uuid"] = data.uuid + item["reason"] = data.reason obj: Borrowed = await borrowedController.create(obj_in=item) material.borrowed += obj.borrowing await material.save() diff --git a/backend/app/api/v1/material/material.py b/backend/app/api/v1/material/material.py index 8d13906..415bc47 100644 --- a/backend/app/api/v1/material/material.py +++ b/backend/app/api/v1/material/material.py @@ -19,11 +19,15 @@ @router.post("/dutyOver", summary="接班") -async def duty_over(data: DutyOverInfo): +async def duty_over( + data: DutyOverInfo, + area: str = Query("glb", description="区域"), + metaType: str = Query("", description="物资类型") +): note = await dutyNotesController.create(data.materialNote) await dutyLogController.create_all(data.materialData, note) onDutyInfo = OnDutyInfo() - await onDutyInfo.setGlbDutyInfo(data.dutyPerson, data.dutyPersonDepart, data.dutyDate) + onDutyInfo.setDutyInfo(area, metaType, data.dutyPerson, data.dutyPersonDepart) result = { "dutyPerson": data.dutyPerson, @@ -34,26 +38,27 @@ async def duty_over(data: DutyOverInfo): @router.get("/meta", summary="获取物资源数据") async def get_meta( - depart: str = Query("glb", description="物资部门"), + area: str = Query("glb", description="物资区域"), + metaType: str = Query("", description="物资类型"), page: int = Query(1, description="页码"), - page_size: int = Query(1000, description="每页数量"), + pageSize: int = Query(1000, description="每页数量"), name: str = Query("", description="物资名称"), ): - q = Q(depart__contains=depart) + q = Q(Q(area__contains=area), Q(type__contains=metaType)) if name: q &= Q(name__contains=name) - total, material_objs = await materialController.list(page=page, page_size=page_size, search=q) + total, material_objs = await materialController.list(page=page, page_size=pageSize, search=q) data = [await obj.to_dict() for obj in material_objs] - return SuccessExtra(msg="物资数据获取成功", data=data, total=total, page=page, page_size=page_size) + return SuccessExtra(msg="物资数据获取成功", data=data, total=total, page=page, pageSize=pageSize) @router.get("/all_meta", summary="获取所有物资源数据") -async def get_all_meta(depart: str = Query("glb", description="物资部门")): - q = Q() - if depart: - q &= Q(depart__contains=depart) - +async def get_all_meta( + area: str = Query("glb", description="物资区域"), + metaType: str = Query("tool", description="物资类型") +): + q = Q(Q(area__contains=area), Q(type__contains=metaType)) material_objs = await materialController.all(search=q) data = [await obj.to_dict() for obj in material_objs] return Success(data=data) @@ -62,7 +67,7 @@ async def get_all_meta(depart: str = Query("glb", description="物资部门")): @router.post("/add_meta", summary="添加或修改物资源数据") async def add_meta(data: Union[MaterialCreate, MaterialUpdate]): if hasattr(data, "id"): - result: Material = await materialController.update(data.id, data.update_dict()) + result: Material = await materialController.update(data.id, data) else: data: dict = data.model_dump() data["uuid"] = generate_uuid(data["name"]) @@ -85,24 +90,30 @@ async def delete_meta(data: dict[str, list[int]]): return Success(msg="删除成功") -@router.get("/glb_duty_info", summary="获取隔离办值班信息") -async def get_glb_duty_info(): +@router.get("/duty_info", summary="获取隔离办值班信息") +async def get_duty_info( + area: str = Query("glb", description="物资区域"), + metaType: str = Query("", description="物资类型"), +): info = OnDutyInfo() - data = await info.getGlbDutyInfo() + data = info.getDutyInfo(area, metaType) return Success(data=data) @router.get("/glb_attention", summary="获取隔离办物资注意事项") async def get_glb_attention(): - q = Q(depart__contains="glb") + q = Q(area__contains="glb") total, note_objs = await materialAttentionController.list(page=1, page_size=100, search=q) data = [await obj.to_dict() for obj in note_objs] return Success(msg="隔离办注意事项", data=data) -@router.get("/glb_latest_note", summary="获取隔离办最近一条备注") -async def get_glb_latest_note(): - q = Q(depart__contains="glb") +@router.get("/latest_note", summary="获取最近一条备注") +async def get_latest_note( + area: str = Query("glb", description="物资区域"), + metaType: str = Query("tool", description="物资类型") +): + q = Q(Q(area__contains=area), Q(type__contains=metaType)) data = await dutyNotesController.latest(search=q) if data: data = await data.to_dict() @@ -117,7 +128,7 @@ async def get_fk_list( page_size: int = Query(1000, description="每页数量"), name: str = Query("", description="物资名称") ): - q = Q(depart__contains="fk") + q = Q(area__contains="fk") if name: q &= Q(name__contains=name) @@ -132,7 +143,7 @@ async def get_fk_list( page_size: int = Query(1000, description="每页数量"), name: str = Query("", description="物资名称") ): - q = Q(depart__contains="wk") + q = Q(area__contains="wk") if name: q &= Q(name__contains=name) @@ -143,7 +154,7 @@ async def get_fk_list( @router.get("/duty_over_list/list", summary="获取接班清单") async def get_duty_over_list(area: str = Query("glb", description="部门")): - q = Q(depart__contains=area) + q = Q(area__contains=area) total, duty_over_list_objs = await dutyOverListController.list(page=1, page_size=1000, search=q) data = [await obj.to_dict() for obj in duty_over_list_objs] return SuccessExtra(msg="接班清单获取成功", data=data, total=total, page=1, pageSize=1000) @@ -155,7 +166,7 @@ async def update_duty_over_list(data: list[dict], area: str = Query("glb", descr if item.get("id"): await dutyOverListController.update(item["id"], item) else: - item["depart"] = area + item["area"] = area await dutyOverListController.create(item) return Success(msg="更新成功") diff --git a/backend/app/api/v1/menus/menus.py b/backend/app/api/v1/menus/menus.py index 89de7a1..d127267 100644 --- a/backend/app/api/v1/menus/menus.py +++ b/backend/app/api/v1/menus/menus.py @@ -13,10 +13,10 @@ @router.get("/tree", summary="查看菜单树") async def menu_tree(): - parent_menus = await menu_controller.model.filter(parent_id=0).order_by("order") + parent_menus = await menu_controller.model.filter(parent_id=0).order_by("rank") res_menu = [] for menu in parent_menus: - child_menu = await menu_controller.model.filter(parent_id=menu.id).order_by("order") + child_menu = await menu_controller.model.filter(parent_id=menu.id).order_by("rank") menu_dict = await menu.to_dict() menu_dict["children"] = [await obj.to_dict() for obj in child_menu] res_menu.append(menu_dict) diff --git a/backend/app/controllers/area.py b/backend/app/controllers/area.py new file mode 100644 index 0000000..0e87a15 --- /dev/null +++ b/backend/app/controllers/area.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# @FileName :area.py +# @Time :2024/6/9 上午9:53 +# @Author :dayezi +from app.core.crud import CRUDBase + +from app.models import MaterialArea +from app.schemas.area import MaterialAreaCreate, MaterialAreaUpdate + + +class MaterialAreaController(CRUDBase[MaterialArea, MaterialAreaCreate, MaterialAreaUpdate]): + def __init__(self): + super().__init__(model=MaterialArea) + + +areaController = MaterialAreaController() diff --git a/backend/app/controllers/materialType.py b/backend/app/controllers/materialType.py new file mode 100644 index 0000000..cd38f7f --- /dev/null +++ b/backend/app/controllers/materialType.py @@ -0,0 +1,16 @@ +# coding=utf-8 +# @FileName :materialType.py +# @Time :2024/6/7 下午3:30 +# @Author :dayezi +from app.core.crud import CRUDBase + +from app.models import MaterialType +from app.schemas.materialType import MaterialTypeCreate, MaterialTypeUpdate + + +class MaterialTypeController(CRUDBase[MaterialType, MaterialTypeCreate, MaterialTypeUpdate]): + def __init__(self): + super().__init__(model=MaterialType) + + +materialTypeController = MaterialTypeController() diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index da7c5ed..441cadf 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -3,3 +3,5 @@ from .material import * from .dutyLog import * from .borrowed import * +from .materialType import * +from .area import * diff --git a/backend/app/models/area.py b/backend/app/models/area.py new file mode 100644 index 0000000..9d452ae --- /dev/null +++ b/backend/app/models/area.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# @FileName :area.py +# @Time :2024/6/7 下午3:25 +# @Author :dayezi +from tortoise import fields + +from .base import BaseModel, TimestampMixin, UUIDModel + + +class MaterialArea(BaseModel, TimestampMixin, UUIDModel): + key = fields.CharField(max_length=20, description="物资区域关键字") + name = fields.CharField(max_length=50, description="物资区域名称") + + class Meta: + table = "material_area" + + class PydanticMeta: + exclude = "id" diff --git a/backend/app/models/base.py b/backend/app/models/base.py index b9f6512..3be5492 100644 --- a/backend/app/models/base.py +++ b/backend/app/models/base.py @@ -33,6 +33,7 @@ async def to_dict(self, m2m: bool = False, exclude_fields: list[str] | None = No if isinstance(v, datetime) ) value.update((k, str(v)) for k, v in value.items() if isinstance(v, UUID)) + value.pop("password", "xxx") # 删除用户模型中的密码字段 d[field] = values return d diff --git a/backend/app/models/borrowed.py b/backend/app/models/borrowed.py index 92c36b7..c8dbf9c 100644 --- a/backend/app/models/borrowed.py +++ b/backend/app/models/borrowed.py @@ -13,6 +13,7 @@ class Borrowed(BaseModel, UUIDModel): uuid = fields.UUIDField(pk=False, description="借用人uuid") phone = fields.CharField(max_length=20, description="借用人手机号") userDepart = fields.CharField(max_length=20, description="借用人部门") + reason = fields.CharField(max_length=200, description="借用原因") borrowTime = fields.DatetimeField(auto_now_add=True, description="借用时间") borrowApproveStatus = fields.BooleanField(default=False, description="借用审批状态,False为未审批,True未已审批") borrowApproveWhether = fields.BooleanField(null=True, description="借用通过状态,False为驳回,True为通过") diff --git a/backend/app/models/dutyLog.py b/backend/app/models/dutyLog.py index 436e5a1..8ab7fdc 100644 --- a/backend/app/models/dutyLog.py +++ b/backend/app/models/dutyLog.py @@ -12,14 +12,15 @@ class DutyLog(BaseModel): """ 值班日志 """ - position = fields.CharField(max_length=20, null=False, description="位置") + type = fields.CharField(max_length=20, description="物资类型") + area = fields.CharField(max_length=20, null=False, description="物资所在区域") name = fields.CharField(max_length=50, null=False, description="名称") + position = fields.CharField(max_length=20, null=False, description="位置") model = fields.CharField(max_length=20, null=False, description="型号") number = fields.IntField(max_length=20, null=False, description="数量") - nowNumber = fields.IntField(max_length=20,null=False, description="当前数量") + nowNumber = fields.IntField(max_length=20, null=False, description="当前数量") dutyPerson = fields.CharField(max_length=20, null=False, description="当班人员") dutyPersonDepart = fields.CharField(max_length=20, null=False, description="当班人员部门") - depart = fields.CharField(max_length=20, null=False, description="部门") dutyDate = fields.DatetimeField(auto_now_add=True, description="交班时间") dutyNote: fields.ForeignKeyRelation["DutyNotes"] = fields.ForeignKeyField("models.DutyNotes", related_name="duty_log", null=True) @@ -28,13 +29,14 @@ class Meta: table = "dutyLogs" class PydanticMeta: - exclude = ("id",) + exclude = ("id", "dutyDate") class DutyNotes(BaseModel): """值班日志备注""" note = fields.CharField(max_length=510, null=False, description="备注") - depart = fields.CharField(max_length=255, null=False, description="部门") + type = fields.CharField(max_length=20, null=False, description="类型") + area = fields.CharField(max_length=20, null=False, description="区域") dutyDate = fields.DatetimeField(auto_now_add=True, description="交班时间") dutyLog: fields.ReverseRelation["DutyLog"] @@ -42,6 +44,6 @@ class Meta: table = "dutyNotes" class PydanticMeta: - exclude = ("id",) + exclude = ("id", "dutyDate") diff --git a/backend/app/models/material.py b/backend/app/models/material.py index c1b33ed..c3d6a77 100644 --- a/backend/app/models/material.py +++ b/backend/app/models/material.py @@ -1,16 +1,21 @@ from tortoise import fields from .base import BaseModel, TimestampMixin, UUIDModel +from .borrowed import Borrowed class Material(BaseModel, TimestampMixin, UUIDModel): + type = fields.CharField(max_length=20, description="物资类型") + area = fields.CharField(max_length=20, description="物资所属区域") name = fields.CharField(max_length=50, unique=True, description="物资名字") model = fields.CharField(max_length=20, null=True, description="物资型号") position = fields.CharField(max_length=50, description="物资位置") - number = fields.IntField(description="物资原数量") + number = fields.IntField(description="物资库存数量") checking = fields.IntField(default=0, description="物资送检数量") borrowed = fields.IntField(default=0, description="物资外借数量") - depart = fields.CharField(max_length=20, description="物资所属部门") + + # 物资被借的信息 + borrowedInfo: fields.ManyToManyRelation["Borrowed"] class Meta: table = "material" @@ -21,7 +26,7 @@ class PydanticMeta: class AttentionNote(BaseModel, TimestampMixin, UUIDModel): note = fields.CharField(max_length=255, description="事项") - depart = fields.CharField(max_length=20, description="所属部门") + area = fields.CharField(max_length=20, description="所属区域") class Meta: table = "attention_note" @@ -31,7 +36,7 @@ class PydanticMeta: class DutyOverList(BaseModel, TimestampMixin): - depart = fields.CharField(max_length=20, description="部门") + area = fields.CharField(max_length=20, description="部门") content = fields.CharField(max_length=255, description="交接班清单内容") class Meta: diff --git a/backend/app/models/materialType.py b/backend/app/models/materialType.py new file mode 100644 index 0000000..72b4c8e --- /dev/null +++ b/backend/app/models/materialType.py @@ -0,0 +1,19 @@ +# coding=utf-8 +# @FileName :materialType.py +# @Time :2024/6/7 下午3:24 +# @Author :dayezi +from tortoise import fields + +from .base import BaseModel, TimestampMixin, UUIDModel + + +class MaterialType(BaseModel, TimestampMixin, UUIDModel): + key = fields.CharField(max_length=20, description="物资类型关键字") + name = fields.CharField(max_length=50, description="物资类型名称") + + class Meta: + table = "material_type" + + class PydanticMeta: + exclude = "id" + diff --git a/backend/app/models/users.py b/backend/app/models/users.py index 9ff15c4..952dbc0 100644 --- a/backend/app/models/users.py +++ b/backend/app/models/users.py @@ -2,6 +2,7 @@ from .base import BaseModel, TimestampMixin, UUIDModel from .enums import MethodType +from .borrowed import Borrowed class User(BaseModel, TimestampMixin, UUIDModel): @@ -23,6 +24,7 @@ class User(BaseModel, TimestampMixin, UUIDModel): related_name="user_depart", null=True ) + borrowed: fields.ManyToManyRelation["Borrowed"] class Meta: table = "user" diff --git a/backend/app/schemas/area.py b/backend/app/schemas/area.py new file mode 100644 index 0000000..2f708ff --- /dev/null +++ b/backend/app/schemas/area.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# @FileName :area.py +# @Time :2024/6/9 上午9:52 +# @Author :dayezi +from tortoise.contrib.pydantic import pydantic_model_creator + +from app.models import MaterialArea + + +MaterialAreaSchema = pydantic_model_creator(MaterialArea) + + +class MaterialAreaCreate(MaterialAreaSchema): + ... + + +class MaterialAreaUpdate(MaterialAreaSchema): + id: int diff --git a/backend/app/schemas/borrowed.py b/backend/app/schemas/borrowed.py index eaa11fe..01b8035 100644 --- a/backend/app/schemas/borrowed.py +++ b/backend/app/schemas/borrowed.py @@ -34,6 +34,7 @@ class CreateBorrowedInfo(BaseModel): username: str phone: str depart: str + reason: str baseData: list[MaterialBorrowed] diff --git a/backend/app/schemas/dutyLog.py b/backend/app/schemas/dutyLog.py index fa31835..96a10f6 100644 --- a/backend/app/schemas/dutyLog.py +++ b/backend/app/schemas/dutyLog.py @@ -29,7 +29,6 @@ class DutyNoteUpdate(DutyNotesPydantic): class DutyOverInfo(BaseModel): materialData: list[DutyLogCreate] materialNote: DutyNoteCreate - dutyDate: str dutyPerson: str dutyPersonDepart: str diff --git a/backend/app/schemas/materialType.py b/backend/app/schemas/materialType.py new file mode 100644 index 0000000..a21fb93 --- /dev/null +++ b/backend/app/schemas/materialType.py @@ -0,0 +1,19 @@ +# coding=utf-8 +# @FileName :materialType.py +# @Time :2024/6/7 下午3:27 +# @Author :dayezi +from tortoise.contrib.pydantic import pydantic_model_creator + +from app.models import MaterialType + + +MaterialTypeSchema = pydantic_model_creator(MaterialType) + + +class MaterialTypeCreate(MaterialTypeSchema): + ... + + +class MaterialTypeUpdate(MaterialTypeSchema): + id: int + diff --git a/backend/app/utils/dutyInfo.ini b/backend/app/utils/dutyInfo.ini index 8e82d5a..f43ff42 100644 --- a/backend/app/utils/dutyInfo.ini +++ b/backend/app/utils/dutyInfo.ini @@ -1,14 +1,29 @@ -[glb] +[glb.tool] dutyperson = test -dutypersondepart = eee -takeovertime = 2024-05-07 17:15:51 +dutypersondepart = aaaqqqwwweee +takeovertime = 2024-06-19 18:25:17 -[wk] +[glb.key] +dutyperson = admin +dutypersondepart = aaaqqqwwweee +takeovertime = 2024-06-19 14:27:16 + +[wk.tool] +dutyperson = admin +dutypersondepart = adminDepart +takeovertime = 2020-01-01 00:00:00 + +[wk.key] +dutyperson = admin +dutypersondepart = adminDepart +takeovertime = 2020-01-01 00:00:00 + +[fk.tool] dutyperson = admin dutypersondepart = adminDepart takeovertime = 2020-01-01 00:00:00 -[fk] +[fk.key] dutyperson = admin dutypersondepart = adminDepart takeovertime = 2020-01-01 00:00:00 diff --git a/backend/app/utils/onDutyInfo.py b/backend/app/utils/onDutyInfo.py index d953b11..6f81b8d 100644 --- a/backend/app/utils/onDutyInfo.py +++ b/backend/app/utils/onDutyInfo.py @@ -5,6 +5,8 @@ from configparser import ConfigParser from pathlib import Path +from app.utils import now + path = Path.joinpath(Path(__file__).parent, "dutyInfo.ini") @@ -13,17 +15,16 @@ def __init__(self): self.conf = ConfigParser() self.conf.read(filenames=path, encoding='utf-8') - async def getGlbDutyInfo(self) -> dict[str, str]: + def getDutyInfo(self, area: str, metaType: str) -> dict[str, str]: return { - "dutyPerson": self.conf["glb"]["dutyPerson"], - "dutyPersonDepart": self.conf["glb"]["dutyPersonDepart"], + "dutyPerson": self.conf[f"{area}.{metaType}"]["dutyPerson"], + "dutyPersonDepart": self.conf[f"{area}.{metaType}"]["dutyPersonDepart"], } - async def setGlbDutyInfo(self, dutyPerson: str, dutyPersonDepart: str, takeoverTime: str) -> None: - self.conf["glb"]["dutyPerson"] = dutyPerson - self.conf["glb"]["dutyPersonDepart"] = dutyPersonDepart - self.conf["glb"]["takeoverTime"] = takeoverTime + def setDutyInfo(self, area: str, metaType: str, dutyPerson: str, dutyPersonDepart: str) -> None: + self.conf[f"{area}.{metaType}"]["dutyPerson"] = dutyPerson + self.conf[f"{area}.{metaType}"]["dutyPersonDepart"] = dutyPersonDepart + self.conf[f"{area}.{metaType}"]["takeoverTime"] = now() with open(path, "w", encoding="utf-8") as f: self.conf.write(f) - diff --git a/backend/poetry.lock b/backend/poetry.lock index 6e399ab..934203c 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,5 +1,28 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +[[package]] +name = "aiomysql" +version = "0.2.0" +description = "MySQL driver for asyncio." +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiomysql-0.2.0-py3-none-any.whl", hash = "sha256:b7c26da0daf23a5ec5e0b133c03d20657276e4eae9b73e040b72787f6f6ade0a"}, + {file = "aiomysql-0.2.0.tar.gz", hash = "sha256:558b9c26d580d08b8c5fd1be23c5231ce3aeff2dadad989540fee740253deb67"}, +] + +[package.dependencies] +PyMySQL = ">=1.0" + +[package.extras] +rsa = ["PyMySQL[rsa] (>=1.0)"] +sa = ["sqlalchemy (>=1.3,<1.4)"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "aiosqlite" version = "0.17.0" @@ -1179,4 +1202,4 @@ reference = "tsinghua" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "8ac865e616da791d1533d4827fa849e282747b7d3e61286ab22bc35e3e912285" +content-hash = "a7251f841f098cbaef76ae230a800e55942908a2a55a3a718434fe45db6f2d33" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 4480629..5733473 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -26,6 +26,7 @@ cryptography = "^42.0.5" ruamel-yaml = "^0.18.6" pymysql = "^1.1.0" python-multipart = "^0.0.9" +aiomysql = "^0.2.0" [tool.black] line-length = 120 diff --git a/frontend/build/info.ts b/frontend/build/info.ts index 2ca6aee..4843e79 100644 --- a/frontend/build/info.ts +++ b/frontend/build/info.ts @@ -7,7 +7,7 @@ import boxen, { type Options as BoxenOptions } from "boxen"; dayjs.extend(duration); const welcomeMessage = gradientString("cyan", "magenta").multiline( - `您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://yiming_chang.gitee.io/pure-admin-doc\nhttps://pure-admin-utils.netlify.app` + `您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://pure-admin.github.io/pure-admin-doc/\nhttps://pure-admin-utils.netlify.app` ); const boxenOptions: BoxenOptions = { diff --git a/frontend/src/api/admin.ts b/frontend/src/api/admin.ts index 7bfec57..0e7ebdc 100644 --- a/frontend/src/api/admin.ts +++ b/frontend/src/api/admin.ts @@ -35,7 +35,8 @@ export const deleteDutyOverList = (id: string) => { }; export const searchDutyLogs = ( - depart: string, + area: string, + metaType: string, page: number, pageSize: number, data?: object @@ -46,7 +47,8 @@ export const searchDutyLogs = ( { data, params: { - depart, + area, + metaType, page, pageSize } diff --git a/frontend/src/api/material.ts b/frontend/src/api/material.ts index 153c9ff..f823608 100644 --- a/frontend/src/api/material.ts +++ b/frontend/src/api/material.ts @@ -9,23 +9,26 @@ import type { import type { addResult, MaterialResult } from "@/types/admin"; export const getMaterialMeta = async ( - depart: string, + area: string, + metaType: string, page?: number, - page_size?: number + pageSize?: number ) => { return http.request("get", baseUrlApi("/material/meta"), { params: { - depart, + area, + metaType, page, - page_size + pageSize } }); }; -export const getAllMaterialMeta = async (depart: string) => { +export const getAllMaterialMeta = async (area: string, metaType: string) => { return http.request("get", baseUrlApi("/material/all_meta"), { params: { - depart + area, + metaType } }); }; @@ -51,26 +54,33 @@ export const getGlbAttentionList = () => { ); }; -export const getGlbDutyInfo = () => { +export const getDutyInfo = (area: string, metaType: string) => { return http.request( "get", - baseUrlApi("/material/glb_duty_info") + baseUrlApi("/material/duty_info"), + { params: { area, metaType } } ); }; -export const getLatestNote = () => { - return http.request( - "get", - baseUrlApi("/material/glb_latest_note") - ); +export const getLatestNote = (area: string, metaType: string) => { + return http.request("get", baseUrlApi("/material/latest_note"), { + params: { + area, + metaType + } + }); }; -export const dutyOver = (data?: object) => { +export const dutyOver = (area: string, metaType: string, data?: object) => { return http.request( "post", baseUrlApi("/material/dutyOver"), { - data + data, + params: { + area, + metaType + } } ); }; diff --git a/frontend/src/store/modules/metaArea.ts b/frontend/src/store/modules/metaArea.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/store/modules/metaType.ts b/frontend/src/store/modules/metaType.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/types/admin.ts b/frontend/src/types/admin.ts index d7b9130..a3076e6 100644 --- a/frontend/src/types/admin.ts +++ b/frontend/src/types/admin.ts @@ -1,4 +1,4 @@ -import type { BaseResult, MaterialItem } from "@/types/base"; +import type { BaseResult, MaterialItem, User } from "@/types/base"; export type addResult = BaseResult & { data: MaterialItem; @@ -9,3 +9,22 @@ export type MaterialResult = BaseResult & { total: number; data: [MaterialItem]; }; + +export type BorrowedInfo = { + id: number; + uuid: string; + username: string; + userDepart: string; + phone: string; + reason: string; + material: [MaterialItem]; + borrowing: number; + borrowTime: string; + borrowApproveStatus: boolean; + borrowApproveTime: string; + borrowApproveUser: [User]; + borrowApproveWhether: boolean; + returnApproveStatus: boolean; + returnApproveTime: string; + returnApproveUser: [User]; +}; diff --git a/frontend/src/types/base.ts b/frontend/src/types/base.ts index 40bcf24..a78383a 100644 --- a/frontend/src/types/base.ts +++ b/frontend/src/types/base.ts @@ -7,12 +7,32 @@ export type MaterialItem = { uuid?: string; name: string; model: string; - depart?: string; + type: string; + area: string; position: string; number: number; + nowNumber?: number; checking?: number; borrowing?: number; borrowed?: number; created_at?: string; updated_at?: string; }; + +export type User = { + id: number; + uuid: string; + username: string; + nickname: string; + email: string; + phone: string; + avatar?: string; + depart_id: number; + is_superuser: boolean; + remark: string; + sex: number; + status: number; + created_at: string; + updated_at: string; + last_login: string; +}; diff --git a/frontend/src/types/material.ts b/frontend/src/types/material.ts index 9dea29b..4b0b604 100644 --- a/frontend/src/types/material.ts +++ b/frontend/src/types/material.ts @@ -3,7 +3,8 @@ import type { BaseResult } from "@/types/base"; export type LatestNote = BaseResult & { data: { note: string; - depart: string; + area: string; + type: string; dutyDate: string; }; }; diff --git a/frontend/src/utils/http/index.ts b/frontend/src/utils/http/index.ts index 5cf4226..5eba358 100644 --- a/frontend/src/utils/http/index.ts +++ b/frontend/src/utils/http/index.ts @@ -13,7 +13,7 @@ import { stringify } from "qs"; import NProgress from "../progress"; import { getToken, formatToken, removeToken } from "@/utils/auth"; import { useUserStoreHook } from "@/store/modules/user"; -import {baseUrlApi, staticUrl} from "@/api/utils"; +import { baseUrlApi, staticUrl } from "@/api/utils"; import { router, resetRouter } from "@/router"; import { message } from "@/utils/message"; import { errorNotification } from "@/utils/notification"; @@ -39,6 +39,9 @@ class PureHttp { this.httpInterceptorsResponse(); } + /** 重复请求次数 */ + private static requestCount = 5; + /** token过期后,暂存待执行的请求 */ private static requests = []; @@ -142,15 +145,28 @@ class PureHttp { } return response.data; }, - (error: PureHttpError) => { + async (error: PureHttpError) => { console.log(error); const $error = error; $error.isCancelRequest = Axios.isCancel($error); // 关闭进度条动画 NProgress.done(); // 所有的响应异常 区分来源为取消请求/非取消请求 - if (error.response == null) { - errorNotification("服务器超时故障,请重新刷新~"); + if (error.response == null || error.response.status == 500) { + const config = error.config; + // 判断是否重复发起请求 + if (Boolean(PureHttp.requestCount)) { + PureHttp.requestCount--; + console.log(`第${5 - PureHttp.requestCount}次重试`); + // 延时发起请求 + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + // 重新发起请求 + return instance(config); + } else { + errorNotification("服务器异常,请刷新~"); + } } if (error.response.status == 403) { resetRouter(); diff --git a/frontend/src/views/admin/Approval.vue b/frontend/src/views/admin/Approval.vue index 02532e0..eb4d10a 100644 --- a/frontend/src/views/admin/Approval.vue +++ b/frontend/src/views/admin/Approval.vue @@ -113,9 +113,28 @@ const columns: TableColumnList = [ prop: "phone" }, { - label: "借用数量", + label: "数量", prop: "borrowing" }, + { + label: "借用理由", + prop: "reason", + cellRenderer: ({ row }) => ( + ( + + {row.reason} + + ), + default: () =>

{row.reason}

+ }} + >
+ ) + }, { label: "借用时间", prop: "borrowTime" @@ -317,6 +336,25 @@ const returnFormColumns: TableColumnList = [ label: "借用数量", prop: "borrowing" }, + { + label: "借用理由", + prop: "reason", + cellRenderer: ({ row }) => ( + ( + + {row.reason} + + ), + default: () =>

{row.reason}

+ }} + >
+ ) + }, { label: "借用时间", prop: "borrowTime" diff --git a/frontend/src/views/admin/MaterialMeta.vue b/frontend/src/views/admin/MaterialMeta.vue index 121377c..5d7715d 100644 --- a/frontend/src/views/admin/MaterialMeta.vue +++ b/frontend/src/views/admin/MaterialMeta.vue @@ -1,5 +1,5 @@ - + diff --git a/frontend/src/views/glb/material.vue b/frontend/src/views/glb/material.vue index eba3a84..8fb1e2b 100644 --- a/frontend/src/views/glb/material.vue +++ b/frontend/src/views/glb/material.vue @@ -2,15 +2,15 @@ import { ref, reactive, computed, onMounted } from "vue"; import { getMaterialMeta, - getGlbAttentionList, - getGlbDutyInfo, dutyOver, - getLatestNote + getLatestNote, + getDutyInfo } from "@/api/material"; import { useUserStoreHook } from "@/store/modules/user"; import { successNotification, errorNotification } from "@/utils/notification"; -import formatCurrentTime from "@/utils/formatDatetime"; import { getDutyOverList } from "@/api/admin"; +import PureTable from "@pureadmin/table"; +import type { MaterialItem } from "@/types/base"; defineOptions({ name: "GlbMaterial" @@ -24,22 +24,8 @@ enum StepStatus { Warning = "warning", Error = "error" } -interface tableDataRow { - id?: number; - uuid?: string; - name: string; - model: string; - position: string; - number: string; - nowNumber?: number; - created_at?: string; - updated_at?: string; - dutyPerson?: string; - dutyPersonDepart?: string; - depart?: string; - dutyDate?: string; -} -interface tableData extends Array {} + +interface tableData extends Array {} const columns: TableColumnList = [ { label: "序号", type: "index", width: "60" }, @@ -47,18 +33,14 @@ const columns: TableColumnList = [ { label: "名称", prop: "name" }, { label: "型号", prop: "model", width: "200" }, { label: "数量", prop: "number", width: "100" }, + { label: "外借数量", prop: "borrowed", width: "100" }, + { label: "送检数量", prop: "checking", width: "100" }, { label: "当前数量", width: "150", prop: "nowNumber", cellRenderer: ({ row }) => ( - <> - - + ) }, { @@ -66,16 +48,14 @@ const columns: TableColumnList = [ prop: "confirm", width: "100", cellRenderer: ({ row }) => ( - <> - handleConfirm(row)} - plain - > - 确认 - - + handleConfirm(row)} + plain + > + 确认 + ) } ]; @@ -86,7 +66,7 @@ onMounted(() => { const initGlb = () => { loading.value = true; - getMaterialMeta("glb") + getMaterialMeta("glb", "tool") .then(res => { confirmedData.length = 0; confirmedData.push(...res.data); @@ -98,10 +78,10 @@ const initGlb = () => { attention.length = 0; attention.push(...res.data); }); - getLatestNote().then(res => { + getLatestNote("glb", "tool").then(res => { lastRemark.value = res.data.note; }); - getGlbDutyInfo().then(res => { + getDutyInfo("glb", "tool").then(res => { dutyPerson.value = res.data.dutyPerson; dutyPersonDepart.value = res.data.dutyPersonDepart; }); @@ -117,19 +97,26 @@ const confirmedData: tableData = reactive([]); const step1Init = ref(true); const step1Status = computed(() => { - return confirmedData.every(item => item.nowNumber === Number(item.number)) + return confirmedData.every( + item => item.nowNumber === item.number - item.borrowed - item.checking + ) ? StepStatus.Success : StepStatus.Error; }); const handleConfirm = row => { - row.nowNumber = Number(row.number); + row.nowNumber = Number(row.number) - row.borrowed - row.checking; step1Init.value = false; }; +const confirmBtnStatus = (row): "success" | "warning" => { + return row.nowNumber == row.number - row.borrowed - row.checking + ? "success" + : "warning"; +}; const handleConfirmAll = () => { // 确认所有 confirmedData.forEach(row => { - row.nowNumber = Number(row.number); + row.nowNumber = row.number - row.borrowed - row.checking; step1Init.value = false; }); }; @@ -179,9 +166,8 @@ const handoverConfirm = () => { }; const handover = () => { - const date = formatCurrentTime(); let data = { - materialData: confirmedData.map((data: tableDataRow) => { + materialData: confirmedData.map((data: MaterialItem) => { return { name: data.name, model: data.model, @@ -190,21 +176,20 @@ const handover = () => { nowNumber: data.nowNumber, dutyPerson: useUserStoreHook()?.username, dutyPersonDepart: useUserStoreHook()?.depart, - depart: "glb", - dutyDate: date + area: "glb", + type: "tool" }; }), materialNote: { note: remark.value, - depart: "glb", - dutyDate: date + area: "glb", + type: "tool" }, - dutyDate: date, dutyPerson: useUserStoreHook()?.username, dutyPersonDepart: useUserStoreHook()?.depart }; handleOverBtnLoading.value = true; - dutyOver(data) + dutyOver("glb", "tool", data) .then(res => { initGlb(); handleOverBtnLoading.value = false; @@ -219,8 +204,8 @@ const handover = () => {