You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
crmeb/app/pages/supply_chain/wish_menu/index.vue

414 lines
9.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="page">
<view class="date-card">
<view class="date-row">
<view class="date-btn" @click="changeDateBy(-1)"></view>
<picker mode="date" :value="selectedDate" @change="onDatePicked">
<view class="date-text">{{ selectedDate }}</view>
</picker>
<view class="date-btn" @click="changeDateBy(1)"></view>
</view>
<view class="date-tip">默认当天可切换查看不同日期菜单</view>
</view>
<view class="section">
<view class="section-title">当日菜单</view>
<view class="meal-card" v-for="meal in mealSections" :key="meal.key">
<view class="meal-title">{{ meal.label }}</view>
<view v-if="meal.list.length">
<view class="dish-item" v-for="item in meal.list" :key="item.id">
<view class="dish-main">
<view class="dish-name">{{ item.itemName || '未命名菜品' }}</view>
<view class="dish-desc" v-if="item.remark">{{ item.remark }}</view>
<view class="dish-price" v-if="item.itemPrice !== undefined && item.itemPrice !== null">
¥{{ item.itemPrice }}
</view>
</view>
<view class="action-row">
<view class="action like" @click="submitLike(item, '1')">
👍 {{ item.likeCount || 0 }}
</view>
<view class="action dislike" @click="submitLike(item, '2')">
👎 {{ item.dislikeCount || 0 }}
</view>
</view>
</view>
</view>
<view v-else class="empty">暂无{{ meal.label }}菜品</view>
</view>
</view>
<view class="section">
<view class="rank-header">
<view class="section-title">菜品排行</view>
<view class="rank-tabs">
<view
class="rank-tab"
:class="rankingType === 'like' ? 'active' : ''"
@click="switchRanking('like')"
>
点赞排行
</view>
<view
class="rank-tab"
:class="rankingType === 'dislike' ? 'active' : ''"
@click="switchRanking('dislike')"
>
点踩排行
</view>
</view>
</view>
<view v-if="rankingList.length">
<view class="rank-item" v-for="(item, index) in rankingList" :key="item.id || index">
<view class="rank-left">
<text class="rank-no" :class="index < 3 ? 'top' : ''">{{ index + 1 }}</text>
<text class="rank-name">{{ item.itemName || '未命名菜品' }}</text>
</view>
<view class="rank-right">
<text v-if="rankingType === 'like'">👍 {{ item.likeCount || 0 }}</text>
<text v-else>👎 {{ item.dislikeCount || 0 }}</text>
</view>
</view>
</view>
<view v-else class="empty">暂无排行数据</view>
</view>
</view>
</template>
<script>
import {
listDailyMenuDetails,
likeDailyMenuItem,
getDailyMenuRanking
} from '@/api/property.js';
export default {
data() {
return {
selectedDate: '',
menuList: [],
rankingType: 'like',
rankingList: [],
mealSections: [
{ key: 'breakfast', label: '早餐', list: [] },
{ key: 'lunch', label: '中餐', list: [] },
{ key: 'dinner', label: '晚餐', list: [] }
]
};
},
onLoad() {
this.selectedDate = this.formatDate(new Date());
this.loadPageData();
},
methods: {
async loadPageData() {
await Promise.all([this.loadMenuByDate(), this.loadRanking()]);
},
async loadMenuByDate() {
try {
uni.showLoading({ title: '加载菜单中...', mask: true });
const res = await listDailyMenuDetails({ menuDate: this.selectedDate });
const list = res?.data || [];
this.menuList = list;
this.groupMeals(list);
} catch (e) {
this.menuList = [];
this.groupMeals([]);
uni.showToast({
title: typeof e === 'string' ? e : '菜单加载失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
},
groupMeals(list) {
const breakfast = [];
const lunch = [];
const dinner = [];
list.forEach((item) => {
const mealType = this.normalizeMealType(item.mealType);
if (mealType === 'breakfast') {
breakfast.push(item);
} else if (mealType === 'lunch') {
lunch.push(item);
} else if (mealType === 'dinner') {
dinner.push(item);
}
});
this.mealSections = [
{ key: 'breakfast', label: '早餐', list: breakfast },
{ key: 'lunch', label: '中餐', list: lunch },
{ key: 'dinner', label: '晚餐', list: dinner }
];
},
normalizeMealType(type) {
if (!type) return '';
const v = String(type).trim();
if (v === '早餐' || v === '0' || v.toLowerCase() === 'breakfast') return 'breakfast';
if (v === '中餐' || v === '午餐' || v === '1' || v.toLowerCase() === 'lunch') return 'lunch';
if (v === '晚餐' || v === '2' || v.toLowerCase() === 'dinner') return 'dinner';
return '';
},
async submitLike(item, likeType) {
if (!item || !item.id) return;
try {
uni.showLoading({ title: likeType === '1' ? '点赞中...' : '点踩中...', mask: true });
await likeDailyMenuItem({
menuDtlId: item.id,
likeType
});
uni.showToast({
title: likeType === '1' ? '点赞成功' : '点踩成功',
icon: 'success'
});
await Promise.all([this.loadMenuByDate(), this.loadRanking()]);
} catch (e) {
uni.showToast({
title: typeof e === 'string' ? e : '操作失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
},
async loadRanking() {
try {
const res = await getDailyMenuRanking({
limit: 10,
rankingType: this.rankingType,
startDate: this.selectedDate,
endDate: this.selectedDate
});
this.rankingList = res?.data?.list || [];
} catch (e) {
this.rankingList = [];
}
},
async switchRanking(type) {
if (this.rankingType === type) return;
this.rankingType = type;
await this.loadRanking();
},
async onDatePicked(e) {
this.selectedDate = e.detail.value;
await this.loadPageData();
},
async changeDateBy(step) {
const d = new Date(this.selectedDate.replace(/-/g, '/'));
d.setDate(d.getDate() + step);
this.selectedDate = this.formatDate(d);
await this.loadPageData();
},
formatDate(date) {
const y = date.getFullYear();
const m = `${date.getMonth() + 1}`.padStart(2, '0');
const d = `${date.getDate()}`.padStart(2, '0');
return `${y}-${m}-${d}`;
}
}
};
</script>
<style lang="scss">
.page {
min-height: 100vh;
background: #f6f7fb;
padding: 24rpx;
}
.date-card,
.section,
.meal-card {
background: #fff;
border-radius: 16rpx;
}
.date-card {
padding: 24rpx;
margin-bottom: 20rpx;
}
.date-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.date-btn {
width: 160rpx;
text-align: center;
height: 64rpx;
line-height: 64rpx;
background: #f2f4f8;
color: #333;
border-radius: 10rpx;
font-size: 26rpx;
}
.date-text {
min-width: 260rpx;
text-align: center;
font-size: 30rpx;
color: #1f2d3d;
font-weight: 600;
}
.date-tip {
margin-top: 16rpx;
color: #8a94a6;
font-size: 24rpx;
}
.section {
padding: 24rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
color: #222;
font-weight: 600;
margin-bottom: 18rpx;
}
.meal-card {
border: 1rpx solid #edf0f5;
padding: 20rpx;
margin-bottom: 16rpx;
}
.meal-title {
font-size: 28rpx;
color: #3b82f6;
font-weight: 600;
margin-bottom: 14rpx;
}
.dish-item {
padding: 16rpx 0;
border-bottom: 1rpx solid #f2f3f7;
}
.dish-item:last-child {
border-bottom: none;
}
.dish-name {
font-size: 28rpx;
color: #1f2937;
font-weight: 500;
}
.dish-desc {
margin-top: 8rpx;
color: #6b7280;
font-size: 24rpx;
}
.dish-price {
margin-top: 8rpx;
color: #ef4444;
font-size: 24rpx;
}
.action-row {
margin-top: 12rpx;
display: flex;
gap: 16rpx;
}
.action {
height: 52rpx;
line-height: 52rpx;
padding: 0 20rpx;
border-radius: 26rpx;
font-size: 24rpx;
}
.like {
background: #ecfdf3;
color: #16a34a;
}
.dislike {
background: #fef2f2;
color: #dc2626;
}
.rank-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.rank-tabs {
display: flex;
background: #f3f4f6;
border-radius: 999rpx;
padding: 4rpx;
}
.rank-tab {
padding: 10rpx 20rpx;
font-size: 24rpx;
color: #6b7280;
border-radius: 999rpx;
}
.rank-tab.active {
background: #fff;
color: #2563eb;
font-weight: 600;
}
.rank-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18rpx 0;
border-bottom: 1rpx solid #f2f3f7;
}
.rank-item:last-child {
border-bottom: none;
}
.rank-left {
display: flex;
align-items: center;
}
.rank-no {
width: 40rpx;
text-align: center;
font-size: 26rpx;
color: #6b7280;
margin-right: 14rpx;
}
.rank-no.top {
color: #f97316;
font-weight: 700;
}
.rank-name {
font-size: 27rpx;
color: #1f2937;
}
.rank-right {
font-size: 26rpx;
color: #111827;
}
.empty {
color: #9ca3af;
font-size: 24rpx;
padding: 20rpx 0;
text-align: center;
}
</style>