feat(projects): 新增Django综合案例示例代码及素材

This commit is contained in:
100gle
2022-08-10 16:15:39 +08:00
parent b4c2257308
commit 3bf65b2755
65 changed files with 1496 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todolist.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PopupConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'popup'

View File

@@ -0,0 +1,35 @@
# Generated by Django 4.0.4 on 2022-06-28 13:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='收集箱', max_length=100, unique=True, verbose_name='分类名称')),
],
),
migrations.CreateModel(
name='Task',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='任务名称')),
('priority', models.IntegerField(choices=[(0, '一般'), (1, '优先'), (3, '紧急')], default=0, verbose_name='任务优先级')),
('description', models.TextField(blank=True, max_length=500, null=True, verbose_name='任务描述')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('is_done', models.BooleanField(choices=[(False, '未完成'), (True, '已完成')], default=False, verbose_name='是否完成')),
('group', models.ForeignKey(default=0, on_delete=django.db.models.deletion.DO_NOTHING, to='popup.group', verbose_name='所属分类')),
],
),
]

View File

@@ -0,0 +1,47 @@
from django.db import models
class Task(models.Model):
"""the task record in todolist"""
priority_choices = (
(0, "一般"),
(1, "优先"),
(3, "紧急"),
)
is_done_choices = (
(False, "未完成"),
(True, "已完成"),
)
name = models.CharField(max_length=100, verbose_name="任务名称")
priority = models.IntegerField(
choices=priority_choices, default=0, verbose_name="任务优先级"
)
description = models.TextField(
max_length=500, verbose_name="任务描述", blank=True, null=True
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
is_done = models.BooleanField(
default=False, verbose_name="是否完成", choices=is_done_choices
)
group = models.ForeignKey(
"Group", on_delete=models.DO_NOTHING, default=0, verbose_name="所属分类"
)
def __repr__(self):
return f"<Task {self.name}>"
class Group(models.Model):
name = models.CharField(
max_length=100,
default="收集箱",
unique=True,
verbose_name="分类名称",
)
def __repr__(self):
return f"<Group '{self.name}'>"

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,12 @@
from django.urls import path
from .views import about, create_task, delete_task, index, query_all_tasks, update_task
urlpatterns = [
path("", index, name="index"),
path("about/", about, name="about"),
path("tasks/", query_all_tasks, name="tasks"),
path("tasks/create/", create_task, name="create_task"),
path("tasks/update/<int:task_id>/", update_task, name="update_task"),
path("tasks/delete/<int:task_id>/", delete_task, name="delete_task"),
]

View File

@@ -0,0 +1,112 @@
from django.http.response import HttpResponseNotAllowed
from django.shortcuts import redirect, render
from django.utils.html import format_html
from .models import Group, Task
def index(request):
welcome = "欢迎来到 Todolist 练习项目 Popup 应用主页!"
index_content = format_html(
"""
这是<a href="https://sspai.com/series/271" style="text-decoration: none;">《100 小时后请叫我程序员》</a>课程用于进行 Django 实践练习的网站,主要包括了以下几部分:
<ul>
<li><b>少数派 Logo</b>:你可以点击跳转至少数派首页;</li>
<li><b>Home</b>:即当前页面;</li>
<li><b>Task</b>:用于实践的待办清单页面,你可以在里面创建、更新以及删除任务;</li>
<li><b>About</b>:对个人或网站信息进行简要说明的介绍页。</li>
</ul>
"""
)
ctx = dict(
title="home",
welcome=welcome,
index_content=index_content,
)
return render(request, "index.html", context=ctx)
def about(request):
title = "About"
header = f"{title} Me"
about_content = """
这是一段简单的About Me 页面。
它可以用来展示关于个人的一些信息,或者是简单的介绍。
例如:
""".strip().splitlines()
items = [
("昵称", "100gle"),
("职业", "少数派作者"),
("编程语言", "Python、Golang、JavaScript 等等"),
]
ctx = dict(
title=title,
header=header,
about_content=about_content,
items=items,
)
return render(request, "about.html", context=ctx)
def query_all_tasks(request):
tasks = Task.objects.all()
fields = ["序号", "任务名称", "优先级", "任务描述", "是否完成", "分组"]
if Group.objects.count() == 0:
groups = [
Group(name="收集箱"),
Group(name="生活"),
Group(name="工作"),
]
Group.objects.bulk_create(groups)
groups = Group.objects.all()
ctx = dict(
title="Task",
tasks=tasks,
fields=fields,
groups=groups,
)
return render(request, "tasks.html", context=ctx)
def create_task(request):
"""create a task"""
if request.method == "POST":
name = request.POST.get("taskName")
priority = request.POST.get("taskPriority")
description = request.POST.get("taskDescription")
group_id = request.POST.get("taskGroup")
group = Group.objects.get(id=group_id)
Task.objects.create(
name=name, priority=priority, description=description, group=group
)
return redirect("/tasks/")
else:
return HttpResponseNotAllowed(["POST"])
def update_task(request, task_id):
"""update a task"""
task = Task.objects.get(id=task_id)
task.is_done = False if task.is_done else True
task.save()
return redirect("/tasks/")
def delete_task(request, task_id):
"""delete a task"""
task = Task.objects.get(id=task_id)
task.delete()
return redirect("/tasks/")

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,149 @@
/* base layout */
.container {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1 {
margin-left: 15px;
}
p {
margin: 10px;
word-spacing: 1.5px;
}
ul {
list-style: none;
}
/* navbar */
.navbar ul {
display: flex;
align-items: center;
justify-content: space-between;
text-align: center;
list-style: none;
padding-left: 0;
}
.navbar a {
display: flex;
justify-content: center;
align-items: center;
text-decoration: none;
border: none;
font-size: 20px;
font-weight: bold;
}
.navbar a:hover {
color: goldenrod !important;
}
.content,
.about {
margin: 0 10px;
}
.content li::before,
.about li::before {
content: "\1F449";
padding-right: 5px;
}
/* data table */
.data-table {
display: table;
table-layout: fixed;
width: 100%;
border-collapse: collapse;
}
.data-table th,
.data-table td {
text-align: center;
word-wrap: break-word;
overflow-wrap: break-word;
}
.data-table th:first-child {
width: 5%;
}
.data-table th:nth-child(2) {
width: 15%;
}
.data-table th:nth-child(4) {
width: 30%;
}
.data-table td[class="ops"] {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.data-table .ops span {
display: block;
color: var(--accent);
cursor: pointer;
}
.btn-add-task {
display: flex;
flex-direction: row-reverse;
}
.btn-add-task > button {
display: block;
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 20px;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
}
.btn-add-task > button:hover {
background-color: rgb(204, 77, 77);
}
.task-form {
display: none;
flex-direction: column;
align-items: center;
}
.form {
display: block;
width: 80%;
}
.form > div {
display: flex;
flex-direction: row;
justify-content: space-between;
vertical-align: middle;
}
.form .btn-submit {
text-align: center;
transition: 0.1ms;
}
.form .btn-submit > input {
display: block;
width: 100%;
font-size: 20px;
transition: 0.1ms;
}
#task-textarea {
width: 60%;
resize: none;
}
/* footer */
#footer {
text-align: center;
}

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="123" height="44" viewBox="0 0 123 44" style="enable-background:new 0 0 123 44;" xml:space="preserve">
<style type="text/css">
.st0{fill:#292525;}
.st1{fill:#DA282A;}
.st2{fill:#FFFFFF;}
</style>
<path class="st0" d="M59.8,31.7C59.8,31.7,59.7,31.7,59.8,31.7c-0.1,0-0.2-0.1-0.2-0.1l-0.9-1.3c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0.1-0.1c0,0,0.1,0,0.1,0c6.6-0.6,11.8-2.9,17-7.5c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0.1
l0.9,1.1c0,0,0.1,0.1,0,0.2c0,0.1,0,0.1-0.1,0.1C71.6,28.6,66.6,30.8,59.8,31.7L59.8,31.7z" />
<path class="st0" d="M69.2,12.2h-1.5c-0.1,0-0.2,0.1-0.2,0.2v12.4c0,0.1,0.1,0.2,0.2,0.2h1.5c0.1,0,0.2-0.1,0.2-0.2V12.5
C69.4,12.3,69.3,12.2,69.2,12.2z" />
<path class="st0" d="M60.7,23.5H59c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1l3.6-7.9c0,0,0-0.1,0.1-0.1
c0,0,0.1,0,0.1,0h1.7c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1l-3.6,7.9c0,0,0,0.1-0.1,0.1
C60.8,23.5,60.7,23.5,60.7,23.5z" />
<path class="st0" d="M75.6,21c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1l-3.1-5.4c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0.1-0.1
c0,0,0.1,0,0.1,0h1.8c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1l3.1,5.4c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1
c0,0-0.1,0-0.1,0H75.6L75.6,21z" />
<path class="st0" d="M108.5,31.6C108.5,31.6,108.4,31.6,108.5,31.6l-1.4-0.5c-0.1,0-0.1-0.1-0.1-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1l0.8-2c0.3-0.8,0.6-1.6,0.8-2.5c0.3-1.3,0.4-2.5,0.4-3.8V14c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1
c5.3,0.1,7.5-0.5,10.3-1.3l0.7-0.2c0,0,0.1,0,0.1,0s0.1,0,0.1,0.1l0.8,1.2c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1
c0,0-0.1,0-0.1,0l-0.3,0.1c-2.9,0.9-5,1.5-9.8,1.6c-0.1,0-0.1,0-0.1,0.1c0,0-0.1,0.1-0.1,0.1v6.9c0,1.4-0.2,2.7-0.5,4.1l-0.1,0.3
c-0.2,0.8-0.4,1.7-0.8,2.5l-0.8,2C108.7,31.5,108.6,31.6,108.5,31.6C108.6,31.6,108.5,31.6,108.5,31.6z" />
<path class="st0" d="M105.6,22c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1L103,18c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1
c0,0,0,0,0.1-0.1l1.8-1.1c0,0,0.1,0,0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1l-1.9-2.9c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0,0,0.1-0.1l1.3-0.8c0,0,0.1,0,0.2,0c0.1,0,0.1,0,0.1,0.1l2.3,3.6c0,0,0,0,0,0.1l0.6,0.9
c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0-0.1,0.1l-1.8,1.1c0,0-0.1,0-0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1
c0,0,0,0.1,0,0.1l1.4,2.2c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.1-0.1,0.1l-1.3,0.8C105.7,22,105.6,22,105.6,22z" />
<path class="st0" d="M104.4,31.8C104.4,31.8,104.4,31.8,104.4,31.8l-1.5-0.6c0,0-0.1,0-0.1,0c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1l3.3-8.3c0,0,0-0.1,0-0.1c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0l1.4,0.6c0,0,0.1,0,0.1,0c0,0,0,0,0,0.1
c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1l-3.3,8.3c0,0,0,0,0,0.1C104.6,31.7,104.6,31.7,104.4,31.8C104.5,31.8,104.5,31.8,104.4,31.8z" />
<path class="st0" d="M120.8,31.7C120.8,31.7,120.8,31.7,120.8,31.7c-0.1,0-0.2-0.1-0.2-0.1l-1.6-2.3c-0.3-0.4-0.6-0.8-0.8-1.3
l-0.8-1.6c-0.3-0.6-0.6-1.3-0.8-2l-1.5-5.7c0,0,0-0.1-0.1-0.1c0,0-0.1,0-0.1,0c-0.4,0-0.7,0-1.1,0.1c0,0-0.1,0-0.1,0
c0,0,0,0.1,0,0.1v10.9c0,0.1,0,0.1,0.1,0.1c0,0,0.1,0.1,0.1,0.1h2.6c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.4
c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1h-4.3c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2V17.6c0,0,0,0,0-0.1V17
c0-0.1,0-0.1,0.1-0.2c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c2.4,0,4.7-0.3,7-0.8l0.9-0.2c0,0,0.1,0,0.1,0c0,0,0.1,0.1,0.1,0.1l0.7,1.3
c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1c0,0-0.1,0-0.1,0l-0.9,0.2c-1,0.2-1.9,0.4-2.9,0.6c0,0,0,0-0.1,0c0,0,0,0,0,0
c0,0,0,0,0,0.1c0,0,0,0,0,0.1l0.9,3.5c0,0,0,0.1,0,0.1c0,0,0,0,0.1,0.1c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l2.9-1.2c0,0,0.1,0,0.1,0
c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0.1l0.5,1.3c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.1l-3.2,1.3c0,0-0.1,0-0.1,0.1
c0,0,0,0.1,0,0.1c0.2,0.6,0.4,1.1,0.6,1.6l0.7,1.3c0.2,0.4,0.4,0.8,0.7,1.2l1.9,2.7c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1
c0,0,0,0.1,0,0.1c0,0-0.1,0-0.1,0.1L120.8,31.7C120.9,31.7,120.9,31.7,120.8,31.7z" />
<path class="st0" d="M86.8,22.3h-1.5c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2v-4c0,0,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1h-4
c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2v-1.4c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1h4c0,0,0.1,0,0.1-0.1c0,0,0.1-0.1,0.1-0.1
v-3.5c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1h1.5c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2V16c0,0,0,0.1,0.1,0.1
c0,0,0.1,0.1,0.1,0.1h4c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.4c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1h-4c0,0,0,0-0.1,0
c0,0,0,0-0.1,0c0,0,0,0,0,0.1c0,0,0,0,0,0.1v4c0,0.1,0,0.1-0.1,0.2C86.9,22.3,86.9,22.3,86.8,22.3z" />
<path class="st0" d="M83,16.2c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1l-1.5-2.8c0-0.1,0-0.1,0-0.2c0-0.1,0.1-0.1,0.1-0.1l1.4-0.7
c0.1,0,0.1,0,0.2,0c0.1,0,0.1,0.1,0.1,0.1l1.5,2.8c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1-0.1,0.1-0.1,0.1L83,16.2
C83,16.2,83,16.2,83,16.2z" />
<path class="st0" d="M89,16.2c0,0-0.1,0-0.1,0l-1.2-1c0,0-0.1-0.1-0.1-0.1c0-0.1,0-0.1,0-0.2l2.1-2.5c0,0,0.1-0.1,0.1-0.1
c0.1,0,0.1,0,0.2,0l1.2,1c0,0,0.1,0.1,0.1,0.1c0,0.1,0,0.1,0,0.2l-2.1,2.5C89.1,16.1,89.1,16.2,89,16.2C89.1,16.2,89,16.2,89,16.2z" />
<path class="st0" d="M82.2,22c-0.1,0-0.1,0-0.1-0.1l-1.2-1c0,0,0,0-0.1-0.1c0,0,0-0.1,0-0.1c0-0.1,0-0.1,0.1-0.2l2.2-2.6
c0,0,0,0,0.1-0.1c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l1.2,1c0,0,0,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1
c0,0,0,0.1,0,0.1l-2.2,2.6C82.4,21.9,82.3,21.9,82.2,22C82.3,22,82.3,22,82.2,22z" />
<path class="st0" d="M89.8,21.9C89.7,21.9,89.7,21.8,89.8,21.9c-0.1,0-0.2-0.1-0.2-0.1l-1.9-2.6c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1c0,0,0,0,0.1-0.1L89,18c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0.1l1.9,2.6c0,0,0,0.1,0,0.2
c0,0.1,0,0.1-0.1,0.1l-1.2,0.9C89.8,21.8,89.8,21.9,89.8,21.9z" />
<path class="st0" d="M91.6,30.2C91.6,30.2,91.7,30.2,91.6,30.2c0.1,0.1,0.1,0.1,0.1,0.2c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1l-1.2,1
c-0.1,0.1-0.1,0.1-0.2,0.1c-0.1,0-0.2,0-0.3,0l-3.6-1.7c0,0-0.1,0-0.1,0s-0.1,0-0.1,0l-1.4,0.6c-0.6,0.3-1.3,0.5-1.9,0.7l-1.7,0.5
c-0.1,0-0.1,0-0.2,0c-0.1,0-0.1-0.1-0.1-0.1l-0.3-1.1c0-0.1,0-0.1,0-0.2c0-0.1,0-0.1,0-0.2c0-0.1,0.1-0.1,0.1-0.1
c0,0,0.1-0.1,0.2-0.1l1.8-0.6c0.3-0.1,0.7-0.2,1-0.4l0.4-0.2l-0.8-0.4l-1.8-0.8c0,0-0.1,0-0.1-0.1c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1l1-1.8c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0-0.1-0.1-0.1-0.1c0,0-0.1,0-0.1,0h-1.1c-0.1,0-0.2,0-0.3-0.1
c-0.1-0.1-0.1-0.2-0.1-0.3v-0.9c0-0.1,0-0.2,0.1-0.3c0.1-0.1,0.2-0.1,0.3-0.1h2.5l0.3-0.5c0,0,0-0.1,0.1-0.1c0,0,0.1,0,0.1,0H86
c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1L86,23.3h5.2c0.1,0,0.2,0,0.3,0.1c0.1,0.1,0.1,0.2,0.1,0.3v1.6
c0,0.1,0,0.2,0,0.2c0,0.1-0.1,0.1-0.1,0.2l-1,1c-0.7,0.7-1.4,1.3-2.2,1.8L91.6,30.2z M84.4,26.9l1.8,0.8c0,0,0.1,0,0.1,0
c0,0,0.1,0,0.1,0l0.9-0.6c0.6-0.4,1.1-0.9,1.7-1.4l0.6-0.6h-4.4c-0.1,0-0.2,0-0.2,0.1c-0.1,0-0.1,0.1-0.2,0.2l-0.6,1
c0,0,0,0.1-0.1,0.2c0,0.1,0,0.1,0,0.2c0,0.1,0,0.1,0.1,0.1C84.3,26.9,84.3,26.9,84.4,26.9z" />
<path class="st0" d="M98,28l2.1,2.3c0,0,0.1,0.1,0.1,0.2c0,0.1,0,0.1-0.1,0.2l-1.1,1c0,0,0,0-0.1,0c0,0-0.1,0-0.1,0
c-0.1,0-0.1,0-0.2-0.1L96.2,29c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0.1l-2.5,2.7c0,0-0.1,0.1-0.2,0.1
c-0.1,0-0.1,0-0.2-0.1l-1.1-1c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1l2-2.1l0.8-0.9c0,0,0.1-0.1,0.1-0.1
c0-0.1,0-0.1-0.1-0.1l-0.2-0.3c-0.8-1-1.3-2.2-1.6-3.5l-0.2-0.7c-0.1-0.7-0.2-1.3-0.2-2v-4c0-0.1,0-0.1,0.1-0.2c0,0,0,0,0.1,0
c0,0,0.1,0,0.1,0h1.5c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v3.8c0,0.6,0.1,1.3,0.2,1.9l0.1,0.3c0.2,1,0.6,2,1.2,2.9
c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0.6-0.9,1-1.9,1.3-3l0-0.1c0.2-0.7,0.2-1.3,0.2-2v-4.4c0,0,0-0.1,0-0.1
c0,0-0.1,0-0.1,0h-4.9c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2v-2.9c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1h1.5
c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.3h5c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.4v5c0,0.7-0.1,1.4-0.2,2.2l-0.2,0.7
c-0.3,1.2-0.8,2.4-1.6,3.4L97.4,27c0,0-0.1,0.1-0.1,0.2c0,0.1,0,0.1,0.1,0.2L98,28z" />
<path class="st1" d="M22,44c12.2,0,22-9.8,22-22C44,9.8,34.2,0,22,0C9.8,0,0,9.8,0,22C0,34.2,9.8,44,22,44z" />
<path class="st2" d="M32,11.3l-21.2,3.6c-0.2,0-0.4,0.1-0.6,0.3c-0.1,0.2-0.2,0.4-0.3,0.6l-0.2,1.8c0,0.2,0,0.3,0,0.4
c0.1,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3,0.1,0.4,0l4.1-0.7v14.7c0,0.2,0,0.4,0.1,0.5c0.1,0.2,0.2,0.3,0.4,0.4
c0.2,0.1,0.3,0.1,0.5,0.1c0.2,0,0.4-0.1,0.5-0.2l1.6-1c0.2-0.1,0.3-0.3,0.4-0.5c0.1-0.2,0.2-0.4,0.2-0.6v-14l5.7-0.9v10.3
c0,0.6,0.2,1.3,0.5,1.8l2.5,4.7c0.1,0.1,0.2,0.3,0.3,0.3c0.1,0.1,0.3,0.1,0.4,0.1h2.2c0.1,0,0.3,0,0.4-0.1c0.1-0.1,0.2-0.2,0.3-0.3
c0.1-0.1,0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4l-2.5-4.9C28.2,27,28,26.4,28,25.7V15.7l4-0.7c0.2,0,0.4-0.1,0.5-0.3
c0.1-0.2,0.2-0.4,0.3-0.6l0.2-1.8c0-0.2,0-0.3,0-0.5s-0.1-0.3-0.2-0.4s-0.2-0.2-0.4-0.2C32.3,11.3,32.1,11.2,32,11.3z" />
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,16 @@
const addTaskButton = document.querySelector(".btn-add-task");
const formDiv = document.querySelector(".task-form");
const taskStatus = document.querySelector(".task-status");
const updateOp = document.querySelector(".update-op");
// show task form
if (addTaskButton) {
addTaskButton.addEventListener("click", () => {
// use inline style to override css property.
if (formDiv.style.display === "") {
formDiv.style.display = "none";
}
formDiv.style.display =
formDiv.style.display == "none" ? "flex" : "none";
});
}

View File

@@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block content %}
<article class="about">
<h1>{{ header }}</h1>
<section>
<p>
{% for content in about_content %}
{{ content }} <br />
{% endfor %}
</p>
<ul>
{% for item in items %}
<li>{{ item.0 }}{{ item.1 }}</li>
{% endfor %}
</ul>
</section>
</article>
{% endblock %}

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% if title == "home" %}
Todolist Web App
{% else %}
Todolist Web App - {% block title %}{% endblock %}
{% endif %}
</title>
{% load static %}
<link rel="stylesheet" href={% static "css/simple-v1.min.css" %}>
<link rel="stylesheet" href={% static "css/style.css" %}>
</head>
<body>
<div class="container">
<nav class="navbar">
<ul>
<li>
<a href="https://sspai.com" class="logo">
<img src={% static "images/sspai-logo-light.svg" %} alt="logo">
</a>
</li>
<li><a href={% url "index" %}>Home</a></li>
<li><a href={% url "tasks" %}>Task</a></li>
<li><a href={% url "about" %}>About</a></li>
</ul>
</nav>
<hr />
<main>
{% block content %}
{% endblock %}
</main>
<footer id="footer">
<p class="copyright">&copy; 2022-Present 100gle & 少数派</p>
</footer>
</div>
<script type="text/javascript" src={% static "js/main.js" %}></script>
</body>
</html>

View File

@@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block content%}
<article class="content">
<h1>🗣 <i>Hello, world</i></h1>
<section>
<p>{{ welcome }}</p>
<p>
{{ index_content }}
</p>
</section>
</article>
{% endblock%}

View File

@@ -0,0 +1,96 @@
{% extends "base.html" %}
{% block title%}
{{ title }}
{% endblock %}
{% block content %}
<div class="data-table">
<h1>Tasks</h1>
{% if not tasks %}
<p>🤔 Whoops……似乎任务都做完了那么可以点击下方的按钮添加任务</p>
{% else %}
<table>
<thead>
<tr>
{% for field in fields %}
<th>{{ field }}</th>
{% endfor%}
<th>操作</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr>
<td>{{ task.id }}</td>
<td>{{ task.name }}</td>
<td>
{% if task.priority == 1 %}
{{ "一般" }}
{% elif task.priority == 2 %}
{{ "优先" }}
{% else %}
{{ "紧急" }}
{% endif %}
</td>
<td class="task-decs">{{ task.description }}</td>
<td class="task-status">
{% if task.is_done %}
{{ "已完成" }}
{% else %}
{{ "未完成" }}
{% endif %}
</td>
<td>{{ task.group.name }}</td>
<td>
<div class="ops">
<span>
<a href="/tasks/update/{{task.id}}">更新</a>
</span>
<span>
<a href="/tasks/delete/{{task.id}}">删除</a>
</span>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
<div class="btn-add-task">
<button>+</button>
</div>
<div class="task-form">
<form action={% url "create_task" %} method="post" class="form">
{% csrf_token %}
<div>
<label for="taskName">任务名称:</label>
<input type="text" name="taskName" id="taskName" placeholder="请输入待办事项名称" />
</div>
<div>
<label for="taskPriority">优先级:</label>
<select name="taskPriority" id="taskPriority">
<option value="1" selected>一般</option>
<option value="2">优先</option>
<option value="3">紧急</option>
</select>
</div>
<div>
<label for="taskGroup"> 分组: </label>
<select name="taskGroup" id="taskGroup">
{% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="task-textarea">任务备注:</label>
<textarea name="taskDescription" id="task-textarea" rows="5" placeholder="备注内容(可选)"></textarea>
</div>
<div class="btn-submit">
<input type="submit" value="添加" />
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,16 @@
"""
ASGI config for todolist project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todolist.settings')
application = get_asgi_application()

View File

@@ -0,0 +1,126 @@
"""
Django settings for todolist project.
Generated by 'django-admin startproject' using Django 4.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-h$0(p$ca027=i@rg(!3ut8zm@((lfkvjdc%=g$f6gecl#5c)ly"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"popup",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "todolist.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "todolist.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = "zh-Hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = "static/"
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

View File

@@ -0,0 +1,22 @@
"""todolist URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("popup.urls")),
]

View File

@@ -0,0 +1,16 @@
"""
WSGI config for todolist project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todolist.settings')
application = get_wsgi_application()