您的位置:首页 > 科技 > IT业 > h5制作官网_百度站长工具链接提交_网络营销平台名词解释_农夫山泉软文300字

h5制作官网_百度站长工具链接提交_网络营销平台名词解释_农夫山泉软文300字

2025/3/18 6:20:38 来源:https://blog.csdn.net/cf8833/article/details/146329195  浏览:    关键词:h5制作官网_百度站长工具链接提交_网络营销平台名词解释_农夫山泉软文300字
h5制作官网_百度站长工具链接提交_网络营销平台名词解释_农夫山泉软文300字

说明:
fastapi+angular外卖系统

1.美食分类(粥,粉,面,炸鸡,炒菜,西餐,奶茶等等)

2.商家列表 (kfc,兰州拉面,湘菜馆,早餐店,重庆小面,潮汕砂锅粥,蜜雪冰城等等)

商家item:
商家店名,评分,月销量,人均价格,起送价格,配送费价格,店铺位置,商家标签,商家分类

3.商家详情页

商家店名,评分,月销量

菜品分类(比如炒饭,拉面,盖饭,单人套餐,双人套餐,米线,酒水饮料)

菜品列表
菜品item:
菜品名,评分,月销量,菜品标签,菜品分类,菜品价格

显示评价列表,包括用户评分和评价内容

商家详情,包括店铺位置,商家联系电话,营业时间,订餐注意事项,店铺政策

4.结算页

填写用户名,用户联系电话,收货地址,
商品详细清单 店铺名,菜品名,购买数量,菜品单价,配送费,打包费,总金额,提交订单功能

5.订单详情页

订单单号,订单状态(已完成,已退款,待接单,待配送),店铺名称,菜品名称,购买数量,菜品单价,配送费,打包费,总金额

配送信息,订单下单时间,配送完成时间,配送地址,骑手名,骑手联系电话,支付方式,备注信息等等

6.评价表
用户对菜品的评价
效果图:
在这里插入图片描述
json:

/* Merchant List Endpoint - GET http://localhost:8000/merchants */
{"data": [{"id": 7,"name": "Haidilao Hot Pot","rating": 4.9,"monthly_sales": 4000,"avg_price": 80.0,"min_order_price": 50.0,"delivery_fee": 0.0,"location": "3F Food Court","tags": "Hot Pot,24/7","contact_phone": "13800138444","business_hours": "24/7","notice": "Free delivery","policy": "Service first","categories": "Hot Pot"},// Other merchants follow similar pattern...]
}/* Food Categories Endpoint - GET http://localhost:8000/categories */
{"data": [{"id": 7, "name": "Bubble Tea"},{"id": 10, "name": "Japanese"},{"id": 8, "name": "Hot Pot"},// Other categories...]
}/* Merchant Details - GET http://localhost:8000/merchants/1 */
{"merchant": {"id": 1,"name": "KFC","rating": 4.5,"monthly_sales": 2000,"avg_price": 35.0,"min_order_price": 20.0,"delivery_fee": 5.0,"location": "People's Square, City Center","tags": "Fast Food,24/7","contact_phone": "13800138000","business_hours": "07:00-23:00","notice": "Order during business hours","policy": "Unconditional refund"},"categories": [{"id": 1,"name": "Fried Chicken Meals","dishes": "[{\"id\": 1, \"name\": \"Spicy Chicken Burger Meal\", \"tags\": \"Popular\", \"price\": 35.00, \"sales\": 800, \"rating\": 4.50}]"}],"reviews": [{"rating": 4.8,"content": "The burger was delicious!","review_time": "2024-03-01T13:00:00","dish_name": "Spicy Chicken Burger Meal"}]
}/* Create Order - POST http://localhost:8000/orders */
Request Body:
{"customer_name": "Wang Xiaoming","customer_phone": "13800138000","delivery_address": "No.123 Zhangjiang Road, Pudong, Shanghai","merchant_id": 1,"payment_method": "Alipay","items": [{"dish_id": 1, "quantity": 2, "price": 35.00},{"dish_id": 2, "quantity": 1, "price": 40.00}],"note": "Need invoice, no chili please"
}Response:
{"order_id": 14}/* Order Details - GET http://localhost:8000/orders/1 */
{"order": {"id": 1,"customer_name": "Zhang San","status": "Completed","payment_method": "WeChat Pay","merchant_name": "KFC"},"items": [{"dish_name": "Spicy Chicken Burger Meal", "quantity": 2},{"dish_name": "Orleans Wings Meal", "quantity": 1}]
}/* Dish Reviews - GET http://localhost:8000/dishes/1/reviews */
[{"rating": 4.8,"content": "Perfectly crispy chicken!","review_time": "2024-03-01T13:00:00","quantity": 2}
]

网络请求: C:\Users\wangrusheng\WebstormProjects\untitled4\src\app\services\merchant.service.ts

// merchant.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';@Injectable({providedIn: 'root'
})
export class MerchantService {private apiUrl = 'http://localhost:8000';constructor(private http: HttpClient) { }getCategories(): Observable<any> {return this.http.get(`${this.apiUrl}/categories`);}getMerchants(categoryId?: number, page = 1, sortBy = 'rating'): Observable<any> {const params: any = {page: page.toString(),sort_by: sortBy};if (categoryId) params.category_id = categoryId.toString();return this.http.get(`${this.apiUrl}/merchants`, { params });}getMerchantDetail(id: number): Observable<any> {console.log("getMerchantDetail",id)return this.http.get(`${this.apiUrl}/merchants/${id}`);}createOrder(orderData: any): Observable<any> {return this.http.post(`${this.apiUrl}/orders`, orderData);}getOrderDetail(id: number): Observable<any> {return this.http.get(`${this.apiUrl}/orders/${id}`);}submitReview(reviewData: any): Observable<any> {return this.http.post(`${this.apiUrl}/reviews`, reviewData);}
}

step1:sql


-- 1. 美食分类表
CREATE TABLE category (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL UNIQUE
);select *from category-- 2. 商家表
CREATE TABLE merchant (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(100) NOT NULL,rating DECIMAL(3,2) DEFAULT 0.00,monthly_sales INT DEFAULT 0,avg_price DECIMAL(8,2) DEFAULT 0.00,min_order_price DECIMAL(8,2) DEFAULT 0.00,delivery_fee DECIMAL(8,2) DEFAULT 0.00,location VARCHAR(255),tags VARCHAR(255),contact_phone VARCHAR(20),business_hours VARCHAR(100),notice TEXT,policy TEXT
);-- 商家分类关联表
CREATE TABLE merchant_category (merchant_id INT,category_id INT,PRIMARY KEY (merchant_id, category_id),FOREIGN KEY (merchant_id) REFERENCES merchant(id),FOREIGN KEY (category_id) REFERENCES category(id)
);-- 3. 菜品分类表
CREATE TABLE dish_category (id INT PRIMARY KEY AUTO_INCREMENT,merchant_id INT NOT NULL,name VARCHAR(50) NOT NULL,FOREIGN KEY (merchant_id) REFERENCES merchant(id)
);-- 4. 菜品表
CREATE TABLE dish (id INT PRIMARY KEY AUTO_INCREMENT,merchant_id INT NOT NULL,category_id INT NOT NULL,name VARCHAR(100) NOT NULL,rating DECIMAL(3,2) DEFAULT 0.00,monthly_sales INT DEFAULT 0,tags VARCHAR(255),price DECIMAL(8,2) NOT NULL,FOREIGN KEY (merchant_id) REFERENCES merchant(id),FOREIGN KEY (category_id) REFERENCES dish_category(id)
);-- 5. 订单表
CREATE TABLE `order` (id INT PRIMARY KEY AUTO_INCREMENT,customer_name VARCHAR(100) NOT NULL,customer_phone VARCHAR(20) NOT NULL,delivery_address VARCHAR(255) NOT NULL,merchant_id INT NOT NULL,total_amount DECIMAL(10,2) NOT NULL,delivery_fee DECIMAL(8,2) DEFAULT 0.00,packing_fee DECIMAL(8,2) DEFAULT 0.00,status ENUM('已完成','已退款','待接单','待配送') DEFAULT '待接单',order_time DATETIME NOT NULL,complete_time DATETIME,rider_name VARCHAR(100),rider_phone VARCHAR(20),payment_method VARCHAR(50),note TEXT,FOREIGN KEY (merchant_id) REFERENCES merchant(id)
);select *from db_school.order;-- 6. 订单明细表
CREATE TABLE order_item (id INT PRIMARY KEY AUTO_INCREMENT,order_id INT NOT NULL,dish_id INT NOT NULL,quantity INT NOT NULL,price DECIMAL(8,2) NOT NULL,FOREIGN KEY (order_id) REFERENCES `order`(id),FOREIGN KEY (dish_id) REFERENCES dish(id)
);-- 7. 评价表
CREATE TABLE review (id INT PRIMARY KEY AUTO_INCREMENT,order_item_id INT NOT NULL UNIQUE,rating DECIMAL(3,2) NOT NULL,content TEXT,review_time DATETIME NOT NULL,FOREIGN KEY (order_item_id) REFERENCES order_item(id)
);-- Insert food categories
INSERT INTO category (name) VALUES
('Porridge'),('Rice Noodles'),('Noodles'),('Fried Chicken'),('Stir-Fry'),('Western Food'),('Bubble Tea');INSERT INTO category (name) VALUES
('Hot Pot'), ('BBQ'), ('Japanese Cuisine');-- Insert merchant data
INSERT INTO merchant (name, rating, monthly_sales, avg_price, min_order_price, delivery_fee, location, tags, contact_phone, business_hours, notice, policy) VALUES
('KFC', 4.5, 2000, 35.00, 20.00, 5.00, 'People''s Square, City Center', 'Fast Food,24/7 Service', '13800138000', '07:00-23:00', 'Please order during business hours', 'Unconditional refund'),
('Lanzhou Beef Noodles', 4.7, 1500, 20.00, 15.00, 3.00, '100 Zhongshan Road', 'Noodles,Halal', '13900139000', '08:00-22:00', 'Advance reservation recommended', 'Free packaging'),
('Mixue Ice Cream & Tea', 4.6, 3000, 10.00, 0.00, 0.00, '50 Jiefang Road', 'Bubble Tea,Desserts', '13700137000', '09:00-22:00', 'Minimum order ¥10', 'Made fresh');-- Additional merchants...
INSERT INTO merchant (name, rating, monthly_sales, avg_price, min_order_price, delivery_fee, location, tags, contact_phone, business_hours, notice, policy) VALUES
('McDonald''s', 4.6, 2500, 30.00, 20.00, 4.00, '5 Commercial Street', 'Fast Food,Burgers', '13800138111', '07:00-23:30', '24/7 delivery', 'Coupons accepted'),
('Pizza Hut', 4.4, 1800, 50.00, 30.00, 6.00, 'City Center Plaza', 'Pizza,Western Food', '13800138222', '10:00-22:00', 'Happy Hour deals', 'Utensils provided'),
('Starbucks', 4.7, 3000, 35.00, 0.00, 5.00, '1F Shopping Mall', 'Coffee,Desserts', '13800138333', '08:00-21:00', 'Takeaway discounts', 'Eco-cup discount'),
('Haidilao Hot Pot', 4.9, 4000, 80.00, 50.00, 0.00, '3F Food Court', 'Hot Pot,24/7', '13800138444', '24/7', 'Free delivery', 'Service first'),
('Shaxian Snacks', 4.3, 1200, 15.00, 10.00, 2.00, 'Community Street', 'Snacks,Economy', '13800138555', '06:30-22:00', 'Large portions', 'Free rice refill'),
('Zhen Gongfu', 4.5, 1500, 25.00, 15.00, 3.00, 'Near Train Station', 'Fast Food,Steamed Dishes', '13800138666', '09:00-21:00', 'Quick service', 'Self-pickup available'),
('Burger King', 4.6, 2000, 40.00, 25.00, 5.00, 'Block B Commercial Area', 'Burgers,Fries', '13800138777', '08:00-24:00', 'King Meal deals', 'Reward points');-- Special noodle merchants
INSERT INTO merchant (name, rating, monthly_sales, avg_price, min_order_price, delivery_fee, location, tags, contact_phone, business_hours, notice, policy)
VALUES ('Liuzhou Luosifen',4.8,1800,18.00,15.00,2.50,'No.8 Food Street','Luosifen,Authentic','13811112222','09:00-22:00','Customizable spiciness','Free extra bamboo shoots'
);-- Link categories (assuming Rice Noodles category_id=2)
INSERT INTO merchant_category (merchant_id, category_id)
VALUES((SELECT id FROM merchant WHERE name='Liuzhou Luosifen'), 2),((SELECT id FROM merchant WHERE name='Nanning Old Friend Noodles'), 2);-- Insert dish categories
INSERT INTO dish_category (merchant_id, name) VALUES
(1,'Fried Chicken Meals'),(1,'Burger Meals'),
(2,'Signature Noodles'),(2,'Rice Bowls'),
(3,'Classic Bubble Tea'),(3,'Summer Specials');-- Insert dishes
INSERT INTO dish (merchant_id, category_id, name, rating, monthly_sales, tags, price) VALUES
(1,1,'Spicy Chicken Burger Meal',4.5,800,'Popular',35.00),
(1,2,'Orleans Wings Meal',4.6,600,'New',40.00),
(2,3,'Beef Noodles',4.8,500,'Signature',18.00),
(2,4,'Braised Beef Rice Bowl',4.7,300,'Recommended',22.00),
(3,5,'Pearl Milk Tea',4.7,1500,'Classic',8.00),
(3,6,'Strawberry Sundae',4.8,1000,'Seasonal',6.00);-- Insert orders
INSERT INTO `order` (customer_name, customer_phone, delivery_address, merchant_id, total_amount, delivery_fee, packing_fee, status, order_time, payment_method) VALUES
('Zhang San','13800001111','Tech Park Building 1',1,77.00,5.00,2.00,'Completed','2024-03-01 12:00:00','WeChat Pay'),
('Li Si','13900002222','University Dorm 3',2,40.00,3.00,1.00,'Pending Delivery','2024-03-02 18:30:00','Alipay');-- Insert reviews
INSERT INTO review (order_item_id, rating, content, review_time) VALUES
(1,4.8,'Burgers were delicious!','2024-03-01 13:00:00'),
(3,4.9,'Very authentic noodles','2024-03-02 19:00:00');

step2:fastapi

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import pymysql.cursors
from typing import List, Optional
from pydantic import BaseModel
from datetime import datetime
app = FastAPI()# CORS配置
app.add_middleware(CORSMiddleware,allow_origins=["*"],allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
)# 数据库配置(根据实际情况修改)
DB_CONFIG = {'host': 'localhost','user': 'root','password': '123456','db': 'db_school','charset': 'utf8mb4','cursorclass': pymysql.cursors.DictCursor
}# 基础数据模型
class Category(BaseModel):id: intname: strclass MerchantBase(BaseModel):name: strrating: floatmonthly_sales: intavg_price: float# API 请求模型
class CreateOrderItem(BaseModel):dish_id: intquantity: intprice: floatclass CreateOrder(BaseModel):customer_name: strcustomer_phone: strdelivery_address: strmerchant_id: intitems: List[CreateOrderItem]payment_method: strnote: Optional[str] = Nonedef db_query(query: str, params=None, fetch_one=False):try:connection = pymysql.connect(**DB_CONFIG)with connection.cursor() as cursor:cursor.execute(query, params)result = cursor.fetchone() if fetch_one else cursor.fetchall()connection.commit()connection.close()return resultexcept Exception as e:raise HTTPException(status_code=500, detail=str(e))# 1. 获取所有美食分类
@app.get("/categories")
async def get_categories():"""获取所有美食分类"""query = "SELECT id, name FROM category"return {"data": db_query(query)}# 2. 商家列表接口
@app.get("/merchants")
async def get_merchants(category_id: Optional[int] = None,page: int = 1,page_size: int = 10,sort_by: str = 'rating'
):"""获取商家列表(支持分类筛选和排序)"""offset = (page - 1) * page_sizebase_query = """SELECT m.*, GROUP_CONCAT(c.name) AS categories FROM merchant mLEFT JOIN merchant_category mc ON m.id = mc.merchant_idLEFT JOIN category c ON mc.category_id = c.id"""where = []params = []if category_id:where.append("mc.category_id = %s")params.append(category_id)if where:base_query += " WHERE " + " AND ".join(where)base_query += " GROUP BY m.id"# 排序逻辑sort_columns = {'rating': 'm.rating DESC','sales': 'm.monthly_sales DESC','price': 'm.avg_price ASC'}order_by = sort_columns.get(sort_by, 'm.rating DESC')base_query += f" ORDER BY {order_by} LIMIT %s OFFSET %s"params.extend([page_size, offset])return {"data": db_query(base_query, params)}# 3. 商家详情
@app.get("/merchants/{merchant_id}")
async def get_merchant_detail(merchant_id: int):"""获取商家详细信息"""# 商家基本信息merchant = db_query("SELECT * FROM merchant WHERE id = %s", (merchant_id,), True)if not merchant:raise HTTPException(404, "商家不存在")# 菜品分类及菜品categories = db_query("""SELECT dc.id, dc.name, (SELECT JSON_ARRAYAGG(JSON_OBJECT('id',d.id,'name',d.name,'price',d.price,'rating',d.rating,'sales',d.monthly_sales,'tags',d.tags)) FROM dish d WHERE d.category_id = dc.id) AS dishesFROM dish_category dc WHERE dc.merchant_id = %s""", (merchant_id,))# 评价reviews = db_query("""SELECT r.rating, r.content, r.review_time, d.name AS dish_nameFROM review rJOIN order_item oi ON r.order_item_id = oi.idJOIN dish d ON oi.dish_id = d.idWHERE d.merchant_id = %s ORDER BY r.review_time DESC LIMIT 10""", (merchant_id,))return {"merchant": merchant,"categories": categories,"reviews": reviews}# 4. 创建订单接口
@app.post("/orders")
async def create_order(order_data: CreateOrder):"""创建新订单"""connection = pymysql.connect(**DB_CONFIG)try:with connection.cursor() as cursor:# 计算总金额total = sum(item.price * item.quantity for item in order_data.items)# 获取商家配送费cursor.execute("SELECT delivery_fee, min_order_price FROM merchant WHERE id = %s",(order_data.merchant_id,))merchant_info = cursor.fetchone()if not merchant_info:raise HTTPException(status_code=404, detail="Merchant not found")if total < merchant_info['min_order_price']:raise HTTPException(status_code=400,detail=f"订单金额需达到{merchant_info['min_order_price']}元")# 创建订单order_query = """INSERT INTO `order` (customer_name, customer_phone, delivery_address,merchant_id, total_amount, delivery_fee, status, order_time, payment_method, note) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""order_params = (order_data.customer_name,order_data.customer_phone,order_data.delivery_address,order_data.merchant_id,total,merchant_info['delivery_fee'],'待接单',datetime.now(),order_data.payment_method,order_data.note)cursor.execute(order_query, order_params)order_id = cursor.lastrowid# 创建订单明细for item in order_data.items:cursor.execute("""INSERT INTO order_item (order_id, dish_id, quantity, price)VALUES (%s,%s,%s,%s)""", (order_id, item.dish_id, item.quantity, item.price))connection.commit()return {"order_id": order_id}except Exception as e:connection.rollback()raise HTTPException(status_code=500, detail=str(e))finally:connection.close()# 5. 订单详情接口
@app.get("/orders/{order_id}")
async def get_order_detail(order_id: int):"""获取订单详情"""order_query = """SELECT o.*, m.name AS merchant_name FROM `order` oJOIN merchant m ON o.merchant_id = m.idWHERE o.id = %s"""order = db_query(order_query, (order_id,), fetch_one=True)if not order:raise HTTPException(status_code=404, detail="订单不存在")items_query = """SELECT oi.*, d.name AS dish_name FROM order_item oiJOIN dish d ON oi.dish_id = d.idWHERE oi.order_id = %s"""items = db_query(items_query, (order_id,))return {"order": order, "items": items}# 6. 获取菜品评价
@app.get("/dishes/{dish_id}/reviews")
async def get_dish_reviews(dish_id: int):"""获取菜品评价"""return db_query("""SELECT r.rating, r.content, r.review_time, oi.quantity FROM review rJOIN order_item oi ON r.order_item_id = oi.idWHERE oi.dish_id = %sORDER BY r.review_time DESC""", (dish_id,))if __name__ == "__main__":import uvicornuvicorn.run(app, host="0.0.0.0", port=8000)

step3:商家列表 C:\Users\wangrusheng\WebstormProjects\untitled4\src\app\merchants-list\merchants-list.component.html

import { Component, OnInit } from '@angular/core';import { MerchantService} from '../services/merchant.service';
import {NgForOf, NgIf} from '@angular/common';
import {RouterLink} from '@angular/router';@Component({selector: 'app-merchants-list',imports: [NgForOf,NgIf,RouterLink],templateUrl: './merchants-list.component.html',styleUrl: './merchants-list.component.css'
})
export class MerchantsListComponent  implements OnInit {categories: any[] = [];merchants: any[] = [];selectedCategoryId: number | null = null;isLoading = false;constructor(private merchantService: MerchantService) { }ngOnInit() {this.loadCategories();this.loadMerchants(); // 初始加载全部商家}// 加载分类数据loadCategories() {this.merchantService.getCategories().subscribe(res => {this.categories = res.data;});}// 加载商家数据loadMerchants(categoryId?: number) {this.isLoading = true;this.merchantService.getMerchants(categoryId).subscribe({next: (res) => {this.merchants = res.data;this.isLoading = false;},error: () => {this.isLoading = false;alert('加载商家失败,请重试');}});}// 分类选择处理selectCategory(categoryId: number | null) {this.selectedCategoryId = categoryId;this.loadMerchants(categoryId ?? undefined);}
}
<!-- takeout.component.html -->
<div class="container"><!-- 左侧分类导航 --><div class="categories-sidebar"><h3>美食分类</h3><ul><li*ngFor="let category of categories"[class.active]="selectedCategoryId === category.id"(click)="selectCategory(category.id)">{{category.name}}</li><li[class.active]="!selectedCategoryId"(click)="selectCategory(null)">全部商家</li></ul></div><!-- 右侧商家列表 --><div class="merchant-list"><!-- 加载状态 --><div *ngIf="isLoading" class="loading">加载中...</div><!-- 商家列表 --><div *ngFor="let merchant of merchants" class="merchant-card"><div class="merchant-header"><h3>{{merchant.name}}</h3><span class="rating">{{merchant.rating}}</span></div><div class="merchant-info"><div class="meta"><span>月售 {{merchant.monthly_sales}}</span><span>起送 ¥{{merchant.min_order_price}}</span><span>配送 ¥{{merchant.delivery_fee}}</span></div><a [routerLink]="['/merchants-detail', merchant.id]" class="detail-link">查看详情</a><div class="tags"><span *ngFor="let tag of merchant.tags.split(',')">{{tag}}</span></div></div></div></div>
</div>
/* takeout.component.css */
.container {display: grid;grid-template-columns: 200px 1fr;gap: 20px;padding: 20px;
}.categories-sidebar {background: #f5f5f5;padding: 15px;border-radius: 8px;
}.categories-sidebar ul {list-style: none;padding: 0;margin: 0;
}.categories-sidebar li {padding: 10px;cursor: pointer;border-radius: 4px;margin-bottom: 5px;transition: all 0.2s;
}.categories-sidebar li:hover {background: #eee;
}.categories-sidebar li.active {background: #007bff;color: white;
}.merchant-list {display: grid;gap: 15px;
}.merchant-card {padding: 20px;border: 1px solid #ddd;border-radius: 8px;
}.merchant-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 10px;
}.rating {color: #ffd700;font-weight: bold;
}.meta span {margin-right: 15px;color: #666;
}.tags span {display: inline-block;background: #f0f0f0;padding: 4px 8px;border-radius: 4px;margin: 5px 5px 0 0;font-size: 0.9em;
}.loading {text-align: center;padding: 20px;color: #666;
}

step4:商家详情 C:\Users\wangrusheng\WebstormProjects\untitled4\src\app\merchants-detail\merchants-detail.component.css

// merchants-detail.component.ts
import { Component, OnInit } from '@angular/core';
import {ActivatedRoute, RouterLink} from '@angular/router';
import { MerchantService } from '../services/merchant.service';
import { DatePipe, NgFor, NgIf } from '@angular/common';@Component({selector: 'app-merchants-detail',standalone: true,imports: [NgIf, NgFor, DatePipe, RouterLink],templateUrl: './merchants-detail.component.html',styleUrls: ['./merchants-detail.component.css']
})
export class MerchantsDetailComponent implements OnInit {merchantDetail: any;isLoading = true;errorMessage = '';constructor(private route: ActivatedRoute,private merchantService: MerchantService) { }ngOnInit() {const id = this.route.snapshot.paramMap.get('id');if (id) {this.loadMerchantDetail(+id);} else {this.errorMessage = '无效的商家ID';this.isLoading = false;}}loadMerchantDetail(id: number) {this.merchantService.getMerchantDetail(id).subscribe({next: (res) => {this.merchantDetail = {...res,categories: res.categories.map((cat: any) => ({...cat,dishes: JSON.parse(cat.dishes)}))};this.isLoading = false;},error: (err) => {this.errorMessage = '加载商家详情失败,请稍后重试';this.isLoading = false;}});}getRatingStars(rating: number): string {return '★'.repeat(Math.round(rating));}
}<!-- merchants-detail.component.html -->
<div class="merchant-container"><!-- 加载状态 --><div *ngIf="isLoading" class="loading">加载中...</div><!-- 错误提示 --><div *ngIf="errorMessage" class="error">{{ errorMessage }}</div><!-- 商家详情内容 --><div *ngIf="merchantDetail" class="merchant-content"><!-- 商家基本信息 --><section class="merchant-info"><h1>{{ merchantDetail.merchant.name }}</h1><div class="stats"><span class="rating">{{ getRatingStars(merchantDetail.merchant.rating) }}<em>({{ merchantDetail.merchant.rating }})</em></span><span>月售 {{ merchantDetail.merchant.monthly_sales }}</span><span>人均 ¥{{ merchantDetail.merchant.avg_price }}</span></div><div class="details"><div class="detail-item"><label>起送价:</label><span>¥{{ merchantDetail.merchant.min_order_price }}</span></div><div class="detail-item"><label>配送费:</label><span>¥{{ merchantDetail.merchant.delivery_fee }}</span></div><div class="detail-item"><label>营业时间:</label><span>{{ merchantDetail.merchant.business_hours }}</span></div><div class="detail-item"><label>联系电话:</label><a href="tel:{{ merchantDetail.merchant.contact_phone }}">{{ merchantDetail.merchant.contact_phone }}</a></div><div class="detail-item"><label>地址:</label><address>{{ merchantDetail.merchant.location }}</address></div></div><div class="tags"><span *ngFor="let tag of merchantDetail.merchant.tags.split(',')"class="tag">{{ tag }}</span></div></section><!-- 菜品分类 --><!-- merchants-detail.component.html --><section *ngFor="let category of merchantDetail.categories" class="menu-category"><h2>{{ category.name }}</h2><div class="dishes"><div *ngFor="let dish of category.dishes" class="dish-card"><!-- 其他菜品信息 --><div class="dish-info"><h3>{{ dish.name }}</h3><div class="dish-meta"><span class="price">¥{{ dish.price }}</span><span class="sales">月售 {{ dish.sales }}</span><span class="dish-rating">{{ getRatingStars(dish.rating) }}</span></div><div class="dish-tags"><span *ngFor="let tag of dish.tags.split(',')"class="tag">{{ tag }}</span></div><button class="book-button"[routerLink]="['/merchants-order']"[state]="{orderData: {dish: dish,merchant: merchantDetail.merchant}}">立即购买</button></div></div></div></section><!-- 用户评价 --><section class="reviews"><h2>用户评价 ({{ merchantDetail.reviews.length }})</h2><div *ngFor="let review of merchantDetail.reviews" class="review-card"><div class="review-header"><span class="rating">{{ getRatingStars(review.rating) }}</span><span class="dish">{{ review.dish_name }}</span><time [title]="review.review_time | date:'long'">{{ review.review_time | date:'yyyy-MM-dd HH:mm' }}</time></div><p class="content">{{ review.content }}</p></div></section></div>
</div>/* merchants-detail.component.css */
.merchant-container {max-width: 1200px;margin: 20px auto;padding: 20px;
}.loading, .error {text-align: center;padding: 40px;font-size: 1.2rem;
}.error {color: #dc3545;
}.merchant-content {background: #fff;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.1);padding: 24px;
}.merchant-info h1 {color: #333;margin-bottom: 16px;
}.stats {display: flex;gap: 20px;margin-bottom: 24px;color: #666;
}.rating {color: #ffd700;font-size: 1.2em;
}
.rating em {color: #666;font-style: normal;font-size: 0.9em;margin-left: 8px;
}.details {display: grid;grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));gap: 16px;margin-bottom: 24px;
}.detail-item {display: flex;justify-content: space-between;padding: 8px 0;border-bottom: 1px solid #eee;
}.detail-item label {color: #999;
}.tags {display: flex;gap: 8px;flex-wrap: wrap;
}.tag {background: #f0f0f0;padding: 4px 12px;border-radius: 12px;font-size: 0.9em;color: #666;
}/* 菜品分类样式 */
.menu-category {margin: 32px 0;border-top: 2px solid #eee;padding-top: 24px;
}.menu-category h2 {color: #444;margin-bottom: 16px;
}.dishes {display: grid;grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));gap: 20px;
}.dish-card {border: 1px solid #eee;border-radius: 8px;padding: 16px;transition: transform 0.2s;
}.dish-card:hover {transform: translateY(-2px);
}.dish-meta {display: flex;gap: 12px;margin: 8px 0;color: #666;
}.price {color: #e4393c;font-weight: bold;
}.dish-rating {color: #ffd700;
}/* 评价样式 */
.reviews {margin-top: 40px;
}.review-card {padding: 16px;border: 1px solid #eee;border-radius: 8px;margin-bottom: 16px;
}.review-header {display: flex;gap: 12px;align-items: center;margin-bottom: 8px;color: #666;
}.review-header time {font-size: 0.9em;color: #999;
}.content {color: #333;line-height: 1.6;
}

step5:立即购买C:\Users\wangrusheng\WebstormProjects\untitled4\src\app\merchants-order\merchants-order.component.css

// merchants-order.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {Location, NgIf} from '@angular/common';
import { MerchantService } from '../services/merchant.service';// 类型定义
interface Dish {id: number;name: string;price: number;// 其他字段根据实际API响应添加
}interface Merchant {id: number;name: string;delivery_fee: number;location: string;// 其他字段根据实际API响应添加
}interface OrderState {orderData: {dish: Dish;merchant: Merchant;};
}@Component({selector: 'app-merchants-order',templateUrl: './merchants-order.component.html',imports: [ReactiveFormsModule,NgIf],styleUrls: ['./merchants-order.component.css']
})
export class MerchantsOrderComponent implements OnInit {orderForm: FormGroup;selectedDish?: Dish;merchant?: Merchant;loading = false;constructor(private fb: FormBuilder,private location: Location,private router: Router,private route: ActivatedRoute,private merchantService: MerchantService) {this.orderForm = this.fb.group({customer_name: ['', [Validators.required, Validators.minLength(2)]],customer_phone: ['', [Validators.required, Validators.pattern(/^1[3-9]\d{9}$/)]],delivery_address: ['', [Validators.required, Validators.minLength(5)]],payment_method: ['alipay', Validators.required],note: [''],quantity: [1, [Validators.required, Validators.min(1), Validators.max(10)]]});}ngOnInit(): void {this.handleRouteState();this.setupFormListeners();}private handleRouteState(): void {const state = this.location.getState() as OrderState;console.log('路由状态:', state);if (state?.orderData?.dish && state?.orderData?.merchant) {this.selectedDish = state.orderData.dish;this.merchant = state.orderData.merchant;this.initFormWithFallbackData();} else {console.warn('无效的路由状态,尝试通过参数加载');this.loadFromQueryParams();}}private loadFromQueryParams(): void {this.route.queryParams.subscribe(params => {if (params['merchantId'] && params['dishId']) {this.merchantService.getMerchantDetail(params['merchantId']).subscribe({next: merchant => {this.merchant = merchant;this.selectedDish = this.findDishInCategories(merchant.categories, params['dishId']);if (!this.selectedDish) {this.redirectToMerchantList();}},error: () => this.redirectToMerchantList()});} else {this.redirectToMerchantList();}});}private findDishInCategories(categories: any[], dishId: string): Dish | undefined {for (const category of categories) {const dishes = JSON.parse(category.dishes);const found = dishes.find((d: any) => d.id === +dishId);if (found) return found;}return undefined;}private initFormWithFallbackData(): void {this.orderForm.patchValue({delivery_address: this.merchant?.location || ''});}private setupFormListeners(): void {this.orderForm.get('quantity')?.valueChanges.subscribe(value => {if (value > 10) {this.orderForm.get('quantity')?.setValue(10);}});}get totalAmount(): string {if (!this.selectedDish || !this.merchant) return '0.00';const quantity = this.orderForm.get('quantity')?.value || 1;return (this.selectedDish.price * quantity + this.merchant.delivery_fee).toFixed(2);}onSubmit(): void {if (this.orderForm.invalid || !this.selectedDish || !this.merchant) return;const orderData = {...this.orderForm.value,merchant_id: this.merchant.id,items: [{dish_id: this.selectedDish.id,quantity: this.orderForm.value.quantity,price: this.selectedDish.price}]};this.loading = true;this.merchantService.createOrder(orderData).subscribe({next: (res) => {this.router.navigate(['/merchants-order-detail', res.order_id]);this.loading = false;},error: (err) => {console.error('订单提交失败:', err);alert(`提交失败: ${err.error?.detail || '服务器错误'}`);this.loading = false;}});}private redirectToMerchantList(): void {this.router.navigate(['/merchants'], {queryParamsHandling: 'preserve'});}
}<!-- merchants-order.component.html -->
<div class="order-container"><h2>确认订单</h2><form *ngIf="merchant && selectedDish" [formGroup]="orderForm" (ngSubmit)="onSubmit()"><!-- 商家信息 --><div class="merchant-info"><h3>{{ merchant.name }}</h3><p>{{ merchant.location }}</p></div><!-- 商品信息 --><div class="dish-info"><h4>{{ selectedDish.name }}</h4><div class="price-quantity"><span class="price">¥{{ selectedDish.price }}</span><div class="quantity-control"><label>数量:</label><input type="number" formControlName="quantity" min="1"></div></div></div><!-- 订单表单 --><div class="form-section"><div class="form-group"><label>收货人姓名</label><input type="text" formControlName="customer_name"><div *ngIf="orderForm.get('customer_name')?.errors?.['required']" class="error">姓名不能为空</div></div><div class="form-group"><label>联系电话</label><input type="tel" formControlName="customer_phone"><div *ngIf="orderForm.get('customer_phone')?.errors?.['required']" class="error">电话不能为空</div><div *ngIf="orderForm.get('customer_phone')?.errors?.['pattern']" class="error">请输入有效的手机号</div></div><div class="form-group"><label>配送地址</label><textarea formControlName="delivery_address"></textarea><div *ngIf="orderForm.get('delivery_address')?.errors?.['required']" class="error">地址不能为空</div></div><div class="form-group"><label>支付方式</label><select formControlName="payment_method"><option value="alipay">支付宝</option><option value="wechat">微信支付</option><option value="cash">货到付款</option></select></div><div class="form-group"><label>备注</label><textarea formControlName="note"></textarea></div></div><!-- 费用汇总 --><div class="price-summary"><div class="summary-item"><span>商品总额:</span>
<!--        <span>¥{{ (selectedDish.price * orderForm.value.quantity).toFixed(2) }}</span>--></div><div class="summary-item"><span>配送费:</span><span>¥{{ merchant.delivery_fee }}</span></div><div class="summary-item total"><span>总计:</span><span>¥{{ totalAmount }}</span></div></div><button type="submit" [disabled]="orderForm.invalid || loading" class="submit-btn">{{ loading ? '提交中...' : '提交订单' }}</button></form>
</div>.order-container {max-width: 600px;margin: 20px auto;padding: 20px;background: #fff;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}.merchant-info {margin-bottom: 20px;padding-bottom: 15px;border-bottom: 1px solid #eee;
}.dish-info {margin-bottom: 20px;
}.form-group {margin-bottom: 15px;
}.form-group label {display: block;margin-bottom: 5px;font-weight: bold;
}.form-group input,
.form-group textarea,
.form-group select {width: 100%;padding: 8px;border: 1px solid #ddd;border-radius: 4px;
}.error {color: #dc3545;font-size: 0.9em;margin-top: 5px;
}.price-summary {margin-top: 20px;padding: 15px;background: #f8f9fa;border-radius: 4px;
}.summary-item {display: flex;justify-content: space-between;margin-bottom: 10px;
}.total {font-weight: bold;font-size: 1.1em;
}.submit-btn {width: 100%;padding: 12px;background: #007bff;color: white;border: none;border-radius: 4px;cursor: pointer;margin-top: 20px;
}.submit-btn:disabled {background: #6c757d;cursor: not-allowed;
}

step6:订单详情C:\Users\wangrusheng\WebstormProjects\untitled4\src\app\merchants-order-detail\merchants-order-detail.component.css

// merchants-order-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { MerchantService } from '../services/merchant.service';
import { DatePipe, CurrencyPipe, NgIf, NgFor } from '@angular/common';interface OrderItem {id: number;dish_name: string;quantity: number;price: number;
}interface OrderDetail {id: number;customer_name: string;customer_phone: string;delivery_address: string;total_amount: number;delivery_fee: number;packing_fee: number;status: string;order_time: string;complete_time: string | null;rider_name: string | null;rider_phone: string | null;payment_method: string;note: string | null;merchant_name: string;
}@Component({selector: 'app-merchants-order-detail',standalone: true,imports: [NgIf, NgFor, DatePipe, CurrencyPipe],templateUrl: './merchants-order-detail.component.html',styleUrls: ['./merchants-order-detail.component.css']
})
export class MerchantsOrderDetailComponent implements OnInit {orderId!: number;orderId2!: number;order!: OrderDetail;items: OrderItem[] = [];isLoading = true;errorMessage = '';constructor(private route: ActivatedRoute,private router: Router,private merchantService: MerchantService) {}ngOnInit(): void {this.route.params.subscribe(params => {this.orderId = +params['order_id'];this.orderId2 = params['order_id'];console.log('从路由获取的order_id:', this.orderId+"==="+this.orderId2);this.loadOrderDetail();});}loadOrderDetail(): void {this.isLoading = true;this.merchantService.getOrderDetail(this.orderId).subscribe({next: (res) => {this.order = res.order;this.items = res.items;this.isLoading = false;},error: (err) => {console.error('加载失败:', err);this.errorMessage = '无法加载订单详情,请稍后重试';this.isLoading = false;}});}getStatusClass(): string {switch(this.order.status) {case '已完成': return 'status-completed';case '待接单': return 'status-pending';case '配送中': return 'status-delivering';default: return 'status-default';}}
}<!-- merchants-order-detail.component.html -->
<div class="order-detail-container"><!-- 加载状态 --><div *ngIf="isLoading" class="loading">加载中...</div><!-- 错误提示 --><div *ngIf="errorMessage" class="error">{{ errorMessage }}</div><!-- 订单详情 --><div *ngIf="order" class="order-content"><!-- 订单头信息 --><div class="order-header"><h2>订单号:{{ order.id }}</h2><span [class]="getStatusClass()">{{ order.status }}</span></div><!-- 基本信息 --><div class="section"><h3>订单信息</h3><div class="info-grid"><div class="info-item"><label>下单时间:</label><span>{{ order.order_time | date:'yyyy-MM-dd HH:mm' }}</span></div><div class="info-item"><label>商家名称:</label><span>{{ order.merchant_name }}</span></div><div class="info-item"><label>支付方式:</label><span>{{ order.payment_method }}</span></div></div></div><!-- 商品清单 --><div class="section"><h3>商品清单</h3><table class="item-table"><thead><tr><th>商品名称</th><th>单价</th><th>数量</th><th>小计</th></tr></thead><tbody><tr *ngFor="let item of items"><td>{{ item.dish_name }}</td><td>{{ item.price | currency:'CNY':'symbol':'1.2-2' }}</td><td>{{ item.quantity }}</td><td>{{ item.price * item.quantity | currency:'CNY':'symbol':'1.2-2' }}</td></tr></tbody></table></div><!-- 费用明细 --><div class="section"><h3>费用明细</h3><div class="fee-details"><div class="fee-item"><span>商品总额:</span><span>{{ order.total_amount - order.delivery_fee - order.packing_fee | currency:'CNY':'symbol':'1.2-2' }}</span></div><div class="fee-item"><span>配送费:</span><span>{{ order.delivery_fee | currency:'CNY':'symbol':'1.2-2' }}</span></div><div class="fee-item"><span>包装费:</span><span>{{ order.packing_fee | currency:'CNY':'symbol':'1.2-2' }}</span></div><div class="fee-item total"><span>实付金额:</span><span>{{ order.total_amount | currency:'CNY':'symbol':'1.2-2' }}</span></div></div></div><!-- 配送信息 --><div class="section"><h3>配送信息</h3><div class="delivery-info"><p>收货人:{{ order.customer_name }}</p><p>联系电话:{{ order.customer_phone }}</p><p>配送地址:{{ order.delivery_address }}</p><div *ngIf="order.rider_name" class="rider-info"><p>骑手:{{ order.rider_name }}</p><p>骑手电话:{{ order.rider_phone }}</p></div></div></div><!-- 备注信息 --><div *ngIf="order.note" class="section"><h3>订单备注</h3><p class="note">{{ order.note }}</p></div></div>
</div>/* merchants-order-detail.component.css */
.order-detail-container {max-width: 1200px;margin: 20px auto;padding: 20px;background: #fff;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}.loading, .error {text-align: center;padding: 40px;font-size: 1.2rem;
}.error {color: #dc3545;
}.order-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 20px;padding-bottom: 15px;border-bottom: 1px solid #eee;
}.section {margin-bottom: 30px;padding: 15px;background: #f8f9fa;border-radius: 6px;
}.info-grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));gap: 15px;
}.item-table {width: 100%;border-collapse: collapse;margin-top: 10px;
}.item-table th,
.item-table td {padding: 12px;text-align: left;border-bottom: 1px solid #eee;
}.item-table th {background: #f8f9fa;
}.fee-details {max-width: 400px;
}.fee-item {display: flex;justify-content: space-between;margin-bottom: 10px;
}.fee-item.total {font-weight: bold;border-top: 1px solid #eee;padding-top: 10px;font-size: 1.1em;
}.note {color: #666;line-height: 1.6;
}/* 状态标签样式 */
.status-completed { color: #28a745; }
.status-pending { color: #ffc107; }
.status-delivering { color: #17a2b8; }
.status-default { color: #6c757d; }

end

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com