feat: 现代化翻页组件 + 完整项目更新
- 新增 Pagination 通用组件:首页/上一页/页码/下一页/末页导航 - PickModal 和 SwapModal 集成分页功能,每页20项 - 搜索/筛选切换自动回到第1页 - 现代化CSS样式:hover浮起动效、主色高亮、移动端适配 - 新增 dashboard、admin 管理模板 - 菜系(cuisine)模型和迁移 - 构建脚本 build.py 支持 PyInstaller 打包 - 前端资源重新构建
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""Step 1: Create the Cuisine table (no changes to Dish yet)."""
|
||||
|
||||
dependencies = [
|
||||
('meals', '0005_dish_pinyin'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Cuisine',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, unique=True, verbose_name='菜系名称')),
|
||||
('description', models.CharField(blank=True, default='', max_length=200, verbose_name='描述')),
|
||||
('sort_order', models.IntegerField(default=0, help_text='数值越小越靠前', verbose_name='排序')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='启用')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '菜系',
|
||||
'verbose_name_plural': '菜系',
|
||||
'ordering': ['sort_order', 'id'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,93 @@
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
CUISINE_SEEDS = [
|
||||
('家常菜', 0),
|
||||
('本帮菜', 10),
|
||||
('江浙菜', 20),
|
||||
('川菜', 30),
|
||||
('粤菜', 40),
|
||||
('鲁菜', 50),
|
||||
('湘菜', 60),
|
||||
('西北菜', 70),
|
||||
('面点小吃', 80),
|
||||
]
|
||||
|
||||
|
||||
def seed_cuisines(apps, schema_editor):
|
||||
"""Create Cuisine rows and save dish->cuisine mapping to a temp table."""
|
||||
Cuisine = apps.get_model('meals', 'Cuisine')
|
||||
|
||||
name_to_id = {}
|
||||
for name, order in CUISINE_SEEDS:
|
||||
obj, _ = Cuisine.objects.get_or_create(name=name, defaults={'sort_order': order})
|
||||
name_to_id[name] = obj.pk
|
||||
|
||||
from django.db import connection
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT id, cuisine FROM meals_dish WHERE cuisine IS NOT NULL AND cuisine != ''")
|
||||
rows = cursor.fetchall()
|
||||
|
||||
for _, cuisine_name in rows:
|
||||
if cuisine_name not in name_to_id:
|
||||
obj, _ = Cuisine.objects.get_or_create(name=cuisine_name, defaults={'sort_order': 90})
|
||||
name_to_id[cuisine_name] = obj.pk
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("CREATE TABLE _temp_dish_cuisine (dish_id INTEGER, cuisine_id INTEGER)")
|
||||
for dish_id, cuisine_name in rows:
|
||||
cuisine_id = name_to_id.get(cuisine_name)
|
||||
if cuisine_id:
|
||||
cursor.execute(
|
||||
"INSERT INTO _temp_dish_cuisine (dish_id, cuisine_id) VALUES (%s, %s)",
|
||||
[dish_id, cuisine_id],
|
||||
)
|
||||
|
||||
|
||||
def update_cuisine_fk(apps, schema_editor):
|
||||
"""Populate Dish.cuisine_id from the temp table, then clean up."""
|
||||
from django.db import connection
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"UPDATE meals_dish SET cuisine_id = "
|
||||
"(SELECT cuisine_id FROM _temp_dish_cuisine "
|
||||
"WHERE _temp_dish_cuisine.dish_id = meals_dish.id) "
|
||||
"WHERE EXISTS (SELECT 1 FROM _temp_dish_cuisine "
|
||||
"WHERE _temp_dish_cuisine.dish_id = meals_dish.id)"
|
||||
)
|
||||
cursor.execute("DROP TABLE _temp_dish_cuisine")
|
||||
|
||||
|
||||
def reverse_seed(apps, schema_editor):
|
||||
"""Reverse: clean up temp table and delete Cuisine rows."""
|
||||
from django.db import connection
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("DROP TABLE IF EXISTS _temp_dish_cuisine")
|
||||
Cuisine = apps.get_model('meals', 'Cuisine')
|
||||
Cuisine.objects.all().delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""Step 2: Seed cuisines from existing data, then alter Dish.cuisine to FK."""
|
||||
|
||||
dependencies = [
|
||||
('meals', '0006_cuisine_dish_cuisine_fk'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(seed_cuisines, reverse_seed),
|
||||
migrations.AlterField(
|
||||
model_name='dish',
|
||||
name='cuisine',
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='dishes',
|
||||
to='meals.cuisine',
|
||||
verbose_name='菜系',
|
||||
),
|
||||
),
|
||||
migrations.RunPython(update_cuisine_fk, migrations.RunPython.noop),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.8 on 2026-08-27 06:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('meals', '0007_seed_cuisines_and_update_dish'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='cuisine',
|
||||
name='id',
|
||||
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user