feat: api创建

This commit is contained in:
zhangyh
2025-10-24 17:37:15 +08:00
parent 21384516ce
commit 5fe30c82c8
14 changed files with 197 additions and 86 deletions

12
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "0.0.0", "version": "0.0.0",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@microsoft/fetch-event-source": "^2.0.1",
"axios": "^1.3.6", "axios": "^1.3.6",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
@@ -581,6 +582,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@microsoft/fetch-event-source": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz",
"integrity": "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==",
"license": "MIT"
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -8827,6 +8834,11 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"@microsoft/fetch-event-source": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz",
"integrity": "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

View File

@@ -14,6 +14,7 @@
"postinstall": "husky install" "postinstall": "husky install"
}, },
"dependencies": { "dependencies": {
"@microsoft/fetch-event-source": "^2.0.1",
"axios": "^1.3.6", "axios": "^1.3.6",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",

33
src/api/login.ts Normal file
View File

@@ -0,0 +1,33 @@
import request from '@/utils/request'
interface LoginParamsType {
name?: string // 姓名
email: string // 邮箱
password: string // 密码
operationType: 'REGISTER' | 'LOGIN' | 'FORGET_PWD'
verifyCode?: string // 验证码
}
export const precheckAndSendEmail = (data: LoginParamsType) => {
return request({
url: '/api/auth/precheckAndSendEmail',
method: 'post',
data
})
}
export const fetchLogin = (data: LoginParamsType) => {
return request({
url: '/api/auth/registerOrLogin',
method: 'post',
data
})
}
export const resetPassword = (data: LoginParamsType) => {
return request({
url: '/api/auth/forgotPwd',
method: 'post',
data
})
}

View File

@@ -7,3 +7,4 @@ store.use(createPersistedState())
export default store export default store
export * from './modules/generate' export * from './modules/generate'
export * from './modules/overall' export * from './modules/overall'
export * from './modules/userInfo'

View File

@@ -3,40 +3,73 @@ import { defineStore } from 'pinia'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
export const useUserInfoStore = defineStore('userInfo', () => { export const useUserInfoStore = defineStore('userInfo', () => {
// state const state = ref({
const num = ref(0) userInfo: {},
const name = ref('张三') customerId: '',
const token = ref('') token: '',
generateParams: {
stylistId: '',
sex: ''
}
})
// getters // getters
const getUserInfo = computed(() => ({ const getUserInfo = computed(() => state.value.userInfo)
num: num.value,
name: name.value,
token: token.value
}))
// actions // actions
const setUserInfo = (data: any) => { const setUserInfo = (data: any) => {
name.value = data.name state.value.userInfo = data
token.value = data.token
} }
const loginOut = () => { const setCustomerId = (data: string) => {
state.value.customerId = data
}
const setToken = (data: string) => {
state.value.token = data
}
const getGenerateParams = () => {
return state.value.generateParams
}
const setGenerateParams = (data: any) => {
state.value.generateParams = data
}
const resetGenerateParams = () => {
state.value.customerId = ''
state.value.generateParams = {
stylistId: '',
sex: ''
}
}
const login = async (data: any) => {
return new Promise((resolve, reject) => {})
}
const logOut = () => {
// 处理退出登录的一些逻辑 // 处理退出登录的一些逻辑
return new Promise((rez) => { return new Promise((resolve) => {
rez('111') state.value.token = ''
state.value.userInfo = {}
state.value.customerId = ''
resetGenerateParams()
resolve('')
}) })
} }
return { return {
// state state,
num,
name,
token,
// getters
getUserInfo, getUserInfo,
// actions setToken,
setUserInfo, setUserInfo,
loginOut setCustomerId,
setGenerateParams,
getGenerateParams,
resetGenerateParams,
logOut,
login
} }
}) })

View File

@@ -10,7 +10,7 @@ import { useOverallStore } from '@/stores'
// 创建axios实例 // 创建axios实例
console.log(import.meta.env.VITE_APP_URL,123) console.log(import.meta.env.VITE_APP_URL,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
// baseURL: import.meta.env.VITE_APP_URL, // api的base_url // baseURL: import.meta.env.VITE_APP_URL, // api的base_url
timeout: 20000, // 请求超时时间 timeout: 20000, // 请求超时时间
}) })

View File

@@ -13,8 +13,7 @@
<div class="message-text"> <div class="message-text">
{{ value.content }} {{ value.content }}
</div> </div>
<!-- AI消息显示操作栏 --> <!-- <div v-if="!isMyself" class="message-actions flex">
<div v-if="!isMyself" class="message-actions flex">
<SvgIcon <SvgIcon
v-for="item in actionList" v-for="item in actionList"
:key="item.value" :key="item.value"
@@ -22,7 +21,7 @@
size="39" size="39"
@click.stop="handleClickAction(item.value)" @click.stop="handleClickAction(item.value)"
/> />
</div> </div> -->
</div> </div>
</div> </div>
</template> </template>

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="chat-list" ref="chatListRef"> <div class="chat-list" ref="chatListRef">
<div class="chat-message-item" v-for="message in list" :key="message.id"> <div class="chat-message-item" v-for="message in list" :key="message.id">
<NoticeItem :value="message" @send-message="handleSendMessage" /> <NoticeItem :value="message" />
</div> </div>
</div> </div>
</template> </template>

View File

@@ -8,7 +8,7 @@
</div> </div>
<template v-else> <template v-else>
<div class="content flex-1" v-if="!isLoading"> <div class="content flex-1" v-if="!isLoading">
<NoticeList ref="noticeListRef" :list="messageList" @send-message="handleSendMessage" /> <NoticeList ref="noticeListRef" :list="messageList" />
</div> </div>
<div class="footer" v-if="!isLoading"> <div class="footer" v-if="!isLoading">
<InputArea @send-message="handleSendMessage" /> <InputArea @send-message="handleSendMessage" />
@@ -24,12 +24,14 @@ import HeaderTitle from '@/components/HeaderTitle.vue'
import NoticeList from './components/NoticeList.vue' import NoticeList from './components/NoticeList.vue'
import InputArea from './components/InputArea.vue' import InputArea from './components/InputArea.vue'
import GenerateLoading from './components/GenerateLoading.vue' import GenerateLoading from './components/GenerateLoading.vue'
import { ref, onMounted, onActivated } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { fetchEventSource } from '@microsoft/fetch-event-source'
const router = useRouter() const router = useRouter()
defineOptions({ defineOptions({
name:'asistant' name: 'asistant'
}) })
// 定义NoticeList组件引用类型 // 定义NoticeList组件引用类型
@@ -80,26 +82,38 @@ const messageList = ref<ChatMessage[]>([
} }
]) ])
onMounted(() => { onMounted(() => {})
console.log('🚀 组件挂载 - onMounted 触发')
// handleSendMessage('123')
})
onActivated(() => { onUnmounted(() => {
console.log('🔄 缓存页面激活 - onActivated 触发') abort.abort()
console.log('当前消息数量:', messageList.value.length)
}) })
const handleSendMessage = (message: string): void => { const handleSendMessage = (message: string): void => {
console.log('收到消息:', message) console.log('发送:', message)
messageList.value.push({ handleFetchMessage()
id: '1', }
content: message,
userId: '1', const abort = new AbortController()
time: new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }), const handleFetchMessage = () => {
thumb: '' fetchEventSource('/api/sse', {
method: 'POST',
openWhenHidden: true,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
signal: abort.signal,
onopen: async (res) => {
console.log('连接建立', res)
},
onmessage: (event) => {
console.log('收到消息', event)
},
onerror: (error) => {
console.log('错误', error)
},
onclose: () => {
console.log('连接关闭')
}
}) })
//
} }
const handleContinue = () => { const handleContinue = () => {

View File

@@ -48,6 +48,7 @@ import { ref, reactive, computed } 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'
const router = useRouter() const router = useRouter()
@@ -108,23 +109,19 @@ const handleLogin = async () => {
isLoading.value = true isLoading.value = true
try { const response: any = await fetchLogin({ ...formData, operationType: 'LOGIN' } as any)
// 模拟登录API调用 if (response.code === 200) {
await new Promise((resolve) => setTimeout(resolve, 2000)) console.log('登录成功', response)
// showToast('登录成功')
// 这里添加实际的登录API调用 // router.push('/stylist/customer')
// const response = await loginAPI(formData) } else {
showToast(response.message)
showToast('登录成功')
// 登录成功后跳转到主页或工作台
router.push('/stylist/customer')
} catch (error) {
console.error('登录失败:', error)
showToast('登录失败,请重试')
} finally {
isLoading.value = false
} }
// showToast('登录成功')
// 登录成功后跳转到主页或工作台
// router.push('/stylist/customer')
} }
// 处理忘记密码 // 处理忘记密码

View File

@@ -16,7 +16,7 @@
</div> </div>
<div class="login-container"> <div class="login-container">
<form @submit.prevent="handleLogin" class="login-form"> <form @submit.prevent="handleConfirm" class="login-form">
<div class="input-group"> <div class="input-group">
<input <input
type="text" type="text"
@@ -68,6 +68,7 @@ import { ref, reactive, computed } 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'
const router = useRouter() const router = useRouter()
@@ -151,7 +152,7 @@ const goBack = () => {
} }
// 处理登录 // 处理登录
const handleLogin = async () => { const handleConfirm = async () => {
if (!validateForm()) { if (!validateForm()) {
showToast('请检查输入信息') showToast('请检查输入信息')
return return
@@ -159,22 +160,13 @@ const handleLogin = async () => {
isLoading.value = true isLoading.value = true
try { const res:any = await fetchLogin({...formData, operationType: 'REGISTER'} as any)
// 模拟登录API调用 if(res.code === 200) {
await new Promise((resolve) => setTimeout(resolve, 2000)) console.log('注册成功', res)
// showToast('注册成功')
// 这里添加实际的登录API调用 // router.push('/stylist/customer')
// const response = await loginAPI(formData) } else {
showToast(res.message)
showToast('登录成功')
// 登录成功后跳转到主页或工作台
router.push('/workshop')
} catch (error) {
console.error('登录失败:', error)
showToast('登录失败,请重试')
} finally {
isLoading.value = false
} }
} }

View File

@@ -26,11 +26,11 @@
<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 type="text" placeholder="Name" class="form-input" /> <input v-model="customeData.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 type="email" placeholder="Email" class="form-input" /> <input v-model="customeData.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,6 +42,9 @@
<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 { showToast } from 'vant'
const userInfoStore = useUserInfoStore()
const router = useRouter() const router = useRouter()
type PageMode = 'form' | 'entry' type PageMode = 'form' | 'entry'
@@ -51,8 +54,20 @@ const handleChangeMode = (mode: PageMode) => {
pageMode.value = mode pageMode.value = mode
} }
const handleConfirm = () => { const customeData = ref({
console.log('handleConfirm') name: '',
email: ''
})
const handleConfirm = async () => {
if (customeData.value.name === '' || customeData.value.email === '') {
showToast('please input name and email')
return
}
// await 查找顾客ID
// userInfoStore.setCustomerId('')
router.push('/stylist/index') router.push('/stylist/index')
} }
</script> </script>

View File

@@ -14,7 +14,7 @@
<van-icon name="arrow-left" color="#fff" size="40" /> <van-icon name="arrow-left" color="#fff" size="40" />
</div> </div>
<van-swipe touchable ref="swiperRef"> <van-swipe touchable ref="swiperRef" @change="handleChangeCurrent">
<van-swipe-item v-for="item in stylists" :key="item.id"> <van-swipe-item v-for="item in stylists" :key="item.id">
<div class="swiper-container" @click="handleClickStylist(item)"> <div class="swiper-container" @click="handleClickStylist(item)">
<img :src="item.image" /> <img :src="item.image" />
@@ -64,9 +64,10 @@
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import Video from './components/Video.vue' import Video from './components/Video.vue'
import { useUserInfoStore } from '@/stores'
const router = useRouter() const router = useRouter()
const userInfoStore = useUserInfoStore()
// stylist数据 // stylist数据
const stylists = ref<any[]>([ const stylists = ref<any[]>([
{ {
@@ -94,11 +95,17 @@ const stylists = ref<any[]>([
image: '/src/assets/images/male.png' image: '/src/assets/images/male.png'
} }
]) ])
const currentChoosed=ref('')
const swiperRef = ref<any>(null) const swiperRef = ref<any>(null)
const showVideo = ref<boolean>(false) const showVideo = ref<boolean>(false)
const videoRef = ref<any>(null) const videoRef = ref<any>(null)
const handleChangeCurrent=(index:number)=>{
currentChoosed.value = stylists.value[index].id
}
const handleChangeSwiper = (type: 'next' | 'prev') => { const handleChangeSwiper = (type: 'next' | 'prev') => {
if (type === 'next') { if (type === 'next') {
swiperRef.value.next() swiperRef.value.next()
@@ -113,7 +120,10 @@ const handleClickStylist = (item: any) => {
} }
const handleContinue = () => { const handleContinue = () => {
// 跳转到下一个页面 const generateParams = userInfoStore.getGenerateParams()
generateParams.stylistId = currentChoosed.value
userInfoStore.setGenerateParams(generateParams)
router.push('/stylist/sex') router.push('/stylist/sex')
} }

View File

@@ -18,7 +18,9 @@
<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'
const router = useRouter() const router = useRouter()
const userInfoStore = useUserInfoStore()
const options = ref<any[]>([ const options = ref<any[]>([
{ label: 'Female', value: '1' }, { label: 'Female', value: '1' },
@@ -26,7 +28,9 @@ const options = ref<any[]>([
]) ])
const handleSelect = (value: string) => { const handleSelect = (value: string) => {
console.log(value) const generateParams = userInfoStore.getGenerateParams()
generateParams.sex = value
userInfoStore.setGenerateParams(generateParams)
router.push('/stylist/dressfor') router.push('/stylist/dressfor')
} }
</script> </script>