Skip to content

Commit 96531af

Browse files
committed
添加菜谱删除功能,更新路由和视图以支持删除操作;新增 GitHub Copilot 使用说明和样式指南
1 parent 0775280 commit 96531af

6 files changed

Lines changed: 96 additions & 1 deletion

File tree

.github/instructions.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# GitHub Copilot Instructions
2+
3+
## Project Overview
4+
This is a CRUD recipe application built with Node.js, Express, and Handlebars templating engine. The app allows users to create, read, update, and delete recipes with ingredients and cooking methods.
5+
6+
## Technology Stack
7+
- **Backend**: Node.js with Express.js
8+
- **Database**: SQLite with custom database layer
9+
- **Templating**: Handlebars.js
10+
- **Testing**: Jest
11+
- **Styling**: Vanilla CSS
12+
13+
## Code Style Guidelines
14+
- Use ES6+ features and async/await for asynchronous operations
15+
- Follow RESTful routing conventions for recipe operations
16+
- Use semantic HTML and BEM-style CSS classes where appropriate
17+
- Keep database queries in the `src/database/` directory
18+
- Store all view templates in the `views/` directory with Handlebars syntax
19+
20+
## Key Patterns
21+
- Database operations should use the custom connection layer in `src/database/`
22+
- Routes are defined in `src/routes.js` following Express patterns
23+
- Handlebars helpers for text formatting (split, newline, add) are available
24+
- Use proper error handling for database operations and route handlers
25+
- Follow the existing form structure for CRUD operations
26+
27+
## Testing
28+
- Test files are in `__tests__/` directory
29+
- Use Jest for unit and integration testing
30+
- Include tests for both database operations and route handlers
31+
- Mock database connections in tests using the test setup files
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
applyTo: "public/*.css"
3+
---
4+
5+
- Use semantic class names (`.recipe-*`, `.btn-*`, `.form-*`)
6+
- Follow BEM methodology where appropriate
7+
- Use consistent spacing units (8px, 16px, 24px)
8+
- Use CSS custom properties for colors
9+
- Mobile-first responsive design
10+
- Use flexbox for layouts
11+
- Ensure accessibility with proper contrast and focus states
12+
- Ensure buttons have adequate touch targets (44px minimum)

.vscode/mcp.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"servers": {
3+
"github": {
4+
"type": "http",
5+
"url": "https://api.githubcopilot.com/mcp/"
6+
}
7+
}
8+
}

__tests__/routes.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,27 @@ describe('Routes', () => {
6464
expect(recipe).toBeDefined();
6565
expect(recipe.title).toBe(newRecipe.title);
6666
});
67+
68+
test('POST /recipes/:id/delete should delete a recipe', async () => {
69+
// 先插入一条菜谱
70+
const recipe = {
71+
title: 'Delete Me',
72+
ingredients: 'To be deleted',
73+
method: 'To be deleted'
74+
};
75+
await request(app).post('/recipes').send(recipe);
76+
const created = await db.get('SELECT * FROM recipes WHERE title = ?', [recipe.title]);
77+
expect(created).toBeDefined();
78+
79+
// 执行删除
80+
const response = await request(app)
81+
.post(`/recipes/${created.id}/delete`)
82+
.send();
83+
expect(response.status).toBe(302);
84+
expect(response.headers.location).toBe('/recipes');
85+
86+
// 验证已删除
87+
const deleted = await db.get('SELECT * FROM recipes WHERE id = ?', [created.id]);
88+
expect(deleted).toBeUndefined();
89+
});
6790
});

src/routes.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ router.get('/', (req, res) => {
99

1010
router.get('/recipes', async (req, res) => {
1111
const db = await getDbConnection()
12-
const recipes = await db.all('SELECT * FROM recipes')
12+
const recipes = await db.all('SELECT * FROM recipes ORDER BY title DESC')
1313
res.render('recipes', { recipes })
1414
})
1515

@@ -40,4 +40,19 @@ router.post('/recipes/:id/edit', async (req, res) => {
4040
res.redirect(`/recipes/${recipeId}`)
4141
})
4242

43+
// 删除菜谱
44+
router.post('/recipes/:id/delete', async (req, res) => {
45+
const db = await getDbConnection();
46+
const recipeId = req.params.id;
47+
await db.run('DELETE FROM recipes WHERE id = ?', [recipeId]);
48+
res.redirect('/recipes');
49+
});
50+
51+
// 获取一个随机菜谱
52+
router.get('/recipes/random', async (req, res) => {
53+
const db = await getDbConnection()
54+
const recipe = await db.get('SELECT * FROM recipes ORDER BY RANDOM() LIMIT 1')
55+
res.render('recipe', { recipe })
56+
})
57+
4358
module.exports = router

views/recipe.hbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
<div class="recipe-actions">
66
<button class="btn btn-primary" onclick="showEditForm()">Edit Recipe</button>
77
<a href="/recipes" class="btn btn-secondary">Back to All Recipes</a>
8+
<form action="/recipes/{{recipe.id}}/delete" method="POST" style="display:inline;">
9+
<button type="submit" class="btn btn-danger" onclick="return confirm('确定要删除该菜谱吗?')">Delete</button>
10+
</form>
811
</div>
912
</div>
1013

@@ -67,6 +70,9 @@
6770
<div class="form-actions">
6871
<button type="submit" class="btn btn-primary">Update Recipe</button>
6972
<button type="button" class="btn btn-secondary" onclick="hideEditForm()">Cancel</button>
73+
<form action="/recipes/{{recipe.id}}/delete" method="POST" style="display:inline; margin-left: 10px;">
74+
<button type="submit" class="btn btn-danger" onclick="return confirm('确定要删除该菜谱吗?')">Delete</button>
75+
</form>
7076
</div>
7177
</form>
7278
</div>

0 commit comments

Comments
 (0)