This commit is contained in:
LittleBoy 2023-04-07 15:44:01 +08:00
parent a9dea3af14
commit 0fbd474799
11 changed files with 443 additions and 76 deletions

View File

@ -26,4 +26,8 @@
.justify-between{
justify-content: space-between;
}
img,image{
max-width: 100%;
}
</style>

100
components/AddToCart.vue Normal file
View File

@ -0,0 +1,100 @@
<template>
<view class="bottom-control">
<view class="cart flex-center">
<text class="iconfont icon-Buy cart-icon"></text>
<text class="cart-price">{{(carts.price/100).toFixed(2)}}</text>
</view>
<view class="add-to-cart" @click="addFoodToCart">
<text>加入购物车</text>
</view>
</view>
</template>
<script lang="ts" setup>
import { defineComponent, onMounted, ref } from "vue";
import { addToCart, cartList } from "../service/api";
defineComponent({ name: "AddToCart" })
const props = defineProps<{ id : any }>()
const carts = ref({
price: 0,
count: 0
})
const loadCartList = () => {
cartList().then((list) => {
let count = 0, price = 0;
list.forEach(c => {
count += c.count;
price += c.count * c.foodPrice
})
carts.value = {
count,
price,
}
})
}
onMounted(loadCartList)
function addFoodToCart(){
if(props.id){
addToCart(props.id).then(loadCartList).catch(()=>{
uni.showToast({
title: '加入购物车失败'
})
})
}
}
</script>
<style scoped lang="scss">
$bottom-height: 44px;
.bottom-control {
position: fixed;
left: 30px;
right: 30px;
bottom: 40px;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
border-radius: 40px;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2);
line-height: $bottom-height;
box-sizing: border-box;
}
.add-to-cart {}
.add-to-cart {
background-color: $uni-color-primary;
font-size: 14px;
height: 100%;
padding: 0 20px;
color: #fff;
border-radius: $bottom-height;
height: $bottom-height;
transform: translateX(1px);
box-sizing: border-box;
&:active {
background-color: $uni-color-primary-1;
}
}
.cart {
height: $bottom-height;
color: $uni-color-primary;
border-radius: $bottom-height;
padding: 0 20px;
&:active {
background-color: #fff;
}
}
.cart-icon {
font-size: 28px;
margin-right: 10px;
}
.cart-price {}
</style>

View File

@ -0,0 +1,52 @@
<template>
<view class="title-bar flex-center">
<view class="back" @click="back">
<text class="iconfont icon-packup"></text>
</view>
<view class="content"><slot></slot></view>
</view>
</template>
<script>
export default {
name: "CustNavBar",
data() {
return {}
},
methods:{
back(){
uni.navigateBack()
}
}
}
</script>
<style scoped lang="scss">
.title-bar{
position: fixed;
top:0;
left:0;
right:0;
padding:10px;
z-index: 2;
}
.back{
background-color: rgba(0, 0, 0, 0.5);
border-radius: 40px;
color: #fff;
display: inline-block;
padding: 4px;
}
.icon-packup{
transform: rotate(-90deg);
font-size: 20px;
display: block;
width: 24px;
height: 24px;
position: relative;
top:-1px
}
.content{
flex:1;
}
</style>

5
components/index.ts Normal file
View File

@ -0,0 +1,5 @@
import AddToCart from './AddToCart.vue';
export {
AddToCart
}

View File

@ -3,6 +3,9 @@
{
"path": "pages/index/index",
"style": {
"app-plus": {
"titleNView": false
}
}
}, {
"path": "pages/order/index",
@ -16,26 +19,32 @@
"enablePullDownRefresh": false
}
}, {
"path": "pages/index/search",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "pages/index/detail",
"style": {
"enablePullDownRefresh": false,
"app-plus": {
"titleNView": false
}
}
}, {
"path": "pages/index/category",
"style": {
"app-plus": {
"titleNView": false
}
}
}
,{
"path" : "pages/index/search",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/index/detail",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
],
],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "我爱外卖",

94
pages/index/category.vue Normal file
View File

@ -0,0 +1,94 @@
<template>
<view class="page-category">
<view class="my-nav">
<CustNavBar>
<view class="search-input-container">
<view class="search-inut">搜索 鱼香肉丝 试试</view>
</view>
</CustNavBar>
</view>
<view class="category-list-container">
<view class="left-category">
<view class="category-item" v-for="c in categorys"
:class="{active:c.id == activeCategory}" :key="c.id"
@click="loadListByCid(c.id)">
<text>{{c.title}}</text>
</view>
</view>
<view class="foods-list-container">
<FoodsList v-if="foodsList" :foodsList="foodsList"></FoodsList>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { allCategories, loadCategoryGoods } from "../../service/api";
import { CategoryModel } from "../../service/models";
const foodsList = ref([])
const categorys = ref<CategoryModel[]>([])
const activeCategory = ref(0)
allCategories().then(list => {
categorys.value = list
})
function loadListByCid(cid: number){
activeCategory.value = cid
loadCategoryGoods(cid).then(list=>{
foodsList.value = list;
})
}
</script>
<style lang="scss">
.page-category {
min-height: 100vh;
.my-nav {
height: 50px;
}
.search-input-container {
margin-left: 10px;
}
.search-inut {
border: solid 1px $uni-color-primary;
width: 100%;
border-radius: 30px;
box-sizing: border-box;
height: 28px;
line-height: 28px;
color: #666;
font-size: 14px;
padding: 0px 10px;
}
.category-list-container {
height: calc(100vh - 50px);
display: flex;
}
.left-category {
height: 100%;
background-color: #eee;
width: 100px;
text-align: center;
padding: 10px 0;
box-sizing: border-box;
}
.category-item {
padding: 10px 0;
&.active{
background-color: #fff;
}
}
.foods-list-container{
flex:1;
overflow-y: auto;
scroll-behavior: smooth;
}
}
</style>

View File

@ -1,27 +1,32 @@
<template>
<view class="page-detail" v-if="f">
<view class="foods-cover">
<image class="cover" :src="f.cover" mode=""></image>
</view>
<view class="foods-name">
<text>{{f.title}}</text>
</view>
<view class="sale-count">
<text>{{f.saleCount}}</text>
</view>
<view class="sale-control">
<view class="sale-price">
<text class="price">{{f.price}}</text>
<text class="coupon">{{f.coupon}}</text>
<CustNavBar></CustNavBar>
<view class="foods-basic">
<view class="foods-cover">
<image class="cover" :src="f.cover" mode=""></image>
</view>
<view class="add-to-cart">
<text class="iconfont icon-add"></text>
<text>加入购物车</text>
<view class="foods-info-wrapper">
<view class="foods-name">
<text>{{f.title}}</text>
</view>
<view class="sale-control flex-center justify-between">
<view class="sale-count">
<text>销量{{f.saleCount}} </text>
</view>
<view class="sale-price">
<text class="unit"></text>
<text
class="price">{{f.coupon ? (((f.price+f.coupon)/100).toFixed(2)) : (f.price/100).toFixed(2)}}</text>
<text v-if="f.coupon && f.coupon != 0" class="coupon-price">{{(f.price/100).toFixed(2)}}</text>
</view>
</view>
<view class="coupon-info">
{{f.couponDesc}}
</view>
</view>
</view>
<view class="foods-detail-content" v-html="f.content">
</view>
<view class="foods-detail-content" v-html="f.content"></view>
<AddToCart :id="id || 0" />
</view>
</template>
@ -29,26 +34,97 @@
import { foodsDetail } from '../../service/api';
import { ref } from "vue";
import { FoodsModel } from '../../service/models';
import { AddToCart } from '../../components'
const props = defineProps<{
id : any
id ?: any
}>();
const f = ref<FoodsModel>()
foodsDetail(props.id).then((foods) => {
foodsDetail(Number(props.id || 1)).then((foods) => {
uni.setNavigationBarTitle({
title: foods.title
})
f.value = foods
})
</script>
<style lang="scss">
.page-detail{
img,image,umi-image{
max-width: 100%;
height: auto;
}
.foods-cover{
.cover{
display: block;
width: 100%;
.page-detail {
background-color: $uni-color-primary;
min-height: 100vh;
box-sizing: border-box;
padding-bottom: 10px;
img,
image,
umi-image {
max-width: 100%;
height: auto;
}
.foods-basic {
position: relative;
}
.foods-cover {
.cover {
display: block;
width: 100%;
height: 340px;
}
}
.coupon-info {
color: #999;
font-size: 13px;
margin-top: 10px;
}
.foods-info-wrapper {
padding: 15px;
position: absolute;
background-color: #fff;
border-radius: 10px;
left: 20px;
right: 20px;
bottom: -60px;
}
.foods-detail-content {
background-color: #fff;
margin: 80px 20px 20px;
border-radius: 10px;
padding: 15px 15px 50px;
}
.foods-name {
font-weight: bold;
font-size: 18px;
}
.sale-count {
color: #999;
font-size: 13px;
margin: 4px 0;
}
.sale-price {
.unit {
font-size: 12px;
}
.price {
font-size: 18px;
color: $uni-color-primary;
font-weight: bold;
}
}
.coupon-price {
text-decoration: line-through;
font-size: 12px;
margin-left: 5px;
}
}
}
</style>

View File

@ -20,7 +20,7 @@
<view class="title">{{c.id}}{{c.title}}</view>
</view>
</view>
<FoodsList :foodsList="foodsList"></FoodsList>
<FoodsList v-if="foodsList" :foodsList="foodsList"></FoodsList>
</view>
</template>
@ -50,20 +50,16 @@
}
})
function loadFoodByCid(cid) {
uni.request({
url: API_URL + '/api/food/category/' + cid,
success(ret) {
foodsList.value = ret.data.data
// console.log(ret.data.data)
}
function loadFoodByCid(cid : any) {
uni.navigateTo({
url: `/pages/index/category?cid=${cid}`
})
}
function showSearchPage(){
function showSearchPage() {
uni.navigateTo({
url:'/pages/index/search'
url: '/pages/index/search'
})
}
@ -148,9 +144,8 @@
box-sizing: border-box;
image {
width: 40px;
height: 40px;
width: 32px;
height: 32px;
}
}
</style>

View File

@ -1,4 +1,4 @@
import { FoodsModel, ResponseModel } from "./models"
import { CartModel, CategoryModel, FoodsModel, ResponseModel } from "./models"
export const API_URL = 'http://localhost:8080'
function request<T>(url : string, method : 'GET' | 'POST' = 'GET', data : any = null) {
@ -32,11 +32,19 @@ function request<T>(url : string, method : 'GET' | 'POST' = 'GET', data : any =
}
export function foodsDetail(id : any) {
return request<FoodsModel>(`/api/food/${id}`)
// uni.request({
// url: API_URL + '/api/food/recommend',
// success(ret) {
// foodsList.value = ret.data.data
// // console.log(ret.data.data)
// }
// })
}
export function addToCart(fid : any, count = 1) {
return request(`/api/carts`, 'POST', {
fid, count
})
}
export function cartList() {
return request<CartModel[]>(`/api/carts/list`)
}
export function allCategories() {
return request<CategoryModel[]>(`/api/category`);
}
export function loadCategoryGoods(cid : any) {
return request<FoodsModel[]>(`/api/food/category/${cid}`);
}

View File

@ -29,4 +29,31 @@ export type FoodsModel = {
recommendEndTime ?: any;
saleCount : number;
state : number;
}
export interface CartModel {
createBy?: any;
createTime: string;
updateBy?: any;
updateTime?: any;
remark?: any;
fid: number;
uid: number;
foodsCover: string;
foodsTitle: string;
foodPrice: number;
count: number;
state: number;
}
export interface CategoryModel {
createBy?: any;
createTime: string;
updateBy?: any;
updateTime: string;
remark?: any;
id: number;
title: string;
sort: number;
cover: string;
state: number;
}

View File

@ -16,6 +16,7 @@
/* 行为相关颜色 */
$uni-color-primary: #ff5801;
$uni-color-primary-1: #ff4203;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
@ -74,7 +75,3 @@ $uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
img,image{
max-width: 100%;
}