This commit is contained in:
李志鹏
2025-10-27 11:52:42 +08:00
9 changed files with 282 additions and 229 deletions

View File

@@ -3,12 +3,12 @@ import request from '@/utils/request'
interface LoginParamsType { interface LoginParamsType {
name?: string // 姓名 name?: string // 姓名
email: string // 邮箱 email: string // 邮箱
password: string // 密码 password?: string // 密码
operationType: 'REGISTER' | 'LOGIN' | 'FORGET_PWD' operationType: 'REGISTER' | 'LOGIN' | 'FORGET_PWD'
verifyCode?: string // 验证码 verifyCode?: string // 验证码
} }
export const precheckAndSendEmail = (data: LoginParamsType) => { export const precheckAndSendEmail = (data: LoginParamsType): Promise<ApiResponse> => {
return request({ return request({
url: '/api/auth/precheckAndSendEmail', url: '/api/auth/precheckAndSendEmail',
method: 'post', method: 'post',
@@ -16,7 +16,7 @@ export const precheckAndSendEmail = (data: LoginParamsType) => {
}) })
} }
export const fetchLogin = (data: LoginParamsType) => { export const fetchRegisterOrLogin = (data: LoginParamsType): Promise<LoginResponse> => {
return request({ return request({
url: '/api/auth/registerOrLogin', url: '/api/auth/registerOrLogin',
method: 'post', method: 'post',
@@ -24,10 +24,17 @@ export const fetchLogin = (data: LoginParamsType) => {
}) })
} }
export const resetPassword = (data: LoginParamsType) => { export const resetPassword = (data: LoginParamsType): Promise<ApiResponse> => {
return request({ return request({
url: '/api/auth/forgotPwd', url: '/api/auth/forgotPwd',
method: 'post', method: 'post',
data data
}) })
} }
export const checkLoginStatus = (): Promise<ApiResponse<LoginResponse>> => {
return request({
url: '/api/auth/checkLoginStatus',
method: 'get'
})
}

View File

@@ -98,3 +98,16 @@ export function getTryOnEffectStyleList(styleId: string | number) {
method: 'get', method: 'get',
}) })
} }
interface CustomerInfo {
name: string
email: string
}
export const customerCheckin = (data: CustomerInfo) => {
return request({
url: '/api/customers/checkIn',
method: 'get',
params: data,
})
}

View File

@@ -2,114 +2,122 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { useUserInfoStore } from './userInfo' import { useUserInfoStore } from './userInfo'
export const useGenerateStore = defineStore({ export const useGenerateStore = defineStore({
id: 'generate', // 必须指明唯一的pinia仓库的id id: 'generate', // 必须指明唯一的pinia仓库的id
state: () => { state: () => {
return { return {
style: { style: {
id: '', id: '',
oldId: '',//表示从生成页面返回回来需要调整的样式id oldId: '' //表示从生成页面返回回来需要调整的样式id
}, },
model: { model: {
id: '', id: ''
}, },
originalTryOn: {//生成穿好衣服的回参 originalTryOn: {
id: '', //生成穿好衣服的回参
isLike: false,//是否喜欢 id: '',
tryOnUrl:'', isLike: false, //是否喜欢
}, tryOnUrl: ''
isGenerate: false,//点击继续按钮后是否需要生成 },
isGenerate: false, //点击继续按钮后是否需要生成
/** 顾客照片信息 */ /** 顾客照片信息 */
photoInfo: { photoInfo: {
id: "", id: '',
url: "", url: '',
file: null, file: null
}, },
/** AI魔改信息 */ /** AI魔改信息 */
customizeInfo: { customizeInfo: {
inputText: "", inputText: '',
tryOnId: "", tryOnId: '',
tryOnUrl: "", tryOnUrl: '',
styleUrl: "", styleUrl: '',
isRegenerated: "", isRegenerated: '',
isFavorite: false, isFavorite: false
}, },
} customerInfo: {
}, customerId: '',
getters: { visitRecordId: ''
/** 顾客id */ }
customerId: (state) => useUserInfoStore().state.customerId, }
/** 进店记录id */ },
visitRecordId: (state) => useUserInfoStore().state.visitRecordId, getters: {
/** 服装id */ /** 顾客id */
styleId: (state) => state.style.id || state.style.oldId, customerId: (state) => state.customerInfo.customerId,
/** 模特照片id */ /** 进店记录id */
modelPhotoId: (state) => state.model.id, visitRecordId: (state) => state.customerInfo.visitRecordId,
/** 原始试穿id-优先AI魔改 */ /** 服装id */
originalTryOnId: (state) => state.customizeInfo.tryOnId || state.originalTryOn.id, styleId: (state) => state.style.id || state.style.oldId,
/** 顾客照片id */ /** 模特照片id */
customerPhotoId: (state) => state.photoInfo.id, modelPhotoId: (state) => state.model.id,
}, /** 原始试穿id-优先AI魔改 */
actions: { originalTryOnId: (state) => state.customizeInfo.tryOnId || state.originalTryOn.id,
selectStyle(data: any) { /** 顾客照片id */
this.style.id = data.id customerPhotoId: (state) => state.photoInfo.id
console.log(this) },
}, actions: {
//生成后去掉id 设置oldId来修改样式 selectStyle(data: any) {
useStyleGenerate() { this.style.id = data.id
if(!this.style.id)return console.log(this)
this.style.oldId = this.style.id },
this.style.id = '' //生成后去掉id 设置oldId来修改样式
}, useStyleGenerate() {
updateStyle(data) { if (!this.style.id) return
console.log(data) this.style.oldId = this.style.id
if (data.id == this.style.oldId) { this.style.id = ''
this.style.oldId = '' },
} updateStyle(data) {
}, console.log(data)
//模特相关 if (data.id == this.style.oldId) {
selectModel(data: any) { this.style.oldId = ''
this.model.id = data.id }
}, },
setIsGenerate(isGenerate: boolean) { //模特相关
this.isGenerate = isGenerate selectModel(data: any) {
}, this.model.id = data.id
clearProductData(){ },
this.style = { setIsGenerate(isGenerate: boolean) {
id: '', this.isGenerate = isGenerate
oldId: '', },
} clearProductData() {
this.model = { this.style = {
id: '', id: '',
} oldId: ''
this.originalTryOn = { }
id: '', this.model = {
isLike: false, id: ''
tryOnUrl:'', }
} this.originalTryOn = {
this.isGenerate = false id: '',
}, isLike: false,
/** 更新顾客照片信息 */ tryOnUrl: ''
updatePhotoInfo(data: any) { }
this.photoInfo.id = data.id || "" this.isGenerate = false
if (!data.photoUrl) this.photoInfo.url = "" },
this.photoInfo.file = null /** 更新顾客照片信息 */
}, updatePhotoInfo(data: any) {
/** 清空 AI魔改信息 */ this.photoInfo.id = data.id || ''
clearCustomizeInfo() { if (!data.photoUrl) this.photoInfo.url = ''
this.customizeInfo.inputText = "" this.photoInfo.file = null
this.customizeInfo.tryOnId = "" },
this.customizeInfo.tryOnUrl = "" /** 清空 AI魔改信息 */
this.customizeInfo.styleUrl = "" clearCustomizeInfo() {
this.customizeInfo.isRegenerated = "" this.customizeInfo.inputText = ''
this.customizeInfo.isFavorite = false this.customizeInfo.tryOnId = ''
}, this.customizeInfo.tryOnUrl = ''
//设置默认数据 this.customizeInfo.styleUrl = ''
clearGenerateData() { this.customizeInfo.isRegenerated = ''
this.clearProductData() this.customizeInfo.isFavorite = false
this.updatePhotoInfo() },
this.clearCustomizeInfo() //设置默认数据
} clearGenerateData() {
} this.clearProductData()
}) this.updatePhotoInfo({})
this.clearCustomizeInfo()
},
setCustomerInfo(data: any) {
this.customerInfo = data
}
}
})

View File

@@ -1,6 +1,7 @@
// 每一个存储的模块命名规则use开头store结尾 // 每一个存储的模块命名规则use开头store结尾
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { removeLocal, setLocal } from '@/utils/local'
export const useUserInfoStore = defineStore('userInfo', () => { export const useUserInfoStore = defineStore('userInfo', () => {
const state = ref({ const state = ref({
@@ -27,6 +28,7 @@ export const useUserInfoStore = defineStore('userInfo', () => {
const setToken = (data: string) => { const setToken = (data: string) => {
state.value.token = data state.value.token = data
setLocal(data, 'token')
} }
const getGenerateParams = () => { const getGenerateParams = () => {
@@ -45,16 +47,13 @@ export const useUserInfoStore = defineStore('userInfo', () => {
} }
} }
const login = async (data: any) => {
return new Promise((resolve, reject) => {})
}
const logOut = () => { const logOut = () => {
// 处理退出登录的一些逻辑 // 处理退出登录的一些逻辑
return new Promise((resolve) => { return new Promise((resolve) => {
state.value.token = '' state.value.token = ''
state.value.userInfo = {} state.value.userInfo = {}
state.value.customerId = '' state.value.customerId = ''
removeLocal('token')
resetGenerateParams() resetGenerateParams()
resolve('') resolve('')
}) })
@@ -69,7 +68,6 @@ export const useUserInfoStore = defineStore('userInfo', () => {
setGenerateParams, setGenerateParams,
getGenerateParams, getGenerateParams,
resetGenerateParams, resetGenerateParams,
logOut, logOut
login
} }
}) })

45
src/types/api.d.ts vendored Normal file
View File

@@ -0,0 +1,45 @@
// 全局API响应类型定义
declare global {
// 基础API响应结构
interface ApiResponse<T = any> {
success: boolean
message: string
data?: T
code?: number
errMsg?: string
}
// 登录/注册相关响应
interface LoginResponse {
token?: string
user?: {
id: string
name: string
email: string
}
}
// 通用列表响应
interface ListResponse<T> {
list: T[]
total: number
page: number
pageSize: number
}
// 分页参数
interface PaginationParams {
page: number
pageSize: number
}
// 通用错误响应
interface ErrorResponse {
success: false
message: string
code?: number
errMsg?: string
}
}
export {}

View File

@@ -6,20 +6,31 @@ import { getLocal } from '@/utils/local'
import router from '@/router/index' import router from '@/router/index'
import { useOverallStore, useGenerateStore } from '@/stores' import { useOverallStore, useGenerateStore } from '@/stores'
// 扩展 AxiosRequestConfig 接口
declare module 'axios' {
interface AxiosRequestConfig {
loading?: boolean
loadingDom?: any
repeatRequest?: boolean
meta?: {
responseAll?: boolean
}
}
}
// 创建axios实例 // 创建axios实例
// console.log(import.meta.env,123) // console.log(import.meta.env,123)
const service = axios.create({ const service = axios.create({
// baseURL: import.meta.env.VITE_APP_URL, // api的base_url // baseURL: import.meta.env.VITE_APP_URL, // api的base_url
timeout: 20000, // 请求超时时间 timeout: 20000 // 请求超时时间
}) })
if(import.meta.env.MODE != 'development'){ if (import.meta.env.MODE != 'development') {
service.defaults.baseURL = import.meta.env.VITE_APP_URL service.defaults.baseURL = import.meta.env.VITE_APP_URL
} }
axios.defaults.headers.post["Content-Type"] = "application/json"; axios.defaults.headers.post['Content-Type'] = 'application/json'
axios.defaults.headers.post['lang'] = 'en'; //配置语言请求头 axios.defaults.headers.post['lang'] = 'en' //配置语言请求头
axios.defaults.withCredentials = true; //跨域携带cookie axios.defaults.withCredentials = true //跨域携带cookie
// request拦截器 // request拦截器
service.interceptors.request.use( service.interceptors.request.use(
@@ -36,9 +47,9 @@ service.interceptors.request.use(
} }
// 如果登录了有token则请求携带token // 如果登录了有token则请求携带token
// Do something before request is sent // Do something before request is sent
if (store.token) { if (store.state.token) {
config.headers.Authorization = getLocal('token') // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改 config.headers.Authorization = getLocal('token') // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
// config.headers['X-Token'] = getLocal('token') // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改 // config.headers['X-Token'] = getLocal('token') // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
} }
return config return config
}, },
@@ -65,14 +76,15 @@ service.interceptors.response.use(
} }
const res = response.data const res = response.data
// 处理异常的情况 // 处理异常的情况
console.log(res) console.log(res)
if (res.code != 0) { if (res.code != 0) {
showToast({ showToast({
message: res.errMsg, message: res.errMsg || res.message,
type: 'fail', // type: 'fail',
duration: 5000 duration: 5000,
position:'top'
}) })
return Promise.reject(new Error('error')) return Promise.reject(new Error('error'))
} else { } else {
// 默认只返回data不返回状态码和message // 默认只返回data不返回状态码和message
@@ -165,14 +177,13 @@ const LoadingInstance: { _count: number } = {
_count: 0 _count: 0
} }
function openLoading(loadingDom: any) { function openLoading(loadingDom: any) {
useOverallStore().setLoading(true) useOverallStore().setLoading(true)
} }
function closeLoading() { function closeLoading() {
if (LoadingInstance._count > 0) LoadingInstance._count-- if (LoadingInstance._count > 0) LoadingInstance._count--
if (LoadingInstance._count === 0) { if (LoadingInstance._count === 0) {
useOverallStore().setLoading(false) useOverallStore().setLoading(false)
} }
} }
export default service export default service

View File

@@ -46,14 +46,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed } from 'vue' import { ref, reactive, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useUserInfoStore } from '@/stores'
import { showToast } from 'vant' import { showToast } from 'vant'
import { google } from '@/assets/base64' import { google } from '@/assets/base64'
import { fetchLogin } from '@/api/login' import { fetchRegisterOrLogin } from '@/api/login'
const router = useRouter() const router = useRouter()
const userInfoStore = useUserInfoStore()
// 表单数据 // 表单数据
const formData = reactive<Record<string, string>>({ const formData = reactive({
email: '', email: '',
password: '' password: ''
}) })
@@ -109,19 +111,14 @@ const handleLogin = async () => {
isLoading.value = true isLoading.value = true
const response: any = await fetchLogin({ ...formData, operationType: 'LOGIN' } as any) fetchRegisterOrLogin({ ...formData, operationType: 'LOGIN' }).then((response) => {
if (response.code === 200) { console.log('登录成功', response)
console.log('登录成功', response) userInfoStore.setToken(response.token)
// showToast('登录成功') userInfoStore.setUserInfo(response.user)
// router.push('/stylist/customer') showToast('登录成功')
} else { router.replace('/stylist/customer')
showToast(response.message) })
}
// showToast('登录成功')
// 登录成功后跳转到主页或工作台
// router.push('/stylist/customer')
} }
// 处理忘记密码 // 处理忘记密码

View File

@@ -16,24 +16,12 @@
</div> </div>
<div class="login-container"> <div class="login-container">
<form @submit.prevent="handleConfirm" class="login-form"> <div class="login-form">
<div class="input-group"> <div class="input-group">
<input <input type="text" v-model="formData.name" placeholder="Name" class="input-field" />
type="text"
v-model="formData.name"
placeholder="Name"
class="input-field"
required
/>
</div> </div>
<div class="input-group"> <div class="input-group">
<input <input type="email" v-model="formData.email" placeholder="Email" class="input-field" />
type="email"
v-model="formData.email"
placeholder="Email"
class="input-field"
required
/>
</div> </div>
<div class="input-group pwd"> <div class="input-group pwd">
<input <input
@@ -41,19 +29,18 @@
v-model="formData.password" v-model="formData.password"
placeholder="Your Password" placeholder="Your Password"
class="input-field" class="input-field"
required
/> />
</div> </div>
<!-- 登录按钮 --> <!-- 登录按钮 -->
<button type="submit" class="login-button">Sign Up</button> <button type="button" class="login-button" @click="handleConfirm">Sign Up</button>
<!-- Google登录按钮 --> <!-- Google登录按钮 -->
<div type="button" class="google-button" @click="handleGoogleLogin"> <div type="button" class="google-button" @click="handleSignupByGoogle">
<img :src="google" class="google-icon" /> <img :src="google" class="google-icon" />
Sign up with Google Sign up with Google
</div> </div>
</form> </div>
</div> </div>
</div> </div>
@@ -64,16 +51,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed } from 'vue' import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { showToast } from 'vant' import { showToast } from 'vant'
import { google } from '@/assets/base64' import { google } from '@/assets/base64'
import { fetchLogin } from '@/api/login' import { fetchRegisterOrLogin } from '@/api/login'
const router = useRouter() const router = useRouter()
// 表单数据 // 表单数据
const formData = reactive<Record<string, string>>({ const formData = reactive({
name: '', name: '',
email: '', email: '',
password: '' password: ''
@@ -135,23 +122,12 @@ const validateForm = () => {
return isValid return isValid
} }
// 计算属性:表单是否有效
const isFormValid = computed(() => {
return (
formData.name &&
formData.email &&
formData.password &&
validateEmail(formData.email) &&
validatePassword(formData.password)
)
})
// 返回上一页 // 返回上一页
const goBack = () => { const goBack = () => {
router.go(-1) router.go(-1)
} }
// 处理登录 // 处理注册
const handleConfirm = async () => { const handleConfirm = async () => {
if (!validateForm()) { if (!validateForm()) {
showToast('请检查输入信息') showToast('请检查输入信息')
@@ -160,26 +136,25 @@ const handleConfirm = async () => {
isLoading.value = true isLoading.value = true
const res:any = await fetchLogin({...formData, operationType: 'REGISTER'} as any) fetchRegisterOrLogin({ ...formData, operationType: 'REGISTER' })
if(res.code === 200) { .then((res) => {
console.log('注册成功', res) if (res.success) {
// showToast('注册成功') showToast('注册成功')
// router.push('/stylist/customer') router.push('/login')
} else { } else {
showToast(res.message) showToast(res.message)
} }
} })
.catch((err) => {
// 处理忘记密码 showToast(err.message)
const handleForgotPassword = () => { })
showToast('忘记密码功能开发中...') .finally(() => {
console.log('11111111111') isLoading.value = false
// 这里可以跳转到忘记密码页面 })
// router.push('/forgot-password')
} }
// 处理Google登录 // 处理Google登录
const handleGoogleLogin = async () => { const handleSignupByGoogle = async () => {
try { try {
isLoading.value = true isLoading.value = true
showToast('Google登录功能开发中...') showToast('Google登录功能开发中...')
@@ -193,23 +168,6 @@ const handleGoogleLogin = async () => {
isLoading.value = false isLoading.value = false
} }
} }
// 实时验证输入
const handleEmailInput = () => {
if (formData.email && !validateEmail(formData.email)) {
formErrors.email = '请输入有效的邮箱地址'
} else {
formErrors.email = ''
}
}
const handlePasswordInput = () => {
if (formData.password && !validatePassword(formData.password)) {
formErrors.password = '密码至少需要6位字符'
} else {
formErrors.password = ''
}
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@@ -2,7 +2,7 @@
<div class="customer-container safe-area-top" :class="{ 'form-mode': pageMode === 'form' }"> <div class="customer-container safe-area-top" :class="{ 'form-mode': pageMode === 'form' }">
<template v-if="pageMode === 'entry'"> <template v-if="pageMode === 'entry'">
<div class="setting flex flex-between"> <div class="setting flex flex-between">
<van-icon name="arrow-left" color="#fff" size="70" /> <van-icon name="arrow-left" color="#fff" size="70" @click="handleBack" />
<SvgIcon name="setting" size="70" /> <SvgIcon name="setting" size="70" />
</div> </div>
<div class="content flex flex-center flex-column"> <div class="content flex flex-center flex-column">
@@ -26,11 +26,16 @@
<div class="glass-form"> <div class="glass-form">
<div class="form-field"> <div class="form-field">
<label class="field-label">Customer Name</label> <label class="field-label">Customer Name</label>
<input v-model="customeData.name" type="text" placeholder="Name" class="form-input" /> <input v-model="customerData.name" type="text" placeholder="Name" class="form-input" />
</div> </div>
<div class="form-field email"> <div class="form-field email">
<label class="field-label">Customer Email</label> <label class="field-label">Customer Email</label>
<input v-model="customeData.email" type="email" placeholder="Email" class="form-input" /> <input
v-model="customerData.email"
type="email"
placeholder="Email"
class="form-input"
/>
</div> </div>
<button class="confirm-btn" @click="handleConfirm">Confirm</button> <button class="confirm-btn" @click="handleConfirm">Confirm</button>
</div> </div>
@@ -42,33 +47,44 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useUserInfoStore } from '@/stores' import { useGenerateStore } from '@/stores'
import { showToast } from 'vant' import { showToast } from 'vant'
const userInfoStore = useUserInfoStore() import { customerCheckin } from '@/api/workshop'
const router = useRouter() const router = useRouter()
const generateStore = useGenerateStore()
type PageMode = 'form' | 'entry' type PageMode = 'form' | 'entry'
const pageMode = ref<PageMode>('entry') const pageMode = ref<PageMode>('entry')
const handleBack = () => {
router.go(-1)
}
const handleChangeMode = (mode: PageMode) => { const handleChangeMode = (mode: PageMode) => {
pageMode.value = mode pageMode.value = mode
} }
const customeData = ref({ const customerData = ref({
name: '', name: '',
email: '' email: ''
}) })
const handleConfirm = async () => { const handleConfirm = async () => {
if (customeData.value.name === '' || customeData.value.email === '') { if (customerData.value.name === '' || customerData.value.email === '') {
showToast('please input name and email') showToast({
message: 'please input name and email',
position: 'top'
})
return return
} }
console.log('customerData.value', customerData.value)
// await 查找顾客ID customerCheckin(customerData.value).then((res) => {
// userInfoStore.setCustomerId('') console.log('res', res)
generateStore.setCustomerInfo(res)
router.push('/stylist/index') router.push('/stylist/index')
})
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>