update. 完成前端页面展示、列表和详情页

update. 调整融合主菜单与板块
update. 分离新闻模块
This commit is contained in:
ngfchl
2026-04-01 14:17:03 +08:00
parent af9d7a676f
commit 777ca50328
55 changed files with 2190 additions and 567 deletions
-48
View File
@@ -1,48 +0,0 @@
from typing import Optional, TypeVar, Generic
from ninja import Schema
T = TypeVar('T')
class CommonResponse(Schema, Generic[T]):
code: int
msg: Optional[str]
data: Optional[T] = None
succeed: bool = True
"""
统一的json返回格式
"""
def __init__(self, code: int = 0, data: object = None, msg: str = '', succeed: bool = True):
super().__init__(code=code, data=data, msg=msg, succeed=succeed)
@classmethod
def success(cls, code=0, data=None, msg=''):
return cls(code, data, msg, succeed=True)
@classmethod
def error(cls, code=-1, data=None, msg=''):
return cls(code, data, msg, succeed=False)
def to_dict(self):
return {
"code": self.code,
"msg": self.msg,
"data": self.data,
"succeed": self.succeed,
}
class DotDict(dict):
"""实现支持 "." 表示法的字典类"""
def __getattr__(self, attr):
try:
return self[attr]
except Exception:
return None
def __setattr__(self, attr, value):
self[attr] = value
+95
View File
@@ -0,0 +1,95 @@
import os
import hashlib
from datetime import datetime
def _get_file_md5(file_obj):
"""
通用辅助函数:计算任意文件对象的 MD5
"""
md5 = hashlib.md5()
# 关键步骤:重置文件指针到开头
# 因为 Django 在处理上传时可能已经读取了一部分文件头
try:
file_obj.seek(0)
except Exception:
pass
# 分块读取计算,防止大文件撑爆内存
for chunk in file_obj.chunks():
md5.update(chunk)
# 计算完后再次重置指针,确保不影响 Django 后续的保存操作
try:
file_obj.seek(0)
except Exception:
pass
return md5.hexdigest()
def upload_image_and_rename(instance, filename):
"""
通用图片上传处理函数
逻辑:存入 images/年/月/日/md5.ext
"""
# 1. 获取后缀
ext = os.path.splitext(filename)[1].lower()
# 2. 生成日期路径
date_str = datetime.now().strftime('%Y/%m/%d')
# 3. 计算 MD5
# 这里直接利用传入的 filename 对应的文件对象(在内存或临时文件中)
# 注意:这里假设 filename 是有效的 UploadedFile 对象名称,
# 但最稳妥的方式其实是直接操作 file_obj。
# 由于 upload_to 签名限制,我们通常通过 instance 获取字段值,
# 但为了通用性,我们尝试从 instance 的 __dict__ 中寻找 FileField 对象。
file_obj = None
# 遍历 instance 的所有属性,找到第一个 FileField 类型的值
for field in instance._meta.get_fields():
if hasattr(field, 'upload_to') and hasattr(instance, field.name):
val = getattr(instance, field.name)
if val and hasattr(val, 'chunks'): # 确认是文件对象
file_obj = val
break
# 如果找不到文件对象(极少见情况),使用随机字符串防止报错
if not file_obj:
import uuid
new_filename = f"{uuid.uuid4().hex}{ext}"
else:
md5_hash = _get_file_md5(file_obj)
new_filename = f"{md5_hash}{ext}"
# 4. 返回路径
return os.path.join('images', date_str, new_filename)
def upload_file_and_rename(instance, filename):
"""
通用文件上传处理函数
逻辑:存入 uploads/年/月/日/md5.ext
"""
ext = os.path.splitext(filename)[1].lower()
date_str = datetime.now().strftime('%Y/%m/%d')
file_obj = None
# 同样的通用查找逻辑
for field in instance._meta.get_fields():
if hasattr(field, 'upload_to') and hasattr(instance, field.name):
val = getattr(instance, field.name)
if val and hasattr(val, 'chunks'):
file_obj = val
break
if not file_obj:
import uuid
new_filename = f"{uuid.uuid4().hex}{ext}"
else:
md5_hash = _get_file_md5(file_obj)
new_filename = f"{md5_hash}{ext}"
return os.path.join('uploads', date_str, new_filename)