feat(system): 新增在线状态

This commit is contained in:
nongyehong
2024-03-17 00:00:50 +08:00
parent c2fb6d7d7a
commit ed7be38a23
67 changed files with 338 additions and 114 deletions

View File

@@ -1,13 +1,13 @@
{
"name": "hula-im-tauri",
"private": true,
"type": "module",
"version": "v1.3.0-beta",
"packageManager": "pnpm@8.15.4",
"engines": {
"node": ">=18.12.0",
"pnpm": ">=8.10.0"
},
"type": "module",
"author": {
"name": "nongyehong",
"email": "2439646234@qq.com",
@@ -26,7 +26,7 @@
"preview": "vite preview",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build",
"tauri:icon": "tauri icon public/logo.png",
"tauri:icon": "tauri icon /logo.png",
"preinstall": "npx only-allow pnpm",
"commit": "git add . && lint-staged && git-cz && conventional-changelog -p cz-config.cjs -i CHANGELOG.md -s -r 0",
"changelog": "conventional-changelog -p cz-config.cjs -i CHANGELOG.md -s -r 0",

BIN
public/status/aiziji@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/status/banzhuan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
public/status/boring@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
public/status/chigua@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
public/status/crush.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
public/status/emonew@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
public/status/fish@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/status/game_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
public/status/ganzuoye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
public/status/imfine_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
public/status/jinli@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
public/status/luck@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
public/status/music@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
public/status/nandehutu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
public/status/signal_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
public/status/stayup_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
public/status/study_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
public/status/timi_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/status/tkong.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
public/status/tv_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
public/status/wang_3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
public/status/xiadaxue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
public/status/ximao.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,7 +1,7 @@
<template>
<!-- 输入框 -->
<ContextMenu class="relative w-full h-100px" @select="$event.click()" :menu="menuList">
<n-scrollbar style="max-height: 100px">
<!-- 输入框 -->
<div
class="message-input"
ref="messageInputDom"
@@ -14,6 +14,10 @@
</n-scrollbar>
</ContextMenu>
<!-- @提及框 -->
<div v-if="ait" class="absolute w-180px h-160px bg-#fff top--130px left-20px rounded-8px">123</div>
<!-- 发送按钮 -->
<n-config-provider :theme="lightTheme">
<n-button-group size="small" class="pr-20px">
<n-button
@@ -36,8 +40,8 @@ import { lightTheme } from 'naive-ui'
import { MsgEnum } from '@/enums'
import Mitt from '@/utils/Bus.ts'
import { createFileOrVideoDom } from '@/utils/CreateDom.ts'
import { RegExp } from '@/utils/RegExp.ts'
const ait = ref(false)
const menuList = ref([
{ label: '剪切', icon: 'screenshot', disabled: true },
{ label: '复制', icon: 'copy', disabled: true },
@@ -70,8 +74,6 @@ const menuList = ref([
const msgInput = ref('')
// 输入框dom元素
const messageInputDom = ref()
// 自定义输入框子节点元素列表
const childNodes = ref<any>([])
/**
* 将指定节点插入到光标位置
@@ -168,16 +170,11 @@ const getMessageContentType = () => {
let hasText = false
let hasImage = false
let hasVideo = false
let hasHyperlink = false
const elements = messageInputDom.value.childNodes
for (let element of elements) {
if (element.nodeType === Node.TEXT_NODE && element.nodeValue.trim() !== '') {
if (RegExp.isHyperlink(element.nodeValue)) {
hasHyperlink = true
} else {
hasText = true
}
hasText = true
} else if (element.tagName === 'IMG') {
hasImage = true
} else if (element.tagName === 'VI DEO' || (element.tagName === 'A' && element.href.match(/\.(mp4|webm)$/i))) {
@@ -191,8 +188,6 @@ const getMessageContentType = () => {
return MsgEnum.MIXED
} else if (hasImage) {
return MsgEnum.IMAGE
} else if (hasHyperlink) {
return MsgEnum.HYPERLINK
} else {
return MsgEnum.TEXT
}
@@ -204,7 +199,17 @@ const send = () => {
const contentType = getMessageContentType()
const msg = {
type: contentType,
content: msgInput.value
content: msgInput.value,
hyperlinks: [] as any
}
const hyperlinkRegex = /(\bhttps?:\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])/gi
const foundHyperlinks = msg.content.match(hyperlinkRegex)
if (foundHyperlinks && foundHyperlinks.length > 0) {
msg.hyperlinks = foundHyperlinks
msg.content = msg.content.replace(hyperlinkRegex, (match) => {
return `<a class="color-inherit" href="${match}" target="_blank" rel="noopener noreferrer">${match}</a>`
})
}
// 判断文本信息是否超过限制
if (msg.type === MsgEnum.TEXT && msg.content.length > 2000) {
@@ -223,8 +228,8 @@ const send = () => {
/* 当输入框手动输入值的时候触发input事件 */
const handleInput = (e: Event) => {
childNodes.value = (e.target as HTMLInputElement).childNodes
msgInput.value = (e.target as HTMLInputElement).innerHTML
ait.value = msgInput.value.endsWith('@')
}
/* input的keydown事件 */

View File

@@ -43,25 +43,13 @@
<!-- 消息为文本类型 -->
<div
v-if="item.type === MsgEnum.TEXT"
:class="[
{ active: activeBubble === item.key },
activeItem.type === RoomTypeEnum.GROUP ? '' : 'm-[10px_0]',
item.accountId === userId ? 'bubble-oneself' : 'bubble'
]"
v-html="item.content"></div>
<!-- 消息为超链接时 -->
<div
v-if="item.type === MsgEnum.HYPERLINK"
style="white-space: pre-wrap"
:class="[
{ active: activeBubble === item.key },
activeItem.type === RoomTypeEnum.GROUP ? '' : 'm-[10px_0]',
item.accountId === userId ? 'bubble-oneself' : 'bubble'
]">
<!-- rel="noopener noreferrer" 让网站安全跳转防止钓鱼网站 -->
<a class="color-inherit" target="_blank" rel="noopener noreferrer" :href="item.content">
{{ item.content }}
</a>
<span v-html="item.content"></span>
</div>
<!-- 消息为为图片类型(不固定宽度和高度), 多张图片时渲染 -->

View File

@@ -52,6 +52,8 @@ import { appWindow } from '@tauri-apps/api/window'
import Mitt from '@/utils/Bus'
import { useWindow } from '@/hooks/useWindow.ts'
import { alwaysOnTop } from '@/stores/alwaysOnTop.ts'
import { listen } from '@tauri-apps/api/event'
import { EventEnum } from '@/enums'
/**
* 新版defineProps可以直接结构 { minW, maxW, closeW } 如果需要使用默认值withDefaults的时候使用新版解构方式会报错
@@ -90,6 +92,12 @@ watchEffect(() => {
if (alwaysOnTopStatus.value) {
appWindow.setAlwaysOnTop(alwaysOnTopStatus.value as boolean)
}
listen(EventEnum.LOGOUT, async () => {
/* 退出账号前把窗口全部关闭 */
if (appWindow.label !== 'login') {
await appWindow.close()
}
})
})
// todo 放大的时候图个拖动了窗口,窗口会变回原来的大小,但是图标的状态没有改变

View File

@@ -32,7 +32,9 @@ export enum EventEnum {
/** 窗口关闭 */
WIN_CLOSE = 'winClose',
/** 窗口显示 */
WIN_SHOW = 'winShow'
WIN_SHOW = 'winShow',
/** 退出账号 */
LOGOUT = 'logout'
}
/** 主题类型 */
@@ -50,7 +52,9 @@ export enum StoresEnum {
/* 置顶 */
ALWAYS_ON_TOP = 'alwaysOnTop',
/* 设置 */
SETTING = 'setting'
SETTING = 'setting',
/* 在线状态 */
ONLINE_STATUS = 'onlineStatus'
}
/**

View File

@@ -1,7 +1,10 @@
import { useWindow } from '@/hooks/useWindow.ts'
import { emit } from '@tauri-apps/api/event'
import { EventEnum } from '@/enums'
import { delay } from 'lodash-es'
const { createWebviewWindow } = useWindow()
const itemsTop = ref<LO.Top[]>([
const itemsTop = ref<OPT.L.Top[]>([
{
url: 'message',
icon: 'message',
@@ -18,7 +21,7 @@ const itemsTop = ref<LO.Top[]>([
iconAction: 'fire-action'
}
])
const itemsBottom: LO.Bottom[] = [
const itemsBottom: OPT.L.Bottom[] = [
{
title: '邮件',
url: '/mail',
@@ -42,7 +45,7 @@ const itemsBottom: LO.Bottom[] = [
}
]
/* 设置列表菜单项 */
const moreList = ref<LO.MoreList[]>([
const moreList = ref<OPT.L.MoreList[]>([
{
label: '检查更新',
icon: 'arrow-circle-up',
@@ -72,8 +75,13 @@ const moreList = ref<LO.MoreList[]>([
icon: 'power',
click: async () => {
// todo 退出账号 需要关闭其他的全部窗口
// 1.需要退出账号
await createWebviewWindow('登录', 'login', 320, 448, 'home', true, false, 320, 448)
await createWebviewWindow('登录', 'login', 320, 448, 'home', true, false, 320, 448).then(() => {
/* 给一点延迟,不然创建登录窗口后还没有来得及设置阴影和圆角效果 */
delay(async () => {
/* 通知全部打开的窗口然后关闭 */
await emit(EventEnum.LOGOUT)
}, 300)
})
}
}
])

View File

@@ -8,9 +8,9 @@
<img class="rounded-50% wh-full bg-#fff" :src="'https://picsum.photos/140'" alt="" />
<div
class="bg-[--bg-avatar] text-10px rounded-50% w-10px h-10px absolute bottom-0 right-0"
class="bg-[--bg-avatar] text-10px rounded-50% w-12px h-12px absolute bottom--2px right--2px"
style="border: 2px solid var(--bg-avatar)">
<div class="rounded-50% bg-#059669 wh-full"></div>
<img class="rounded-50% wh-full" :src="url" alt="" />
</div>
</div>
</template>
@@ -25,13 +25,13 @@
<span class="text-18px">用户名</span>
<span class="text-12px text-#909090">账号 763868126381</span>
<n-flex
@click="openContent('在线状态', 'onlineStatus', 360, 480)"
@click="openContent('在线状态', 'onlineStatus', 320, 480)"
:size="5"
align="center"
style="margin-left: -4px"
class="item-hover">
<div class="rounded-50% bg-#059669 w-12px h-12px"></div>
<span>在线</span>
<img class="rounded-50% w-18px h-18px" :src="url" alt="" />
<span>{{ title }}</span>
</n-flex>
</n-flex>
</n-space>
@@ -42,12 +42,12 @@
</n-flex>
</n-flex>
<!-- 地址 -->
<n-flex :size="26">
<n-flex :size="26" class="select-none">
<span class="text-#707070">所在地</span>
<span>中国</span>
</n-flex>
<!-- 动态 -->
<n-flex :size="40">
<n-flex :size="40" class="select-none">
<span class="text-#707070">动态</span>
<n-image-group>
<n-space :size="6">
@@ -130,6 +130,8 @@ import Mitt from '@/utils/Bus.ts'
import { EventEnum } from '@/enums'
import { listen } from '@tauri-apps/api/event'
import { itemsTop, itemsBottom, moreList } from './config.ts'
import { onlineStatus } from '@/stores/onlineStatus.ts'
import { storeToRefs } from 'pinia'
/*当前选中的元素 默认选中itemsTop的第一项*/
const activeItem = ref<string>(itemsTop.value[0].url)
@@ -138,6 +140,8 @@ const infoShow = ref(false)
/* 已打开窗口的列表 */
const openWindowsList = ref(new Set())
const { createWebviewWindow } = useWindow()
const OLStatusStore = onlineStatus()
const { url, title } = storeToRefs(OLStatusStore)
watchEffect(async () => {
Mitt.on('updateMsgTotal', (event) => {

View File

@@ -1,6 +1,6 @@
import Dynamic from '@/views/home-window/Dynamic.vue'
import Mail from '@/views/home-window/Mail.vue'
import About from '@/views/home-window/more/About.vue'
import OnlineStatus from '@/views/home-window/OnlineStatus.vue'
import OnlineStatus from '@/views/home-window/onlineStatus/index.vue'
export { Dynamic, Mail, About, OnlineStatus }

View File

@@ -0,0 +1,15 @@
import { defineStore } from 'pinia'
import { StoresEnum } from '@/enums'
export const onlineStatus = defineStore(StoresEnum.ONLINE_STATUS, {
state: () => ({
url: '',
title: ''
}),
actions: {
setOnlineStatus(url: string, title: string) {
this.url = url
this.title = title
}
}
})

View File

@@ -16,6 +16,7 @@ export const setting = defineStore(StoresEnum.SETTING, {
accountInfo: {
account: '',
password: '',
name: '',
avatar: ''
}
}
@@ -46,18 +47,12 @@ export const setting = defineStore(StoresEnum.SETTING, {
this.login.autoStartup = autoStartup
},
/* 设置用户保存的登录信息 */
// TODO 用户登录时候保存登录信息到sessionStorage中然后判断用户是否开启自动登录或者记住登录 (nyh -> 2024-03-15 09:33:30)
setAccountInfo(accountInfo: STO.Setting['login']['accountInfo']) {
const { account, password, avatar } = accountInfo
this.login.accountInfo.account = account
this.login.accountInfo.password = password
this.login.accountInfo.avatar = avatar
this.login.accountInfo = accountInfo
},
/* 清空账号信息 */
clearAccount() {
this.login.accountInfo.account = ''
this.login.accountInfo.password = ''
this.login.accountInfo.avatar = ''
this.login.accountInfo = { account: '', avatar: '', name: '', password: '' }
}
}
})

View File

@@ -127,10 +127,10 @@ html[data-theme='dark'] {
// 气泡闪烁动画
@keyframes bubble-twinkle {
0% {
transform: scale(0.4);
transform: scale(0.1);
}
50% {
transform: scale(1.1);
transform: scale(1.01);
}
100% {
transform: scale(1);

View File

@@ -1,3 +1,4 @@
/* 修改自定义的关闭和最小化图标的样式 */
:deep(.hover-box) {
@apply w-28px h24px flex-center hover:bg-#e7e7e7;
svg {

View File

@@ -29,6 +29,7 @@ declare module 'vue' {
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
NDropdown: typeof import('naive-ui')['NDropdown']
NEllipsis: typeof import('naive-ui')['NEllipsis']
NFlex: typeof import('naive-ui')['NFlex']
NIcon: typeof import('naive-ui')['NIcon']
NIconWrapper: typeof import('naive-ui')['NIconWrapper']

View File

@@ -1,33 +1,42 @@
/* 主页左侧选项 */
declare namespace LO {
/* 顶部的选项 */
type Top = {
url: string
icon: string
iconAction?: string
badge?: number
/* 选项命名空间 */
declare namespace OPT {
/* 主页左侧选项 */
namespace L {
/* 顶部的选项 */
type Top = {
url: string
icon: string
iconAction?: string
badge?: number
}
/* 底部的选项 */
type Bottom = {
title: string
url: string
label: string
icon: string
iconAction?: string
}
/* 更多的选项 */
type MoreList = {
label: string
icon: string
click: () => void
}
/* 设置页面的侧边栏选项 */
type SettingSide = {
url: string
label: string
icon: string
}
}
/* 底部的选项 */
type Bottom = {
/* 在线状态 */
type Online = {
url: string
title: string
url: string
label: string
icon: string
iconAction?: string
}
/* 更多的选项 */
type MoreList = {
label: string
icon: string
click: () => void
}
/* 设置页面的侧边栏选项 */
type SettingSide = {
url: string
label: string
icon: string
}
}

View File

@@ -15,6 +15,7 @@ declare namespace STO {
accountInfo: {
account: string
password: string
name: string
avatar: string
}
}

View File

@@ -1,8 +0,0 @@
<template>
<div class="wh-full bg-[--right-bg-color]">
<ActionBar :shrink="false" :max-w="false" :min-w="false" />
<n-alert title="公告" type="warning"> 此功能有待开发中请联系开发者 </n-alert>
</div>
</template>
<script setup lang="ts"></script>

View File

@@ -1,5 +1,5 @@
/* 侧边栏选项 */
const sideOptions = ref<LO.SettingSide[]>([
const sideOptions = ref<OPT.L.SettingSide[]>([
{
url: '/general',
label: '通用',

View File

@@ -0,0 +1,88 @@
const statusItem: OPT.Online[] = [
{
url: '/status/weather_3x.png',
title: '今日天气'
},
{
url: '/status/hardtosay@3x.png',
title: '一言难尽'
},
{
url: '/status/toohard@3x.png',
title: '我太难了'
},
{
url: '/status/nandehutu.png',
title: '难得糊涂'
},
{
url: '/status/fullofyuanqi@3x.png',
title: '元气满满'
},
{
url: '/status/happytofly@3x.png',
title: '嗨到飞起'
},
{
url: '/status/luck@2x.png',
title: '水逆退散'
},
{
url: '/status/jinli@2x.png',
title: '好运锦鲤'
},
{
url: '/status/relationship_3x.png',
title: '恋爱中'
},
{
url: '/status/crush.png',
title: '我crush了'
},
{
url: '/status/tkong.png',
title: '被掏空'
},
{
url: '/status/music@2x.png',
title: '听歌中'
},
{
url: '/status/imfine_3x.png',
title: '我没事'
},
{
url: '/status/study_3x.png',
title: '学习中'
},
{
url: '/status/sleeping_3x.png',
title: '睡觉中'
},
{
url: '/status/banzhuan.png',
title: '搬砖中'
},
{
url: '/status/bequiet@3x.png',
title: '想静静'
},
{
url: '/status/yundongzhong@2x.png',
title: '运动中'
},
{
url: '/status/woxiangkaile.png',
title: '我想开了'
},
{
url: '/status/signal_3x.png',
title: '信号弱'
},
{
url: '/status/tv_3x.png',
title: '追剧中'
}
]
export { statusItem }

View File

@@ -0,0 +1,95 @@
<template>
<main class="wh-full bg-#fff select-none">
<ActionBar class="absolute right-0 w-full" :shrink="false" :max-w="false" :min-w="false" />
<n-space
vertical
:size="130"
style="background: linear-gradient(to bottom, rgba(82, 174, 163, 0.4) 0%, #f1f1f1 100%)"
class="wh-full p-20px box-border">
<!-- 当前选中的状态 -->
<n-flex justify="center" align="center" class="pt-80px">
<img class="w-34px h-34px" :src="activeItem.url" alt="" />
<span class="text-22px">{{ activeItem.title }}</span>
</n-flex>
<!-- 状态 -->
<n-space vertical class="w-full h-100vh bg-#f1f1f1 rounded-6px box-border p-12px">
<n-scrollbar style="max-height: 255px">
<n-flex align="center" justify="space-between" :size="10">
<n-space
@click="handleActive(item, index)"
:class="{ active: activeItem.index === index }"
v-for="(item, index) in statusItem"
:key="item.title"
vertical
justify="center"
align="center"
:size="2"
class="status-item">
<img class="w-24px h-24px" :src="item.url" alt="" />
<span class="text-11px">{{ item.title }}</span>
</n-space>
</n-flex>
</n-scrollbar>
</n-space>
</n-space>
</main>
</template>
<script setup lang="ts">
import { statusItem } from './config.ts'
import { onlineStatus } from '@/stores/onlineStatus.ts'
const OLStatusStore = onlineStatus()
/* 选中的状态 */
const activeItem = reactive({
index: -1,
title: '',
url: ''
})
/**
* 处理选中的状态
* @param { OPT.Online } item 状态
* @param index 选中的下标
*/
const handleActive = (item: OPT.Online, index: number) => {
activeItem.index = index
activeItem.title = item.title
activeItem.url = item.url
OLStatusStore.setOnlineStatus(item.url, item.title)
}
</script>
<style scoped lang="scss">
.status-item {
width: 56px;
height: 56px;
&:not(.active):hover {
background: #ccc;
border-radius: 8px;
cursor: pointer;
}
}
.active {
background: var(--bg-active-msg);
border-radius: 8px;
cursor: pointer;
span {
color: #fff;
}
}
:deep(.action-close) {
svg {
color: #404040;
}
}
/* 隐藏naive UI的滚动条 */
:deep(
.n-scrollbar > .n-scrollbar-rail.n-scrollbar-rail--vertical > .n-scrollbar-rail__scrollbar,
.n-scrollbar + .n-scrollbar-rail.n-scrollbar-rail--vertical > .n-scrollbar-rail__scrollbar
) {
display: none;
}
</style>

View File

@@ -12,7 +12,7 @@
<img
style="border: 2px solid #fff"
class="w-80px h-80px rounded-50% bg-#fff"
:src="avatarRef || 'public/logo.png'"
:src="avatarRef || '/logo.png'"
alt="" />
</n-flex>
@@ -105,10 +105,13 @@
<img
style="border: 2px solid #fff"
class="w-110px h-110px rounded-50% bg-#fff"
:src="login.accountInfo.avatar || 'public/logo.png'"
:src="login.accountInfo.avatar || '/logo.png'"
alt="" />
</n-flex>
<n-flex justify="center" class="text-18px">{{ login.accountInfo.account }}</n-flex>
<n-flex justify="center">
<n-ellipsis style="max-width: 200px" class="text-18px">{{ login.accountInfo.name }}</n-ellipsis>
</n-flex>
</n-space>
<n-flex justify="center">
@@ -147,42 +150,41 @@ import { lightTheme } from 'naive-ui'
import { setting } from '@/stores/setting.ts'
import { storeToRefs } from 'pinia'
type Account = {
account: string
password: string
avatar?: string
}
const settingStore = setting()
const { login } = storeToRefs(settingStore)
const accountRef = ref()
const passwordRef = ref()
const avatarRef = ref()
const nameRef = ref()
const protocol = ref()
const loginDisabled = ref(false)
const loading = ref(false)
const arrowStatus = ref(false)
const isAutoLogin = ref(false)
/* todo 模拟账号列表 */
const accountOption = ref<Account[]>([
const accountOption = ref<STO.Setting['login']['accountInfo'][]>([
{
account: 'hula',
password: '123456',
name: '超级GG帮',
avatar: 'https://picsum.photos/140?1'
},
{
account: 'hula1',
password: '123456',
name: '二狗子',
avatar: 'https://picsum.photos/140?2'
},
{
account: 'hula2',
password: '123456',
name: '李山离',
avatar: 'https://picsum.photos/140?3'
},
{
account: 'hula3',
password: '123456',
name: '牛什么呢',
avatar: 'https://picsum.photos/140?4'
}
])
@@ -196,7 +198,7 @@ watchEffect(() => {
loginDisabled.value = !(accountRef.value && passwordRef.value && protocol.value)
// 情况账号的时候设置默认头像
if (!accountRef.value) {
avatarRef.value = 'public/logo.png'
avatarRef.value = '/logo.png'
}
})
@@ -213,31 +215,35 @@ const delAccount = (index: number) => {
}
accountRef.value = null
passwordRef.value = null
avatarRef.value = 'public/logo.png'
avatarRef.value = '/logo.png'
}
/**
* 给账号赋值
* @param { Account } item 账户信息
* @param item 账户信息
* */
const giveAccount = (item: Account) => {
const { account, password, avatar } = item
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
arrowStatus.value = false
}
/*登录后创建主页窗口*/
const loginWin = () => {
loading.value = true
loginText.value = '网络连接中'
delay(async () => {
await createWebviewWindow('HuLa', 'home', 960, 720, 'login', false, true)
loading.value = false
loginText.value = '登录'
if (!login.value.autoLogin || login.value.accountInfo.password === '') {
settingStore.setAccountInfo({ account: accountRef.value, password: passwordRef.value, avatar: avatarRef.value })
settingStore.setAccountInfo({
account: accountRef.value,
password: passwordRef.value,
avatar: avatarRef.value,
name: nameRef.value
})
}
}, 1000)
}
@@ -253,7 +259,12 @@ const handleClickOutside = (event: MouseEvent) => {
onMounted(() => {
if (login.value.autoLogin && login.value.accountInfo.password !== '') {
isAutoLogin.value = true
loginWin()
// TODO 检查用户网络是否连接 (nyh -> 2024-03-16 12:06:59)
loginText.value = '网络连接中'
delay(() => {
loginWin()
loginText.value = '登录'
}, 1000)
}
window.addEventListener('click', handleClickOutside, true)
})

View File

@@ -36,10 +36,9 @@ export default defineConfig(({ mode }: ConfigEnv) => {
plugins: [
/**
* !实验性功能
* 开启defineModel
* 开启defineProps解构语法
* */
vue({ script: { propsDestructure: true, defineModel: true } }),
vue({ script: { propsDestructure: true } }),
vueJsx(), // 开启jsx功能
unocss(), // 开启unocss
AutoImport({
@@ -77,7 +76,7 @@ export default defineConfig(({ mode }: ConfigEnv) => {
// 最小化拆分包
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString()
return 'invariable'
}
}
}