diff --git a/back_end/saolei/userprofile/models.py b/back_end/saolei/userprofile/models.py index 160e13b7..53bc3357 100644 --- a/back_end/saolei/userprofile/models.py +++ b/back_end/saolei/userprofile/models.py @@ -47,7 +47,7 @@ class UserProfile(AbstractUser): },) realname = models.CharField( - max_length=10, unique=False, blank=True, default='请修改为实名', null=False) + max_length=10, unique=False, blank=True, default='匿名', null=False) # 头像 avatar = RestrictedImageField(upload_to='avatar/%Y%m%d/', max_length=100, max_upload_size=MaxSizes.avatar, blank=True, null=True) diff --git a/back_end/saolei/videomanager/views.py b/back_end/saolei/videomanager/views.py index 5177b79b..4cec20d0 100644 --- a/back_end/saolei/videomanager/views.py +++ b/back_end/saolei/videomanager/views.py @@ -6,7 +6,7 @@ from .models import VideoModel, ExpandVideoModel from .view_utils import update_personal_record, update_personal_record_stock, video_all_fields from userprofile.models import UserProfile -from django.http import HttpResponse, JsonResponse, FileResponse +from django.http import HttpResponse, JsonResponse, FileResponse, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseNotAllowed, HttpResponseNotFound import json, urllib from utils import ComplexEncoder from django.core.paginator import Paginator @@ -32,9 +32,9 @@ def video_upload(request): if request.method == 'POST': if request.user.is_banned: - return JsonResponse({"status": 101, "msg": "用户被封禁!"}) + return HttpResponseForbidden() # 用户被封禁 if request.user.userms.video_num_total >= request.user.userms.video_num_limit: - return JsonResponse({"status": 188, "msg": "用户录像仓库已满!"}) + return HttpResponse(status = 402) # 录像仓库已满 # response = {'status': 100, 'msg': None} # request.POST['file'] = request.FILES @@ -52,7 +52,7 @@ def video_upload(request): # 查重 collisions = list(VideoModel.objects.filter(timems=data["timems"], bv=data["bv"]).filter(video__cl=data["cl"], video__op=data["op"], video__isl=data["isl"], video__designator=data["designator"])) if collisions: - return JsonResponse({"status": 200, "msg": "录像已存在"}) + return HttpResponse(status = 409) # 表中添加数据 e_video = ExpandVideoModel.objects.create(designator=data["designator"], @@ -101,25 +101,20 @@ def video_upload(request): # print(review_video_ids) # update_personal_record(request, data, e_video) - return JsonResponse({"status": 100, "msg": None}) + return HttpResponse() else: # print(video_form.errors) - return JsonResponse({"status": 666, "msg": "小型网站,请勿攻击!"}) - elif request.method == 'GET': - return HttpResponse("别瞎玩") + return HttpResponseBadRequest() else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 根据id向后台请求软件类型(适配flop播放器用) def get_software(request): if request.method != 'GET': - return HttpResponse("别瞎玩") - try: - video = VideoModel.objects.get(id=request.GET["id"]) - # print({"status": 100, "msg": video.software}) - return JsonResponse({"status": 100, "msg": video.software}) - except Exception: - return JsonResponse({"status": 104, "msg": "file not exist!"}) + return HttpResponseNotAllowed() + video = VideoModel.objects.get(id=request.GET["id"]) + # print({"status": 100, "msg": video.software}) + return JsonResponse({"msg": video.software}) # 给预览用的接口,区别是结尾是文件后缀 # 坑:如果做成必须登录才能下载,由于Django的某种特性,会重定向资源, @@ -127,31 +122,27 @@ def get_software(request): @ratelimit(key='ip', rate='20/m') def video_preview(request): if request.method != 'GET': - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 这里性能可能有问题 - try: - video = VideoModel.objects.get(id=int(request.GET["id"][:-4])) - # video.file.name是相对路径(含upload_to),video.file.path是绝对路径 - # print(settings.MEDIA_ROOT / "assets" / video.file.name) - file_path = settings.MEDIA_ROOT / video.file.name - response =FileResponse(open(file_path, 'rb')) - response['Content-Type']='application/octet-stream' - # response['Content-Disposition']=f'attachment;filename="{video.file.name.split("/")[2]}"' - file_name = video.file.name.split("/")[2] - file_name_uri = urllib.parse.quote(file_name) - response['Content-Disposition'] = f'attachment; filename="{file_name_uri}"' - response['Access-Control-Expose-Headers']='Content-Disposition' - - return response - except Exception: - return JsonResponse({"status": 104, "msg": "file not exist!"}) + video = VideoModel.objects.get(id=int(request.GET["id"][:-4])) + # video.file.name是相对路径(含upload_to),video.file.path是绝对路径 + # print(settings.MEDIA_ROOT / "assets" / video.file.name) + file_path = settings.MEDIA_ROOT / video.file.name + response =FileResponse(open(file_path, 'rb')) + response['Content-Type']='application/octet-stream' + # response['Content-Disposition']=f'attachment;filename="{video.file.name.split("/")[2]}"' + file_name = video.file.name.split("/")[2] + file_name_uri = urllib.parse.quote(file_name) + response['Content-Disposition'] = f'attachment; filename="{file_name_uri}"' + response['Access-Control-Expose-Headers']='Content-Disposition' + return response # 给下载用的接口,区别是结尾没有文件后缀 # @login_required(login_url='/') @ratelimit(key='ip', rate='20/m') def video_download(request): if request.method != 'GET': - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() try: video = VideoModel.objects.get(id=request.GET["id"]) response =FileResponse(open(video.file.path, 'rb')) @@ -159,15 +150,7 @@ def video_download(request): response['Content-Disposition']=f'attachment;filename="{video.file.name.split("/")[2]}"' return response except VideoModel.DoesNotExist: - return JsonResponse({"status": 104, "msg": "录像不存在!"}) - # try: - # video = VideoModel.objects.get(id=request.GET["id"]) - # response =FileResponse(open(video.file.path, 'rb')) - # response['Content-Type']='application/octet-stream' - # response['Content-Disposition']=f'attachment;filename="{video.file.name.split("/")[2]}"' - # return response - # except Exception: - # return JsonResponse({"status": 104, "msg": "file not exist!"}) + return HttpResponseNotFound() # 录像查询(无需登录) # 按任何基础指标+难度+模式,排序,分页 @@ -207,11 +190,8 @@ def video_query(request): # t=json.dumps(response, cls=ComplexEncoder) # print(t) return JsonResponse(json.dumps(response, cls=ComplexEncoder), safe=False) - - elif request.method == 'POST': - return HttpResponse("别瞎玩") else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 按id查询这个用户的所有录像 @@ -225,22 +205,7 @@ def video_query_by_id(request): return JsonResponse(json.dumps({"videos": list(videos)}, cls=ComplexEncoder), safe=False) else: - return HttpResponse("别瞎玩") - - -# { -# "1": "{\"time\": \"2023-12-16 14:52:40\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"b\", \"mode\": \"00\", \"timems\": \"4770\", \"bv\": 23, \"bvs\": 4.821802935010482}", -# "3": "{\"time\": \"2023-12-16 14:52:52\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"i\", \"mode\": \"00\", \"timems\": \"20390\", \"bv\": 71, \"bvs\": 3.4330554193231975}", -# "4": "{\"time\": \"2023-12-16 15:17:58\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"b\", \"mode\": \"12\", \"timems\": \"1530\", \"bv\": 4, \"bvs\": 2.6143790849673203}", -# "8": "{\"time\": \"2023-12-16 15:26:22\", \"player\": \"www333\", \"level\": \"e\", \"mode\": \"00\", \"timems\": \"51940\", \"bv\": 149, \"bvs\": 2.849441663457836}", -# "9": "{\"time\": \"2023-12-16 15:26:26\", \"player\": \"www333\", \"level\": \"b\", \"mode\": \"00\", \"timems\": \"3250\", \"bv\": 18, \"bvs\": 5.538461538461538}", -# "10": "{\"time\": \"2023-12-16 15:26:30\", \"player\": \"www333\", \"level\": \"i\", \"mode\": \"00\", \"timems\": \"20110\", \"bv\": 69, \"bvs\": 3.431128791645947}", -# "7": "{\"time\": \"2023-12-16 15:24:07\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"i\", \"mode\": \"00\", \"timems\": \"15280\", \"bv\": 31, \"bvs\": 2.0287958115183247}", -# "6": "{\"time\": \"2023-12-16 15:24:02\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"e\", \"mode\": \"00\", \"timems\": \"59450\", \"bv\": 193, \"bvs\": 3.2127838519764507}", -# "2": "{\"time\": \"2023-12-16 14:52:48\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"e\", \"mode\": \"00\", \"timems\": \"61710\", \"bv\": 193, \"bvs\": 3.0789175174201913}", -# "5": "{\"time\": \"2023-12-16 15:23:59\", \"player\": \"\\u5b9e\\u540d\", \"level\": \"b\", \"mode\": \"12\", \"timems\": \"1580\", \"bv\": 6, \"bvs\": 3.7974683544303796}" -# } - + return HttpResponseNotAllowed() # 上传的录像进入数据库后,更新用户的录像数目 def update_video_num(video: VideoModel, add = True): @@ -291,7 +256,7 @@ def review_queue(request): review_video_ids.update({str(key, encoding="utf-8"): review_video_ids.pop(key)}) return JsonResponse(review_video_ids, encoder=ComplexEncoder) else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 获取最新录像 # http://127.0.0.1:8000/video/newest_queue @@ -302,7 +267,7 @@ def newest_queue(request): newest_queue_ids.update({str(key, encoding="utf-8"): newest_queue_ids.pop(key)}) return JsonResponse(newest_queue_ids, encoder=ComplexEncoder) else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 获取谁破纪录的消息 @@ -312,7 +277,7 @@ def news_queue(request): news_queue = cache.lrange("news_queue", 0, -1) return JsonResponse(news_queue, encoder=ComplexEncoder, safe=False) else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 获取全网被冻结的录像 @@ -324,7 +289,7 @@ def freeze_queue(request): freeze_queue_ids.update({str(key, encoding="utf-8"): freeze_queue_ids.pop(key)}) return JsonResponse(freeze_queue_ids, encoder=ComplexEncoder) else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 【管理员】审核通过队列里的录像,未审核或冻结状态的录像可以审核通过 @@ -337,7 +302,7 @@ def approve(request): res = [] for _id in ids: if not isinstance(_id, int): - return HttpResponse("审核录像的id应为正整数。") + return HttpResponseNotFound() # id应为正整数 video_i = VideoModel.objects.filter(id=_id) if not video_i: res.append("Null") @@ -365,7 +330,7 @@ def approve(request): # logger.info(f'{request.user.id} approve {json.dumps(ids)} response {json.dumps(res)}') return JsonResponse(res, safe=False) else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() # 【管理员】冻结队列里的录像,未审核或审核通过的录像可以冻结 # 两种用法,冻结指定的录像id,或冻结某用户的所有录像 @@ -392,7 +357,7 @@ def freeze(request): res = [] for _id in ids: if not isinstance(_id, int) or _id < 1: - return HttpResponse("冻结录像的id应为正整数。") + return HttpResponseNotFound() # id应为正整数 video_i = VideoModel.objects.filter(id=_id) if not video_i: res.append("Null") @@ -424,7 +389,7 @@ def freeze(request): logger.info(f'{request.user.id} freeze {json.dumps(ids)} response {json.dumps(res)}') return JsonResponse(json.dumps(res), safe=False) else: - return HttpResponse("别瞎玩") + return HttpResponseNotAllowed() diff --git a/front_end/package.json b/front_end/package.json index 289f01a3..929c9c41 100644 --- a/front_end/package.json +++ b/front_end/package.json @@ -17,6 +17,8 @@ "@types/js-cookie": "^3.0.3", "@types/lowdb": "^1.0.11", "@types/node": "^20.5.1", + "@vueuse/components": "^10.11.0", + "@vueuse/core": "^10.11.0", "axios": "^1.7.2", "echarts": "^5.5.0", "element-plus": "^2.7.0", diff --git a/front_end/src/App.vue b/front_end/src/App.vue index 34ab5bce..003573ec 100644 --- a/front_end/src/App.vue +++ b/front_end/src/App.vue @@ -92,6 +92,10 @@ import logo_2 from "@/assets/logo2.png"; import { useRouter } from "vue-router"; const router = useRouter(); +import { useDark, useToggle } from '@vueuse/core'; +const isDark = useDark() +useToggle(isDark) + // const player_visible = ref(false) const notice_visible = ref(false); const never_show_notice = ref(false); diff --git a/front_end/src/components/PreviewNumber.vue b/front_end/src/components/PreviewNumber.vue index 8d5c76bf..6c26057e 100644 --- a/front_end/src/components/PreviewNumber.vue +++ b/front_end/src/components/PreviewNumber.vue @@ -3,8 +3,8 @@ - + {{ data.text }} @@ -15,8 +15,11 @@ // 某个数字或字符串,点击后预览 import { onMounted, watch, ref, toRefs } from "vue"; import useCurrentInstance from "@/utils/common/useCurrentInstance"; +import { generalNotification } from "@/utils/system/status"; +import { useI18n } from "vue-i18n"; const { proxy } = useCurrentInstance(); const preview_visible = ref(false); +const t = useI18n(); const data = defineProps({ id: { @@ -59,13 +62,9 @@ const preview = (id: Number | undefined) => { }, } } - - }).catch( - (res) => { - // console.log("报错"); - // console.log(res); - } - ) + }).catch((error: any) => { + generalNotification(t, error.response.status, t.t('common.action.getSoftware')) + }) } const playVideo = function (uri: string) { @@ -87,13 +86,7 @@ const playVideo = function (uri: string) { }); } -onMounted(() => { - -}); - - \ No newline at end of file + \ No newline at end of file diff --git a/front_end/src/components/VideoList.vue b/front_end/src/components/VideoList.vue index 81243524..f5ecc51e 100644 --- a/front_end/src/components/VideoList.vue +++ b/front_end/src/components/VideoList.vue @@ -51,8 +51,12 @@ import { useUserStore } from '@/store'; const store = useUserStore() import { ms_to_s, approve, freeze, simple_formatter } from '@/utils'; +import { generalNotification } from '@/utils/system/status'; +import { useI18n } from 'vue-i18n'; const { proxy } = useCurrentInstance(); +const t = useI18n(); + const data = defineProps({ videos: { type: Array, @@ -142,12 +146,9 @@ const preview = (row: any, column: any, event: Event) => { }, } } - }).catch( - (res) => { - // console.log("报错"); - // console.log(res); - } - ) + }).catch((error: any) => { + generalNotification(t, error.response.status, t.t('common.action.getSoftware')); + }) } diff --git a/front_end/src/i18n/index.ts b/front_end/src/i18n/index.ts index 372191bc..9d43e31a 100644 --- a/front_end/src/i18n/index.ts +++ b/front_end/src/i18n/index.ts @@ -31,6 +31,7 @@ export default createI18n({ fallbackLocale: dev.local, fallbackWarn: false, missingWarn: false, + warnHtmlMessage: false, locale: getDefaultLocale(), messages: getMessages(), }) diff --git a/front_end/src/i18n/locales/en.ts b/front_end/src/i18n/locales/en.ts index f30153b1..ba53fb1b 100644 --- a/front_end/src/i18n/locales/en.ts +++ b/front_end/src/i18n/locales/en.ts @@ -75,6 +75,7 @@ export const en = { login: 'Login', logout: 'Logout', register: 'Register', + setting: 'Settings', downloads: 'Downloads', links: 'Links', team: 'Team' @@ -99,7 +100,7 @@ export const en = { dragOrClick: `Drag files here or click here to select`, uploadAll: 'Upload All ({0})', cancelAll: 'Clear All', - constraintNote: '*File size maximum is 5MB. File count maximum is 99.', + constraintNote: '*File size maximum is 5MB.', error: { collision: 'Video already exist', custom: 'Custom level is currently not supported', @@ -114,4 +115,14 @@ export const en = { } } }, + setting: { + appearance: 'Appearance', + darkMode: 'Dark Mode', + languageSwitch: 'Language Switch', + menuFontSize: 'Menu Font Size', + menuHeight: 'Menu Height', + menuLayout: 'Menu Layout', + menuLayoutAbstract: 'Abstract', + menuLayoutDefault: 'Default', + }, } \ No newline at end of file diff --git a/front_end/src/i18n/locales/zh-cn.ts b/front_end/src/i18n/locales/zh-cn.ts index 69857fd1..0f4a75b2 100644 --- a/front_end/src/i18n/locales/zh-cn.ts +++ b/front_end/src/i18n/locales/zh-cn.ts @@ -5,8 +5,11 @@ export const zhCn = { name: '简体中文', common: { action: { + getSoftware: '获取录像信息', getUserProfile: '查询用户', setUserProfile: '修改用户', + uploadFile: '上传文件', + videoQuery: '查询录像', }, hide: '隐藏', level: { @@ -31,12 +34,14 @@ export const zhCn = { emptyEmailCode: '请输入6位邮箱验证码!', emptyPassword: '请输入密码!', emptyUsername: '请输入用户名!', + fileTooLarge: '文件大小不能超过{0}', invalidEmail: '邮箱格式不正确!', invalidEmailCode: '邮箱验证码格式不正确!请点击邮箱验证码并打开邮箱查收。', invalidPassword: '密码格式不正确!长度应该为6-20位。', invalidUsername: '用户名格式不正确!长度不超过20位。', logoutFail: '退出失败!', logoutSuccess: '退出成功!', + realNameRequired: '请修改为实名', }, prop: { action: '操作', @@ -58,6 +63,8 @@ export const zhCn = { Forbidden: '权限不足', InternalServerError: '后端发生错误', NotFound: '找不到数据', + PayloadTooLarge: '文件过大', + UnsupportedMediaType: '不支持的文件类型', }, show: '显示', toDo: '敬请期待', @@ -125,7 +132,7 @@ export const zhCn = { dragOrClick: `将录像拉到此处或 点击此处选择`, uploadAll: '一键上传({0}个)', cancelAll: '全部清空', - constraintNote: '*单个文件大小不能超过5M,文件数量不能超过99', + constraintNote: '*单个文件大小不能超过5MB', error: { collision: '录像已存在', custom: '暂不支持自定义级别', @@ -153,4 +160,14 @@ export const zhCn = { termsAndConditions: '元扫雷网用户协议', confirm: '注册', }, + setting: { + appearance: '外观设置', + darkMode: '深色模式', + languageSwitch: '语言切换', + menuFontSize: '菜单字号', + menuHeight: '菜单高度', + menuLayout: '菜单排版', + menuLayoutAbstract: '抽象', + menuLayoutDefault: '默认', + }, } diff --git a/front_end/src/main.ts b/front_end/src/main.ts index 3ae21db3..8e804c51 100644 --- a/front_end/src/main.ts +++ b/front_end/src/main.ts @@ -14,6 +14,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; // 全局挂载axios import 'highlight.js/styles/stackoverflow-light.css' +import 'element-plus/theme-chalk/dark/css-vars.css' const app = createApp(App) diff --git a/front_end/src/store/index.ts b/front_end/src/store/index.ts index 47bd1291..8535a639 100644 --- a/front_end/src/store/index.ts +++ b/front_end/src/store/index.ts @@ -31,6 +31,7 @@ export const useUserStore = defineStore('user', { export const useLocalStore = defineStore('local', { state: () => ({ + darkmode: false, language: "zh-cn", language_show: true, menu_font_size: 18, diff --git a/front_end/src/utils/index.ts b/front_end/src/utils/index.ts index df6c5d85..50393f87 100644 --- a/front_end/src/utils/index.ts +++ b/front_end/src/utils/index.ts @@ -22,6 +22,7 @@ export function simple_formatter(f: Function): Function{ } import { ComponentCustomProperties } from "vue"; +import { generalNotification } from "./system/status"; export async function approve(proxy: ComponentCustomProperties & Record, id: number) { var status; await proxy.$axios.get('video/approve?ids=[' + id + ']').then(function (response) { diff --git a/front_end/src/utils/system/status.ts b/front_end/src/utils/system/status.ts index e26177e6..34068f78 100644 --- a/front_end/src/utils/system/status.ts +++ b/front_end/src/utils/system/status.ts @@ -9,6 +9,8 @@ const notificationMessage: { [code: number]: string} = { 400: 'common.response.BadRequest', 403: 'common.response.Forbidden', 404: 'common.response.NotFound', + 413: 'common.response.PayloadTooLarge', + 415: 'common.response.UnsupportedMediaType', 500: 'common.response.InternalServerError', }; @@ -20,4 +22,4 @@ export function generalNotification(t: any, status: number, action: string) { type: notificationType[type], duration: local.notification_duration, }) -} \ No newline at end of file +} diff --git a/front_end/src/views/SettingView.vue b/front_end/src/views/SettingView.vue index 7212091c..9838af90 100644 --- a/front_end/src/views/SettingView.vue +++ b/front_end/src/views/SettingView.vue @@ -1,19 +1,27 @@