✨ feat(system): 新增在线状态
@@ -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
|
After Width: | Height: | Size: 7.6 KiB |
BIN
public/status/bangbangtang@2x.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/status/banzhuan.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/status/bequiet@3x.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
public/status/boring@3x.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
public/status/chigua@2x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/status/chuqulang2.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/status/crush.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/status/eating01@3x.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/status/emonew@2x.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
public/status/fish@2x.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
public/status/fullofyuanqi@3x.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/status/game_3x.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
public/status/ganzuoye.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/status/gototravel.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/status/guodongzhi.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
public/status/guonianhuijia.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
public/status/happytofly@3x.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
public/status/hardtosay@3x.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
public/status/imfine_3x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/status/jinli@2x.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
public/status/luck@2x.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
public/status/meizizi@3x.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
public/status/music@2x.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
public/status/nandehutu.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
public/status/qiuxingdazi.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
public/status/relationship_3x.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
public/status/signal_3x.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
public/status/sleeping_3x.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
public/status/stayup_3x.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
public/status/study_3x.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
public/status/timi_3x.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
public/status/tkong.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
public/status/toohard@3x.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/status/tv_3x.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
public/status/wang_3x.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/status/weather_3x.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
public/status/woxiangkaile.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
public/status/xiadaxue.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
public/status/xiaojindou.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/status/ximao.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
public/status/xinnianhao-xin1.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
public/status/xinnianhao.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
public/status/yiqiyuanmeng.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
public/status/youzaizai@3x.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
public/status/yundongzhong@2x.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -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事件 */
|
||||
|
||||
@@ -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>
|
||||
|
||||
<!-- 消息为为图片类型(不固定宽度和高度), 多张图片时渲染 -->
|
||||
|
||||
@@ -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 放大的时候图个拖动了窗口,窗口会变回原来的大小,但是图标的状态没有改变
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
15
src/stores/onlineStatus.ts
Normal 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
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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: '' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* 修改自定义的关闭和最小化图标的样式 */
|
||||
:deep(.hover-box) {
|
||||
@apply w-28px h24px flex-center hover:bg-#e7e7e7;
|
||||
svg {
|
||||
|
||||
1
src/typings/components.d.ts
vendored
@@ -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']
|
||||
|
||||
65
src/typings/options.d.ts
vendored
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
1
src/typings/stores.d.ts
vendored
@@ -15,6 +15,7 @@ declare namespace STO {
|
||||
accountInfo: {
|
||||
account: string
|
||||
password: string
|
||||
name: string
|
||||
avatar: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -1,5 +1,5 @@
|
||||
/* 侧边栏选项 */
|
||||
const sideOptions = ref<LO.SettingSide[]>([
|
||||
const sideOptions = ref<OPT.L.SettingSide[]>([
|
||||
{
|
||||
url: '/general',
|
||||
label: '通用',
|
||||
|
||||
88
src/views/home-window/onlineStatus/config.ts
Normal 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 }
|
||||
95
src/views/home-window/onlineStatus/index.vue
Normal 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>
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||