diff --git a/src/apis/food.ts b/src/apis/food.ts new file mode 100644 index 0000000..0d65886 --- /dev/null +++ b/src/apis/food.ts @@ -0,0 +1,81 @@ +import { cloudService } from './index' +import type { IFoodQuery, IFoodRecipe, IFoodRecord, IFoodStats } from '@/types/food' + +export const addFoodRecipeApi = (foodRecipe: IFoodRecipe): Promise => + cloudService({ + url: '/food-service/food/recipe', + method: 'post', + data: foodRecipe + }) + +export const updateFoodRecipeApi = (id: number, foodRecipe: IFoodRecipe): Promise => + cloudService({ + url: `/food-service/food/recipe/${id}`, + method: 'put', + data: foodRecipe + }) + +export const deleteFoodRecipeApi = (id: number): Promise => + cloudService({ + url: `/food-service/food/recipe/${id}`, + method: 'DELETE' + }) + +export const queryFoodRecipeByIdApi = (id: number): Promise => + cloudService({ + url: `/food-service/food/recipe/${id}`, + method: 'get' + }) + +export const queryFoodRecipeApi = (currentPage: number, pageSize: number, + query: IFoodQuery): Promise => + cloudService({ + url: '/food-service/food/recipe/page', + method: 'get', + params: { currentPage, pageSize, ...query } + }) + +export const queryFoodNameListApi = (): Promise => + cloudService({ + url: '/food-service/food/recipe/name', + method: 'get' + }) + +export const addFoodRecordApi = (foodRecord: IFoodRecord): Promise => + cloudService({ + url: '/food-service/food/record', + method: 'post', + data: foodRecord + }) + +export const updateFoodRecordApi = (id: number, foodRecord: IFoodRecord): Promise => + cloudService({ + url: `/food-service/food/record/${id}`, + method: 'put', + data: foodRecord + }) + +export const deleteFoodRecordApi = (id: number): Promise => + cloudService({ + url: `/food-service/food/record/${id}`, + method: 'DELETE' + }) + +export const queryFoodRecordApi = (startDate: string, endDate: string): Promise => + cloudService({ + url: '/food-service/food/record', + method: 'get', + params: { startDate, endDate } + }) + +export const queryFoodCategoryApi = (): Promise => + cloudService({ + url: '/food-service/food/category', + method: 'get' + }) + +export const queryFoodStatsApi = (): Promise => + cloudService({ + url: '/food-service/food/stats', + method: 'get' + }) diff --git a/src/apis/index.ts b/src/apis/index.ts new file mode 100644 index 0000000..d60aee2 --- /dev/null +++ b/src/apis/index.ts @@ -0,0 +1,3 @@ +import createService from 'vue3-common/utils/axiosUtil' + +export const cloudService = createService() diff --git a/src/apis/session.ts b/src/apis/session.ts new file mode 100644 index 0000000..404acfc --- /dev/null +++ b/src/apis/session.ts @@ -0,0 +1,9 @@ +import { cloudService } from './index' +import type { ISession } from '@/types/auth' + +export const loginApi = (name: string, password: string): Promise => + cloudService({ + url: '/food-service/session', + method: 'post', + params: { name, password } + }) diff --git a/src/components/Food/FoodDaily.vue b/src/components/Food/FoodDaily.vue index 416f042..ca15f6c 100644 --- a/src/components/Food/FoodDaily.vue +++ b/src/components/Food/FoodDaily.vue @@ -6,24 +6,19 @@ class="food-calendar card-item"/> - - + + diff --git a/src/components/Food/FoodRecipe.vue b/src/components/Food/FoodRecipe.vue index 7c424ea..254f6a6 100644 --- a/src/components/Food/FoodRecipe.vue +++ b/src/components/Food/FoodRecipe.vue @@ -17,22 +17,17 @@ - - + + diff --git a/src/store/auth.ts b/src/store/auth.ts new file mode 100644 index 0000000..51a2132 --- /dev/null +++ b/src/store/auth.ts @@ -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 + } + } + } +}) diff --git a/src/store/food.ts b/src/store/food.ts index e6dc086..8eb03cb 100644 --- a/src/store/food.ts +++ b/src/store/food.ts @@ -3,11 +3,11 @@ import type { IFoodRecipe, IFoodMaterial, IFoodStep, IFoodStats, IFoodRecord } from '@/types/food' -// import { -// addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi, -// queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi, -// queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi -// } from '@/apis/food.ts' +import { + addFoodRecipeApi, addFoodRecordApi, deleteFoodRecipeApi, deleteFoodRecordApi, + queryFoodNameListApi, queryFoodRecipeByIdApi, queryFoodRecipeApi, queryFoodRecordApi, + queryFoodStatsApi, updateFoodRecipeApi, updateFoodRecordApi, queryFoodCategoryApi +} from '@/apis/food.ts' // import { IDateType, ImagePathEnum } from '@/types' import type { IDialog, IHandleApi } from 'vue3-common/types' import { ElMessage } from 'element-plus' @@ -47,11 +47,6 @@ export const useFoodStore = defineStore('food', { queryDateType: 'month', queryDateList: [] as string[], foodRecipeApiType: 'ADD' as IHandleApi, - foodRecipeDialog: { - visible: false, - title: '', - data: {} - } as IDialog, foodMaterialApiType: 'ADD' as IHandleApi, foodMaterialDialog: { visible: false, @@ -67,11 +62,6 @@ export const useFoodStore = defineStore('food', { } as IDialog, editFoodStepIndex: 0, foodRecordApiType: 'ADD' as IHandleApi, - foodRecordDialog: { - visible: false, - title: '', - data: {} - } as IDialog, editFoodRecordIndex: 0, foodImagePath: 'food', rateColorList: ['#E2DEDC', '#F7BA2A', '#009FA8'], @@ -92,21 +82,9 @@ export const useFoodStore = defineStore('food', { actions: { async queryFoodRecipeList() { const { currentPage, pageSize } = this.pageInfo - // const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe) - const foodRecord: IFoodRecipe = { - category: '家常菜', - 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 + const result = await queryFoodRecipeApi(currentPage, pageSize, this.queryRecipe) + this.foodRecipeList.push(...result.records) + this.pageInfo.totalSize = result.total }, async queryFoodRecordList() { const { year, month } = this.queryRecord @@ -147,19 +125,18 @@ export const useFoodStore = defineStore('food', { async queryFoodCategory() { // this.foodCategoryList = await queryFoodCategoryApi() }, - async handleFoodRecipeApi(id?: number) { - const { data } = this.foodRecipeDialog + async handleFoodRecipeApi(foodRecipe: IFoodRecipe) { switch (this.foodRecipeApiType) { case 'ADD': - // await addFoodRecipeApi(data) + // await addFoodRecipeApi(foodRecipe) ElMessage.success('新增菜谱成功') break case 'UPDATE': - // await updateFoodRecipeApi(data.id as number, data) + // await updateFoodRecipeApi(foodRecipe.id as number, foodRecipe) ElMessage.success('更新菜谱成功') break case 'DELETE': - // await deleteFoodRecipeApi(id as number) + // await deleteFoodRecipeApi(foodRecipe.id as number) ElMessage.success('删除菜谱成功') break default: diff --git a/src/store/user.ts b/src/store/user.ts new file mode 100644 index 0000000..c4d5884 --- /dev/null +++ b/src/store/user.ts @@ -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') + } + } +}) diff --git a/src/types/auth.ts b/src/types/auth.ts new file mode 100644 index 0000000..8f0cd2e --- /dev/null +++ b/src/types/auth.ts @@ -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; +} diff --git a/src/types/food.ts b/src/types/food.ts index aae082f..3424dd7 100644 --- a/src/types/food.ts +++ b/src/types/food.ts @@ -1,5 +1,35 @@ 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[]; } -/** - * 食材信息 - */ -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; -} - /** * 统计信息 */ diff --git a/src/utils/element/elRules.ts b/src/utils/element/elRules.ts new file mode 100644 index 0000000..80413cc --- /dev/null +++ b/src/utils/element/elRules.ts @@ -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' } + ] +} diff --git a/src/views/login/index.vue b/src/views/login/index.vue index caad391..a900032 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -5,19 +5,23 @@ diff --git a/src/views/record/index.vue b/src/views/record/index.vue index 7d147d5..7fc8375 100644 --- a/src/views/record/index.vue +++ b/src/views/record/index.vue @@ -28,6 +28,8 @@
+ + @@ -35,12 +37,17 @@ import FoodRecipe from '@/components/Food/FoodRecipe.vue' import FoodDaily from '@/components/Food/FoodDaily.vue' import FoodTimeline from '@/components/Food/FoodTimeline.vue' +import DraggableButton from '@/components/DraggableButton.vue' import { Search } from '@element-plus/icons-vue' import { onMounted, reactive } from 'vue' import { useFoodStore } from '@/store/food' import type { IFoodSegment } from '@/types/food' +import { useRouter } from 'vue-router' +import { useAppStore } from '@/store/app' const foodStore = useFoodStore() +const router = useRouter() +const appStore = useAppStore() const foodInfo = reactive({ recordSegment: 'recipe' as IFoodSegment, @@ -67,6 +74,25 @@ onMounted(() => { const changeRecordSegmentEvent = () => { 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 + } +} diff --git a/src/views/record/recordForm.vue b/src/views/record/recordForm.vue new file mode 100644 index 0000000..1b26684 --- /dev/null +++ b/src/views/record/recordForm.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/vite.config.ts b/vite.config.ts index a2139ad..793d3b5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -20,6 +20,13 @@ export default defineConfig({ } }, server: { - port: 5175 + port: 5175, + proxy: { + '/food-service': { + target: 'http://127.0.0.1:8090', + changeOrigin: true, + rewrite: (item) => item.replace(/^\/food-service/, '') + } + } } })