From 54819c6861cd7e9d25fb9f6d7c91420ab74cfb5d Mon Sep 17 00:00:00 2001 From: Ching Date: Sun, 13 Feb 2022 01:18:02 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(recipe=20model=EF=BC=9Brecipe=20api):?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=20ingredient=20=E5=92=8C=20recipe-ingredi?= =?UTF-8?q?ent=20model;=20=E5=A2=9E=E5=8A=A0=E7=9B=B8=E5=85=B3=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加 ingredient 和 recipe-ingredient model; 增加相关 API Signed-off-by: Ching --- develop_requirements.txt | 1 + dsite/urls.py | 2 +- .../0004_ingredient_recipeingredient.py | 45 ++++++++++++++ recipe/models.py | 33 ++++++++++- recipe/serializers.py | 58 ++++++++++++++++++- recipe/urls.py | 14 +++-- recipe/views.py | 56 ++++++++++++++---- utils/const.py | 3 + 8 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 recipe/migrations/0004_ingredient_recipeingredient.py diff --git a/develop_requirements.txt b/develop_requirements.txt index d7f671b..53f564a 100644 --- a/develop_requirements.txt +++ b/develop_requirements.txt @@ -33,3 +33,4 @@ zipp==3.5.0 redis==4.1.0 black instagram_private_api +drf-nested-routers diff --git a/dsite/urls.py b/dsite/urls.py index 87236df..8d6bd53 100644 --- a/dsite/urls.py +++ b/dsite/urls.py @@ -18,8 +18,8 @@ Including another URLconf from django.urls import include, path from django.conf.urls import url -from rest_framework import routers from rest_framework.authtoken import views +from rest_framework_nested import routers router = routers.DefaultRouter() # Wire up our API using automatic URL routing. diff --git a/recipe/migrations/0004_ingredient_recipeingredient.py b/recipe/migrations/0004_ingredient_recipeingredient.py new file mode 100644 index 0000000..287c3c9 --- /dev/null +++ b/recipe/migrations/0004_ingredient_recipeingredient.py @@ -0,0 +1,45 @@ +# Generated by Django 3.2.6 on 2022-02-12 13:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [('recipe', '0003_recipe_status')] + + operations = [ + migrations.CreateModel( + name='Ingredient', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128)), + ('unit', models.CharField(max_length=32)), + ('status', models.CharField(default='active', max_length=32)), + ], + ), + migrations.CreateModel( + name='RecipeIngredient', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.FloatField()), + ( + 'ingredient', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='recipe_ingredients', + to='recipe.ingredient', + ), + ), + ( + 'recipe', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='recipe_ingredients', + to='recipe.recipe', + ), + ), + ('status', models.CharField(default='active', max_length=32)), + ], + ), + ] diff --git a/recipe/models.py b/recipe/models.py index 52b566f..0348c7e 100644 --- a/recipe/models.py +++ b/recipe/models.py @@ -6,6 +6,27 @@ from utils import const import utils +class Ingredient(models.Model): + name = models.CharField(max_length=128) + unit = models.CharField(max_length=32) + status = models.CharField(max_length=32, default=const.INGREDIENT_STATUS_ACTIVE) + + +class RecipeIngredient(models.Model): + recipe = models.ForeignKey('Recipe', on_delete=models.CASCADE, related_name='recipe_ingredients') + ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE, related_name='recipe_ingredients') + status = models.CharField(max_length=32, default=const.INGREDIENT_STATUS_ACTIVE) + quantity = models.FloatField() + + def serialize(self): + return { + 'id': self.id, + 'recipe': {'id': self.recipe.id, 'name': self.recipe.name}, + 'ingredient': {'id': self.ingredient.id, 'name': self.ingredient.name, 'unit': self.ingredient.unit}, + 'quantity': self.quantity, + } + + class Recipe(models.Model): name = models.CharField(max_length=128) recipe_type = models.CharField(max_length=32, default=const.RECIPE_TYPE_MEAT) @@ -17,10 +38,20 @@ class Recipe(models.Model): def serialize(self, verbose=False): data = {'id': self.id, 'name': self.name, 'recipe_type': self.recipe_type} if verbose: - data.update({'difficulty': self.difficulty, 'rate': self.rate, 'note': self.note}) + data.update( + { + 'difficulty': self.difficulty, + 'rate': self.rate, + 'note': self.note, + 'recipe_ingredients': self.get_ingredients(), + } + ) return data + def get_ingredients(self): + return [i.serialize() for i in self.recipeingredient_set.order_by('id')] + @classmethod def create_from_str(cls, content): content = content.strip() diff --git a/recipe/serializers.py b/recipe/serializers.py index d5c8050..92d89a2 100644 --- a/recipe/serializers.py +++ b/recipe/serializers.py @@ -1,16 +1,68 @@ -from os import read -from django.contrib.auth.models import User, Group from rest_framework import serializers import recipe.models +from utils import const + + +class IngredientSerializer(serializers.ModelSerializer): + class Meta: + model = recipe.models.Ingredient + fields = '__all__' + + +class RecipeIngredientSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(read_only=True) + ingredient = IngredientSerializer() + + def update(self, instance, validated_data): + if 'ingredient' in validated_data: + validated_data.pop('ingredient') + if 'recipe' in validated_data: + validated_data.pop('recipe') + return super().update(instance, validated_data) + + class Meta: + model = recipe.models.RecipeIngredient + fields = '__all__' class RecipeSerializer(serializers.ModelSerializer): id = serializers.IntegerField(read_only=True) + recipe_ingredients = RecipeIngredientSerializer(many=True) + + def update(self, instance, validated_data): + if 'recipe_ingredients' in validated_data: + recipe_ingredients = validated_data.pop('recipe_ingredients') + if recipe_ingredients: + for recipe_ingredient in recipe_ingredients: + recipe_ingredient['recipe'] = instance + RecipeIngredientSerializer.update(recipe_ingredient) + return super().update(instance, validated_data) + + def create(self, validated_data): + if 'recipe_ingredients' in validated_data: + recipe_ingredients = validated_data.pop('recipe_ingredients') + instance = super().create(validated_data) + if recipe_ingredients: + for recipe_ingredient in recipe_ingredients: + recipe_ingredient['recipe'] = instance + RecipeIngredientSerializer.create(recipe_ingredient) + return instance + + @property + def data(self): + # exclude deleted recipe_ingredients + data_ = super().data + data_['recipe_ingredients'] = [ + ingredient + for ingredient in data_['recipe_ingredients'] + if ingredient['status'] != const.INGREDIENT_STATUS_DELETED + ] + return data_ class Meta: model = recipe.models.Recipe - fields = '__all__' + fields = ('recipe_ingredients', 'id', 'name', 'recipe_type', 'status', 'note', 'rate', 'difficulty') class WeekRecipeSerializer(serializers.ModelSerializer): diff --git a/recipe/urls.py b/recipe/urls.py index 4c3c1b1..6501b7c 100644 --- a/recipe/urls.py +++ b/recipe/urls.py @@ -1,16 +1,22 @@ from django.conf.urls import include, url -# from django.core.urlresolvers import reverse from django.urls import path -from rest_framework import routers from recipe import views # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. +from rest_framework_nested import routers + +router = routers.DefaultRouter() +router.register(r'recipe', views.RecipeAPI) +router.register(r'ingredient', views.IngredientAPI) +recipe_nested_router = routers.NestedSimpleRouter(router, r'recipe', lookup='recipe') +recipe_nested_router.register(r'recipe-ingredient', views.RecipeIngredientAPI) + urlpatterns = [ - url(r'^recipe/(?P\d+)$', views.RecipeAPI.as_view(), name='recipe-detail'), - url(r'^recipe/$', views.RecipeListAPI.as_view()), url(r'^week-recipe/$', views.WeekRecipeListAPI.as_view()), url(r'^daily-recipe/(?P\d+)$', views.DailyRecipeAPI.as_view(), name='dailyrecipe-detail'), + path(r'', include(router.urls)), + path(r'', include(recipe_nested_router.urls)), ] diff --git a/recipe/views.py b/recipe/views.py index 436402b..3739297 100644 --- a/recipe/views.py +++ b/recipe/views.py @@ -12,7 +12,7 @@ import recipe.serializers from utils import const -class RecipeAPI(rest_framework.generics.RetrieveUpdateDestroyAPIView): +class RecipeAPI(viewsets.ModelViewSet): # authentication_classes = (authentication.TokenAuthentication, # authentication.SessionAuthentication, @@ -25,15 +25,10 @@ class RecipeAPI(rest_framework.generics.RetrieveUpdateDestroyAPIView): instance.status = const.RECIPE_STATUS_DELETED instance.save(update_fields=['status']) + def list(self, request, *args, **kwargs): + self.queryset = recipe.models.Recipe.objects.exclude(status=const.RECIPE_STATUS_DELETED) + return super().list(request, *args, **kwargs) -class RecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics.CreateAPIView): - - # authentication_classes = (authentication.TokenAuthentication, - # authentication.SessionAuthentication, - # authentication.BasicAuthentication) - # permission_classes = (permissions.IsAuthenticated,) - queryset = recipe.models.Recipe.objects.exclude(status=const.RECIPE_STATUS_DELETED) - serializer_class = recipe.serializers.RecipeSerializer filterset_fields = { 'recipe_type': const.FILTER_EXACT, 'difficulty': const.FILTER_GTE_LTE, @@ -41,7 +36,7 @@ class RecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics } -class WeekRecipeListAPI(rest_framework.generics.ListAPIView, rest_framework.generics.CreateAPIView): +class WeekRecipeListAPI(rest_framework.generics.ListCreateAPIView): queryset = recipe.models.DailyRecipe.objects.all() serializer_class = recipe.serializers.WeekRecipeSerializer @@ -78,3 +73,44 @@ class DailyRecipeAPI(rest_framework.generics.RetrieveUpdateAPIView): recipes.extend(request.data.get('soup', [])) daily_recipe.recipes.set(recipe.models.Recipe.objects.filter(id__in=recipes)) return Response(daily_recipe.serialize(), status=status.HTTP_201_CREATED, headers={}) + + +class IngredientAPI(viewsets.ModelViewSet): + + # authentication_classes = (authentication.TokenAuthentication, + # authentication.SessionAuthentication, + # authentication.BasicAuthentication) + # permission_classes = (permissions.IsAuthenticated,) + queryset = recipe.models.Ingredient.objects.all() + serializer_class = recipe.serializers.IngredientSerializer + + def list(self, request, *args, **kwargs): + self.queryset = recipe.models.Ingredient.objects.exclude(status=const.INGREDIENT_STATUS_DELETED) + return super().list(request, *args, **kwargs) + + def perform_destroy(self, instance): + instance.status = const.INGREDIENT_STATUS_DELETED + instance.save(update_fields=['status']) + + +class RecipeIngredientAPI(viewsets.ModelViewSet): + + # authentication_classes = (authentication.TokenAuthentication, + # authentication.SessionAuthentication, + # authentication.BasicAuthentication) + # permission_classes = (permissions.IsAuthenticated,) + queryset = recipe.models.RecipeIngredient.objects.exclude(status=const.INGREDIENT_STATUS_DELETED) + serializer_class = recipe.serializers.RecipeIngredientSerializer + + def get_queryset(self): + return self.queryset.filter(recipe=self.kwargs['recipe_pk']) + + def perform_destroy(self, instance): + instance.status = const.INGREDIENT_STATUS_DELETED + instance.save(update_fields=['status']) + + def create(self, request, *args, **kwargs): + recipe_ingredient = recipe.models.RecipeIngredient.objects.create( + recipe_id=kwargs['recipe_pk'], ingredient_id=request.data['ingredient'], quantity=request.data['quantity'] + ) + return Response(recipe_ingredient.serialize(), status=status.HTTP_201_CREATED, headers={}) diff --git a/utils/const.py b/utils/const.py index ff00587..23c6d0a 100644 --- a/utils/const.py +++ b/utils/const.py @@ -14,6 +14,9 @@ RECIPE_TYPE_SOUP = 'soup' RECIPE_STATUS_ACTIVE = 'active' RECIPE_STATUS_DELETED = 'deleted' +INGREDIENT_STATUS_ACTIVE = 'active' +INGREDIENT_STATUS_DELETED = 'deleted' + RECIPE_TYPE_CHOICE = [RECIPE_TYPE_MEAT, RECIPE_TYPE_VEGETABLE, RECIPE_TYPE_SOUP] FILTER_EXACT = ['exact'] From 1c424275f1a88b1378fcdd246779ed9581d87a36 Mon Sep 17 00:00:00 2001 From: Ching Date: Sun, 13 Feb 2022 18:01:51 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(recipe=20edit=20page):=20=E8=8F=9C?= =?UTF-8?q?=E8=B0=B1=E7=BC=96=E8=BE=91=E9=A1=B5=E5=A2=9E=E5=8A=A0=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=A3=9F=E6=9D=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 菜谱编辑页增加添加食材功能 Signed-off-by: Ching --- dsite/settings_default.py | 1 - frontend/src/components/input_recipe.vue | 4 +- .../components/recipe-mobile/recipe-edit.vue | 153 ++++++++++++++++-- .../src/views/recipe-mobile/RecipeDetail.vue | 2 +- frontend/src/views/recipeDetail.vue | 12 +- recipe/serializers.py | 13 +- recipe/views.py | 28 ++++ utils/depoly_notify.py | 1 - 8 files changed, 175 insertions(+), 39 deletions(-) diff --git a/dsite/settings_default.py b/dsite/settings_default.py index f2065ab..2495fb8 100644 --- a/dsite/settings_default.py +++ b/dsite/settings_default.py @@ -27,7 +27,6 @@ DEBUG = True ALLOWED_HOSTS = [] - # Application definition INSTALLED_APPS = [ diff --git a/frontend/src/components/input_recipe.vue b/frontend/src/components/input_recipe.vue index dda6ea6..7dafe3b 100644 --- a/frontend/src/components/input_recipe.vue +++ b/frontend/src/components/input_recipe.vue @@ -138,7 +138,7 @@ export default { }); } else { axios - .put(config.publicPath + '/recipe/recipe/' + recipe_id, data) + .put(config.publicPath + '/recipe/recipe/' + recipe_id + '/', data) .then(function () { ElMessage({ message: '修改成功.', @@ -152,7 +152,7 @@ export default { }, onSubmitDelete(recipe_id) { axios - .delete(config.publicPath + '/recipe/recipe/' + recipe_id) + .delete(config.publicPath + '/recipe/recipe/' + recipe_id + '/') .then(function () { ElMessage.error('删除成功.'); location.reload(); diff --git a/frontend/src/components/recipe-mobile/recipe-edit.vue b/frontend/src/components/recipe-mobile/recipe-edit.vue index 3a8bd98..5502ca9 100644 --- a/frontend/src/components/recipe-mobile/recipe-edit.vue +++ b/frontend/src/components/recipe-mobile/recipe-edit.vue @@ -42,6 +42,57 @@ type="textarea" /> + + + + + + + +
+ + + 添加食材 + + +
+ + + +
@@ -93,6 +144,7 @@ import { Form, Field, CellGroup, + Cell, Radio, RadioGroup, Rate, @@ -100,11 +152,15 @@ import { Toast, Col, Row, + Stepper, + Popup, + Picker, } from 'vant'; import axios from 'axios'; import config from '@/config/index'; import router from '@/router/index'; import constants from '@/utils/constants.js'; +import { ref } from 'vue'; export default { props: ['recipe_'], @@ -118,14 +174,23 @@ export default { [Form.name]: Form, [Field.name]: Field, [CellGroup.name]: CellGroup, + [Cell.name]: Cell, [Radio.name]: Radio, [RadioGroup.name]: RadioGroup, [Rate.name]: Rate, [Button.name]: Button, [Col.name]: Col, [Row.name]: Row, + [Stepper.name]: Stepper, + [Popup.name]: Popup, + [Picker.name]: Picker, }, data() { + const customFieldName = { + text: 'name', + }; + const showIngredientPicker = ref(false); + return { form: { name: null, @@ -133,12 +198,18 @@ export default { rate: 0, difficulty: 0, note: null, + recipe_ingredients: null, }, loading: false, recipe_id: null, + ingredient_columns: null, + customFieldName, + showIngredientPicker, }; }, - mounted() {}, + mounted() { + this.getIngredients(); + }, methods: { onSubmit(recipe_id) { if (!this.form.name) { @@ -153,6 +224,9 @@ export default { difficulty: this.form.difficulty, rate: this.form.rate, note: this.form.note ? this.form.note : null, + recipe_ingredients: this.form.recipe_ingredients + ? this.form.recipe_ingredients + : [], }; if (!recipe_id) { axios @@ -161,14 +235,19 @@ export default { (response) => (response, router.push({ name: 'RecipeMobileHome' })) ); } else { - axios.put(config.publicPath + '/recipe/recipe/' + recipe_id, data).then( - (Toast.success({ - message: '修改成功', - forbidClick: true, - duration: 500, - }), - (this.loading = false)) - ); + axios + .put(config.publicPath + '/recipe/recipe/' + recipe_id + '/', data) + .then( + (response) => ( + response, + Toast.success({ + message: '修改成功', + forbidClick: true, + duration: 500, + }), + (this.loading = false) + ) + ); } }, onSubmitDelete(recipe_id) { @@ -176,14 +255,56 @@ export default { return; } this.loading = true; - axios.delete(config.publicPath + '/recipe/recipe/' + recipe_id).then( - (Toast.success({ - message: '删除成功', + axios + .delete(config.publicPath + '/recipe/recipe/' + recipe_id + '/') + .then( + (Toast.success({ + message: '删除成功', + forbidClick: true, + duration: 500, + }), + (this.loading = false)) + ); + }, + getIngredients() { + axios.get(config.publicPath + '/recipe/ingredient/').then((response) => { + (this.ingredient_columns = response.data['results']), + console.log('ingredient_columns', this.ingredient_columns); + }); + }, + onConfirmIngredient(value) { + var exists = false; + this.showIngredientPicker = false; + if (this.form.recipe_ingredients === null) { + this.form.recipe_ingredients = []; + } + for (let i = 0; i < this.form.recipe_ingredients.length; i++) { + if (this.form.recipe_ingredients[i].ingredient.id == value.id) { + exists = true; + break; + } + } + if (exists) { + Toast.fail({ + message: '该食材已存在', forbidClick: true, - duration: 500, - }), - (this.loading = false)) - ); + duration: 800, + }); + } else { + this.form.recipe_ingredients.push({ + id: 0, + ingredient: { + id: value.id, + name: value.name, + unit: value.unit, + }, // 添加的食材 + }); + } + }, + onChangeIngredientQuantity(value) { + console.log('onChangeIngredientQuantity', value); + console.log('recipe ingerdients ', this.form.recipe_ingredients); + // this.form.recipe_ingredients[value.index].quantity = value.quantity; }, }, }; diff --git a/frontend/src/views/recipe-mobile/RecipeDetail.vue b/frontend/src/views/recipe-mobile/RecipeDetail.vue index e26ec75..b58be04 100644 --- a/frontend/src/views/recipe-mobile/RecipeDetail.vue +++ b/frontend/src/views/recipe-mobile/RecipeDetail.vue @@ -21,7 +21,7 @@ export default { }, mounted() { axios - .get(config.publicPath + '/recipe/recipe/' + this.$route.params.id) + .get(config.publicPath + '/recipe/recipe/' + this.$route.params.id + '/') .then((response) => (this.recipe = response.data)); }, }; diff --git a/frontend/src/views/recipeDetail.vue b/frontend/src/views/recipeDetail.vue index 4489ce3..e66c809 100644 --- a/frontend/src/views/recipeDetail.vue +++ b/frontend/src/views/recipeDetail.vue @@ -2,12 +2,8 @@ - - 首页 - - - 每周菜谱 - + 首页 + 每周菜谱 {{ recipe.name }} @@ -33,7 +29,7 @@ import config from '@/config/index'; export default { name: 'RecipeDetail', components: { input_recipe }, - data: function() { + data: function () { return { recipe: {}, constants: constants, @@ -41,7 +37,7 @@ export default { }, mounted() { axios - .get(config.publicPath + '/recipe/recipe/' + this.$route.params.id) + .get(config.publicPath + '/recipe/recipe/' + this.$route.params.id + '/') .then((response) => (this.recipe = response.data)); }, methods: { diff --git a/recipe/serializers.py b/recipe/serializers.py index 92d89a2..784d918 100644 --- a/recipe/serializers.py +++ b/recipe/serializers.py @@ -5,6 +5,8 @@ from utils import const class IngredientSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(read_only=True) + class Meta: model = recipe.models.Ingredient fields = '__all__' @@ -28,16 +30,7 @@ class RecipeIngredientSerializer(serializers.ModelSerializer): class RecipeSerializer(serializers.ModelSerializer): id = serializers.IntegerField(read_only=True) - recipe_ingredients = RecipeIngredientSerializer(many=True) - - def update(self, instance, validated_data): - if 'recipe_ingredients' in validated_data: - recipe_ingredients = validated_data.pop('recipe_ingredients') - if recipe_ingredients: - for recipe_ingredient in recipe_ingredients: - recipe_ingredient['recipe'] = instance - RecipeIngredientSerializer.update(recipe_ingredient) - return super().update(instance, validated_data) + recipe_ingredients = RecipeIngredientSerializer(many=True, read_only=True) def create(self, validated_data): if 'recipe_ingredients' in validated_data: diff --git a/recipe/views.py b/recipe/views.py index 3739297..57d316d 100644 --- a/recipe/views.py +++ b/recipe/views.py @@ -25,6 +25,34 @@ class RecipeAPI(viewsets.ModelViewSet): instance.status = const.RECIPE_STATUS_DELETED instance.save(update_fields=['status']) + def perform_update(self, serializer): + instance = self.get_object() + if 'recipe_ingredients' in self.request.data: + recipe_ingredients = self.request.data.pop('recipe_ingredients') + if recipe_ingredients: + for recipe_ingredient in recipe_ingredients: + # 没有 id 则新增食材 + if not recipe_ingredient['id'] and recipe_ingredient['quantity']: + ingredient = recipe.models.Ingredient.objects.get(id=recipe_ingredient['ingredient']['id']) + del recipe_ingredient['ingredient'] + del recipe_ingredient['id'] + recipe.models.RecipeIngredient.objects.create( + recipe=instance, ingredient=ingredient, **recipe_ingredient + ) + # 有 id 没数量则删除食材 + elif recipe_ingredient['id'] and not recipe_ingredient['quantity']: + recipe.models.RecipeIngredient.objects.filter(id=recipe_ingredient['id']).update( + status=const.INGREDIENT_STATUS_DELETED + ) + # 有 id 有数量则更新食材 + elif recipe_ingredient['id'] and recipe_ingredient['quantity']: + del recipe_ingredient['ingredient'] + recipe.models.RecipeIngredient.objects.filter(id=recipe_ingredient['id']).update( + **recipe_ingredient + ) + + return super().perform_update(serializer) + def list(self, request, *args, **kwargs): self.queryset = recipe.models.Recipe.objects.exclude(status=const.RECIPE_STATUS_DELETED) return super().list(request, *args, **kwargs) diff --git a/utils/depoly_notify.py b/utils/depoly_notify.py index deda3ac..de3c9d0 100755 --- a/utils/depoly_notify.py +++ b/utils/depoly_notify.py @@ -10,7 +10,6 @@ reload(sys) import utils.lark import git -import ipdb if __name__ == '__main__': # def notify(): From 69a55778ec315dd327bbf4efb2b99ffde0d713d8 Mon Sep 17 00:00:00 2001 From: Ching Date: Sun, 13 Feb 2022 20:06:31 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(recipe=20edit=20page):=20=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E8=8F=9C=E8=B0=B1=E9=A1=B5=E9=9D=A2=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E9=A3=9F=E6=9D=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 编辑菜谱页面增加创建食材功能 Signed-off-by: Ching --- .../components/recipe-mobile/recipe-edit.vue | 137 ++++++++++++++---- recipe/views.py | 2 +- 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/frontend/src/components/recipe-mobile/recipe-edit.vue b/frontend/src/components/recipe-mobile/recipe-edit.vue index 5502ca9..eab9d62 100644 --- a/frontend/src/components/recipe-mobile/recipe-edit.vue +++ b/frontend/src/components/recipe-mobile/recipe-edit.vue @@ -70,7 +70,19 @@
- + + 创建食材 + +
+ + + + + + +
@@ -100,7 +137,6 @@ class="submit-button" round type="danger" - plain hairline :disabled="disable_submit" @click="onSubmitDelete(recipe_id)" @@ -113,7 +149,6 @@ class="submit-button" round type="primary" - plain hairline :disabled="disable_submit" @click="onSubmit(recipe_id)" @@ -126,7 +161,6 @@ class="submit-button" round type="primary" - plain hairline :disabled="disable_submit" @click="onSubmit(recipe_id)" @@ -141,25 +175,26 @@