mirror of
https://gitee.com/HuLaSpark/HuLa.git
synced 2024-11-29 18:28:30 +08:00
perf(views): ⚡ 优化页面收缩功能
优化连接错误提示 优化自动登录时可中断登录
This commit is contained in:
parent
94d2cb1fec
commit
31f7e1732c
@ -7,7 +7,7 @@
|
||||
<title>HuLa</title>
|
||||
|
||||
<!--引入iconpark图标库-->
|
||||
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_78.2ef5ae05e210de3f66b0fe5c58a7a130.js"></script>
|
||||
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_79.b97eb7e69b543ed2cec6ad36e086c40b.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -24,10 +24,7 @@
|
||||
class="flex flex-col w-full"
|
||||
:class="[{ 'items-end': item.accountId === userId }, isGroup ? 'gap-18px' : 'gap-2px']">
|
||||
<!-- 信息时间(单聊) -->
|
||||
<div
|
||||
v-if="!isGroup"
|
||||
class="text-(12px #909090) h-12px w-fit select-none"
|
||||
:class="item.accountId === userId ? 'pr-42px' : 'pl-42px'">
|
||||
<div v-if="!isGroup" class="text-(12px #909090) h-12px w-fit select-none" :class="getTimePosition(item)">
|
||||
<Transition name="fade">
|
||||
<span v-if="hoverBubble.key === item.key">
|
||||
{{ new Date().toLocaleString() }}
|
||||
@ -348,6 +345,12 @@ watchEffect(() => {
|
||||
activeItemRef.value = { ...activeItem }
|
||||
})
|
||||
|
||||
/** 计算单聊时间戳显示的位置 */
|
||||
const getTimePosition = (item: any) => {
|
||||
const pxVal = activeReply.value === item.key ? '68px' : '42px'
|
||||
return item.accountId === userId.value ? `pr-${pxVal}` : `pl-${pxVal}`
|
||||
}
|
||||
|
||||
// 当鼠标进入时触发的处理函数
|
||||
const handleMouseEnter = (key: any) => {
|
||||
// 设置定时器,在1600毫秒后更新悬浮气泡的key值
|
||||
@ -542,13 +545,4 @@ onUnmounted(() => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/scss/chat-main';
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<main data-tauri-drag-region class="resizable select-none" :style="{ width: width + 'px' }">
|
||||
<main
|
||||
data-tauri-drag-region
|
||||
id="center"
|
||||
class="resizable select-none flex flex-col"
|
||||
:style="{ width: `${initWidth}px` }">
|
||||
<div class="resize-handle" @mousedown="initDrag"></div>
|
||||
<ActionBar
|
||||
class="absolute right-0"
|
||||
v-if="shrinkStatus"
|
||||
@ -48,20 +53,47 @@ import router from '@/router'
|
||||
import { MittEnum } from '@/enums'
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
|
||||
// const minWidth = 160 // 设置最小宽度
|
||||
// const maxWidth = 320 // 设置最大宽度
|
||||
const width = ref(250) // 初始化宽度
|
||||
/** 设置最小宽度 */
|
||||
const minWidth = 160
|
||||
/** 设置最大宽度 */
|
||||
const maxWidth = 300
|
||||
/** 初始化宽度 */
|
||||
const initWidth = ref(250)
|
||||
/** 窗口宽度 */
|
||||
const windowWidth = ref(0)
|
||||
/** 是否拖拽 */
|
||||
const isDrag = ref(true)
|
||||
/** 当前消息 */
|
||||
const currentMsg = ref()
|
||||
|
||||
// const startX = ref()
|
||||
// const startWidth = ref()
|
||||
const startX = ref()
|
||||
const startWidth = ref()
|
||||
const shrinkStatus = ref(false)
|
||||
|
||||
// todo 1.了解这里是怎么实现的 2.修改拖拽放大缩小的事件
|
||||
Mitt.on(MittEnum.SHRINK_WINDOW, (event) => {
|
||||
shrinkStatus.value = event as boolean
|
||||
width.value = 250
|
||||
watchEffect(() => {
|
||||
if (windowWidth.value >= 310 && windowWidth.value < 800) {
|
||||
Mitt.emit(MittEnum.SHRINK_WINDOW, true)
|
||||
const center = document.querySelector('#center')
|
||||
center?.classList.add('flex-1')
|
||||
isDrag.value = false
|
||||
}
|
||||
if (windowWidth.value >= 800) {
|
||||
Mitt.emit(MittEnum.SHRINK_WINDOW, false)
|
||||
if (currentMsg.value) {
|
||||
Mitt.emit(MittEnum.MSG_BOX_SHOW, { msgBoxShow: true, ...currentMsg.value })
|
||||
}
|
||||
const center = document.querySelector('#center')
|
||||
center?.classList.remove('flex-1')
|
||||
isDrag.value = true
|
||||
}
|
||||
})
|
||||
|
||||
/** 更新窗口宽度 */
|
||||
const updateWindowWidth = async () => {
|
||||
const { width } = await appWindow.innerSize()
|
||||
windowWidth.value = width
|
||||
}
|
||||
|
||||
const closeMenu = (event: Event) => {
|
||||
const e = event.target as HTMLInputElement
|
||||
const route = router.currentRoute.value.path
|
||||
@ -71,42 +103,54 @@ const closeMenu = (event: Event) => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
/** 定义一个函数,在鼠标拖动时调用 */
|
||||
const doDrag = (e: MouseEvent) => {
|
||||
// 使用 requestAnimationFrame 来处理动画,确保动画在下一帧渲染前执行
|
||||
requestAnimationFrame(() => {
|
||||
// 计算新的宽度
|
||||
const newWidth = startWidth.value + e.clientX - startX.value
|
||||
// 如果新宽度不等于最大宽度,则更新宽度值
|
||||
if (newWidth !== maxWidth) {
|
||||
initWidth.value = clamp(newWidth, minWidth, maxWidth) // 使用 clamp 函数限制宽度值在最小值和最大值之间
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 定义一个函数,用于将数值限制在指定的最小值和最大值之间 */
|
||||
const clamp = (value: number, min: number, max: number) => {
|
||||
return Math.min(Math.max(value, min), max) // 使用 Math.min 和 Math.max 函数来限制数值范围
|
||||
}
|
||||
|
||||
const initDrag = (e: MouseEvent) => {
|
||||
if (!isDrag.value) return
|
||||
startX.value = e.clientX
|
||||
startWidth.value = initWidth.value
|
||||
document.addEventListener('mousemove', doDrag, false)
|
||||
document.addEventListener('mouseup', stopDrag, false)
|
||||
}
|
||||
|
||||
const stopDrag = () => {
|
||||
document.removeEventListener('mousemove', doDrag, false)
|
||||
document.removeEventListener('mouseup', stopDrag, false)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await updateWindowWidth()
|
||||
Mitt.on(MittEnum.SHRINK_WINDOW, (event) => {
|
||||
shrinkStatus.value = event as boolean
|
||||
})
|
||||
Mitt.on(MittEnum.MSG_BOX_SHOW, (event: any) => {
|
||||
if (!event) return
|
||||
currentMsg.value = event
|
||||
})
|
||||
window.addEventListener('resize', updateWindowWidth)
|
||||
window.addEventListener('click', closeMenu, true)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateWindowWidth)
|
||||
window.removeEventListener('click', closeMenu, true)
|
||||
})
|
||||
// watchEffect(() => {
|
||||
// if (width.value === maxWidth) {
|
||||
// Mitt.emit('shrinkWindow', false)
|
||||
// }
|
||||
// })
|
||||
|
||||
// const initDrag = (e: MouseEvent) => {
|
||||
// startX.value = e.clientX
|
||||
// startWidth.value = width.value
|
||||
//
|
||||
// document.addEventListener('mousemove', doDrag, false)
|
||||
// document.addEventListener('mouseup', stopDrag, false)
|
||||
// }
|
||||
//
|
||||
// const doDrag = (e: MouseEvent) => {
|
||||
// const newWidth = startWidth.value + e.clientX - startX.value
|
||||
// if (newWidth <= maxWidth && newWidth >= minWidth) {
|
||||
// width.value = newWidth
|
||||
// } else if (newWidth > maxWidth) {
|
||||
// width.value = maxWidth
|
||||
// } else if (newWidth < minWidth) {
|
||||
// width.value = minWidth
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const stopDrag = () => {
|
||||
// document.removeEventListener('mousemove', doDrag, false)
|
||||
// document.removeEventListener('mouseup', stopDrag, false)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
@ -10,7 +10,8 @@
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
width: 1px;
|
||||
cursor: ew-resize;
|
||||
background-color: #ccc; /** 可以根据需要更改颜色 */
|
||||
z-index: 9999;
|
||||
background-color: var(--split-color);
|
||||
}
|
@ -13,7 +13,6 @@ import Right from './right/index.vue'
|
||||
import Mitt from '@/utils/Bus'
|
||||
import { MittEnum } from '@/enums'
|
||||
|
||||
/** todo home窗口创建的时候已经设置了resizable: true,可以调整大小了,但是还是不可以调整大小 */
|
||||
const shrinkStatus = ref(false)
|
||||
/**
|
||||
* event默认如果没有传递值就为true,所以shrinkStatus的值为false就会发生值的变化
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<main data-tauri-drag-region class="left w-60px h-full p-[30px_6px_15px] box-border flex-col-center select-none">
|
||||
<main data-tauri-drag-region class="left min-w-60px h-full p-[30px_6px_15px] box-border flex-col-center select-none">
|
||||
<!-- 点击时头像内容框 -->
|
||||
<n-popover
|
||||
v-model:show="infoShow"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<main class="flex-1 bg-[--right-bg-color] h-full w-100vw">
|
||||
<main class="flex-1 bg-[--right-bg-color] h-full w-100vw min-w-600px">
|
||||
<ActionBar :current-label="appWindow.label" />
|
||||
<!-- 需要判断当前路由是否是信息详情界面 -->
|
||||
<ChatBox :active-item="activeItem" v-if="msgBoxShow && isChat && activeItem !== -1" />
|
||||
|
@ -4,13 +4,19 @@ import Mitt from '@/utils/Bus.ts'
|
||||
const { VITE_WEBSOCKET_URL } = import.meta.env
|
||||
/** websocket连接对象 */
|
||||
let ws: WebSocket
|
||||
/** 尝试重新连接数 */
|
||||
let reconnectAttempts = 0
|
||||
/** 最大重连次数 */
|
||||
const maxReconnectAttempts = 5
|
||||
/** 重连间隔 */
|
||||
const reconnectDelay = 3000
|
||||
/** 初始化websocket连接 */
|
||||
const initWebSocket = () => {
|
||||
ws = new WebSocket(`${VITE_WEBSOCKET_URL}/`)
|
||||
ws.onopen = () => {
|
||||
// 发送心跳
|
||||
setInterval(() => {
|
||||
sendToServer({
|
||||
setInterval(async () => {
|
||||
await sendToServer({
|
||||
type: WsReqEnum.HEARTBEAT
|
||||
})
|
||||
}, 1000 * 60)
|
||||
@ -31,10 +37,23 @@ const initWebSocket = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 尝试重新连接 */
|
||||
const retryConnection = () => {
|
||||
setTimeout(() => {
|
||||
if (reconnectAttempts < maxReconnectAttempts) {
|
||||
reconnectAttempts++
|
||||
initWebSocket()
|
||||
} else {
|
||||
console.error('已达到最大重连次数,放弃重连')
|
||||
}
|
||||
}, reconnectDelay)
|
||||
}
|
||||
|
||||
// websocket出错重连
|
||||
ws.onerror = () => {
|
||||
if (ws.readyState !== WebSocket.OPEN) return
|
||||
// websocket出错重连
|
||||
initWebSocket()
|
||||
retryConnection()
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,8 +63,13 @@ const initWebSocket = () => {
|
||||
* @param data 传输的json数据对象
|
||||
*/
|
||||
const sendToServer = (data: Record<string, any>) => {
|
||||
const json = JSON.stringify(data)
|
||||
ws.send(json)
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
const json = JSON.stringify(data)
|
||||
ws.send(json)
|
||||
return Promise.resolve(true)
|
||||
} else {
|
||||
return Promise.reject('网络连接失败,请检查网络设置')
|
||||
}
|
||||
}
|
||||
|
||||
export { initWebSocket, sendToServer }
|
||||
|
@ -68,7 +68,7 @@ export const setting = defineStore(StoresEnum.SETTING, {
|
||||
},
|
||||
/** 清空账号信息 */
|
||||
clearAccount() {
|
||||
this.login.accountInfo = { account: '', avatar: '', name: '', password: '', uid: '' }
|
||||
this.login.accountInfo.password = ''
|
||||
}
|
||||
},
|
||||
share: {
|
||||
|
@ -95,6 +95,15 @@
|
||||
border-radius: 0;
|
||||
transition: all 0.4s ease-in-out;
|
||||
}
|
||||
/** 时间搓显示时候的动画 */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
/**! 替换ait的字体颜色 */
|
||||
:deep(#aitSpan) {
|
||||
@apply text-inherit cursor-pointer;
|
||||
|
@ -59,6 +59,8 @@
|
||||
--reply-color: #909090;
|
||||
--reply-hover: #505050;
|
||||
--bg-reply-img-count: #e3e3e3;
|
||||
// 主页面面板分割线样式
|
||||
--split-color: #f1f1f1;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
@ -121,6 +123,8 @@ html[data-theme='dark'] {
|
||||
--reply-color: #e3e3e3;
|
||||
--reply-hover: #b1b1b1;
|
||||
--bg-reply-img-count: #505050;
|
||||
// 主页面面板分割线样式
|
||||
--split-color: #3b3b3b;
|
||||
}
|
||||
/**! end */
|
||||
// 线性动画
|
||||
|
2
src/typings/components.d.ts
vendored
2
src/typings/components.d.ts
vendored
@ -50,10 +50,10 @@ declare module 'vue' {
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSkeleton: typeof import('naive-ui')['NSkeleton']
|
||||
NSplit: typeof import('naive-ui')['NSplit']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
||||
RenderMessage: typeof import('./../components/rightBox/renderMessage/index.vue')['default']
|
||||
|
@ -18,28 +18,26 @@
|
||||
|
||||
<!-- 用户框 多套一层div来移除默认的右键事件然后覆盖掉因为margin空隙而导致右键可用 -->
|
||||
<div @contextmenu.stop="$event.preventDefault()">
|
||||
<div
|
||||
<n-flex
|
||||
v-slide
|
||||
:size="10"
|
||||
@click="handleClick(item.key, 2)"
|
||||
:class="{ active: activeItem === item.key }"
|
||||
class="user-box w-full h-75px mb-5px"
|
||||
v-for="item in friendsList"
|
||||
:key="item.key">
|
||||
<div class="flex items-center h-full pl-6px pr-8px gap-10px">
|
||||
<n-avatar round bordered :color="'#fff'" :size="44" :src="item.avatar" />
|
||||
<n-flex v-slide align="center" :size="10" class="h-75px pl-6px pr-8px flex-1 truncate">
|
||||
<n-avatar round bordered :color="'#fff'" :size="44" :src="item.avatar" fallback-src="/logo.png" />
|
||||
|
||||
<div class="h-38px flex flex-1 flex-col justify-between">
|
||||
<div class="text-14px flex-y-center gap-4px">
|
||||
{{ item.accountName }}
|
||||
</div>
|
||||
<n-flex vertical justify="space-between" class="h-fit flex-1 truncate">
|
||||
<span class="text-14px leading-tight flex-1 truncate">{{ item.accountName }}</span>
|
||||
|
||||
<div class="text w-155px h-14px text-12px flex-y-center gap-4px">
|
||||
<span class="text-12px">[⛅今日天气]</span>
|
||||
<span class="flex-1 truncate">说的很经典哈萨克的哈萨克看到贺卡上</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text leading-tight text-12px flex-1 truncate">
|
||||
[⛅今日天气] 说的很经典哈萨克的哈萨克看到贺卡上
|
||||
</span>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</div>
|
||||
</n-collapse-item>
|
||||
<n-collapse-item title="默认分组" name="3">
|
||||
@ -57,19 +55,14 @@
|
||||
<div
|
||||
@click="handleClick(item.key, 1)"
|
||||
:class="{ active: activeItem === item.key }"
|
||||
class="w-full h-75px mb-5px cursor-pointer"
|
||||
class="w-full h-75px mb-5px"
|
||||
v-for="item in groupChatList"
|
||||
:key="item.key">
|
||||
<!-- 消息框,使用v-slide自定义指令来自动抉择右键菜单位置 -->
|
||||
<div v-slide class="flex items-center h-full pl-6px pr-8px gap-10px">
|
||||
<n-avatar round bordered :color="'#fff'" :size="44" :src="item.avatar" />
|
||||
<n-flex v-slide align="center" :size="10" class="h-75px pl-6px pr-8px flex-1 truncate">
|
||||
<n-avatar round bordered :color="'#fff'" :size="44" :src="item.avatar" fallback-src="/logo.png" />
|
||||
|
||||
<div class="h-38px flex flex-1 flex-col justify-center">
|
||||
<div class="flex-between-center">
|
||||
<span class="text-14px">{{ item.accountName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-14px leading-tight flex-1 truncate">{{ item.accountName }}</span>
|
||||
</n-flex>
|
||||
</div>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
|
@ -17,17 +17,17 @@
|
||||
v-for="item in MockList"
|
||||
:key="item.key">
|
||||
<!-- 消息框,使用v-slide自定义指令来自动抉择右键菜单位置 -->
|
||||
<n-flex v-slide align="center" :size="10" class="h-75px pl-6px pr-8px">
|
||||
<n-avatar round bordered :color="'#fff'" :size="44" :src="item.avatar" fallback-src="/logo.png" alt="" />
|
||||
<n-flex v-slide align="center" :size="10" class="h-75px pl-6px pr-8px flex-1">
|
||||
<n-avatar round bordered :color="'#fff'" :size="44" :src="item.avatar" fallback-src="/logo.png" />
|
||||
|
||||
<n-flex vertical justify="space-between" class="h-38px flex-1">
|
||||
<n-flex align="center" justify="space-between">
|
||||
<span class="text-14px">{{ item.accountName }}</span>
|
||||
<span class="text text-10px">昨天</span>
|
||||
<n-flex vertical justify="space-between" class="h-fit flex-1 truncate">
|
||||
<n-flex :size="4" align="center" justify="space-between" class="flex-1 truncate">
|
||||
<span class="text-14px leading-tight flex-1 truncate">{{ item.accountName }}</span>
|
||||
<span class="text text-10px w-fit truncate text-right">星期一</span>
|
||||
</n-flex>
|
||||
|
||||
<n-flex align="center" justify="space-between">
|
||||
<span class="text w-135px text-12px truncate"> 说的很经典哈萨克的哈萨克看到贺卡上 </span>
|
||||
<span class="text flex-1 leading-tight text-12px truncate"> 说的很经典哈萨克的哈萨克看到贺卡上 </span>
|
||||
|
||||
<!-- 消息提示 -->
|
||||
<n-badge :value="msgTotal" :max="99" />
|
||||
|
@ -9,7 +9,10 @@
|
||||
<n-flex vertical :size="25" v-if="!isAutoLogin">
|
||||
<!-- 头像 -->
|
||||
<n-flex justify="center" class="w-full mt-35px">
|
||||
<img class="w-80px h-80px rounded-50% bg-#fff border-(2px solid #fff)" :src="avatarRef || '/logo.png'" alt="" />
|
||||
<img
|
||||
class="w-80px h-80px rounded-50% bg-#fff border-(2px solid #fff)"
|
||||
:src="info.avatar || '/logo.png'"
|
||||
alt="" />
|
||||
</n-flex>
|
||||
|
||||
<!-- 登录菜单 -->
|
||||
@ -19,7 +22,7 @@
|
||||
size="large"
|
||||
maxlength="16"
|
||||
minlength="6"
|
||||
v-model:value="accountRef"
|
||||
v-model:value="info.account"
|
||||
type="text"
|
||||
:placeholder="accountPH"
|
||||
@focus="accountPH = ''"
|
||||
@ -62,7 +65,7 @@
|
||||
maxlength="16"
|
||||
minlength="6"
|
||||
size="large"
|
||||
v-model:value="passwordRef"
|
||||
v-model:value="info.password"
|
||||
type="password"
|
||||
:placeholder="passwordPH"
|
||||
@focus="passwordPH = ''"
|
||||
@ -116,18 +119,19 @@
|
||||
:loading="loading"
|
||||
:disabled="loginDisabled"
|
||||
class="w-200px mt-12px mb-40px"
|
||||
@click="loginWin"
|
||||
@click="autoLogin"
|
||||
color="#13987f">
|
||||
{{ loginText }}
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
|
||||
<!-- 顶部操作栏 -->
|
||||
<n-flex justify="center" class="text-14px">
|
||||
<!-- 底部操作栏 -->
|
||||
<n-flex justify="center" class="text-14px" id="bottomBar">
|
||||
<div class="color-#13987f cursor-pointer" @click="router.push('/qrCode')">扫码登录</div>
|
||||
<div class="w-1px h-14px bg-#ccc"></div>
|
||||
<n-popover trigger="click" :show-checkmark="false" :show-arrow="false">
|
||||
<div v-if="isAutoLogin" class="color-#13987f cursor-pointer">移除账号</div>
|
||||
<n-popover v-else trigger="click" :show-checkmark="false" :show-arrow="false">
|
||||
<template #trigger>
|
||||
<div class="color-#13987f cursor-pointer">更多选项</div>
|
||||
</template>
|
||||
@ -151,10 +155,16 @@ import { useLogin } from '@/hooks/useLogin.ts'
|
||||
|
||||
const settingStore = setting()
|
||||
const { login } = storeToRefs(settingStore)
|
||||
const accountRef = ref()
|
||||
const passwordRef = ref()
|
||||
const avatarRef = ref()
|
||||
const nameRef = ref()
|
||||
/** 账号信息 */
|
||||
const info = ref({
|
||||
account: '',
|
||||
password: '',
|
||||
avatar: '',
|
||||
name: ''
|
||||
})
|
||||
/** 是否中断登录 */
|
||||
const interruptLogin = ref(false)
|
||||
/** 协议 */
|
||||
const protocol = ref()
|
||||
const loginDisabled = ref(false)
|
||||
const loading = ref(false)
|
||||
@ -199,10 +209,13 @@ const loginText = ref('登录')
|
||||
const { createWebviewWindow } = useWindow()
|
||||
|
||||
watchEffect(() => {
|
||||
loginDisabled.value = !(accountRef.value && passwordRef.value && protocol.value)
|
||||
loginDisabled.value = !(info.value.account && info.value.password && protocol.value)
|
||||
// 清空账号的时候设置默认头像
|
||||
if (!accountRef.value) {
|
||||
avatarRef.value = '/logo.png'
|
||||
if (!info.value.account) {
|
||||
info.value.avatar = '/logo.png'
|
||||
}
|
||||
if (interruptLogin.value) {
|
||||
loginDisabled.value = false
|
||||
}
|
||||
})
|
||||
|
||||
@ -217,9 +230,9 @@ const delAccount = (index: number) => {
|
||||
if (lengthBeforeDelete === 1 && accountOption.value.length === 0) {
|
||||
arrowStatus.value = false
|
||||
}
|
||||
accountRef.value = null
|
||||
passwordRef.value = null
|
||||
avatarRef.value = '/logo.png'
|
||||
info.value.account = ''
|
||||
info.value.password = ''
|
||||
info.value.avatar = '/logo.png'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,25 +241,26 @@ const delAccount = (index: number) => {
|
||||
* */
|
||||
const giveAccount = (item: STO.Setting['login']['accountInfo']) => {
|
||||
const { account, password, avatar, name } = item
|
||||
accountRef.value = account
|
||||
passwordRef.value = password
|
||||
avatarRef.value = avatar
|
||||
nameRef.value = name
|
||||
info.value.account = account || ''
|
||||
info.value.password = password || ''
|
||||
info.value.avatar = avatar
|
||||
info.value.name = name
|
||||
arrowStatus.value = false
|
||||
}
|
||||
|
||||
/**登录后创建主页窗口*/
|
||||
const loginWin = () => {
|
||||
if (interruptLogin.value) return
|
||||
loading.value = true
|
||||
delay(async () => {
|
||||
await createWebviewWindow('HuLa', 'home', 960, 720, 'login', false, true)
|
||||
loading.value = false
|
||||
if (!login.value.autoLogin || login.value.accountInfo.password === '') {
|
||||
settingStore.setAccountInfo({
|
||||
account: accountRef.value,
|
||||
password: passwordRef.value,
|
||||
avatar: avatarRef.value,
|
||||
name: nameRef.value,
|
||||
account: info.value.account,
|
||||
password: info.value.password,
|
||||
avatar: info.value.avatar,
|
||||
name: info.value.name,
|
||||
uid: '123456'
|
||||
})
|
||||
await setLoginState()
|
||||
@ -254,12 +268,27 @@ const loginWin = () => {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/**监听是否点击了除了下拉框外的其他地方*/
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
/** 自动登录 */
|
||||
const autoLogin = () => {
|
||||
interruptLogin.value = false
|
||||
isAutoLogin.value = true
|
||||
// TODO 检查用户网络是否连接 (nyh -> 2024-03-16 12:06:59)
|
||||
loginText.value = '网络连接中'
|
||||
delay(async () => {
|
||||
loginWin()
|
||||
loginText.value = '登录'
|
||||
await setLoginState()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const closeMenu = (event: MouseEvent) => {
|
||||
const target = event.target as Element
|
||||
if (!target.matches('.account-box, .account-box *, .down')) {
|
||||
arrowStatus.value = false
|
||||
}
|
||||
if (target.matches('#bottomBar *') && isAutoLogin.value) {
|
||||
interruptLogin.value = true
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@ -267,20 +296,13 @@ onMounted(async () => {
|
||||
console.error('设置无状态图标失败:', error)
|
||||
})
|
||||
if (login.value.autoLogin && login.value.accountInfo.password !== '') {
|
||||
isAutoLogin.value = true
|
||||
// TODO 检查用户网络是否连接 (nyh -> 2024-03-16 12:06:59)
|
||||
loginText.value = '网络连接中'
|
||||
delay(async () => {
|
||||
loginWin()
|
||||
loginText.value = '登录'
|
||||
await setLoginState()
|
||||
}, 1000)
|
||||
autoLogin()
|
||||
}
|
||||
window.addEventListener('click', handleClickOutside, true)
|
||||
window.addEventListener('click', closeMenu, true)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('click', handleClickOutside, true)
|
||||
window.removeEventListener('click', closeMenu, true)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -14,18 +14,19 @@
|
||||
v-else
|
||||
:size="180"
|
||||
class="rounded-12px relative"
|
||||
:class="{ blur: scanSuccess }"
|
||||
:class="{ blur: scanStatus.show }"
|
||||
:value="QRCode"
|
||||
icon-src="/logo.png"
|
||||
error-correction-level="H" />
|
||||
<!-- 二维码状态 -->
|
||||
<n-flex
|
||||
v-if="scanSuccess"
|
||||
v-if="scanStatus.show"
|
||||
vertical
|
||||
:size="12"
|
||||
align="center"
|
||||
class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<svg class="size-42px"><use href="#success"></use></svg>
|
||||
<span class="text-(16px #e3e3e3)">扫码成功</span>
|
||||
<svg class="size-42px"><use :href="`#${scanStatus.icon}`"></use></svg>
|
||||
<span class="text-(16px #e3e3e3)">{{ scanStatus.text }}</span>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
|
||||
@ -33,7 +34,7 @@
|
||||
|
||||
<!-- 顶部操作栏 -->
|
||||
<n-flex justify="center" class="text-14px mt-48px">
|
||||
<div class="color-#13987f cursor-pointer" @click="toLogin">账密登录</div>
|
||||
<div class="color-#13987f cursor-pointer" @click="router.push('/login')">账密登录</div>
|
||||
<div class="w-1px h-14px bg-#ccc"></div>
|
||||
<div class="color-#13987f cursor-pointer">注册账号</div>
|
||||
</n-flex>
|
||||
@ -56,34 +57,60 @@ const { createWebviewWindow } = useWindow()
|
||||
const loading = ref(true)
|
||||
const loadText = ref('加载中...')
|
||||
const QRCode = ref()
|
||||
const scanSuccess = ref(false)
|
||||
const scanStatus = ref<{
|
||||
status: 'error' | 'success'
|
||||
icon: 'cloudError' | 'success'
|
||||
text: string
|
||||
show: boolean
|
||||
}>({ status: 'success', icon: 'success', text: '扫码成功', show: false })
|
||||
|
||||
const toLogin = () => {
|
||||
router.push('/login')
|
||||
/** 处理二维码登录 */
|
||||
const handleQRCodeLogin = (e: any) => {
|
||||
QRCode.value = e.data.loginUrl
|
||||
loading.value = false
|
||||
loadText.value = '请使用微信扫码登录'
|
||||
}
|
||||
|
||||
/** 处理登录成功 */
|
||||
const handleLoginSuccess = async (e: any) => {
|
||||
scanStatus.value.show = true
|
||||
loadText.value = '登录中...'
|
||||
delay(async () => {
|
||||
await createWebviewWindow('HuLa', 'home', 960, 720, 'login', false, true)
|
||||
settingStore.setAccountInfo({
|
||||
avatar: e.data.avatar,
|
||||
name: e.data.name,
|
||||
uid: e.data.uid
|
||||
})
|
||||
await setLoginState()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/** 处理失败场景 */
|
||||
const handleError = (e: any) => {
|
||||
loading.value = false
|
||||
scanStatus.value = {
|
||||
status: 'error',
|
||||
icon: 'cloudError',
|
||||
text: e,
|
||||
show: true
|
||||
}
|
||||
loadText.value = '请稍后再试'
|
||||
}
|
||||
|
||||
// TODO 做一个二维码过期时间重新刷新二维码的功能 (nyh -> 2024-01-27 00:37:18)
|
||||
onMounted(() => {
|
||||
initWebSocket()
|
||||
Mitt.on(WsResEnum.QRCODE_LOGIN, (e: any) => {
|
||||
QRCode.value = e.data.loginUrl
|
||||
loading.value = false
|
||||
loadText.value = '请使用微信扫码登录'
|
||||
handleQRCodeLogin(e)
|
||||
})
|
||||
Mitt.on(WsResEnum.LOGIN_SUCCESS, (e: any) => {
|
||||
scanSuccess.value = true
|
||||
loadText.value = '登录中...'
|
||||
delay(async () => {
|
||||
await createWebviewWindow('HuLa', 'home', 960, 720, 'login', false, true)
|
||||
settingStore.setAccountInfo({
|
||||
avatar: e.data.avatar,
|
||||
name: e.data.name,
|
||||
uid: e.data.uid
|
||||
})
|
||||
await setLoginState()
|
||||
}, 1000)
|
||||
handleLoginSuccess(e)
|
||||
})
|
||||
delay(() => {
|
||||
sendToServer({ type: WsReqEnum.LOGIN })
|
||||
sendToServer({ type: WsReqEnum.LOGIN }).catch((e) => {
|
||||
handleError(e)
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user