fix(service): 🐛 修复http模块没有设置请求头导致请求报错问题

完善登录逻辑和自动登录逻辑
This commit is contained in:
nongyehong 2024-11-16 17:53:14 +08:00
parent 54ee3ff168
commit 153896480e
16 changed files with 152 additions and 87 deletions

View File

@ -1,13 +1,15 @@
<template> <template>
<!-- 好友详情 --> <!-- 好友详情 -->
<n-flex v-if="content.type === RoomTypeEnum.SINGLE" vertical align="center" :size="30" class="mt-60px select-none"> <n-flex v-if="content.type === RoomTypeEnum.SINGLE" vertical align="center" :size="30" class="mt-60px select-none">
<n-image <n-avatar v-if="item.avatar" class="rounded-50% size-146px border-(2px solid #fff)" :src="item.avatar" />
width="146px"
height="146px" <n-avatar
style="border: 2px solid #fff" v-else
class="rounded-50%" :color="'#909090'"
:src="item.avatar" class="rounded-50% size-146px text-28px border-(2px solid #fff)"
alt="" /> :src="item.avatar">
{{ item.name!.slice(0, 1) }}
</n-avatar>
<span class="text-(20px [--text-color])">{{ item.name }}</span> <span class="text-(20px [--text-color])">{{ item.name }}</span>

View File

@ -56,7 +56,7 @@
</n-virtual-list> </n-virtual-list>
</div> </div>
<!-- 发送按钮 TODO 建议不要放在外面会影响视觉效果可以放在发送按钮里面做提示发送按钮需要修改一下大小 (nyh -> 2024-09-01 23:41:34) --> <!-- 发送按钮 -->
<n-flex align="center" justify="space-between" :size="12"> <n-flex align="center" justify="space-between" :size="12">
<n-config-provider :theme="lightTheme"> <n-config-provider :theme="lightTheme">
<n-button-group size="small" class="pr-20px"> <n-button-group size="small" class="pr-20px">

View File

@ -85,16 +85,6 @@
<div class="pl-20px flex flex-col items-end gap-6px"> <div class="pl-20px flex flex-col items-end gap-6px">
<MsgInput ref="MsgInputRef" /> <MsgInput ref="MsgInputRef" />
</div> </div>
<div v-if="isGuest" class="fuzzy">
<n-flex align="center" :size="0" class="pb-60px text-(14px [--text-color])">
<p>当前为</p>
<p class="color-#c14053 px-2px">游客模式</p>
<p></p>
<p @click="logout(true)" class="color-#13987f px-4px cursor-pointer">扫码登录</p>
<p>后使用</p>
</n-flex>
</div>
</main> </main>
</template> </template>
@ -104,20 +94,12 @@ import { LimitEnum, MsgEnum } from '@/enums'
import { useCommon } from '@/hooks/useCommon.ts' import { useCommon } from '@/hooks/useCommon.ts'
import { WebviewWindow } from '@tauri-apps/api/webviewWindow' import { WebviewWindow } from '@tauri-apps/api/webviewWindow'
import { emit } from '@tauri-apps/api/event' import { emit } from '@tauri-apps/api/event'
import { useLogin } from '@/hooks/useLogin.ts'
import { useSettingStore } from '@/stores/setting.ts'
const { logout } = useLogin()
const { open, onChange, reset } = useFileDialog() const { open, onChange, reset } = useFileDialog()
const { login } = useSettingStore()
const MsgInputRef = ref() const MsgInputRef = ref()
const msgInputDom = ref() const msgInputDom = ref()
const emojiShow = ref() const emojiShow = ref()
const { insertNode, triggerInputEvent, getEditorRange, imgPaste, FileOrVideoPaste } = useCommon() const { insertNode, triggerInputEvent, getEditorRange, imgPaste, FileOrVideoPaste } = useCommon()
/**
* 是否为游客模式
*/
const isGuest = computed(() => login.accountInfo.token === 'test')
/** /**
* 选择表情并把表情插入输入框 * 选择表情并把表情插入输入框
@ -185,13 +167,6 @@ onMounted(() => {
} }
} }
.fuzzy {
@apply bg-transparent select-none cursor-default size-full absolute-flex-center;
overflow: hidden;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
:deep(.n-input .n-input-wrapper) { :deep(.n-input .n-input-wrapper) {
padding: 0; padding: 0;
} }

View File

@ -99,6 +99,7 @@
round round
v-if="avatarExists(item.fromUser.uid)" v-if="avatarExists(item.fromUser.uid)"
:size="34" :size="34"
:color="'#909090'"
@click="selectKey = item.message.id" @click="selectKey = item.message.id"
class="select-none" class="select-none"
:src="getAvatarSrc(item.fromUser.uid)" :src="getAvatarSrc(item.fromUser.uid)"

View File

@ -65,6 +65,7 @@
:special-menu="report"> :special-menu="report">
<n-flex @click="selectKey = item.uid" :key="item.uid" :size="10" align="center" class="item"> <n-flex @click="selectKey = item.uid" :key="item.uid" :size="10" align="center" class="item">
<n-avatar <n-avatar
v-if="item.avatar"
lazy lazy
round round
class="grayscale" class="grayscale"
@ -77,6 +78,23 @@
:intersection-observer-options="{ :intersection-observer-options="{
root: '#image-chat-sidebar' root: '#image-chat-sidebar'
}"></n-avatar> }"></n-avatar>
<n-avatar
v-else
lazy
round
class="grayscale text-10px"
:class="{ 'grayscale-0': item.activeStatus === OnlineEnum.ONLINE }"
:color="'#909090'"
:size="24"
:src="item.avatar"
fallback-src="/logo.png"
:render-placeholder="() => null"
:intersection-observer-options="{
root: '#image-chat-sidebar'
}">
{{ item.name.slice(0, 1) }}
</n-avatar>
<span class="text-12px truncate flex-1">{{ item.name }}</span> <span class="text-12px truncate flex-1">{{ item.name }}</span>
<div v-if="item.uid === 1" class="flex p-4px rounded-4px bg-#f5dadf size-fit select-none"> <div v-if="item.uid === 1" class="flex p-4px rounded-4px bg-#f5dadf size-fit select-none">
<span class="text-(10px #d5304f)">群主</span> <span class="text-(10px #d5304f)">群主</span>

View File

@ -16,20 +16,13 @@ export const useLogin = () => {
/** /**
* *
* @param isToQrcode
*/ */
const logout = async (isToQrcode = false) => { const logout = async () => {
const { createWebviewWindow } = useWindow() const { createWebviewWindow } = useWindow()
localStorage.removeItem('USER_INFO')
localStorage.removeItem('TOKEN')
// todo 退出账号 需要关闭其他的全部窗口 // todo 退出账号 需要关闭其他的全部窗口
await createWebviewWindow('登录', 'login', 320, 448, 'home', false, 320, 448).then(() => { await createWebviewWindow('登录', 'login', 320, 448, 'home', false, 320, 448).then(() => {
emit(EventEnum.LOGOUT) emit(EventEnum.LOGOUT)
emit('logout_success') emit('logout_success')
// 用于跳转到二维码页面
if (isToQrcode) {
localStorage.setItem('isToQrcode', '1')
}
}) })
} }

View File

@ -24,7 +24,20 @@
<n-flex :size="20" class="p-22px select-none" vertical> <n-flex :size="20" class="p-22px select-none" vertical>
<!-- 头像 --> <!-- 头像 -->
<n-flex justify="center"> <n-flex justify="center">
<n-avatar :size="80" :src="editInfo.content.avatar" round style="border: 3px solid #fff" /> <n-avatar
v-if="editInfo.content.avatar"
:size="80"
:src="editInfo.content.avatar"
round
style="border: 3px solid #fff" />
<n-avatar
v-else
:size="80"
:color="'#909090'"
:src="editInfo.content.avatar"
round
style="border: 3px solid #fff" />
</n-flex> </n-flex>
<n-flex v-if="currentBadge" align="center" justify="center"> <n-flex v-if="currentBadge" align="center" justify="center">
<span class="text-(14px #707070)">当前佩戴的徽章:</span> <span class="text-(14px #707070)">当前佩戴的徽章:</span>

View File

@ -4,10 +4,14 @@ import Mitt from '@/utils/Bus.ts'
import { useLogin } from '@/hooks/useLogin.ts' import { useLogin } from '@/hooks/useLogin.ts'
import { useSettingStore } from '@/stores/setting.ts' import { useSettingStore } from '@/stores/setting.ts'
import apis from '@/services/apis.ts' import apis from '@/services/apis.ts'
import { LoginStatus, useWsLoginStore } from '@/stores/ws.ts'
import { useUserStore } from '@/stores/user.ts'
const { createWebviewWindow } = useWindow() const { createWebviewWindow } = useWindow()
const { logout } = useLogin() const { logout } = useLogin()
const settingStore = useSettingStore() const settingStore = useSettingStore()
const loginStore = useWsLoginStore()
const userStore = useUserStore()
const { login } = storeToRefs(settingStore) const { login } = storeToRefs(settingStore)
/** /**
* 使pinia写入了localstorage中 * 使pinia写入了localstorage中
@ -97,7 +101,15 @@ const moreList = ref<OPT.L.MoreList[]>([
.logout() .logout()
.then(async () => { .then(async () => {
await logout() await logout()
login.value.accountInfo.token = '' // 如果没有设置自动登录,则清除用户信息
if (!login.value.autoLogin) {
login.value.accountInfo.token = ''
userStore.userInfo = {}
localStorage.removeItem('USER_INFO')
localStorage.removeItem('TOKEN')
}
userStore.isSign = false
loginStore.loginStatus = LoginStatus.Init
}) })
.catch(() => { .catch(() => {
window.$message.error('退出账号失败') window.$message.error('退出账号失败')

View File

@ -47,7 +47,7 @@ export default {
/** 标记消息,点赞等 */ /** 标记消息,点赞等 */
markMsg: (data?: MarkMsgReq) => PUT<void>(urls.markMsg, data), markMsg: (data?: MarkMsgReq) => PUT<void>(urls.markMsg, data),
/** 获取用户详细信息 */ /** 获取用户详细信息 */
getUserDetail: () => GET<UserInfoType>(urls.getUserInfoDetail, {}), getUserDetail: () => GET<UserInfoType>(urls.getUserInfoDetail),
/** 获取徽章列表 */ /** 获取徽章列表 */
getBadgeList: (): Promise<BadgeType> => GET(urls.getBadgeList), getBadgeList: (): Promise<BadgeType> => GET(urls.getBadgeList),
/** 设置用户勋章 */ /** 设置用户勋章 */

View File

@ -33,9 +33,22 @@ async function Http<T>(
fullResponse?: true, fullResponse?: true,
abort?: AbortController abort?: AbortController
): Promise<{ data: Promise<T>; resp: Response }> { ): Promise<{ data: Promise<T>; resp: Response }> {
// 获取token
const token = localStorage.getItem('TOKEN')
// 构建请求头 // 构建请求头
const httpHeaders = new Headers(options.headers || {}) const httpHeaders = new Headers(options.headers || {})
// 设置Content-Type
if (!httpHeaders.has('Content-Type') && !(options.body instanceof FormData)) {
httpHeaders.set('Content-Type', 'application/json')
}
// 设置Authorization
if (token) {
httpHeaders.set('Authorization', `Bearer ${token}`)
}
// 构建 fetch 请求选项 // 构建 fetch 请求选项
const fetchOptions: RequestInit = { const fetchOptions: RequestInit = {
method: options.method, method: options.method,

View File

@ -40,11 +40,11 @@ class WS {
worker.postMessage(`{"type":"initWS","value":${token ? `"${token}"` : null}}`) worker.postMessage(`{"type":"initWS","value":${token ? `"${token}"` : null}}`)
} }
onWorkerMsg = (e: MessageEvent<any>) => { onWorkerMsg = async (e: MessageEvent<any>) => {
const params: { type: string; value: unknown } = JSON.parse(e.data) const params: { type: string; value: unknown } = JSON.parse(e.data)
switch (params.type) { switch (params.type) {
case 'message': { case 'message': {
this.onMessage(params.value as string) await this.onMessage(params.value as string)
break break
} }
case 'open': { case 'open': {
@ -100,7 +100,7 @@ class WS {
} }
// 收到消息回调 // 收到消息回调
onMessage = (value: string) => { onMessage = async (value: string) => {
// FIXME 可能需要 try catch, // FIXME 可能需要 try catch,
const params: { type: WsResponseMessageType; data: unknown } = JSON.parse(value) const params: { type: WsResponseMessageType; data: unknown } = JSON.parse(value)
const loginStore = useWsLoginStore() const loginStore = useWsLoginStore()
@ -148,14 +148,14 @@ class WS {
} }
]) ])
// 获取用户详情 // 获取用户详情
chatStore.getSessionList(true) await chatStore.getSessionList(true)
// 自定义表情列表 // 自定义表情列表
emojiStore.getEmojiList() await emojiStore.getEmojiList()
break break
} }
// 收到消息 // 收到消息
case WsResponseMessageType.ReceiveMessage: { case WsResponseMessageType.ReceiveMessage: {
chatStore.pushMsg(params.data as MessageType) await chatStore.pushMsg(params.data as MessageType)
Mitt.emit(MittEnum.SEND_MESSAGE, params.data) Mitt.emit(MittEnum.SEND_MESSAGE, params.data)
break break
} }

View File

@ -1,6 +1,6 @@
import { ref } from 'vue' import { ref } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { StoresEnum } from '../enums' import { StoresEnum } from '@/enums'
export const useLoginHistoriesStore = defineStore( export const useLoginHistoriesStore = defineStore(
StoresEnum.LOGIN_HISTORY, StoresEnum.LOGIN_HISTORY,

View File

@ -101,7 +101,7 @@
<n-flex v-if="!isLogining && !isWrongPassword" justify="space-around" align="center" :size="0" class="options"> <n-flex v-if="!isLogining && !isWrongPassword" justify="space-around" align="center" :size="0" class="options">
<p class="text-(14px #fefefe)" @click="isUnlockPage = false">返回</p> <p class="text-(14px #fefefe)" @click="isUnlockPage = false">返回</p>
<p class="text-(14px #fefefe)" @click="logout()">退出登录</p> <p class="text-(14px #fefefe)" @click="logout">退出登录</p>
<p class="text-(14px #fefefe)">忘记密码</p> <p class="text-(14px #fefefe)">忘记密码</p>
<p class="text-(14px #fff)" @click="unlock">进入系统</p> <p class="text-(14px #fff)" @click="unlock">进入系统</p>
</n-flex> </n-flex>

View File

@ -29,15 +29,28 @@
:key="item.uid"> :key="item.uid">
<n-flex align="center" :size="10" class="h-75px pl-6px pr-8px flex-1 truncate"> <n-flex align="center" :size="10" class="h-75px pl-6px pr-8px flex-1 truncate">
<n-avatar <n-avatar
v-if="useUserInfo(item.uid).value.avatar"
round round
bordered bordered
:color="'#fff'"
:size="44" :size="44"
class="grayscale" class="grayscale"
:class="{ 'grayscale-0': item.activeStatus === OnlineEnum.ONLINE }" :class="{ 'grayscale-0': item.activeStatus === OnlineEnum.ONLINE }"
:src="useUserInfo(item.uid).value.avatar" :src="useUserInfo(item.uid).value.avatar"
fallback-src="/logo.png" /> fallback-src="/logo.png" />
<n-avatar
v-else
round
bordered
:color="'#909090'"
:size="44"
class="grayscale"
:class="{ 'grayscale-0': item.activeStatus === OnlineEnum.ONLINE }"
:src="useUserInfo(item.uid).value.avatar"
fallback-src="/logo.png">
{{ useUserInfo(item.uid).value.name?.slice(0, 1) }}
</n-avatar>
<n-flex vertical justify="space-between" class="h-fit flex-1 truncate"> <n-flex vertical justify="space-between" class="h-fit flex-1 truncate">
<span class="text-14px leading-tight flex-1 truncate">{{ <span class="text-14px leading-tight flex-1 truncate">{{
useUserInfo(item.uid).value.name useUserInfo(item.uid).value.name

View File

@ -17,7 +17,11 @@
@dblclick="handleMsgDblclick(item)" @dblclick="handleMsgDblclick(item)"
@select="$event.click(item)"> @select="$event.click(item)">
<n-flex :size="10" align="center" class="h-75px pl-6px pr-8px flex-1"> <n-flex :size="10" align="center" class="h-75px pl-6px pr-8px flex-1">
<n-avatar :color="'#fff'" :size="44" :src="item.avatar" bordered fallback-src="/logo.png" round /> <n-avatar v-if="item.avatar" :size="44" :src="item.avatar" bordered fallback-src="/logo.png" round />
<n-avatar v-else :color="'#909090'" :size="44" :src="item.avatar" bordered fallback-src="/logo.png" round>
{{ item.name.slice(0, 1) }}
</n-avatar>
<n-flex class="h-fit flex-1 truncate" justify="space-between" vertical> <n-flex class="h-fit flex-1 truncate" justify="space-between" vertical>
<n-flex :size="4" align="center" class="flex-1 truncate" justify="space-between"> <n-flex :size="4" align="center" class="flex-1 truncate" justify="space-between">

View File

@ -151,15 +151,21 @@
<script setup lang="ts"> <script setup lang="ts">
import router from '@/router' import router from '@/router'
import { useWindow } from '@/hooks/useWindow.ts' import { useWindow } from '@/hooks/useWindow.ts'
import { delay } from 'lodash-es'
import { lightTheme } from 'naive-ui' import { lightTheme } from 'naive-ui'
import { useSettingStore } from '@/stores/setting.ts' import { useSettingStore } from '@/stores/setting.ts'
import { useLogin } from '@/hooks/useLogin.ts' import { useLogin } from '@/hooks/useLogin.ts'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { useLoginHistoriesStore } from '@/stores/loginHistory.ts' import { useLoginHistoriesStore } from '@/stores/loginHistory.ts'
import apis from '@/services/apis.ts' import apis from '@/services/apis.ts'
import { useUserStore } from '@/stores/user.ts'
import { computedToken } from '@/services/request.ts'
import { useChatStore } from '@/stores/chat.ts'
import { useEmojiStore } from '@/stores/emoji.ts'
const settingStore = useSettingStore() const settingStore = useSettingStore()
const userStore = useUserStore()
const chatStore = useChatStore()
const emojiStore = useEmojiStore()
const loginHistoriesStore = useLoginHistoriesStore() const loginHistoriesStore = useLoginHistoriesStore()
const { loginHistories } = loginHistoriesStore const { loginHistories } = loginHistoriesStore
const { login } = storeToRefs(settingStore) const { login } = storeToRefs(settingStore)
@ -232,25 +238,48 @@ const normalLogin = async () => {
loading.value = true loading.value = true
apis apis
.login({ ...info.value } as unknown as User) .login({ ...info.value } as unknown as User)
.then((token) => { .then(async (token) => {
loginText.value = '登录成功, 正在跳转' loginText.value = '登录成功, 正在跳转'
delay(async () => { if (interruptLogin.value) return
if (interruptLogin.value) return userStore.isSign = true
login.value.accountInfo.token = token login.value.accountInfo.token = token
// // localStorage.setItem('USER_INFO', JSON.stringify(rest))
// TODO iduiduid localStorage.setItem('TOKEN', token)
const userDetail = await apis.getUserDetail() //
const account = { if (localStorage.getItem('wsLogin')) {
...userDetail, localStorage.removeItem('wsLogin')
uid: (userDetail as any).id, }
token // token.
} computedToken.clear()
loading.value = false computedToken.get()
settingStore.setAccountInfo(account) //
loginHistoriesStore.addLoginHistory(account) const userDetail = await apis.getUserDetail()
// // 线
await openHomeWindow() // groupStore.batchUpdateUserStatus([
}, 1000) // {
// activeStatus: OnlineEnum.ONLINE,
// avatar: rest.avatar,
// lastOptTime: Date.now(),
// name: rest.name,
// uid: rest.uid
// }
// ])
//
await chatStore.getSessionList(true)
//
await emojiStore.getEmojiList()
// TODO iduiduid
const account = {
...userDetail,
uid: (userDetail as any).id,
token
}
loading.value = false
settingStore.setAccountInfo(account)
loginHistoriesStore.addLoginHistory(account)
await setLoginState()
//
await openHomeWindow()
}) })
.catch(() => { .catch(() => {
window.$message.error('登录失败') window.$message.error('登录失败')
@ -268,15 +297,14 @@ const autoLogin = () => {
loading.value = true loading.value = true
// TODO (nyh -> 2024-03-16 12:06:59) // TODO (nyh -> 2024-03-16 12:06:59)
loginText.value = '网络连接中' loginText.value = '网络连接中'
// TODO 退tokencheckToken401
apis apis
.checkToken() .checkToken()
.then(() => { .then(async () => {
window.$message.success('登录成功,正在跳转首页') loginText.value = '登录成功, 正在跳转'
delay(async () => { loading.value = false
loading.value = false await openHomeWindow()
await openHomeWindow() await setLoginState()
await setLoginState()
}, 1000)
}) })
.catch(() => { .catch(() => {
window.$message.error('登录失败') window.$message.error('登录失败')
@ -317,13 +345,6 @@ const enterKey = (e: KeyboardEvent) => {
} }
onMounted(async () => { onMounted(async () => {
//
if (localStorage.getItem('isToQrcode')) {
router.push('/qrCode')
await nextTick(() => {
localStorage.removeItem('isToQrcode')
})
}
await getCurrentWebviewWindow().show() await getCurrentWebviewWindow().show()
// //
if (login.value.autoLogin && login.value.accountInfo.token) { if (login.value.autoLogin && login.value.accountInfo.token) {