这是实现后的效果:
账号系统展示
----------创建账号系统
1.首先运行后端进程:ctrl + r 输入uws即可快捷找到历史输入命令
uwsgi --ini scripts/uwsgi.ini
然后将全局配置文件settings中的DEBUG修改成True,这样才能在前端网页报错时方便调试
2.在根目录acapp下,可以创建一个超级用户:
python3 manage.py createsuperuser
这样,就可以用网址/admin进入后台管理,可以管理用户
django系统自带了一个用户系统Users,但信息有限,我们想要加上一个头像,就需要利用django自带的数据库进行扩充,步骤如下:
进入/acapp/game/models,创建所有的表:
mkdir player #创建一个文件夹放置所有关于玩家的表,并进入
touch __init__.py #每个python文件夹都需要
vim player.py #用来存储player这个数据表的信息
django里创建一个数据表就是定义一个class,如下:
` from django.db import models#继承django数据库的基类
from django.contrib.auth.models import User #还需要用到User这个类
class Player(models.Model):#每一个定义的类,都需要继承自models.Model这个类
#每一个Player都唯一对应一个user,如下定义(对应哪个表,当user删除时需要执行什么函数)
user = models.OneToOneField(User, on_delete=models.CASCADE)#user删除时,把他关联的Player一并删掉
photo = models.URLField(max_length = 256, blank = True)#头像的话,(需要存储一个链接,需要定义一个最大长度和是否可以为空)
def __str__(self):#显示每一个Player时,显示的是什么
return str(self.user)`
这样就定义好了Player新的数据表,里面有两个关键字,user表示与谁一一对应,photo表示头像
小技巧:在django家目录下,输入python3 manage.py shell,可以进入一个交互模式,忘记的命令可以在此进行补全回忆
若想让我们创建的数据表出现在管理员页面,还需要在/acapp/game/admin.py里注册进去,如下:
` from django.contrib import admin
from game.models.player.player import Player#先引用进来,路由
admin.site.register(Player)#注册`
然后保存,每一对数据库的数据表进行修改时,都需要执行两个命令:
python3 manage.py makemigrations
python3 manage.py migrate,
执行完之后,我们定义的表就会被应用到服务器里
3.登录流程:
用户每次在浏览器(clent)刷新的时候,先向后台服务器(server)发送一个请求,获取当前玩家的信息(getinfo),server会返回两个信息(username和photo)或者返回未登录,
未来网站会运行在不同的平台上,比如浏览器和acapp或者小程序等等,在不同平台执行的行为可能不同,所以我们需要知道当前运行在什么平台上,不同平台的处理逻辑应该不同
于是对于之前的js代码则需要进行修改:
game/static/js/src/zbase.js下:
...AcGame {
constuctor(id, AcWingOS) //用来识别登录平台
...
this.AcWingOS = AcWingOS; //保存下来
...
}
我们先默认已登录,先写返回第一名玩家信息这个函数:
这里去写需要前后端一起写,每次写一个函数,都需要实现三个东西(views:调用数据库的逻辑,urls:路由,js:实现调用):
一般先写views,
未来设置里可以修改我们的信息,所以我们将信息放到settings里:
创建game/views/settings/getinfo.py,如下:
`from django.http import JsonResponse#每次返回到这里
from game.models.player.player import Player#引入刚才创建的数据库
def getinfo_acapp(request):#处理acapp端的请求
player = Player.objects.all()[0]
return JsonResponse({
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
def getinfo_web(request): #处理web端的请求
player = Player.objects.all()[0]#取出第一位玩家的信息
return JsonResponse({#返回信息
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
def getinfo(request):#处理请求
platform = request.GET.get('platform') #判断是从哪个平台传过来的
if platform == "ACAPP":
return getinfo_acapp(request)
else:
return getinfo_web(request)`
写完views,写urls,
game/urls/settings/index.py,如下:
`from django.urls import path
from game.views.settings.getinfo import getinfo#将刚刚写的函数引入
#写路由
urlpatterns = [
path("getinfo/", getinfo, name = "settings_getinfo"),
] `
写完之后,重启服务,访问网址/settings/getinfo,若成功看到返回值,则成功了,开始写js:
在菜单界面之前加一个登录界面:
class AcGameMenu {
constructor(root) {
this.root = root;
this.$menu = $(`
<div class="ac-game-menu">
<div class="ac-game-menu-field">
<div class="ac-game-menu-field-item ac-game-menu-field-item-single-mode">
单人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-multi-mode">
多人模式
</div>
<br>
<div class="ac-game-menu-field-item ac-game-menu-field-item-settings">
退出
</div>
</div>
</div>
`);
this.$menu.hide(); //默认先关闭
this.root.$ac_game.append(this.$menu);
this.$single_mode = this.$menu.find('.ac-game-menu-field-item-single-mode');
this.$multi_mode = this.$menu.find('.ac-game-menu-field-item-multi-mode');
this.$settings = this.$menu.find('.ac-game-menu-field-item-settings');
this.start();
}
start() {
this.add_listening_events();
}
add_listening_events() {
let outer = this;
this.$single_mode.click(function(){
outer.hide();
outer.root.playground.show();
});
this.$multi_mode.click(function(){
console.log("click multi mode");
});
this.$settings.click(function(){
console.log("click settings");
outer.root.settings.logout_on_remote();
});
}
show() { // 显示menu界面
this.$menu.show();
}
hide() { // 关闭menu界面
this.$menu.hide();
}
}
由于之前写的内容都放在了settigns,所以我们需要重新创建一个模块叫settings,
game/static/js/src/settings/zbase.js,如下:
class Settings {
constructor(root) {
this.root = root;
this.platform = "WEB"; #默认在web平台
if (this.root.AcWingOS) this.platform = "ACAPP"; #判断是什么平台,如果有传入参数,则是acapp平台
this.start();
}
start() {
this.getinfo(); //获取用户信息
}
register() { // 打开注册界面
}
login() { // 打开登录界面
}
getinfo() {
let outer = this;
//背过
$.ajax({
url: "https://app3246.acapp.acwing.com.cn/settings/getinfo/",
type: "GET",
data: {
platform: outer.platform,
},
success: function(resp) { //调用成功的回调函数 resp是getinfo文件中返回的信息字典
console.log(resp);
if (resp.result === "success") { //如果成功,则隐藏登录界面,打开菜单界面
outer.hide();
outer.root.menu.show();
} else { //如果失败,继续弹出登录界面
outer.login();
}
}
});
}
hide(){
}
show(){
}
}
定义之后,一定要继续的在根class下创建该class。
然后补充一下getinfo:
if platform == "ACAPP":
return getinfo_acapp(request)
elif platform == "WEB": //加上判定条件
return getinfo_web(request)
修改js文件之后,重新打包且重启服务,按下F12就可以看到返回的信息,因为当前没有判断是否登录,所以直接默认返回登录成功,接下来我们写一下,判断是否登录:
在game/views/settings/getinfo.py下:
from django.http import JsonResponse
from game.models.player.player import Player
def getinfo_acapp(request):
player = Player.objects.all()[0]
return JsonResponse({
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
def getinfo_web(request):
user = request.user #取得user
if not user.is_authenticated: #判断是否没有登录
return JsonResponse({ #返回字典
'result': "未登录"
})
else:
player = Player.objects.all()[0]
return JsonResponse({
'result': "success",
'username': player.user.username,
'photo': player.photo,
})
def getinfo(request):
platform = request.GET.get('platform')
if platform == "ACAPP":
return getinfo_acapp(request)
elif platform == "WEB":
return getinfo_web(request)
打包重启服务后,发现,还是登录状态,是因为后端管理员页面是登录状态,所以前端依然是登录状态,后台退出登录之后,前端就显示未登录了,而在acapp端,仍然无脑返回登录状态,这样就实现了不同平台的不同逻辑
然后再对settings进行补充:
...
if (this.root.AcWingOS) this.platform = "ACAPP"; //将信息存下来
this.username = "";
this.photo = "";
...
...
getinfo() {
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
outer.username = resp.username;
outer.photo = resp.photo;
...
}
}
}
...
然后进入class Players类,画上头像:
class Player extends AcGameObject {
constructor(playground, x, y, radius, color, speed, is_me) {
super();
...
if (this.is_me) {//判断是否为自己
this.img = new Image();
this.img.src = this.playground.root.settings.photo;
}
}
render() {
if (this.is_me) {//每一帧画的时候,也要进行判断
//以下为画头像的方法
this.ctx.save();
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.stroke();
this.ctx.clip();
this.ctx.drawImage(this.img, this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2);
this.ctx.restore();
} else { //画圆
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
this.ctx.fillStyle = this.color;
this.ctx.fill();
}
}
...
打包且重启服务,然后就会发现自己操控的圆已经有头像了。如果web端正常,而acapp端运行错误,可能是因为图片有权限,可以尝试换一张图片
4.渲染登录界面:
先写js
class Settings {
constructor(root) {
this.root = root;
this.platform = "WEB";
if (this.root.AcWingOS) this.platform = "ACAPP";
this.username = "";
this.photo = "";
this.$settings = $(`
<div class="ac-game-settings">
<div class="ac-game-settings-login">
<div class="ac-game-settings-title">
登录
</div>
<div class="ac-game-settings-username">
<div class="ac-game-settings-item">
<input type="text" placeholder="用户名">
</div>
</div>
<div class="ac-game-settings-password">
<div class="ac-game-settings-item">
<input type="password" placeholder="密码">
</div>
</div>
<div class="ac-game-settings-submit">
<div class="ac-game-settings-item">
<button>登录</button>
</div>
</div>
<div class="ac-game-settings-error-message">
</div>
<div class="ac-game-settings-option">
注册
</div>,
<br> //前两行是共行,可能会影响后面的内容,需要手写换行
<div class="ac-game-settings-acwing">
<img width="30" src="https://app165.acapp.acwing.com.cn/static/image/settings/acwing_logo.png">
<br>
<div>
AcWing一键登录
</div>
</div>
</div>
<div class="ac-game-settings-register">
<div class="ac-game-settings-title">
注册
</div>
<div class="ac-game-settings-username">
<div class="ac-game-settings-item">
<input type="text" placeholder="用户名">
</div>
</div>
<div class="ac-game-settings-password ac-game-settings-password-first">
<div class="ac-game-settings-item">
<input type="password" placeholder="密码">
</div>
</div>
<div class="ac-game-settings-password ac-game-settings-password-second">
<div class="ac-game-settings-item">
<input type="password" placeholder="确认密码">
</div>
</div>
<div class="ac-game-settings-submit">
<div class="ac-game-settings-item">
<button>注册</button>
</div>
</div>
<div class="ac-game-settings-error-message">
</div>
<div class="ac-game-settings-option">
登录
</div>
<br>
<div class="ac-game-settings-acwing">
<img width="30" src="https://app165.acapp.acwing.com.cn/static/image/settings/acwing_logo.png">
<br>
<div>
AcWing一键登录
</div>
</div>
</div>
</div>
`);
this.$login = this.$settings.find(".ac-game-settings-login"); //登录
//将所有元素全部抠出来,便于绑定监听函数
this.$login_username = this.$login.find(".ac-game-settings-username input");
this.$login_password = this.$login.find(".ac-game-settings-password input");
this.$login_submit = this.$login.find(".ac-game-settings-submit button");
this.$login_error_message = this.$login.find(".ac-game-settings-error-message");
this.$login_register = this.$login.find(".ac-game-settings-option");
this.$login.hide(); //都需要先隐藏
this.$register = this.$settings.find(".ac-game-settings-register"); //注册
this.$register_username = this.$register.find(".ac-game-settings-username input");
this.$register_password = this.$register.find(".ac-game-settings-password-first input");
this.$register_password_confirm = this.$register.find(".ac-game-settings-password-second input");
this.$register_submit = this.$register.find(".ac-game-settings-submit button");
this.$register_error_message = this.$register.find(".ac-game-settings-error-message");
this.$register_login = this.$register.find(".ac-game-settings-option");
this.$register.hide();都需要先隐藏
this.root.$ac_game.append(this.$settings); //将settings加入到页面里面
this.start();
}
start() {
this.getinfo();
this.add_listening_events();
}
add_listening_events() { //绑定监听函数
this.add_listening_events_login(); //登录
this.add_listening_events_register(); //注册
}
add_listening_events_login() { //登录界面监听函数
let outer = this;
this.$login_register.click(function() {
outer.register(); //跳到注册界面
});
this.$login_submit.click(function() {
outer.login_on_remote(); //点击登录按钮,调用远程登录
});
}
add_listening_events_register() { //注册界面监听函数
let outer = this;
this.$register_login.click(function() {
outer.login(); //跳到登录界面
});
this.$register_submit.click(function() {
outer.register_on_remote();
});
}
register() { // 打开注册界面
this.$login.hide(); //关闭登录界面
this.$register.show(); //打开登录界面
}
login() { // 打开登录界面
this.$register.hide(); //关闭注册界面
this.$login.show(); //打开登录界面
}
getinfo() {
let outer = this;
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/getinfo/",
type: "GET",
data: {
platform: outer.platform,
},
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
outer.username = resp.username;
outer.photo = resp.photo;
outer.hide();
outer.root.menu.show();
} else {
outer.login();
}
}
});
}
hide() {
this.$settings.hide();
}
show() {
this.$settings.show();
}
}
修改css之前,先下载一下登录界面的图片:
在/acapp/game/static/image/setting,下载图片,
wget https://www.shouyihuo.com/uploads/allimg/140917/1136095301-13.gif,
修改名字
mv 1136095301-13.gif Dragon_Balls.gif
再去修改css
/game/static/css/game.css:
.ac-game-settings {
width: 100%;
height: 100%;
background-image: url("/static/image/menu/Dragon_Balls.gif");
background-size: 100% 100%;
user-select: none;
}
.ac-game-settings-login {
height: 41vh;
width: 20vw;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); //锚点移动正中间
background-color: rgba(0, 0, 0, 0.7);
border-radius: 5px;
}
.ac-game-settings-title {
color: white;
font-size: 3vh;
text-align: center;
padding-top: 2vh;
margin-bottom: 2vh;
}
.ac-game-settings-username {
display: block;
height: 7vh;
}
.ac-game-settings-password {
display: block;
height: 7vh;
}
.ac-game-settings-submit {
display: block;
height: 7vh;
}
.ac-game-settings-acwing {
display: block;
height: 7vh;
}
.ac-game-settings-item {
width: 100%;
height: 100%;
}
.ac-game-settings-item > input {
width: 90%;
line-height: 3vh;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.ac-game-settings-item > button {
color: white;
width: 90%;
line-height: 3vh;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #4CAF50;
border-radius: 5px;
}
.ac-game-settings-error-message {
color: red;
font-size: 0.8vh;
display: inline;
float: left;
padding-left: 1vw;
}
.ac-game-settings-option {
color: white;
font-size: 2vh;
display: inline;
float: right;
padding-right: 1vw;
cursor: pointer;
}
.ac-game-settings-acwing > img {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
cursor: pointer;
display: block;
}
.ac-game-settings-acwing > div {
color: white;
font-size: 1.5vh;
text-align: center;
display: block;
}
.ac-game-settings-register {
height: 49vh;
width: 20vw;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
border-radius: 5px;
}
上面设置一键登录的logo时,需要先将logo下载到服务器:
/game/static/image/setting,
wget 图片网址,下载之后,取名acwing_logo.png,然后去浏览器中输入路由看看有没有下载成功
4.写前后端交互:
老三样 views urls js:
写views,game/views/settings/login.py:
from django.http import JsonResponse #用于返回字符串
from django.contrib.auth import authenticate, login #验证密码是否正确,并且引入login函数
def signin(request):
data = request.GET #get好调试
username = data.get('username')
password = data.get('password')
user = authenticate(username=username, password=password) #验证
if not user: #没有找到用户
return JsonResponse({
'result': "用户名或密码不正确"
})
login(request, user) #否则直接登录,会将信息存进浏览器
return JsonResponse({
'result': "success"
})
写urls,game/urls/settings/index.py:
from game.views.settings.login import signin # 引入自己写的逻辑
urlpatterns = [
...
path("login/", signin, name = "settings_login"), # "settings/login/"
]
写js,game/static/js/src/settings/zbase.js:
...
login_on_remote() { // 在远程服务器上登录
let outer = this;
let username = this.$login_username.val(); //存值
let password = this.$login_password.val();
this.$login_error_message.empty(); //清空错误信息
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/login/",
type: "GET",
data: {
username: username,
password: password,
},
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
location.reload(); //刷新,变成登录成功的状态
} else {
outer.$login_error_message.html(resp.result); //运行失败,显示出来
}
}
});
}
...
同样的方式创建登出,
写views,game/views/settings/logout.py:
from django.http import JsonResponse
from django.contrib.auth import logout
def signout(request):
user = request.user
if not user.is_authenticated:
return JsonResponse({
'result': "success",
})
logout(request)
return JsonResponse({
'result': "success",
})
写urls, game/urls/settings/index.py:
from game.views.settings.logout import signout
urlpatterns = [
...
path("logout/", signout, name="settings_logout"),
...
]
写js,game/static/js/src/settings/zbase.js:
logout_on_remote() { // 在远程服务器上登出
if (this.platform === "ACAPP") return false; //acapp端直接返回,因为acapp没有这个服务
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/logout/",
type: "GET",
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
location.reload();
}
}
});
}
然后借用一下设置按钮,改成退出:
在game/static/js/src/menu/zbase.js:
...
<div class="ac-game-menu-field-item ac-game-menu-field-item-settings">
退出
</div>
...
...
this.$settings.click(function(){
console.log("click settings");
outer.root.settings.logout_on_remote();
});
...
然后打包重启服务即可
同样的方式写注册:
先写vies,game/views/settings/register.py:
from django.http import JsonResponse
from django.contrib.auth import login
from django.contrib.auth.models import User
from game.models.player.player import Player
def register(request):
data = request.GET
username = data.get("username", "").strip() //strip()去掉前后空格
password = data.get("password", "").strip()
password_confirm = data.get("password_confirm", "").strip()
if not username or not password:
return JsonResponse({
'result': "用户名和密码不能为空"
})
if password != password_confirm:
return JsonResponse({
'result': "两个密码不一致",
})
if User.objects.filter(username=username).exists(): //filter查找数据库
return JsonResponse({
'result': "用户名已存在"
})
user = User(username=username)
user.set_password(password)
user.save()
Player.objects.create(user=user, photo="https://img2.baidu.com/it/u=2161949891,656888789&fm=26&fmt=auto")
login(request, user)
return JsonResponse({
'result': "success",
})
写路由game/urls/settings/index.py:
from django.urls import path
from game.views.settings.getinfo import getinfo
from game.views.settings.login import signin
from game.views.settings.logout import signout
from game.views.settings.register import register
urlpatterns = [
path("getinfo/", getinfo, name="settings_getinfo"),
path("login/", signin, name="settings_login"),
path("logout/", signout, name="settings_logout"),
path("register/", register, name="settings_register"),
]
写js,game/static/js/src/settings/zbase.js:
register_on_remote() { // 在远程服务器上注册
let outer = this;
let username = this.$register_username.val();
let password = this.$register_password.val();
let password_confirm = this.$register_password_confirm.val();
this.$register_error_message.empty();
$.ajax({
url: "https://app165.acapp.acwing.com.cn/settings/register/",
type: "GET",
data: {
username: username,
password: password,
password_confirm: password_confirm,
},
success: function(resp) {
console.log(resp);
if (resp.result === "success") {
location.reload(); // 刷新页面
} else {
outer.$register_error_message.html(resp.result);
}
}
});
}
到这里,就完成了注册登录页面。
作者:樱桃小完犊子_1
链接:https://www.acwing.com/activity/content/code/content/4344196/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。