feat:重构模块
This commit is contained in:
21
src/components/Food/Data/FoodBasicData.vue
Normal file
21
src/components/Food/Data/FoodBasicData.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="info-item card-item">
|
||||
<div class="title-info">
|
||||
<svg-icon name="home-basic"/>
|
||||
<span class="sub-title">基础信息</span>
|
||||
</div>
|
||||
|
||||
<div class="info food-basic-info">
|
||||
<el-tag v-if="foodStore.currentFoodRecipe.category">
|
||||
{{ foodStore.currentFoodRecipe.category }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
</script>
|
||||
59
src/components/Food/Data/FoodMaterialData.vue
Normal file
59
src/components/Food/Data/FoodMaterialData.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div class="info-item card-item">
|
||||
<div class="title-info">
|
||||
<svg-icon name="home-material"/>
|
||||
<span class="sub-title">食材信息</span>
|
||||
</div>
|
||||
|
||||
<div class="info material-info">
|
||||
<div class="material-item">
|
||||
<span class="material-title">主料</span>
|
||||
<div v-if="getFoodMaterial('主料').length > 0" class="material-content">
|
||||
<el-tag v-for="(item, index) in getFoodMaterial('主料')"
|
||||
:key="index">{{ `${item.name} ${item.amount}` }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else class="material-content">
|
||||
<el-tag>无</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="material-item">
|
||||
<span class="material-title">配料</span>
|
||||
<div v-if="getFoodMaterial('辅料').length > 0" class="material-content">
|
||||
<el-tag v-for="(item, index) in getFoodMaterial('辅料')"
|
||||
:key="index">{{ `${item.name} ${item.amount}` }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else class="material-content">
|
||||
<el-tag>无</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="material-item">
|
||||
<span class="material-title">辅料</span>
|
||||
<div v-if="getFoodMaterial('调料').length > 0" class="material-content">
|
||||
<el-tag v-for="(item, index) in getFoodMaterial('调料')"
|
||||
:key="index">{{ `${item.name} ${item.amount}` }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else class="material-content">
|
||||
<el-tag>无</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const getFoodMaterial = (type: string) => {
|
||||
return foodStore.currentFoodRecipe.materialList.filter((item) => {
|
||||
return item.type === type
|
||||
})
|
||||
}
|
||||
</script>
|
||||
25
src/components/Food/Data/FoodOthersData.vue
Normal file
25
src/components/Food/Data/FoodOthersData.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="info-item card-item">
|
||||
<div class="title-info">
|
||||
<svg-icon name="home-others"/>
|
||||
<span class="sub-title">其他信息</span>
|
||||
</div>
|
||||
|
||||
<div class="others-info">
|
||||
<span class="others-title">备注:</span>
|
||||
<span v-if="foodStore.currentFoodRecipe.remark">
|
||||
{{ foodStore.currentFoodRecipe.remark }}
|
||||
</span>
|
||||
<span v-else>
|
||||
无
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
</script>
|
||||
32
src/components/Food/Data/FoodRecordData.vue
Normal file
32
src/components/Food/Data/FoodRecordData.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="info-item">
|
||||
<div class="title-info">
|
||||
<svg-icon name="home-work"/>
|
||||
<span class="sub-title">成果展示</span>
|
||||
</div>
|
||||
|
||||
<div v-if="foodStore.currentFoodRecipe.recordList.length > 0" class="info record-info">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="(item, index) in foodStore.currentFoodRecipe.recordList"
|
||||
:key="index" :timestamp="item.date" placement="top">
|
||||
<el-card class="record-card">
|
||||
<span class="record-content">完成人:{{ item.person }}</span>
|
||||
<el-image v-if="item.imageUrl"
|
||||
:src="serviceFileRootPath + item.imageUrl"
|
||||
:preview-src-list="[serviceFileRootPath + item.imageUrl]"
|
||||
fit="contain" alt="暂无图片" class="record-image"/>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
<el-empty v-else description="暂无成果"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
</script>
|
||||
28
src/components/Food/Data/FoodStepData.vue
Normal file
28
src/components/Food/Data/FoodStepData.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="info-item card-item">
|
||||
<div class="title-info">
|
||||
<svg-icon name="home-step"/>
|
||||
<span class="sub-title">菜谱信息</span>
|
||||
</div>
|
||||
|
||||
<div class="step-info">
|
||||
<div v-for="(item, index) in foodStore.currentFoodRecipe.stepList"
|
||||
:key="index" class="step-container">
|
||||
<span class="step-number">第{{ index + 1 }}步</span>
|
||||
<div class="step-body">
|
||||
<span class="step-content">{{ item.content }}</span>
|
||||
<img v-if="item.imageUrl"
|
||||
:src="serviceFileRootPath + item.imageUrl" alt="暂无图片" class="step-image"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
import { SvgIcon } from 'vue3-common'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
</script>
|
||||
102
src/components/Food/FoodCalendar.vue
Normal file
102
src/components/Food/FoodCalendar.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="food-daily">
|
||||
<v-calendar ref="calendarRef" :attributes='dailyInfo.attrs'
|
||||
@dayclick="dateClick" @did-move="changeMonthClick"
|
||||
:is-dark="appStore.isDark"
|
||||
class="food-calendar card-item"/>
|
||||
|
||||
<food-daily-item :date="dailyInfo.calendarDate"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FoodDailyItem from '@/components/Food/FoodDailyItem.vue'
|
||||
import { reactive, watch, ref, onMounted } from 'vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||
import { CalendarDay } from 'v-calendar/dist/types/src/utils/page'
|
||||
import { useAppStore } from '@/store/app.ts'
|
||||
|
||||
const calendarRef = ref()
|
||||
const foodStore = useFoodStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
onMounted(() => {
|
||||
const { month } = foodStore.queryRecord
|
||||
calendarRef.value.move(new Date(month))
|
||||
})
|
||||
|
||||
watch(() => foodStore.isRefresh, () => {
|
||||
updateInfo()
|
||||
})
|
||||
|
||||
const dailyInfo = reactive({
|
||||
calendarDate: '',
|
||||
attrs: [] as any[]
|
||||
})
|
||||
|
||||
const updateInfo = () => {
|
||||
getCalendarMoveDate()
|
||||
getCalendarAttrDates()
|
||||
}
|
||||
|
||||
const getCalendarMoveDate = () => {
|
||||
const foodLength = foodStore.foodRecordList.length
|
||||
if (foodLength > 0) {
|
||||
const today = formatDate(new Date())
|
||||
if (foodStore.foodRecordList.some((item) => item.date === today)) {
|
||||
dailyInfo.calendarDate = today
|
||||
} else {
|
||||
dailyInfo.calendarDate = formatDate(foodStore.foodRecordList[foodLength - 1].date)
|
||||
}
|
||||
} else {
|
||||
dailyInfo.calendarDate = ''
|
||||
}
|
||||
}
|
||||
|
||||
const getCalendarAttrDates = () => {
|
||||
dailyInfo.attrs = []
|
||||
dailyInfo.attrs.push({
|
||||
key: 'today',
|
||||
highlight: true,
|
||||
dates: new Date()
|
||||
})
|
||||
|
||||
const dateList = foodStore.foodRecordList.map((item) => item.date)
|
||||
dailyInfo.attrs.push({
|
||||
dot: 'red',
|
||||
dates: [...new Set(dateList)]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择日期事件
|
||||
* @param day 日期
|
||||
*/
|
||||
const dateClick = (day: CalendarDay) => {
|
||||
dailyInfo.calendarDate = formatDate(day.date)
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择月份事件
|
||||
*/
|
||||
const changeMonthClick = async (value) => {
|
||||
foodStore.queryRecord.month = value[0].id
|
||||
await foodStore.refreshInfo()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-daily {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
|
||||
.food-calendar {
|
||||
width: 100%;
|
||||
justify-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
130
src/components/Food/FoodCard.vue
Normal file
130
src/components/Food/FoodCard.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<div class="life-record-card card-item">
|
||||
<el-carousel v-if="props.foodItem?.recordList.length > 0"
|
||||
arrow="always" @change="changeFoodEvent">
|
||||
<el-carousel-item v-for="(item, index) in getFoodRecord(props.foodItem?.recordList)"
|
||||
:key="index">
|
||||
<el-image :src="serviceFileRootPath + item.imageUrl"
|
||||
fit="contain" alt="暂无成果">
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<span>暂无图片</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
<el-empty v-else description="暂无成果" />
|
||||
|
||||
<div class="record-info">
|
||||
<div class="top-container">
|
||||
<div class="info-list">
|
||||
<div class="info-item">
|
||||
<span class="title">{{ props.foodItem.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-info">
|
||||
<el-button v-if="!isMobile()"
|
||||
type="primary" :icon="Edit" circle :size="'small'"
|
||||
@click="editFoodRecipeClick"/>
|
||||
<el-button type="primary" :icon="MoreFilled" circle :size="'small'"
|
||||
@click="viewFoodRecipeClick()" />
|
||||
<el-button type="danger" :icon="DeleteFilled" circle :size="'small'"
|
||||
@click="deleteFoodRecipeClick()" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bottom-container">
|
||||
<div class="info-list">
|
||||
<div class="info-item category-item">
|
||||
<i class="fa-solid fa-utensils"></i>
|
||||
{{ props.foodItem.category }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="props.foodItem?.recordList.length > 0" class="info-list">
|
||||
<div class="info-item">
|
||||
<i class="fa-solid fa-flag"></i>
|
||||
{{ props.foodItem.recordList[foodCardInfo.currentFoodIndex].person}}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<i class="fa-regular fa-calendar-check"></i>
|
||||
{{ formatDate(props.foodItem.recordList[foodCardInfo.currentFoodIndex].date) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<span>暂无成果</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Edit, MoreFilled, DeleteFilled } from '@element-plus/icons-vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { defineProps, onMounted, PropType, reactive } from 'vue'
|
||||
import { IFoodRecipe, IFoodRecord } from '@/types/food.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
|
||||
const router = useRouter()
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const foodCardInfo = reactive({
|
||||
recommendRate: 0,
|
||||
currentFoodIndex: 0
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
foodItem: {
|
||||
required: true,
|
||||
type: Object as PropType<IFoodRecipe>
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
foodCardInfo.recommendRate = props.foodItem?.recommendRate as number
|
||||
})
|
||||
|
||||
const getFoodRecord = (recordList: IFoodRecord[]) => {
|
||||
return recordList.filter((item) => item.imageUrl)
|
||||
}
|
||||
|
||||
const editFoodRecipeClick = async () => {
|
||||
const id = props.foodItem?.id
|
||||
foodStore.foodRecipeApiType = 'UPDATE'
|
||||
await foodStore.queryFoodRecipe(id as number)
|
||||
foodStore.foodRecipeDialog = {
|
||||
title: '编辑菜谱',
|
||||
visible: true,
|
||||
data: deepCopyObject(foodStore.currentFoodRecipe)
|
||||
}
|
||||
}
|
||||
|
||||
const viewFoodRecipeClick = async () => {
|
||||
await router.push({ name: 'FoodDetailId', params: { id: props.foodItem?.id } })
|
||||
}
|
||||
|
||||
const deleteFoodRecipeClick = () => {
|
||||
foodStore.foodRecipeApiType = 'DELETE'
|
||||
commonElMessageBox(`请确认是否删除该菜谱: ${props.foodItem?.name}?`).then(() => {
|
||||
foodStore.handleFoodRecipeApi(props.foodItem?.id as number)
|
||||
})
|
||||
}
|
||||
|
||||
const changeFoodEvent = (current: number) => {
|
||||
foodCardInfo.currentFoodIndex = current
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use "@/styles/home/life.record.module";
|
||||
</style>
|
||||
117
src/components/Food/FoodDailyItem.vue
Normal file
117
src/components/Food/FoodDailyItem.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<el-empty v-if="dailyItemInfo.itemList.length === 0" />
|
||||
|
||||
<div v-else class="food-daily-item card-item">
|
||||
<daily-summary :date="dailyItemInfo.date"/>
|
||||
|
||||
<div class="daily-list">
|
||||
<daily-card v-for="(item, index) in dailyItemInfo.itemList"
|
||||
:key="index" @click="selectFoodRecordClick(item)"
|
||||
:title="item.name" :content="item.category">
|
||||
<template #icon>
|
||||
<el-avatar>{{ item.category.charAt(0) }}</el-avatar>
|
||||
</template>
|
||||
|
||||
<template #extra>
|
||||
<span>{{ item.person }}</span>
|
||||
<span>{{ item.date }}</span>
|
||||
</template>
|
||||
</daily-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DailySummary from '@/components/DailySummary.vue'
|
||||
import DailyCard from '@/components/DailyCard.vue'
|
||||
import { defineProps, onMounted, reactive, watch } from 'vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAppStore } from '@/store/app.ts'
|
||||
import { formatChineseDate } from 'vue3-common/utils/dateUtil'
|
||||
import { IFoodRecord } from '@/types/food.ts'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
const appStore = useAppStore()
|
||||
const router = useRouter()
|
||||
|
||||
const props = defineProps({
|
||||
date: {
|
||||
required: true,
|
||||
type: String
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.date, () => {
|
||||
getFoodDailyByDate(props.date as string)
|
||||
})
|
||||
|
||||
watch(() => foodStore.isRefresh, () => {
|
||||
getFoodDailyByDate(props.date as string)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getFoodDailyByDate(props.date as string)
|
||||
})
|
||||
|
||||
const dailyItemInfo = reactive({
|
||||
date: '',
|
||||
itemList: [] as IFoodRecord[]
|
||||
})
|
||||
|
||||
/**
|
||||
* 根据日期获取当天的美食记录
|
||||
* @param date
|
||||
*/
|
||||
const getFoodDailyByDate = (date: string) => {
|
||||
if (date) {
|
||||
dailyItemInfo.date = formatChineseDate(date, 'MM月DD日 dddd')
|
||||
dailyItemInfo.itemList = []
|
||||
foodStore.foodRecordList.forEach((item) => {
|
||||
if (item.date === date) {
|
||||
dailyItemInfo.itemList.push(item)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
dailyItemInfo.itemList = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击美食记录按钮
|
||||
* @param foodRecord 美食记录
|
||||
*/
|
||||
const selectFoodRecordClick = async (foodRecord: IFoodRecord) => {
|
||||
foodStore.foodRecordApiType = 'UPDATE'
|
||||
|
||||
if (isMobile()) {
|
||||
appStore.isShowMobileBack = true
|
||||
foodStore.currentFoodRecord = foodRecord
|
||||
await router.push({ name: 'FoodRecordForm' })
|
||||
} else {
|
||||
foodStore.foodRecordDialog = {
|
||||
title: '编辑记录',
|
||||
visible: true,
|
||||
data: deepCopyObject(foodRecord)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-daily-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
padding: 1vh 0.5vw;
|
||||
|
||||
.daily-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
57
src/components/Food/FoodDailyList.vue
Normal file
57
src/components/Food/FoodDailyList.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="food-daily-list">
|
||||
<el-date-picker v-model="foodStore.queryRecord.year"
|
||||
type="year" :editable="false" style="width: 120px;"
|
||||
@change="foodStore.refreshInfo()"/>
|
||||
|
||||
<el-empty v-if="foodDailyInfo.dateList.length === 0"/>
|
||||
|
||||
<el-scrollbar>
|
||||
<food-daily-item v-for="(item, index) in foodDailyInfo.dateList"
|
||||
:key="index" :date="item"/>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FoodDailyItem from '@/components/Food/FoodDailyItem.vue'
|
||||
import { reactive, watch } from 'vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
watch(() => foodStore.isRefresh, () => {
|
||||
updateInfo()
|
||||
})
|
||||
|
||||
const foodDailyInfo = reactive({
|
||||
dateList: [] as string[]
|
||||
})
|
||||
|
||||
const updateInfo = () => {
|
||||
const dateList = foodStore.foodRecordList.map((item) => item.date)
|
||||
foodDailyInfo.dateList = [...new Set(dateList)] as string[]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-daily-list {
|
||||
height: calc(100vh - 120px);
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1vh;
|
||||
|
||||
.el-date-editor {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
.el-scrollbar__view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
116
src/components/Food/FoodDialog.vue
Normal file
116
src/components/Food/FoodDialog.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogData.visible" :title="dialogData.title"
|
||||
center @open="openDialogEvent" class="food-dialog">
|
||||
<el-form ref="formRef" :model="dialogData.data"
|
||||
:rules=rules label-width="80px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="美食名称" prop="name">
|
||||
<el-input v-model="dialogData.data.name" autocomplete="off"
|
||||
placeholder="请输入美食名称" clearable
|
||||
show-word-limit maxlength="20">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="美食菜系" prop="category">
|
||||
<el-select v-model="dialogData.data.category">
|
||||
<el-option v-for="(item, index) in foodCategoryList"
|
||||
:key="index" :label="item" :value="item"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="推荐指数" prop="recommendRate">
|
||||
<el-rate v-model="dialogData.data.recommendRate"
|
||||
:colors="foodStore.rateColorList"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="食材">
|
||||
<food-material-form />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="步骤">
|
||||
<food-step-form />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="dialogData.data.remark" autocomplete="off"
|
||||
placeholder="请输入备注" clearable
|
||||
show-word-limit maxlength="20">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelClick">取消</el-button>
|
||||
<el-button type="primary" @click="confirmClick">保存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FoodMaterialForm from '@/components/Food/Form/FoodMaterialForm.vue'
|
||||
import FoodStepForm from '@/components/Food/Form/FoodStepForm.vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { FormInstance, FormRules } from 'element-plus'
|
||||
import { computed, nextTick, ref, reactive } from 'vue'
|
||||
import { foodRules } from '@/utils/element/elRules.ts'
|
||||
|
||||
const rules = reactive<FormRules>(foodRules)
|
||||
const formRef = ref<FormInstance>()
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const foodCategoryList = ['家常菜', '淮扬菜', '鲁菜', '川菜', '粤菜', '闽菜', '浙菜', '湘菜', '徽菜', '其他']
|
||||
|
||||
const dialogData = computed(() => foodStore.foodRecipeDialog)
|
||||
|
||||
/**
|
||||
* 打开对话框事件
|
||||
*/
|
||||
const openDialogEvent = async () => {
|
||||
await nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 点击确认按钮
|
||||
*/
|
||||
const confirmClick = () => {
|
||||
formRef.value?.validate().then(() => {
|
||||
foodStore.handleFoodRecipeApi()
|
||||
})
|
||||
}
|
||||
|
||||
const cancelClick = () => {
|
||||
dialogData.value.visible = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-dialog {
|
||||
width: 75%;
|
||||
|
||||
.el-dialog__body {
|
||||
height: 65vh;
|
||||
|
||||
.el-button.is-text {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.material-table {
|
||||
height: 45vh;
|
||||
}
|
||||
|
||||
.food-step-table {
|
||||
height: 55vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
68
src/components/Food/FoodQuery.vue
Normal file
68
src/components/Food/FoodQuery.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div v-if="isMobile()" class="mobile-common-query card-item">
|
||||
<div class="query-item">
|
||||
<span>分类:</span>
|
||||
<el-segmented v-model="foodStore.queryRecipe.category"
|
||||
:options="queryCategoryList"
|
||||
@change="queryFoodSegmented"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="common-query">
|
||||
<el-select v-model="foodStore.queryDateType"
|
||||
@change="foodStore.refreshInfo()"
|
||||
style="width: 120px;align-self: center;">
|
||||
<el-option v-for="(item, index) in dateTypeOptions" :key="index"
|
||||
:label="item.label" :value="item.value"/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { ICategory } from '@/types'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const queryCategoryList = ref([] as ICategory[])
|
||||
|
||||
onMounted(async () => {
|
||||
await foodStore.queryFoodCategory()
|
||||
initCategoryList()
|
||||
})
|
||||
|
||||
const dateTypeOptions = [{
|
||||
label: '按月选择',
|
||||
value: 'month'
|
||||
},
|
||||
{
|
||||
label: '按年选择',
|
||||
value: 'year'
|
||||
}]
|
||||
|
||||
const initCategoryList = () => {
|
||||
queryCategoryList.value = []
|
||||
queryCategoryList.value.push({
|
||||
label: '全部',
|
||||
value: ''
|
||||
})
|
||||
foodStore.foodCategoryList.forEach((item) => {
|
||||
queryCategoryList.value.push({
|
||||
label: item,
|
||||
value: item
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const queryFoodSegmented = async () => {
|
||||
foodStore.pageInfo = {
|
||||
currentPage: 1,
|
||||
pageSize: 3,
|
||||
totalSize: 1
|
||||
}
|
||||
foodStore.foodRecipeList = []
|
||||
await foodStore.queryMobileFoodRecipeList()
|
||||
}
|
||||
</script>
|
||||
45
src/components/Food/FoodRecipeList.vue
Normal file
45
src/components/Food/FoodRecipeList.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="food-list card-item">
|
||||
<el-empty v-if="foodStore.foodRecipeList.length === 0" description="暂无美食记录"/>
|
||||
|
||||
<div v-else class="list">
|
||||
<food-card v-for="(item, index) in foodStore.foodRecipeList"
|
||||
:key="index" :food-item="item"/>
|
||||
</div>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="foodStore.pageInfo.currentPage"
|
||||
:page-size="foodStore.pageInfo.pageSize"
|
||||
layout="total, prev, pager, next"
|
||||
:total="foodStore.pageInfo.totalSize"
|
||||
@change="changePageEvent"
|
||||
/>
|
||||
|
||||
<el-button v-if="isLargeScreen()"
|
||||
type="primary" :icon="Plus" circle
|
||||
@click="addFoodRecipeClick" class="add-new" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import FoodCard from '@/components/Food/FoodCard.vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { isLargeScreen } from '@/utils'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const addFoodRecipeClick = () => {
|
||||
foodStore.foodRecipeApiType = 'ADD'
|
||||
foodStore.foodRecipeDialog = {
|
||||
title: '新增菜谱',
|
||||
visible: true,
|
||||
data: foodStore.getEmptyFoodRecipe()
|
||||
}
|
||||
}
|
||||
|
||||
const changePageEvent = (value: number) => {
|
||||
foodStore.pageInfo.currentPage = value
|
||||
foodStore.queryFoodRecipeList()
|
||||
}
|
||||
</script>
|
||||
53
src/components/Food/FoodStats.vue
Normal file
53
src/components/Food/FoodStats.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="common-statistics">
|
||||
<stats-card
|
||||
title="菜谱总数" color="#CA6C65" unit="个" icon="iconfont icon-caipu"
|
||||
:value="foodStore.foodStats.recipeCount" />
|
||||
|
||||
<stats-card
|
||||
title="菜谱类别" color="#6FBB69" unit="种" icon="iconfont icon-leibie"
|
||||
:value="foodStore.foodStats.categoryCount" />
|
||||
|
||||
<stats-card
|
||||
title="做菜次数" color="#E4BF62" unit="次" icon="iconfont icon-cishu"
|
||||
:value="foodStore.foodStats.workCount" />
|
||||
|
||||
<stats-card
|
||||
title="平均次数" color="#5470C6" unit="次/月" icon="iconfont icon-pinlv"
|
||||
:value="getAverageCount()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StatsCard from '@/components/StatsCard.vue'
|
||||
import { onMounted } from 'vue'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
onMounted(async () => {
|
||||
await foodStore.queryFoodStats()
|
||||
})
|
||||
|
||||
const getAverageCount = () => {
|
||||
if (foodStore.queryDateType === 'month') {
|
||||
return foodStore.foodRecordList.length
|
||||
} else {
|
||||
const result = {}
|
||||
|
||||
// 遍历数据,按月统计出现次数
|
||||
foodStore.foodRecordList.forEach((item) => {
|
||||
const date = new Date(item.date)
|
||||
const month = String(date.getMonth() + 1)
|
||||
|
||||
if (result[month]) {
|
||||
result[month].count += 1
|
||||
} else {
|
||||
result[month] = { count: 1 }
|
||||
}
|
||||
})
|
||||
|
||||
return foodStore.foodRecordList.length / Object.keys(result).length
|
||||
}
|
||||
}
|
||||
</script>
|
||||
92
src/components/Food/Form/FoodMaterialDialog.vue
Normal file
92
src/components/Food/Form/FoodMaterialDialog.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-dialog v-model="foodStore.foodMaterialDialog.visible"
|
||||
:title="foodStore.foodMaterialDialog.title"
|
||||
center @open="openDialogEvent"
|
||||
class="food-material-dialog">
|
||||
<el-form ref="formRef" :model="foodStore.foodMaterialDialog.data"
|
||||
:rules="rules">
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select
|
||||
v-model="foodStore.foodMaterialDialog.data.type"
|
||||
placeholder="请选择类型">
|
||||
<el-option
|
||||
v-for="(item, index) in foodMaterialTypeList"
|
||||
:key="index" :label="item" :value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="foodStore.foodMaterialDialog.data.name" autocomplete="off"
|
||||
placeholder="请输入名称" clearable
|
||||
show-word-limit maxlength="20">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="分量" prop="amount">
|
||||
<el-input v-model="foodStore.foodMaterialDialog.data.amount" autocomplete="off"
|
||||
placeholder="请输入分量(1斤/2勺/适量)" clearable
|
||||
show-word-limit maxlength="20">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelClick">取消</el-button>
|
||||
<el-button type="primary" @click="confirmClick">保存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { nextTick, reactive, ref } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import { foodMaterialRules } from '@/utils/element/elRules.ts'
|
||||
const rules = reactive<FormRules>(foodMaterialRules)
|
||||
|
||||
const formRef = ref()
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const foodMaterialTypeList = ['主料', '辅料', '调料']
|
||||
|
||||
/**
|
||||
* 打开对话框事件
|
||||
*/
|
||||
const openDialogEvent = () => {
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击确认按钮
|
||||
*/
|
||||
const confirmClick = () => {
|
||||
formRef.value.validate().then(async () => {
|
||||
if (foodStore.foodMaterialApiType === 'UPDATE') {
|
||||
const { editFoodMaterialIndex: index } = foodStore
|
||||
foodStore.foodRecipeDialog.data.materialList[index] = foodStore.foodMaterialDialog.data
|
||||
} else {
|
||||
foodStore.foodRecipeDialog.data.materialList.push(foodStore.foodMaterialDialog.data)
|
||||
}
|
||||
foodStore.foodMaterialDialog.visible = false
|
||||
})
|
||||
}
|
||||
|
||||
const cancelClick = () => {
|
||||
foodStore.foodMaterialDialog.visible = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-material-dialog {
|
||||
width: 30%;
|
||||
|
||||
.el-dialog__body {
|
||||
// height: 50vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
src/components/Food/Form/FoodMaterialForm.vue
Normal file
56
src/components/Food/Form/FoodMaterialForm.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<el-table :data="foodStore.foodRecipeDialog.data.materialList"
|
||||
border stripe class="material-table">
|
||||
<el-table-column type="index" align="center" width="90">
|
||||
<template #header>
|
||||
<el-button type="primary" @click="handleAddClick">新增</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="类型" align="center" min-width="10%"/>
|
||||
<el-table-column prop="name" label="名称" align="center" min-width="20%"/>
|
||||
<el-table-column prop="amount" label="分量" align="center" min-width="20%"/>
|
||||
<el-table-column label="操作" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" text
|
||||
@click="handleEditClick(scope.$index, scope.row)">编辑</el-button>
|
||||
<el-button type="danger" text
|
||||
@click="handleDeleteClick(scope.$index, scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { IFoodMaterial } from '@/types/food.ts'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const handleAddClick = () => {
|
||||
foodStore.foodMaterialApiType = 'ADD'
|
||||
foodStore.foodMaterialDialog = {
|
||||
title: '新增食材',
|
||||
visible: true,
|
||||
data: foodStore.getEmptyFoodMaterial()
|
||||
}
|
||||
}
|
||||
|
||||
const handleEditClick = (index: number, material: IFoodMaterial) => {
|
||||
foodStore.editFoodMaterialIndex = index
|
||||
foodStore.foodMaterialApiType = 'UPDATE'
|
||||
foodStore.foodMaterialDialog = {
|
||||
title: '编辑食材',
|
||||
visible: true,
|
||||
data: deepCopyObject(material)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteClick = (index: number, material: IFoodMaterial) => {
|
||||
foodStore.foodMaterialApiType = 'DELETE'
|
||||
commonElMessageBox(`确认删除该食材: ${material.name}?`).then(() => {
|
||||
foodStore.foodRecipeDialog.data.materialList.splice(index, 1)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
107
src/components/Food/Form/FoodRecordDialog.vue
Normal file
107
src/components/Food/Form/FoodRecordDialog.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogData.visible" :title="dialogData.title"
|
||||
center @open="openDialogEvent" class="food-record-dialog">
|
||||
<el-form ref="formRef" :model="dialogData.data"
|
||||
:rules="rules" label-width="80">
|
||||
<el-form-item label="菜谱名称" prop="name">
|
||||
<el-select
|
||||
v-model="dialogData.data.name"
|
||||
@focus="foodStore.queryFoodNameList()"
|
||||
:disabled="foodStore.foodRecordApiType === 'UPDATE'"
|
||||
placeholder="请选择菜谱名称">
|
||||
<el-option
|
||||
v-for="(item, index) in foodStore.foodNameList"
|
||||
:key="index" :label="item" :value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="完成时间" prop="date">
|
||||
<el-date-picker
|
||||
v-model="dialogData.data.date"
|
||||
type="date" :editable="false" placeholder="请选择完成时间"
|
||||
value-format="YYYY-MM-DD"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="完成人" prop="person">
|
||||
<el-select
|
||||
v-model="dialogData.data.person"
|
||||
placeholder="请选择完成人">
|
||||
<el-option
|
||||
v-for="(item, index) in ['哥哥', '宝宝']"
|
||||
:key="index" :label="item" :value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图片">
|
||||
<upload-image v-model="dialogData.data.imageUrl"
|
||||
:service-url="fileServiceUrl"
|
||||
:image-limit="1"
|
||||
:service-file-root-path="serviceFileRootPath"
|
||||
:image-path="foodStore.foodImagePath"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelClick">取消</el-button>
|
||||
<el-button type="primary" @click="confirmClick">保存</el-button>
|
||||
<el-button v-if="foodStore.foodRecordApiType === 'UPDATE'"
|
||||
type="danger" @click="deleteClick">删除</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { UploadImage } from 'vue3-common'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { computed, ref, reactive, nextTick } from 'vue'
|
||||
import { FormInstance, FormRules } from 'element-plus'
|
||||
import { foodRecordRules } from '@/utils/element/elRules.ts'
|
||||
import { fileServiceUrl, serviceFileRootPath } from '@/utils'
|
||||
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||
|
||||
const rules = reactive<FormRules>(foodRecordRules)
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const dialogData = computed(() => foodStore.foodRecordDialog)
|
||||
|
||||
/**
|
||||
* 打开对话框事件
|
||||
*/
|
||||
const openDialogEvent = async () => {
|
||||
await nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击确认按钮
|
||||
*/
|
||||
const confirmClick = () => {
|
||||
formRef.value?.validate().then(() => {
|
||||
foodStore.handleFoodRecordApi(foodStore.foodRecordDialog.data)
|
||||
})
|
||||
}
|
||||
|
||||
const cancelClick = () => {
|
||||
dialogData.value.visible = false
|
||||
}
|
||||
|
||||
const deleteClick = () => {
|
||||
commonElMessageBox('是否确认删除该记录?').then(() => {
|
||||
foodStore.foodRecordApiType = 'DELETE'
|
||||
foodStore.handleFoodRecordApi(dialogData.value.data)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-record-dialog {
|
||||
max-width: 600px;
|
||||
}
|
||||
</style>
|
||||
84
src/components/Food/Form/FoodStepDialog.vue
Normal file
84
src/components/Food/Form/FoodStepDialog.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<el-dialog v-model="foodStore.foodStepDialog.visible"
|
||||
:title="foodStore.foodStepDialog.title"
|
||||
center @open="openDialogEvent"
|
||||
class="food-step-dialog">
|
||||
<el-form ref="formRef" :model="foodStore.foodStepDialog.data"
|
||||
:rules="rules">
|
||||
<el-form-item label="内容" prop="content">
|
||||
<el-input v-model="foodStore.foodStepDialog.data.content" autocomplete="off"
|
||||
placeholder="请输入内容" clearable
|
||||
show-word-limit maxlength="20">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图片">
|
||||
<upload-image v-model="foodStore.foodStepDialog.data.imageUrl"
|
||||
:service-url="fileServiceUrl"
|
||||
:image-limit="1"
|
||||
:service-file-root-path="serviceFileRootPath"
|
||||
:image-path="foodStore.foodImagePath"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelClick">取消</el-button>
|
||||
<el-button type="primary" @click="confirmClick">保存</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { UploadImage } from 'vue3-common'
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { FormRules } from 'element-plus'
|
||||
import { foodStepRules } from '@/utils/element/elRules.ts'
|
||||
import { fileServiceUrl, serviceFileRootPath } from '@/utils'
|
||||
|
||||
const rules = reactive<FormRules>(foodStepRules)
|
||||
|
||||
const formRef = ref()
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
/**
|
||||
* 打开对话框事件
|
||||
*/
|
||||
const openDialogEvent = () => {
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击确认按钮
|
||||
*/
|
||||
const confirmClick = () => {
|
||||
formRef.value.validate()
|
||||
.then(async () => {
|
||||
if (foodStore.foodStepApiType === 'UPDATE') {
|
||||
const { editFoodStepIndex: index } = foodStore
|
||||
foodStore.foodRecipeDialog.data.stepList[index] = foodStore.foodStepDialog.data
|
||||
} else {
|
||||
foodStore.foodRecipeDialog.data.stepList.push(foodStore.foodStepDialog.data)
|
||||
}
|
||||
foodStore.foodStepDialog.visible = false
|
||||
})
|
||||
}
|
||||
|
||||
const cancelClick = () => {
|
||||
foodStore.foodStepDialog.visible = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-step-dialog {
|
||||
width: 30%;
|
||||
|
||||
.el-dialog__body {
|
||||
// height: 50vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
79
src/components/Food/Form/FoodStepForm.vue
Normal file
79
src/components/Food/Form/FoodStepForm.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<el-table :data="foodStore.foodRecipeDialog.data.stepList"
|
||||
border class="food-step-table">
|
||||
<el-table-column type="index" align="center" width="90">
|
||||
<template #header>
|
||||
<el-button type="primary" @click="handleAddClick">新增</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="内容" align="center" min-width="20%" />
|
||||
<el-table-column prop="imageUrl" label="图片" align="center" min-width="20%">
|
||||
<template #default="scope">
|
||||
<el-image v-if="scope.row.imageUrl" :src="serviceFileRootPath + scope.row.imageUrl" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" text
|
||||
@click="handleEditClick(scope.$index, scope.row)">编辑</el-button>
|
||||
<el-button type="danger" text
|
||||
@click="handleDeleteClick(scope.$index)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFoodStore } from '@/store/food.ts'
|
||||
import { IFoodStep } from '@/types/food.ts'
|
||||
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||
import { serviceFileRootPath } from '@/utils'
|
||||
|
||||
const foodStore = useFoodStore()
|
||||
|
||||
const handleAddClick = () => {
|
||||
foodStore.foodStepApiType = 'ADD'
|
||||
foodStore.foodStepImageList = []
|
||||
foodStore.foodStepDialog = {
|
||||
title: '新增步骤',
|
||||
visible: true,
|
||||
data: foodStore.getEmptyFoodStep()
|
||||
}
|
||||
}
|
||||
|
||||
const handleEditClick = (index: number, step: IFoodStep) => {
|
||||
foodStore.editFoodStepIndex = index
|
||||
foodStore.foodStepApiType = 'UPDATE'
|
||||
foodStore.foodStepDialog = {
|
||||
title: '编辑步骤',
|
||||
visible: true,
|
||||
data: deepCopyObject(step)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteClick = (index: number) => {
|
||||
commonElMessageBox('确认删除该步骤?').then(() => {
|
||||
foodStore.foodStepApiType = 'DELETE'
|
||||
foodStore.foodRecipeDialog.data.stepList.splice(index, 1)
|
||||
foodStore.foodRecipeDialog.data.stepList.forEach((item) => {
|
||||
if (item.sort > index) {
|
||||
item.sort -= 1
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.food-step-table {
|
||||
.el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
max-height: 200px;
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user