init
This commit is contained in:
184
src/views/dish/add.vue
Normal file
184
src/views/dish/add.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="dish-form">
|
||||
<h2>新增菜品</h2>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
class="form"
|
||||
>
|
||||
<el-form-item label="菜品名称" prop="dishName">
|
||||
<el-input v-model="form.dishName" placeholder="请输入菜品名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="菜品分类" prop="dishType">
|
||||
<el-select v-model="form.dishType" placeholder="请选择分类">
|
||||
<el-option
|
||||
v-for="item in dishTypes"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="价格" prop="dishPrice">
|
||||
<el-input-number
|
||||
v-model="form.dishPrice"
|
||||
:precision="2"
|
||||
:step="0.1"
|
||||
:min="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="配料" prop="dishIngredients">
|
||||
<el-input
|
||||
v-model="form.dishIngredients"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入配料"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图片" prop="dishImage">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:http-request="handleUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<img v-if="form.dishImage" :src="form.dishImage" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||||
<el-button @click="$router.back()">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { addDish } from '@/api/dish'
|
||||
|
||||
const router = useRouter()
|
||||
const formRef = ref(null)
|
||||
|
||||
const dishTypes = [
|
||||
{ label: '热菜过油', value: '1' },
|
||||
{ label: '热菜不过油', value: '2' },
|
||||
{ label: '素菜', value: '3' },
|
||||
{ label: '凉菜', value: '4' },
|
||||
{ label: '汤', value: '5' }
|
||||
]
|
||||
|
||||
const form = ref({
|
||||
dishName: '',
|
||||
dishType: '',
|
||||
dishPrice: 0,
|
||||
dishIngredients: '',
|
||||
dishImage: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
dishName: [
|
||||
{ required: true, message: '请输入菜品名称', trigger: 'blur' }
|
||||
],
|
||||
dishType: [
|
||||
{ required: true, message: '请选择菜品分类', trigger: 'change' }
|
||||
],
|
||||
dishPrice: [
|
||||
{ required: true, message: '请输入价格', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
const handleUpload = async (options) => {
|
||||
const file = options.file
|
||||
// 这里可以使用FileReader读取文件并预览
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => {
|
||||
form.value.dishImage = reader.result // 保存为base64格式
|
||||
}
|
||||
// 如果需要上传到服务器,可以在提交表单时一起处理
|
||||
// 现在我们直接在本地预览
|
||||
}
|
||||
|
||||
const beforeUpload = (file) => {
|
||||
const isImage = file.type.startsWith('image/')
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
|
||||
if (!isImage) {
|
||||
ElMessage.error('只能上传图片文件!')
|
||||
return false
|
||||
}
|
||||
if (!isLt2M) {
|
||||
ElMessage.error('图片大小不能超过 2MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
await addDish(form.value)
|
||||
ElMessage.success('添加成功')
|
||||
router.push('/dish/list')
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dish-form {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
text-align: center;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
}
|
||||
|
||||
.avatar-uploader:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
line-height: 178px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
231
src/views/dish/edit.vue
Normal file
231
src/views/dish/edit.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<div class="dish-form">
|
||||
<h2>编辑菜品</h2>
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
class="form"
|
||||
>
|
||||
<el-form-item label="菜品名称" prop="dishName">
|
||||
<el-input v-model="form.dishName" placeholder="请输入菜品名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="菜品分类" prop="dishType">
|
||||
<el-select v-model="form.dishType" placeholder="请选择分类">
|
||||
<el-option
|
||||
v-for="item in dishTypes"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="价格" prop="dishPrice">
|
||||
<el-input-number
|
||||
v-model="form.dishPrice"
|
||||
:precision="2"
|
||||
:step="0.1"
|
||||
:min="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="配料" prop="dishIngredients">
|
||||
<el-input
|
||||
v-model="form.dishIngredients"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入配料"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图片" prop="dishImage">
|
||||
<el-upload
|
||||
class="avatar-uploader"
|
||||
:http-request="handleUpload"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
>
|
||||
<img v-if="form.dishImage" :src="form.dishImage" class="avatar" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSubmit">保存</el-button>
|
||||
<el-button @click="$router.back()">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { getDishList, updateDish } from '@/api/dish'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const formRef = ref(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const dishTypes = [
|
||||
{ label: '热菜过油', value: '1' },
|
||||
{ label: '热菜不过油', value: '2' },
|
||||
{ label: '素菜', value: '3' },
|
||||
{ label: '凉菜', value: '4' },
|
||||
{ label: '汤', value: '5' }
|
||||
]
|
||||
|
||||
const form = ref({
|
||||
id: '',
|
||||
dishName: '',
|
||||
dishType: '',
|
||||
dishPrice: 0,
|
||||
dishIngredients: '',
|
||||
dishImage: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
dishName: [
|
||||
{ required: true, message: '请输入菜品名称', trigger: 'blur' }
|
||||
],
|
||||
dishType: [
|
||||
{ required: true, message: '请选择菜品分类', trigger: 'change' }
|
||||
],
|
||||
dishPrice: [
|
||||
{ required: true, message: '请输入价格', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
const handleUpload = async (options) => {
|
||||
const file = options.file
|
||||
// 这里可以使用FileReader读取文件并预览
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => {
|
||||
form.value.dishImage = reader.result // 保存为base64格式
|
||||
}
|
||||
// 如果需要上传到服务器,可以在提交表单时一起处理
|
||||
// 现在我们直接在本地预览
|
||||
}
|
||||
|
||||
const beforeUpload = (file) => {
|
||||
const isImage = file.type.startsWith('image/')
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
|
||||
if (!isImage) {
|
||||
ElMessage.error('只能上传图片文件!')
|
||||
return false
|
||||
}
|
||||
if (!isLt2M) {
|
||||
ElMessage.error('图片大小不能超过 2MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const fetchDishDetail = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getDishList()
|
||||
console.log('获取到的菜品列表:', res.data)
|
||||
console.log('当前要查找的ID:', route.params.id)
|
||||
|
||||
// 确保ID是字符串类型进行比较,因为route.params.id是字符串
|
||||
const targetId = route.params.id
|
||||
const dish = res.data.find(item => String(item.id) === targetId)
|
||||
|
||||
console.log('找到的菜品:', dish)
|
||||
|
||||
if (dish) {
|
||||
// 确保价格是数字类型
|
||||
form.value = {
|
||||
...dish,
|
||||
dishPrice: Number(dish.dishPrice)
|
||||
}
|
||||
} else {
|
||||
ElMessage.error('菜品不存在')
|
||||
router.push('/dish/list')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取菜品详情失败:', error)
|
||||
ElMessage.error('获取菜品详情失败')
|
||||
router.push('/dish/list')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
// 显示提交的数据,用于调试
|
||||
console.log('提交的表单数据:', form.value)
|
||||
|
||||
// 不需要在这里转换为FormData,在API中已处理
|
||||
await updateDish(form.value)
|
||||
|
||||
ElMessage.success('更新成功')
|
||||
router.push('/dish/list')
|
||||
} catch (error) {
|
||||
console.error('更新失败:', error)
|
||||
ElMessage.error(error.message || '更新失败')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDishDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dish-form {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
text-align: center;
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
}
|
||||
|
||||
.avatar-uploader:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
line-height: 178px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
345
src/views/dish/list.vue
Normal file
345
src/views/dish/list.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<div class="dish-list">
|
||||
<div class="header">
|
||||
<el-button type="primary" @click="$router.push('/dish/add')">
|
||||
新增菜品
|
||||
</el-button>
|
||||
<el-select v-model="selectedType" placeholder="选择分类" clearable>
|
||||
<el-option
|
||||
v-for="item in dishTypes"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- PC端表格 -->
|
||||
<el-table
|
||||
v-if="!isMobile"
|
||||
v-loading="loading"
|
||||
:data="filteredDishList"
|
||||
style="width: 100%"
|
||||
:max-height="tableHeight"
|
||||
:border="true"
|
||||
class="dish-table"
|
||||
>
|
||||
<el-table-column prop="dishImage" label="图片" width="100" fixed="left">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
:src="scope.row.dishImage"
|
||||
:preview-src-list="[scope.row.dishImage]"
|
||||
fit="cover"
|
||||
style="width: 80px; height: 80px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dishName" label="菜品名称" min-width="120" />
|
||||
<el-table-column prop="dishType" label="分类" min-width="100">
|
||||
<template #default="scope">
|
||||
{{ getDishTypeLabel(scope.row.dishType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="dishPrice" label="价格" min-width="80" />
|
||||
<el-table-column prop="dishIngredients" label="配料" min-width="500" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<div style="white-space: normal; word-break: break-all; line-height: 1.5;">
|
||||
{{ scope.row.dishIngredients }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="$router.push(`/dish/edit/${scope.row.id}`)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="handleDelete(scope.row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 移动端卡片列表 -->
|
||||
<div v-else class="mobile-dish-list">
|
||||
<div v-loading="loading" class="dish-cards">
|
||||
<div v-for="dish in filteredDishList" :key="dish.id" class="dish-card">
|
||||
<div class="dish-card-header">
|
||||
<el-image
|
||||
:src="dish.dishImage"
|
||||
:preview-src-list="[dish.dishImage]"
|
||||
fit="cover"
|
||||
class="dish-image"
|
||||
/>
|
||||
<div class="dish-basic-info">
|
||||
<h3>{{ dish.dishName }}</h3>
|
||||
<p class="dish-type">{{ getDishTypeLabel(dish.dishType) }}</p>
|
||||
<p class="dish-price">¥{{ dish.dishPrice }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dish-ingredients" v-if="dish.dishIngredients">
|
||||
<p class="label">配料:</p>
|
||||
<p>{{ dish.dishIngredients }}</p>
|
||||
</div>
|
||||
|
||||
<div class="dish-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="$router.push(`/dish/edit/${dish.id}`)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleDelete(dish)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getDishList, deleteDish } from '@/api/dish'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
|
||||
const loading = ref(false)
|
||||
const dishList = ref([])
|
||||
const selectedType = ref('')
|
||||
const tableHeight = ref('calc(100vh - 150px)')
|
||||
|
||||
const { width, height } = useWindowSize()
|
||||
const isMobile = computed(() => width.value < 768)
|
||||
|
||||
const dishTypes = [
|
||||
{ label: '热菜过油', value: '1' },
|
||||
{ label: '热菜不过油', value: '2' },
|
||||
{ label: '素菜', value: '3' },
|
||||
{ label: '凉菜', value: '4' },
|
||||
{ label: '汤', value: '5' }
|
||||
]
|
||||
|
||||
const filteredDishList = computed(() => {
|
||||
if (!selectedType.value) return dishList.value
|
||||
return dishList.value.filter(item => item.dishType === selectedType.value)
|
||||
})
|
||||
|
||||
const getDishTypeLabel = (type) => {
|
||||
const found = dishTypes.find(item => item.value === type)
|
||||
return found ? found.label : type
|
||||
}
|
||||
|
||||
const fetchDishList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getDishList()
|
||||
dishList.value = res.data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认删除该菜品吗?', '提示', {
|
||||
type: 'warning'
|
||||
})
|
||||
await deleteDish({ id: row.id })
|
||||
ElMessage.success('删除成功')
|
||||
fetchDishList()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听窗口大小变化,调整表格高度
|
||||
const updateTableHeight = () => {
|
||||
// 减去头部的高度和边距,确保表格填充剩余空间
|
||||
tableHeight.value = isMobile.value ? 'calc(100vh - 230px)' : 'calc(100vh - 150px)'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchDishList()
|
||||
updateTableHeight()
|
||||
window.addEventListener('resize', updateTableHeight)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateTableHeight)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dish-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* PC端表格样式优化 */
|
||||
.dish-table {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
flex: 1;
|
||||
width: 100% !important;
|
||||
--el-table-border-color: #ebeef5;
|
||||
--el-table-header-bg-color: #f5f7fa;
|
||||
}
|
||||
|
||||
:deep(.el-table__inner-wrapper) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-table th) {
|
||||
background-color: #f5f7fa;
|
||||
color: #606266;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
padding: 12px 10px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
:deep(.el-table td) {
|
||||
padding: 15px 10px;
|
||||
height: 90px; /* 增加行高 */
|
||||
}
|
||||
|
||||
:deep(.el-table--border th), :deep(.el-table--border td) {
|
||||
border-right: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
:deep(.el-table__fixed-right) {
|
||||
height: auto !important;
|
||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:deep(.el-table__fixed-right-patch) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 增加移动端卡片样式 */
|
||||
.mobile-dish-list {
|
||||
margin-bottom: 70px;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dish-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.dish-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.dish-card-header {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dish-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.dish-basic-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dish-basic-info h3 {
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.dish-type {
|
||||
color: #909399;
|
||||
margin: 0 0 5px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dish-price {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dish-ingredients {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dish-ingredients .label {
|
||||
color: #909399;
|
||||
margin: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.dish-ingredients p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dish-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.dish-list {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.header > * {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
222
src/views/dish/random.vue
Normal file
222
src/views/dish/random.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<div class="random-dish">
|
||||
<div class="header">
|
||||
<h2>随机选择菜品</h2>
|
||||
<div class="controls">
|
||||
<el-input-number
|
||||
v-model="num"
|
||||
:min="1"
|
||||
:max="10"
|
||||
placeholder="选择数量"
|
||||
style="width: 120px"
|
||||
/>
|
||||
<el-button type="primary" @click="handleRandom" :loading="loading">
|
||||
随机选择
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result" v-if="randomDishes.length">
|
||||
<h3>随机结果</h3>
|
||||
<div class="dish-grid" :class="{ 'mobile-grid': isMobile }">
|
||||
<div v-for="dish in randomDishes" :key="dish.id" class="dish-card">
|
||||
<el-image
|
||||
:src="dish.dishImage"
|
||||
:preview-src-list="[dish.dishImage]"
|
||||
fit="cover"
|
||||
class="dish-image"
|
||||
/>
|
||||
<div class="dish-info">
|
||||
<h4>{{ dish.dishName }}</h4>
|
||||
<p class="type">{{ getDishTypeLabel(dish.dishType) }}</p>
|
||||
<p class="price">¥{{ dish.dishPrice }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-else description="点击上方按钮开始随机选择" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getRandomDish } from '@/api/dish'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
|
||||
const loading = ref(false)
|
||||
const num = ref(1)
|
||||
const randomDishes = ref([])
|
||||
|
||||
const { width } = useWindowSize()
|
||||
const isMobile = computed(() => width.value < 768)
|
||||
|
||||
const dishTypes = [
|
||||
{ label: '热菜过油', value: '1' },
|
||||
{ label: '热菜不过油', value: '2' },
|
||||
{ label: '素菜', value: '3' },
|
||||
{ label: '凉菜', value: '4' },
|
||||
{ label: '汤', value: '5' }
|
||||
]
|
||||
|
||||
const getDishTypeLabel = (type) => {
|
||||
const found = dishTypes.find(item => item.value === type)
|
||||
return found ? found.label : type
|
||||
}
|
||||
|
||||
const handleRandom = async () => {
|
||||
if (num.value < 1) {
|
||||
ElMessage.warning('请选择至少1个菜品')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getRandomDish(num.value)
|
||||
randomDishes.value = res.data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.random-dish {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 30px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #303133;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 30px;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.result h3 {
|
||||
margin-bottom: 20px;
|
||||
color: #303133;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.dish-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mobile-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.dish-card {
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.05);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dish-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
.dish-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dish-info {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.dish-info h4 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.type {
|
||||
color: #909399;
|
||||
margin: 5px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.price {
|
||||
color: #f56c6c;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.random-dish {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dish-image {
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.dish-info {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.dish-info h4 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.type {
|
||||
font-size: 12px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-bottom: 80px; /* 为底部导航留出空间 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user