feat:增加美食表单功能
This commit is contained in:
81
src/apis/food.ts
Normal file
81
src/apis/food.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { cloudService } from './index'
|
||||||
|
import type { IFoodQuery, IFoodRecipe, IFoodRecord, IFoodStats } from '@/types/food'
|
||||||
|
|
||||||
|
export const addFoodRecipeApi = (foodRecipe: IFoodRecipe): Promise<boolean> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/recipe',
|
||||||
|
method: 'post',
|
||||||
|
data: foodRecipe
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateFoodRecipeApi = (id: number, foodRecipe: IFoodRecipe): Promise<boolean> =>
|
||||||
|
cloudService({
|
||||||
|
url: `/food-service/food/recipe/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data: foodRecipe
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteFoodRecipeApi = (id: number): Promise<boolean> =>
|
||||||
|
cloudService({
|
||||||
|
url: `/food-service/food/recipe/${id}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodRecipeByIdApi = (id: number): Promise<any> =>
|
||||||
|
cloudService({
|
||||||
|
url: `/food-service/food/recipe/${id}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodRecipeApi = (currentPage: number, pageSize: number,
|
||||||
|
query: IFoodQuery): Promise<any> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/recipe/page',
|
||||||
|
method: 'get',
|
||||||
|
params: { currentPage, pageSize, ...query }
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodNameListApi = (): Promise<string[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/recipe/name',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const addFoodRecordApi = (foodRecord: IFoodRecord): Promise<boolean> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/record',
|
||||||
|
method: 'post',
|
||||||
|
data: foodRecord
|
||||||
|
})
|
||||||
|
|
||||||
|
export const updateFoodRecordApi = (id: number, foodRecord: IFoodRecord): Promise<boolean> =>
|
||||||
|
cloudService({
|
||||||
|
url: `/food-service/food/record/${id}`,
|
||||||
|
method: 'put',
|
||||||
|
data: foodRecord
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deleteFoodRecordApi = (id: number): Promise<boolean> =>
|
||||||
|
cloudService({
|
||||||
|
url: `/food-service/food/record/${id}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodRecordApi = (startDate: string, endDate: string): Promise<IFoodRecord[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/record',
|
||||||
|
method: 'get',
|
||||||
|
params: { startDate, endDate }
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodCategoryApi = (): Promise<string[]> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/category',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const queryFoodStatsApi = (): Promise<IFoodStats> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/food/stats',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
3
src/apis/index.ts
Normal file
3
src/apis/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import createService from 'vue3-common/utils/axiosUtil'
|
||||||
|
|
||||||
|
export const cloudService = createService()
|
||||||
9
src/apis/session.ts
Normal file
9
src/apis/session.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { cloudService } from './index'
|
||||||
|
import type { ISession } from '@/types/auth'
|
||||||
|
|
||||||
|
export const loginApi = (name: string, password: string): Promise<ISession> =>
|
||||||
|
cloudService({
|
||||||
|
url: '/food-service/session',
|
||||||
|
method: 'post',
|
||||||
|
params: { name, password }
|
||||||
|
})
|
||||||
@@ -6,24 +6,19 @@
|
|||||||
class="food-calendar card-item"/>
|
class="food-calendar card-item"/>
|
||||||
|
|
||||||
<food-daily-item :date="dailyInfo.calendarDate"/>
|
<food-daily-item :date="dailyInfo.calendarDate"/>
|
||||||
|
|
||||||
<draggable-button @click="addClick"/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FoodDailyItem from '@/components/Food/FoodDailyItem.vue'
|
import FoodDailyItem from '@/components/Food/FoodDailyItem.vue'
|
||||||
import DraggableButton from '@/components/DraggableButton.vue'
|
|
||||||
import { reactive, watch, ref, onMounted } from 'vue'
|
import { reactive, watch, ref, onMounted } from 'vue'
|
||||||
import { useFoodStore } from '@/store/food.ts'
|
import { useFoodStore } from '@/store/food.ts'
|
||||||
import { formatDate } from 'vue3-common/utils/dateUtil'
|
import { formatDate } from 'vue3-common/utils/dateUtil'
|
||||||
import { useAppStore } from '@/store/app'
|
import { useAppStore } from '@/store/app'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const calendarRef = ref()
|
const calendarRef = ref()
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { month } = foodStore.queryRecord
|
const { month } = foodStore.queryRecord
|
||||||
@@ -39,12 +34,6 @@ const dailyInfo = reactive({
|
|||||||
attrs: [] as any[]
|
attrs: [] as any[]
|
||||||
})
|
})
|
||||||
|
|
||||||
const addClick = () => {
|
|
||||||
foodStore.foodRecordApiType = 'ADD'
|
|
||||||
foodStore.currentFoodRecord = foodStore.getEmptyFoodRecord()
|
|
||||||
router.push({ name: 'MobileHomeFoodRecordForm' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateInfo = () => {
|
const updateInfo = () => {
|
||||||
getCalendarMoveDate()
|
getCalendarMoveDate()
|
||||||
getCalendarAttrDates()
|
getCalendarAttrDates()
|
||||||
|
|||||||
58
src/components/Food/FoodMaterialForm.vue
Normal file
58
src/components/Food/FoodMaterialForm.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="food-material-form">
|
||||||
|
<div class="button-container">
|
||||||
|
<el-button type="primary" @click="addClick">新增</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="foodStore.currentFoodRecipe.materialList"
|
||||||
|
border stripe class="material-table">
|
||||||
|
<el-table-column type="index" align="center" width="40" />
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useFoodStore } from '@/store/food'
|
||||||
|
import { IFoodMaterial } from '@/types/food'
|
||||||
|
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||||
|
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||||
|
|
||||||
|
const foodStore = useFoodStore()
|
||||||
|
|
||||||
|
const addClick = () => {
|
||||||
|
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.currentFoodRecipe.materialList.splice(index, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.food-material-form {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -17,22 +17,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<arrive-bottom v-if="foodStore.isScrollEnd"/>
|
<arrive-bottom v-if="foodStore.isScrollEnd"/>
|
||||||
|
|
||||||
<draggable-button @click="addClick"/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import FoodCard from '@/components/Food/FoodCard.vue'
|
import FoodCard from '@/components/Food/FoodCard.vue'
|
||||||
import ArriveBottom from '@/components/ArriveButton.vue'
|
import ArriveBottom from '@/components/ArriveButton.vue'
|
||||||
import DraggableButton from '@/components/DraggableButton.vue'
|
|
||||||
import { useFoodStore } from '@/store/food.ts'
|
import { useFoodStore } from '@/store/food.ts'
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const foodRecipeRef = ref()
|
const foodRecipeRef = ref()
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const foodCategoryList = [{
|
const foodCategoryList = [{
|
||||||
label: '全部',
|
label: '全部',
|
||||||
@@ -51,12 +46,6 @@ onBeforeUnmount(() => {
|
|||||||
foodRecipeRef.value.removeEventListener('scroll', handleScroll)
|
foodRecipeRef.value.removeEventListener('scroll', handleScroll)
|
||||||
})
|
})
|
||||||
|
|
||||||
const addClick = () => {
|
|
||||||
foodStore.foodRecordApiType = 'ADD'
|
|
||||||
foodStore.currentFoodRecord = foodStore.getEmptyFoodRecord()
|
|
||||||
router.push({ name: 'MobileHomeFoodRecordForm' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryFoodSegmented = async () => {
|
const queryFoodSegmented = async () => {
|
||||||
foodStore.pageInfo.currentPage = 1
|
foodStore.pageInfo.currentPage = 1
|
||||||
foodStore.foodRecipeList = []
|
foodStore.foodRecipeList = []
|
||||||
|
|||||||
80
src/components/Food/FoodStepForm.vue
Normal file
80
src/components/Food/FoodStepForm.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="food-step-form">
|
||||||
|
<div class="button-container">
|
||||||
|
<el-button type="primary" @click="addClick">新增</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-table :data="foodStore.currentFoodRecipe.stepList"
|
||||||
|
border class="food-step-table">
|
||||||
|
<el-table-column type="index" align="center" width="40" />
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useFoodStore } from '@/store/food'
|
||||||
|
import { IFoodStep } from '@/types/food'
|
||||||
|
import { deepCopyObject } from 'vue3-common/utils/dataUtil'
|
||||||
|
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||||
|
import { serviceFileRootPath } from '@/utils'
|
||||||
|
|
||||||
|
const foodStore = useFoodStore()
|
||||||
|
|
||||||
|
const addClick = () => {
|
||||||
|
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.currentFoodRecipe.stepList.splice(index, 1)
|
||||||
|
foodStore.currentFoodRecipe.stepList.forEach((item) => {
|
||||||
|
if (item.sort > index) {
|
||||||
|
item.sort -= 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.food-step-form {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.food-step-table {
|
||||||
|
.el-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
max-height: 200px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
127
src/store/auth.ts
Normal file
127
src/store/auth.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import type { ILoginType, ISession } from '@/types/auth'
|
||||||
|
import { loginApi } from '@/apis/session'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { emailPattern, phonePattern } from '@/utils/element/elRules'
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore('auth', {
|
||||||
|
state: () => ({
|
||||||
|
loginForm: {
|
||||||
|
username: '132',
|
||||||
|
password: '123',
|
||||||
|
isRemember: false
|
||||||
|
},
|
||||||
|
phoneLoginForm: {
|
||||||
|
phoneNumber: '',
|
||||||
|
code: ''
|
||||||
|
},
|
||||||
|
emailLoginForm: {
|
||||||
|
email: '',
|
||||||
|
code: ''
|
||||||
|
},
|
||||||
|
loginType: 'common' as ILoginType,
|
||||||
|
loginLoading: false,
|
||||||
|
loginAdminLoading: false,
|
||||||
|
isVerified: false,
|
||||||
|
isGettingCode: false,
|
||||||
|
getCodeCountdown: 60,
|
||||||
|
getCodeTimer: null as NodeJS.Timeout | null,
|
||||||
|
homeSession: {} as ISession
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
changeLoginType(type: ILoginType) {
|
||||||
|
this.loginType = type
|
||||||
|
|
||||||
|
if (type === 'common') {
|
||||||
|
this.stopCountdown()
|
||||||
|
}
|
||||||
|
this.resetLoginForm()
|
||||||
|
},
|
||||||
|
getAuthCode(type: ILoginType) {
|
||||||
|
switch (type) {
|
||||||
|
case 'phone':
|
||||||
|
if (phonePattern.test(this.phoneLoginForm.phoneNumber)) {
|
||||||
|
this.isGettingCode = true
|
||||||
|
ElMessage.success('验证码已发送至手机')
|
||||||
|
this.startCountdown()
|
||||||
|
} else {
|
||||||
|
ElMessage.error('请先输入正确的手机号')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'email':
|
||||||
|
if (emailPattern.test(this.emailLoginForm.email)) {
|
||||||
|
this.isGettingCode = true
|
||||||
|
ElMessage.success('验证码已发送至邮箱')
|
||||||
|
this.startCountdown()
|
||||||
|
} else {
|
||||||
|
ElMessage.error('请先输入正确的邮箱地址')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startCountdown() {
|
||||||
|
this.getCodeTimer = setInterval(() => {
|
||||||
|
if (this.getCodeCountdown > 0) {
|
||||||
|
this.getCodeCountdown--
|
||||||
|
} else {
|
||||||
|
clearInterval(this.getCodeTimer as NodeJS.Timeout)
|
||||||
|
this.isGettingCode = false
|
||||||
|
this.getCodeCountdown = 60
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
stopCountdown() {
|
||||||
|
if (this.getCodeTimer) {
|
||||||
|
clearInterval(this.getCodeTimer)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async handleLogin(type: ILoginType) {
|
||||||
|
const { username, password } = this.loginForm
|
||||||
|
const { phoneNumber, code: phoneCode } = this.phoneLoginForm
|
||||||
|
const { email, code: emailCode } = this.emailLoginForm
|
||||||
|
switch (type) {
|
||||||
|
case 'common':
|
||||||
|
console.log(username)
|
||||||
|
console.log(password)
|
||||||
|
this.homeSession = await loginApi(username, password)
|
||||||
|
break
|
||||||
|
case 'phone':
|
||||||
|
this.homeSession = await loginApi(phoneNumber, phoneCode)
|
||||||
|
break
|
||||||
|
case 'email':
|
||||||
|
this.homeSession = await loginApi(email, emailCode)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ElMessage.success('登录成功')
|
||||||
|
},
|
||||||
|
resetLoginForm() {
|
||||||
|
switch (this.loginType) {
|
||||||
|
case 'common':
|
||||||
|
this.loginForm = {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
isRemember: false
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'phone':
|
||||||
|
this.phoneLoginForm = {
|
||||||
|
phoneNumber: '',
|
||||||
|
code: ''
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'email':
|
||||||
|
this.emailLoginForm = {
|
||||||
|
email: '',
|
||||||
|
code: ''
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -3,11 +3,11 @@ import type {
|
|||||||
IFoodRecipe, IFoodMaterial, IFoodStep,
|
IFoodRecipe, IFoodMaterial, IFoodStep,
|
||||||
IFoodStats, IFoodRecord
|
IFoodStats, IFoodRecord
|
||||||
} from '@/types/food'
|
} from '@/types/food'
|
||||||
// import {
|
import {
|
||||||
// addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi,
|
addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi,
|
||||||
// queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi,
|
queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi,
|
||||||
// queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi
|
queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi
|
||||||
// } from '@/apis/food.ts'
|
} from '@/apis/food.ts'
|
||||||
// import { IDateType, ImagePathEnum } from '@/types'
|
// import { IDateType, ImagePathEnum } from '@/types'
|
||||||
import type { IDialog, IHandleApi } from 'vue3-common/types'
|
import type { IDialog, IHandleApi } from 'vue3-common/types'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
@@ -47,11 +47,6 @@ export const useFoodStore = defineStore('food', {
|
|||||||
queryDateType: 'month',
|
queryDateType: 'month',
|
||||||
queryDateList: [] as string[],
|
queryDateList: [] as string[],
|
||||||
foodRecipeApiType: 'ADD' as IHandleApi,
|
foodRecipeApiType: 'ADD' as IHandleApi,
|
||||||
foodRecipeDialog: {
|
|
||||||
visible: false,
|
|
||||||
title: '',
|
|
||||||
data: {}
|
|
||||||
} as IDialog<IFoodRecipe>,
|
|
||||||
foodMaterialApiType: 'ADD' as IHandleApi,
|
foodMaterialApiType: 'ADD' as IHandleApi,
|
||||||
foodMaterialDialog: {
|
foodMaterialDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -67,11 +62,6 @@ export const useFoodStore = defineStore('food', {
|
|||||||
} as IDialog<IFoodStep>,
|
} as IDialog<IFoodStep>,
|
||||||
editFoodStepIndex: 0,
|
editFoodStepIndex: 0,
|
||||||
foodRecordApiType: 'ADD' as IHandleApi,
|
foodRecordApiType: 'ADD' as IHandleApi,
|
||||||
foodRecordDialog: {
|
|
||||||
visible: false,
|
|
||||||
title: '',
|
|
||||||
data: {}
|
|
||||||
} as IDialog<IFoodRecord>,
|
|
||||||
editFoodRecordIndex: 0,
|
editFoodRecordIndex: 0,
|
||||||
foodImagePath: 'food',
|
foodImagePath: 'food',
|
||||||
rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'],
|
rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'],
|
||||||
@@ -92,21 +82,9 @@ export const useFoodStore = defineStore('food', {
|
|||||||
actions: {
|
actions: {
|
||||||
async queryFoodRecipeList() {
|
async queryFoodRecipeList() {
|
||||||
const { currentPage, pageSize } = this.pageInfo
|
const { currentPage, pageSize } = this.pageInfo
|
||||||
// const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe)
|
const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe)
|
||||||
const foodRecord: IFoodRecipe = {
|
this.foodRecipeList.push(...result.records)
|
||||||
category: '家常菜',
|
this.pageInfo.totalSize = result.total
|
||||||
materialList: [],
|
|
||||||
name: '红烧鱼',
|
|
||||||
recommendRate: 4,
|
|
||||||
recordList: [],
|
|
||||||
remark: '',
|
|
||||||
stepList: []
|
|
||||||
}
|
|
||||||
this.foodRecipeList = []
|
|
||||||
this.foodRecipeList.push(foodRecord)
|
|
||||||
this.foodRecipeList.push(foodRecord)
|
|
||||||
// this.foodRecipeList.push(...result.records)
|
|
||||||
// this.pageInfo.totalSize = result.total
|
|
||||||
},
|
},
|
||||||
async queryFoodRecordList() {
|
async queryFoodRecordList() {
|
||||||
const { year, month } = this.queryRecord
|
const { year, month } = this.queryRecord
|
||||||
@@ -147,19 +125,18 @@ export const useFoodStore = defineStore('food', {
|
|||||||
async queryFoodCategory() {
|
async queryFoodCategory() {
|
||||||
// this.foodCategoryList = await queryFoodCategoryApi()
|
// this.foodCategoryList = await queryFoodCategoryApi()
|
||||||
},
|
},
|
||||||
async handleFoodRecipeApi(id?: number) {
|
async handleFoodRecipeApi(foodRecipe: IFoodRecipe) {
|
||||||
const { data } = this.foodRecipeDialog
|
|
||||||
switch (this.foodRecipeApiType) {
|
switch (this.foodRecipeApiType) {
|
||||||
case 'ADD':
|
case 'ADD':
|
||||||
// await addFoodRecipeApi(data)
|
// await addFoodRecipeApi(foodRecipe)
|
||||||
ElMessage.success('新增菜谱成功')
|
ElMessage.success('新增菜谱成功')
|
||||||
break
|
break
|
||||||
case 'UPDATE':
|
case 'UPDATE':
|
||||||
// await updateFoodRecipeApi(data.id as number, data)
|
// await updateFoodRecipeApi(foodRecipe.id as number, foodRecipe)
|
||||||
ElMessage.success('更新菜谱成功')
|
ElMessage.success('更新菜谱成功')
|
||||||
break
|
break
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
// await deleteFoodRecipeApi(id as number)
|
// await deleteFoodRecipeApi(foodRecipe.id as number)
|
||||||
ElMessage.success('删除菜谱成功')
|
ElMessage.success('删除菜谱成功')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|||||||
75
src/store/user.ts
Normal file
75
src/store/user.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { getLocalStorage, setLocalStorage, removeLocalStorage } from 'vue3-common/utils/storageUtil'
|
||||||
|
import type { ILoginForm } from 'vue3-common'
|
||||||
|
import type { ISession, IUser } from '@/types/auth'
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', {
|
||||||
|
state: () => ({
|
||||||
|
userInfo: {
|
||||||
|
id: 0,
|
||||||
|
accountId: 0,
|
||||||
|
accountName: '',
|
||||||
|
username: '',
|
||||||
|
gender: 0,
|
||||||
|
idNumber: '',
|
||||||
|
phoneNumber: '',
|
||||||
|
email: '',
|
||||||
|
birthDate: '',
|
||||||
|
avatar: '',
|
||||||
|
description: '',
|
||||||
|
area: ['', ''],
|
||||||
|
address: '',
|
||||||
|
job: '',
|
||||||
|
tags: []
|
||||||
|
} as IUser
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
setLoginForm(loginForm: ILoginForm) {
|
||||||
|
setLocalStorage('loginForm', JSON.stringify(loginForm))
|
||||||
|
},
|
||||||
|
getLoginForm() {
|
||||||
|
return getLocalStorage('loginForm')
|
||||||
|
},
|
||||||
|
removeLoginForm() {
|
||||||
|
removeLocalStorage('loginForm')
|
||||||
|
},
|
||||||
|
handleSession(session: ISession) {
|
||||||
|
this.setToken(session.saToken.tokenValue)
|
||||||
|
// this.setUserId(session.userInfo.id)
|
||||||
|
// this.userInfo = session.userInfo
|
||||||
|
},
|
||||||
|
async queryUser() {
|
||||||
|
// this.userInfo = await queryUserApi(Number(this.getUserId()))
|
||||||
|
},
|
||||||
|
async updateUser() {
|
||||||
|
// await updateUserApi(this.userInfo.id, this.userInfo)
|
||||||
|
},
|
||||||
|
async updateAccountPassword(oldPassword: string, newPassword: string) {
|
||||||
|
console.log(this.userInfo.accountId)
|
||||||
|
// await updateAccountPasswordApi(this.userInfo.accountId, oldPassword, newPassword)
|
||||||
|
},
|
||||||
|
setToken(token: string) {
|
||||||
|
setLocalStorage('token', token)
|
||||||
|
},
|
||||||
|
getToken() {
|
||||||
|
return getLocalStorage('token')
|
||||||
|
},
|
||||||
|
setUserId(userId: number) {
|
||||||
|
setLocalStorage('userId', String(userId))
|
||||||
|
},
|
||||||
|
getUserId() {
|
||||||
|
return getLocalStorage('userId')
|
||||||
|
},
|
||||||
|
setAccount(accountName: string) {
|
||||||
|
setLocalStorage('account', accountName)
|
||||||
|
},
|
||||||
|
getAccount() {
|
||||||
|
return getLocalStorage('account')
|
||||||
|
},
|
||||||
|
removeAll() {
|
||||||
|
removeLocalStorage('token')
|
||||||
|
removeLocalStorage('account')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
48
src/types/auth.ts
Normal file
48
src/types/auth.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
export type ILoginType = 'common' | 'phone' | 'email' | 'admin'
|
||||||
|
|
||||||
|
export type IAccountType = 'QQ' | 'WeChat' | 'Phone' | 'EMail'
|
||||||
|
|
||||||
|
export interface IUser {
|
||||||
|
id: number;
|
||||||
|
accountId: number;
|
||||||
|
accountName: string;
|
||||||
|
username: string;
|
||||||
|
gender: number;
|
||||||
|
idNumber: string;
|
||||||
|
phoneNumber: string;
|
||||||
|
email: string;
|
||||||
|
birthDate: string;
|
||||||
|
avatar: string;
|
||||||
|
description: string;
|
||||||
|
area: string[];
|
||||||
|
address: string;
|
||||||
|
job: string;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISession {
|
||||||
|
saToken: {
|
||||||
|
tokenName: string;
|
||||||
|
tokenValue: string;
|
||||||
|
isLogin: boolean;
|
||||||
|
loginId: string;
|
||||||
|
loginType: string;
|
||||||
|
tokenTimeout: number;
|
||||||
|
sessionTimeout: number;
|
||||||
|
tokenSessionTimeout: number;
|
||||||
|
tokenActiveTimeout: number;
|
||||||
|
loginDevice: string;
|
||||||
|
tag: string;
|
||||||
|
};
|
||||||
|
userInfo: IUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAccount {
|
||||||
|
id: number;
|
||||||
|
accountName: string;
|
||||||
|
password: string;
|
||||||
|
confirmPassword: string;
|
||||||
|
username: string;
|
||||||
|
state: number;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
@@ -1,5 +1,35 @@
|
|||||||
export type IFoodSegment = 'recipe' | 'daily' | 'timeline'
|
export type IFoodSegment = 'recipe' | 'daily' | 'timeline'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 食材信息
|
||||||
|
*/
|
||||||
|
export interface IFoodMaterial {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
amount: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 步骤信息
|
||||||
|
*/
|
||||||
|
export interface IFoodStep {
|
||||||
|
sort: number;
|
||||||
|
content: string;
|
||||||
|
imageUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成果信息
|
||||||
|
*/
|
||||||
|
export interface IFoodRecord {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
category: string;
|
||||||
|
date: string;
|
||||||
|
person: string;
|
||||||
|
imageUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜谱信息
|
* 菜谱信息
|
||||||
*/
|
*/
|
||||||
@@ -35,36 +65,6 @@ export interface IFoodRecipe {
|
|||||||
recordList: IFoodRecord[];
|
recordList: IFoodRecord[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 食材信息
|
|
||||||
*/
|
|
||||||
export interface IFoodMaterial {
|
|
||||||
type: string;
|
|
||||||
name: string;
|
|
||||||
amount: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 步骤信息
|
|
||||||
*/
|
|
||||||
export interface IFoodStep {
|
|
||||||
sort: number;
|
|
||||||
content: string;
|
|
||||||
imageUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 成果信息
|
|
||||||
*/
|
|
||||||
export interface IFoodRecord {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
category: string;
|
|
||||||
date: string;
|
|
||||||
person: string;
|
|
||||||
imageUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统计信息
|
* 统计信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
71
src/utils/element/elRules.ts
Normal file
71
src/utils/element/elRules.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
export const chinesePattern = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/
|
||||||
|
|
||||||
|
export const amountPattern = /^\d+(\.?\d{1,2})?$/
|
||||||
|
|
||||||
|
export const phonePattern = /^1[3-9]\d{9}$/
|
||||||
|
|
||||||
|
export const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
||||||
|
|
||||||
|
export const loginRules = {
|
||||||
|
username: [
|
||||||
|
{ required: true, message: '请输入账号', trigger: 'blur' },
|
||||||
|
{ pattern: /^[0-9a-zA-Z]+$/, message: '请输入正确的格式: 数字或英文字母', trigger: ['blur'] }
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
|
{ pattern: /^[0-9a-zA-Z]+$/, message: '请输入正确的格式: 数字或英文字母', trigger: ['blur'] }
|
||||||
|
],
|
||||||
|
phoneNumber: [
|
||||||
|
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||||
|
{ pattern: phonePattern, message: '请输入正确的手机号格式', trigger: ['blur'] }
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
|
||||||
|
{ pattern: emailPattern, message: '请输入正确的邮箱格式', trigger: ['blur'] }
|
||||||
|
],
|
||||||
|
code: [
|
||||||
|
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||||||
|
{ pattern: /^\d{6}$/, message: '请输入6位数字验证码', trigger: ['blur'] }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const foodRules = {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||||
|
{ pattern: new RegExp(chinesePattern), message: '请输入中英文或数字' }
|
||||||
|
],
|
||||||
|
category: [
|
||||||
|
{ required: true, message: '请选择菜系', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const foodMaterialRules = {
|
||||||
|
type: [
|
||||||
|
{ required: true, message: '请选择类型', trigger: 'change' }
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
amount: [
|
||||||
|
{ required: true, message: '请输入分量', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const foodStepRules = {
|
||||||
|
content: [
|
||||||
|
{ required: true, message: '请输入内容', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const foodRecordRules = {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请选择菜谱名称', trigger: 'change' }
|
||||||
|
],
|
||||||
|
date: [
|
||||||
|
{ required: true, message: '请选择完成时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
person: [
|
||||||
|
{ required: true, message: '请选择完成人', trigger: 'change' }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -5,19 +5,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useAuthStore } from '@/store/auth'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useUserStore } from '@/store/user'
|
||||||
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
import { isMobile } from 'vue3-common/utils/layoutUtil'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const authStore = useAuthStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const loginClick = () => {
|
const loginClick = async () => {
|
||||||
if (!isMobile()) {
|
if (!isMobile()) {
|
||||||
ElMessage.error('暂不支持PC端')
|
ElMessage.error('暂不支持PC端')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
await authStore.handleLogin('common')
|
||||||
router.push('/record')
|
userStore.handleSession(authStore.homeSession)
|
||||||
|
await router.push('/record')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
<div v-if="foodInfo.recordSegment === 'timeline'" class="food-calendar">
|
<div v-if="foodInfo.recordSegment === 'timeline'" class="food-calendar">
|
||||||
<food-timeline />
|
<food-timeline />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<draggable-button v-show="foodInfo.recordSegment !== 'timeline'" @click="addClick"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -35,12 +37,17 @@
|
|||||||
import FoodRecipe from '@/components/Food/FoodRecipe.vue'
|
import FoodRecipe from '@/components/Food/FoodRecipe.vue'
|
||||||
import FoodDaily from '@/components/Food/FoodDaily.vue'
|
import FoodDaily from '@/components/Food/FoodDaily.vue'
|
||||||
import FoodTimeline from '@/components/Food/FoodTimeline.vue'
|
import FoodTimeline from '@/components/Food/FoodTimeline.vue'
|
||||||
|
import DraggableButton from '@/components/DraggableButton.vue'
|
||||||
import { Search } from '@element-plus/icons-vue'
|
import { Search } from '@element-plus/icons-vue'
|
||||||
import { onMounted, reactive } from 'vue'
|
import { onMounted, reactive } from 'vue'
|
||||||
import { useFoodStore } from '@/store/food'
|
import { useFoodStore } from '@/store/food'
|
||||||
import type { IFoodSegment } from '@/types/food'
|
import type { IFoodSegment } from '@/types/food'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useAppStore } from '@/store/app'
|
||||||
|
|
||||||
const foodStore = useFoodStore()
|
const foodStore = useFoodStore()
|
||||||
|
const router = useRouter()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const foodInfo = reactive({
|
const foodInfo = reactive({
|
||||||
recordSegment: 'recipe' as IFoodSegment,
|
recordSegment: 'recipe' as IFoodSegment,
|
||||||
@@ -67,6 +74,25 @@ onMounted(() => {
|
|||||||
const changeRecordSegmentEvent = () => {
|
const changeRecordSegmentEvent = () => {
|
||||||
foodStore.refreshInfo()
|
foodStore.refreshInfo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addClick = () => {
|
||||||
|
appStore.isShowMobileBack = true
|
||||||
|
|
||||||
|
switch (foodInfo.recordSegment) {
|
||||||
|
case 'recipe':
|
||||||
|
foodStore.foodRecipeApiType = 'ADD'
|
||||||
|
foodStore.currentFoodRecipe = foodStore.getEmptyFoodRecipe()
|
||||||
|
router.push('/record/recipeForm')
|
||||||
|
break
|
||||||
|
case 'daily':
|
||||||
|
foodStore.foodRecordApiType = 'ADD'
|
||||||
|
foodStore.currentFoodRecord = foodStore.getEmptyFoodRecord()
|
||||||
|
router.push('/record/recordForm')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
99
src/views/record/recipeForm.vue
Normal file
99
src/views/record/recipeForm.vue
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<template>
|
||||||
|
<div class="food-recipe-form common-mobile-view">
|
||||||
|
<el-form ref="formRef" :model="foodStore.currentFoodRecipe"
|
||||||
|
:rules="rules" label-width="80px">
|
||||||
|
<el-form-item label="美食名称" prop="name">
|
||||||
|
<el-input v-model="foodStore.currentFoodRecipe.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="foodStore.currentFoodRecipe.category"
|
||||||
|
placeholder="请选择或输入菜系">
|
||||||
|
<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="foodStore.currentFoodRecipe.recommendRate"
|
||||||
|
:colors="foodStore.rateColorList"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="食材">
|
||||||
|
<food-material-form />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="步骤">
|
||||||
|
<food-step-form />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="foodStore.currentFoodRecipe.remark" autocomplete="off"
|
||||||
|
placeholder="请输入备注" clearable
|
||||||
|
show-word-limit maxlength="20">
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<el-button type="primary" @click="confirmClick">确认</el-button>
|
||||||
|
<el-button type="danger" @click="deleteClick">删除</el-button>
|
||||||
|
<el-button type="info" @click="cancelClick">取消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import FoodMaterialForm from '@/components/Food/FoodMaterialForm.vue'
|
||||||
|
import FoodStepForm from '@/components/Food/FoodStepForm.vue'
|
||||||
|
import { useFoodStore } from '@/store/food'
|
||||||
|
import { FormInstance, FormRules } from 'element-plus'
|
||||||
|
import { computed, ref, reactive } from 'vue'
|
||||||
|
import { foodRules } from '@/utils/element/elRules'
|
||||||
|
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||||
|
|
||||||
|
const rules = reactive<FormRules>(foodRules)
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const foodStore = useFoodStore()
|
||||||
|
|
||||||
|
const foodCategoryList = ['家常菜', '淮扬菜', '鲁菜', '川菜', '粤菜', '闽菜', '浙菜', '湘菜', '徽菜', '其他']
|
||||||
|
|
||||||
|
const dialogData = computed(() => foodStore.foodRecipeDialog)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击确认按钮
|
||||||
|
*/
|
||||||
|
const confirmClick = () => {
|
||||||
|
formRef.value.validate().then(() => {
|
||||||
|
foodStore.handleFoodRecipeApi(foodStore.currentFoodRecipe)
|
||||||
|
history.back()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelClick = () => {
|
||||||
|
history.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteClick = () => {
|
||||||
|
commonElMessageBox('是否确认删除该菜谱?').then(() => {
|
||||||
|
foodStore.foodRecipeApiType = 'DELETE'
|
||||||
|
foodStore.handleFoodRecipeApi(foodStore.currentFoodRecipe)
|
||||||
|
history.back()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.food-recipe-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
101
src/views/record/recordForm.vue
Normal file
101
src/views/record/recordForm.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<div class="food-record-form common-mobile-view">
|
||||||
|
<el-form ref="formRef" :model="foodStore.currentFoodRecord"
|
||||||
|
:rules="rules" label-width="80">
|
||||||
|
<el-form-item label="菜谱名称" prop="name">
|
||||||
|
<el-select
|
||||||
|
v-model="foodStore.currentFoodRecord.name"
|
||||||
|
@focus="foodStore.queryFoodNameList()"
|
||||||
|
allow-create filterable
|
||||||
|
: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="foodStore.currentFoodRecord.date"
|
||||||
|
type="date" :editable="false" placeholder="请选择完成时间"
|
||||||
|
value-format="YYYY-MM-DD"/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="完成人" prop="person">
|
||||||
|
<el-select
|
||||||
|
v-model="foodStore.currentFoodRecord.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="foodStore.currentFoodRecord.imageUrl"
|
||||||
|
:service-url="fileServiceUrl"
|
||||||
|
:image-limit="1"
|
||||||
|
:service-file-root-path="serviceFileRootPath"
|
||||||
|
:image-path="foodStore.foodImagePath"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<el-button type="primary" @click="confirmClick">确认</el-button>
|
||||||
|
<el-button type="danger" @click="deleteClick">删除</el-button>
|
||||||
|
<el-button type="info" @click="cancelClick">取消</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { UploadImage } from 'vue3-common'
|
||||||
|
import { useFoodStore } from '@/store/food.ts'
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { FormRules } from 'element-plus'
|
||||||
|
import { foodRecordRules } from '@/utils/element/elRules'
|
||||||
|
import { fileServiceUrl, serviceFileRootPath } from '@/utils'
|
||||||
|
import { commonElMessageBox } from 'vue3-common/utils/elUtil'
|
||||||
|
|
||||||
|
const rules = reactive<FormRules>(foodRecordRules)
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
const foodStore = useFoodStore()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击确认按钮
|
||||||
|
*/
|
||||||
|
const confirmClick = () => {
|
||||||
|
formRef.value.validate().then(() => {
|
||||||
|
foodStore.handleFoodRecordApi(foodStore.currentFoodRecord)
|
||||||
|
history.back()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancelClick = () => {
|
||||||
|
history.back()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteClick = () => {
|
||||||
|
commonElMessageBox('是否确认删除该记录?').then(() => {
|
||||||
|
foodStore.foodRecordApiType = 'DELETE'
|
||||||
|
foodStore.handleFoodRecordApi(foodStore.currentFoodRecord)
|
||||||
|
history.back()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.food-record-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,6 +20,13 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5175
|
port: 5175,
|
||||||
|
proxy: {
|
||||||
|
'/food-service': {
|
||||||
|
target: 'http://127.0.0.1:8090',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (item) => item.replace(/^\/food-service/, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user