Skip to content

Commit ded84e0

Browse files
authored
Merge pull request eee555#52 from putianyi889/debug-4
增加管理员操作接口与界面
2 parents 42c72bd + 2a772b3 commit ded84e0

File tree

10 files changed

+176
-54
lines changed

10 files changed

+176
-54
lines changed

back_end/saolei/userprofile/urls.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,13 @@
88
path('register/', views.user_register, name='register'),
99
path('retrieve/', views.user_retrieve, name='retrieve'),
1010
path('set_staff/', views.set_staff, name='set_staff'),
11-
path('set_banned/', views.set_banned, name='set_banned'),
1211
path('del_user_info/', views.del_user_info, name='del_user_info'),
1312
# path('delete/<int:id>/', views.user_delete, name='delete'),
1413
path('captcha/', include('captcha.urls')),
1514
path('refresh_captcha/',views.refresh_captcha),
1615
path('get_email_captcha/',views.get_email_captcha),
17-
path('modify_realname/',views.modify_realname_n),
18-
path('modify/',views.modify_n),
16+
path('get/',views.get_userProfile),
17+
path('set/',views.set_userProfile),
1918

2019
# path('captcha/captcha', views.captcha, name='captcha'),
2120
# path('edit/<int:id>/', views.profile_edit, name='edit'),

back_end/saolei/userprofile/views.py

Lines changed: 33 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
logger = logging.getLogger(__name__)
33
from django.contrib.auth import authenticate, login, logout
4-
from django.http import HttpResponse, JsonResponse
4+
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound
55
from .forms import UserLoginForm, UserRegisterForm, UserRetrieveForm, EmailForm
66
from captcha.models import CaptchaStore
77
import json
@@ -15,7 +15,6 @@
1515
from django.conf import settings
1616
from config.flags import EMAIL_SKIP
1717

18-
1918
# Create your views here.
2019

2120

@@ -204,29 +203,6 @@ def set_staff(request):
204203
else:
205204
return HttpResponse("别瞎玩")
206205

207-
# 【管理员】封禁用户。封禁后,用户可以登录,但不能上传录像、不能改任何个人信息
208-
# http://127.0.0.1:8000/userprofile/set_banned/?id=1&is_banned=True
209-
def set_banned(request):
210-
if request.user.is_staff and request.method == 'GET':
211-
user = UserProfile.objects.get(id=request.GET["id"])
212-
if user.is_staff and not request.user.is_superuser:
213-
return HttpResponse("没有封禁管理员的权限!")
214-
# user.is_banned = request.GET["is_banned"]
215-
logger.info(f'{request.user.id} set_banned {request.GET["id"]} {request.GET["is_banned"]}')
216-
if request.GET["is_banned"] == "True":
217-
user.is_banned = True
218-
user.save()
219-
return HttpResponse(f'封禁用户"{user.realname}"成功!')
220-
elif request.GET["is_banned"] == "False":
221-
user.is_banned = False
222-
user.save()
223-
return HttpResponse(f'解封用户"{user.realname}"成功!')
224-
else:
225-
return HttpResponse('失败!is_banned需要为"True"或"False"')
226-
else:
227-
return HttpResponse("别瞎玩")
228-
229-
230206
# 【管理员】删除用户的个人信息,从服务器磁盘上完全删除,但不影响是否封禁
231207
# http://127.0.0.1:8000/userprofile/del_user_info/?id=1
232208
def del_user_info(request):
@@ -309,28 +285,37 @@ def judge_captcha(captchaStr, captchaHashkey):
309285
return False
310286

311287

312-
# 【管理员】给用户增加1次修改姓名的机会
313-
# http://127.0.0.1:8000/userprofile/modify_realname?id=1
314-
def modify_realname_n(request):
315-
if request.user.is_staff and request.method == 'GET':
316-
user = UserProfile.objects.get(id=request.GET["id"])
317-
user.left_realname_n += 1
318-
logger.info(f'{request.user.id} add left_realname_n for {request.GET["id"]} ({user.left_realname_n})')
319-
return HttpResponse(f"为用户\"{user.realname}\"(id: {user.id})增加一次修改姓名的次数成功!")
320-
else:
321-
return HttpResponse("别瞎玩")
322-
288+
get_userProfile_fields = ["id", "userms__designators", "userms__video_num_limit", "username", "first_name", "last_name", "email", "realname", "signature", "country", "left_realname_n", "left_avatar_n", "left_signature_n", "is_banned"]
323289

324-
# 【站长】给用户增加x、y、z次(对应)修改姓名、头像和签名的机会
325-
# http://127.0.0.1:8000/userprofile/modify?id=1&x=0&y=1&z=200
326-
def modify_n(request):
327-
if request.user.is_superuser and request.method == 'GET':
328-
user = UserProfile.objects.get(id=request.GET["id"])
329-
user.left_realname_n += request.GET["x"]
330-
user.left_avatar_n += request.GET["y"]
331-
user.left_signature_n += request.GET["z"]
332-
logger.info(f'{request.user.id}(superuser) modify_n for {request.GET["id"]} ({user.left_realname_n}, {user.left_avatar_n}, {user.left_signature_n})')
333-
return HttpResponse(f"为用户\"{user.realname}\"(id: {user.id})增加修改姓名、头像、签名的次数成功!目前剩余({user.left_realname_n}, {user.left_avatar_n}, {user.left_signature_n})")
290+
def get_userProfile(request):
291+
if request.method != 'GET':
292+
return HttpResponseBadRequest()
293+
if request.user.is_staff:
294+
list = UserProfile.objects.filter(id=request.GET["id"]).values(*get_userProfile_fields)
295+
if len(list) == 0:
296+
return HttpResponseNotFound()
297+
return JsonResponse(list[0])
334298
else:
335-
return HttpResponse("别瞎玩")
336-
299+
return HttpResponseForbidden()
300+
301+
set_userProfile_fields = ["id", "userms__designators", "userms__video_num_limit", "username", "first_name", "last_name", "email", "realname", "signature", "country", "left_realname_n", "left_avatar_n", "left_signature_n", "is_banned"]
302+
def set_userProfile(request):
303+
if request.method == 'POST':
304+
if not request.user.is_staff:
305+
return HttpResponseForbidden() # 非管理员不能使用该api
306+
userid = request.POST.get("id")
307+
user = UserProfile.objects.get(id=userid)
308+
if user.is_staff and user != request.user:
309+
return HttpResponseForbidden() # 不能修改除自己以外管理员的信息
310+
field = request.POST.get("field")
311+
if field not in set_userProfile_fields:
312+
return HttpResponseForbidden() # 只能修改特定的域
313+
if field == "is_banned" and user.is_superuser:
314+
return HttpResponseForbidden() # 站长不可被封禁
315+
value = request.POST.get("value")
316+
logger.info(f'{request.user.id}(staff) changes {userid}.{field} from {getattr(user, field)} to {value}')
317+
setattr(user, field, value)
318+
user.save()
319+
return HttpResponse()
320+
else:
321+
return HttpResponseBadRequest()

front_end/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@types/node": "^20.5.1",
2020
"axios": "^1.7.2",
2121
"echarts": "^5.5.0",
22-
"element-plus": "^2.2.32",
22+
"element-plus": "^2.7.0",
2323
"flag-icon-css": "^4.1.7",
2424
"highlight.js": "^11.9.0",
2525
"image-conversion": "^2.1.1",

front_end/src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ body {
214214
height: v-bind("local.menu_height + 'px'");
215215
position: fixed;
216216
width: 100%;
217-
z-index: 2010;
217+
z-index: 1010; //message的z索引为2015
218218
user-select: none;
219219
overflow-x: auto;
220220
overflow-y: hidden;

front_end/src/i18n/locales/zh-cn.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ export const zhCn = {
44
local: 'zh-cn',
55
name: '简体中文',
66
common: {
7+
action: {
8+
getUserProfile: '查询用户',
9+
setUserProfile: '修改用户',
10+
},
711
hide: '隐藏',
812
level: {
913
b: '初级',
@@ -17,6 +21,8 @@ export const zhCn = {
1721
dg: '递归'
1822
},
1923
msg: {
24+
actionFail: '{0}失败!',
25+
actionSuccess: '{0}成功',
2026
agreeTAC: '请同意用户协议!',
2127
confirmPasswordFail: '两次输入的密码不一致!',
2228
connectionFail: '无法连接到服务器!',
@@ -46,6 +52,13 @@ export const zhCn = {
4652
timems: '用时',
4753
upload_time: '上传时间',
4854
},
55+
response: {
56+
OK: '',
57+
BadRequest: '无法识别的请求',
58+
Forbidden: '权限不足',
59+
InternalServerError: '后端发生错误',
60+
NotFound: '找不到数据',
61+
},
4962
show: '显示',
5063
toDo: '敬请期待',
5164
},

front_end/src/router/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ const routes: Array<RouteRecordRaw> = [
5252
name: 'upload',
5353
component: () => import('../views/UploadView.vue')
5454
},
55+
{
56+
path: '/staff',
57+
name: 'staff',
58+
component: () => import('../views/StaffView.vue')
59+
},
5560
]
5661

5762
const router = createRouter({

front_end/src/store/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const useLocalStore = defineStore('local', {
3636
menu_font_size: 18,
3737
menu_height: 60,
3838
menu_icon: false,
39+
notification_duration: 4500,
3940
}),
4041
persist: true,
4142
})

front_end/src/utils/system/status.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useLocalStore } from "@/store";
2+
import { ElNotification } from "element-plus"
3+
const local = useLocalStore();
4+
5+
const notificationType = ['', '', 'success', '', 'error', 'error'];
6+
const notificationTitle = ['', '', 'common.msg.actionSuccess', '', 'common.msg.actionFail', 'common.msg.actionFail'];
7+
const notificationMessage: { [code: number]: string} = {
8+
200: 'common.response.OK',
9+
400: 'common.response.BadRequest',
10+
403: 'common.response.Forbidden',
11+
404: 'common.response.NotFound',
12+
500: 'common.response.InternalServerError',
13+
};
14+
15+
export function generalNotification(t: any, status: number, action: string) {
16+
let type = Math.floor(status / 100);
17+
ElNotification({
18+
title: t.t(notificationTitle[type], [action]),
19+
message: t.t(notificationMessage[status]),
20+
type: notificationType[type],
21+
duration: local.notification_duration,
22+
})
23+
}

front_end/src/views/SettingView.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77
<el-descriptions-item label="菜单高度">
88
<el-slider v-model="local.menu_height" size="small" :min="20" :max="60" style="width: 100px; display: inline-block; height: 9px"></el-slider>
99
</el-descriptions-item>
10-
<el-descriptions-item label="菜单字号"><el-input-number v-model="local.menu_font_size" size="small" :min="10"></el-input-number></el-descriptions-item>
10+
<el-descriptions-item label="菜单字号"><el-input-number v-model="local.menu_font_size" size="small" :min="10"/></el-descriptions-item>
11+
<el-descriptions-item label="通知关闭"><el-tooltip>
12+
<template #content>
13+
通知自动关闭的时间,单位毫秒。<br/>值为0则不会自动关闭。
14+
</template>
15+
<el-input-number v-model="local.notification_duration" size="small" :min="0" :step="1000"/>
16+
</el-tooltip></el-descriptions-item>
1117
</el-descriptions>
1218
<el-descriptions v-if="store.login_status == LoginStatus.IsLogin" title="个人信息" :column="3">
1319
<el-descriptions-item label="用户id">{{ store.user.id }}</el-descriptions-item>

front_end/src/views/StaffView.vue

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<template>
2+
<div>
3+
用户ID
4+
<el-input-number v-model="userid" :controls="false" :min="0"></el-input-number>
5+
<el-button @click="getUser">查询</el-button>
6+
</div>
7+
<div>
8+
域<el-select v-model="userfield">
9+
<el-option v-for="field in descriptionitems" :value="field"></el-option>
10+
</el-select>
11+
</div>
12+
<div>
13+
值<el-input v-model="uservalue"></el-input>
14+
</div>
15+
<div>
16+
<el-button @click="setUser(userid, userfield, uservalue)">修改</el-button>
17+
</div>
18+
<el-descriptions title="UserProfile">
19+
<el-descriptions-item v-for="item in descriptionitems" :label="item">{{ userprofile[item] }}</el-descriptions-item>
20+
</el-descriptions>
21+
</template>
22+
23+
<script lang="ts" setup>
24+
25+
import useCurrentInstance from '@/utils/common/useCurrentInstance';
26+
import { generalNotification } from '@/utils/system/status';
27+
import { ref } from 'vue';
28+
29+
import { useI18n } from 'vue-i18n';
30+
const t = useI18n();
31+
32+
const { proxy } = useCurrentInstance();
33+
34+
const userid = ref(0);
35+
const userfield = ref("");
36+
const uservalue = ref("");
37+
const descriptionitems = ["username", "first_name", "last_name", "email", "realname", "country", "is_banned", "left_realname_n", "left_avatar_n", "left_signature_n"]
38+
39+
interface UserProfile {
40+
userms__designators: Array<String>;
41+
userms__video_num_limit: Number;
42+
username: String;
43+
first_name: String;
44+
last_name: String;
45+
email: String;
46+
realname: String;
47+
signature: String;
48+
country: String;
49+
is_banned: Boolean;
50+
left_realname_n: Number;
51+
left_avatar_n: Number;
52+
left_signature_n: Number;
53+
}
54+
55+
const userprofile = ref<UserProfile>({
56+
userms__designators: [],
57+
userms__video_num_limit: 0,
58+
username: "",
59+
first_name: "",
60+
last_name: "",
61+
email: "",
62+
realname: "",
63+
signature: "",
64+
country: "",
65+
is_banned: false,
66+
left_realname_n: 0,
67+
left_avatar_n: 0,
68+
left_signature_n: 0,
69+
});
70+
71+
const getUser = () => {
72+
proxy.$axios.get('userprofile/get', {params: {id: userid.value}}).then(
73+
function (response: any) {
74+
userprofile.value = response.data;
75+
}
76+
).catch(error => {
77+
generalNotification(t, error.response.status, t.t('common.action.getUserProfile'))
78+
})
79+
}
80+
81+
const setUser = (id: number, field: string, value: string) => {
82+
proxy.$axios.post('userprofile/set/', {id: id, field: field, value: value}).then(
83+
function (response: any) {
84+
generalNotification(t, response.status, t.t('common.action.setUserProfile'));
85+
getUser();
86+
}
87+
)
88+
}
89+
90+
</script>

0 commit comments

Comments
 (0)