mirror of
https://gitee.com/HuLaSpark/HuLa.git
synced 2024-11-29 18:28:30 +08:00
🌻 update(custom): 更新侧边栏功能显示
This commit is contained in:
parent
196d2d12a8
commit
acc01945d4
@ -7,7 +7,7 @@
|
||||
<title>Tauri + Vue + TS</title>
|
||||
|
||||
<!--引入iconpark图标库-->
|
||||
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_61.f6c25d2ccb13bf96747417c789d6c0f4.js"></script>
|
||||
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_63.de195ae6484b5e9104a9599e1be84393.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "hula-im-tauri",
|
||||
"private": true,
|
||||
"version": "v1.0.0-alpha",
|
||||
"version": "v1.2.8-alpha",
|
||||
"packageManager": "pnpm@8.14.1",
|
||||
"engines": {
|
||||
"node": ">=18.12.0",
|
||||
@ -47,6 +47,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.23.3",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@tauri-apps/cli": "^1.5.7",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.10.4",
|
||||
|
@ -43,6 +43,9 @@ devDependencies:
|
||||
'@babel/eslint-parser':
|
||||
specifier: ^7.23.3
|
||||
version: 7.23.9(@babel/core@7.24.0)(eslint@8.56.0)
|
||||
'@rollup/plugin-terser':
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4
|
||||
'@tauri-apps/cli':
|
||||
specifier: ^1.5.7
|
||||
version: 1.5.9
|
||||
@ -1079,6 +1082,20 @@ packages:
|
||||
resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==}
|
||||
dev: true
|
||||
|
||||
/@rollup/plugin-terser@0.4.4:
|
||||
resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^2.0.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
dependencies:
|
||||
serialize-javascript: 6.0.2
|
||||
smob: 1.4.1
|
||||
terser: 5.28.1
|
||||
dev: true
|
||||
|
||||
/@rollup/pluginutils@5.1.0:
|
||||
resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -5210,6 +5227,10 @@ packages:
|
||||
is-fullwidth-code-point: 5.0.0
|
||||
dev: true
|
||||
|
||||
/smob@1.4.1:
|
||||
resolution: {integrity: sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==}
|
||||
dev: true
|
||||
|
||||
/source-map-js@1.0.2:
|
||||
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -7,7 +7,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "HuLa-IM-Tauri",
|
||||
"version": "0.0.0"
|
||||
"version": "v1.2.8-alpha"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
16
src/App.vue
16
src/App.vue
@ -27,14 +27,14 @@ onMounted(() => {
|
||||
}
|
||||
document.documentElement.dataset.theme = THEME.value
|
||||
window.addEventListener('dragstart', preventDrag)
|
||||
// /* 禁用浏览器默认的快捷键 */
|
||||
// window.addEventListener('keydown', (e) => {
|
||||
// if (e.ctrlKey || e.metaKey || e.altKey) {
|
||||
// e.preventDefault()
|
||||
// }
|
||||
// })
|
||||
// /* 禁止右键菜单 */
|
||||
// window.addEventListener('contextmenu', (e) => e.preventDefault(), false)
|
||||
/* 禁用浏览器默认的快捷键 */
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if (e.ctrlKey || e.metaKey || e.altKey) {
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
/* 禁止右键菜单 */
|
||||
window.addEventListener('contextmenu', (e) => e.preventDefault(), false)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
@ -10,8 +10,18 @@ export const maximizeWindow = async () => {
|
||||
await appWindow.maximize()
|
||||
}
|
||||
|
||||
/** 关闭 */
|
||||
export const closeWindow = async () => {
|
||||
/**
|
||||
* 关闭窗口
|
||||
* @param label 窗口标签
|
||||
* @example
|
||||
* 传入窗口标签后开启窗口关闭的监听事件,使用appWindow.emit事件
|
||||
* 事件名称: windowsClose
|
||||
*/
|
||||
export const closeWindow = async (label: string) => {
|
||||
if (label !== void 0) {
|
||||
const win = WebviewWindow.getByLabel(label)
|
||||
win?.emit('windowsClose', label)
|
||||
}
|
||||
await appWindow.close()
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,9 @@ const themeOverrides: GlobalThemeOverrides = {
|
||||
tabTextColorActiveSegment: '#059669',
|
||||
tabTextColorHoverSegment: '#059669',
|
||||
tabPaddingMediumSegment: '4px'
|
||||
},
|
||||
Popover: {
|
||||
padding: '5px'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,21 +6,13 @@
|
||||
<!-- 输入框顶部选项栏 -->
|
||||
<nav class="flex-between-center p-[10px_22px_5px] select-none">
|
||||
<nav class="input-options flex-y-center">
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg class="mr-18px"><use href="#smiling-face"></use></svg>
|
||||
</template>
|
||||
<span>表情</span>
|
||||
</n-popover>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<div class="flex-center gap-2px mr-12px">
|
||||
<svg><use href="#screenshot"></use></svg>
|
||||
@ -29,11 +21,7 @@
|
||||
</template>
|
||||
<span>截图</span>
|
||||
</n-popover>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<div class="flex-center gap-2px mr-12px">
|
||||
<svg><use href="#file2"></use></svg>
|
||||
@ -42,41 +30,25 @@
|
||||
</template>
|
||||
<span>文件</span>
|
||||
</n-popover>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg class="mr-18px"><use href="#photo"></use></svg>
|
||||
</template>
|
||||
<span>图片</span>
|
||||
</n-popover>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg class="mr-18px"><use href="#shake"></use></svg>
|
||||
</template>
|
||||
<span>窗口抖动</span>
|
||||
</n-popover>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg class="mr-18px"><use href="#red-packet"></use></svg>
|
||||
</template>
|
||||
<span>红包</span>
|
||||
</n-popover>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg class="mr-18px"><use href="#voice"></use></svg>
|
||||
</template>
|
||||
@ -84,11 +56,7 @@
|
||||
</n-popover>
|
||||
</nav>
|
||||
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg class="w-22px h-22px cursor-pointer outline-none"><use href="#history"></use></svg>
|
||||
</template>
|
||||
|
@ -7,11 +7,7 @@
|
||||
<!-- 顶部右边选项栏 -->
|
||||
<nav class="options flex-y-center gap-20px color-[--icon-color]">
|
||||
<div class="options-box">
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg @click="handleClick"><use href="#phone-telephone"></use></svg>
|
||||
</template>
|
||||
@ -20,11 +16,7 @@
|
||||
</div>
|
||||
|
||||
<div class="options-box">
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg><use href="#video-one"></use></svg>
|
||||
</template>
|
||||
@ -33,11 +25,7 @@
|
||||
</div>
|
||||
|
||||
<div class="options-box">
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg><use href="#screen-sharing"></use></svg>
|
||||
</template>
|
||||
@ -46,11 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="options-box">
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg><use href="#remote-control"></use></svg>
|
||||
</template>
|
||||
@ -59,11 +43,7 @@
|
||||
</div>
|
||||
|
||||
<div class="options-box">
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
:show-arrow="false"
|
||||
placement="bottom"
|
||||
style="padding: 5px; border: 1px solid rgba(90, 90, 90, 0.3)">
|
||||
<n-popover trigger="hover" :show-arrow="false" placement="bottom">
|
||||
<template #trigger>
|
||||
<svg><use href="#launch"></use></svg>
|
||||
</template>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<component :is="componentMap[message.type]" :body="message.body" :data-message-id="message.id" />
|
||||
<component :is="componentMap" :body="message.body" :data-message-id="message.id" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { MsgEnum } from '@/enums'
|
||||
|
@ -1,6 +1,23 @@
|
||||
<template>
|
||||
<!-- user-select: none让元素不可以选中 -->
|
||||
<div data-tauri-drag-region class="flex justify-end select-none">
|
||||
<!-- 固定在最顶层 -->
|
||||
<div v-if="topWinLabel !== void 0" @click="handleAlwaysOnTop" class="hover-box">
|
||||
<n-popover trigger="hover">
|
||||
<template #trigger>
|
||||
<svg
|
||||
v-if="alwaysOnTopStatus"
|
||||
class="w-14px h-14px color-[--action-bar-icon-color] outline-none cursor-pointer">
|
||||
<use href="#onTop"></use>
|
||||
</svg>
|
||||
<svg v-else class="w-16px h-16px color-[--action-bar-icon-color] outline-none cursor-pointer">
|
||||
<use href="#notOnTop"></use>
|
||||
</svg>
|
||||
</template>
|
||||
<span v-if="alwaysOnTopStatus">取消置顶</span>
|
||||
<span v-else>置顶</span>
|
||||
</n-popover>
|
||||
</div>
|
||||
<!-- 收缩页面 -->
|
||||
<div v-if="shrink" @click="shrinkWindow" class="hover-box">
|
||||
<svg class="w-16px h-16px color-[--action-bar-icon-color] cursor-pointer"><use href="#left-bar"></use></svg>
|
||||
@ -21,7 +38,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 关闭窗口 -->
|
||||
<div v-if="closeW" @click="closeWindow" class="action-close">
|
||||
<div v-if="closeW" @click="closeWindow(currentLabel as string)" class="action-close">
|
||||
<svg class="w-14px h-14px color-[--action-bar-icon-color] cursor-pointer">
|
||||
<use href="#close"></use>
|
||||
</svg>
|
||||
@ -34,6 +51,7 @@ import { closeWindow, maximizeWindow, minimizeWindow, unmaximize } from '@/commo
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import Mitt from '@/utils/Bus'
|
||||
import { useWindow } from '@/hooks/useWindow.ts'
|
||||
import { alwaysOnTop } from '@/stores/alwaysOnTop.ts'
|
||||
|
||||
/**
|
||||
* 新版defineProps可以直接结构 { minW, maxW, closeW } 如果需要使用默认值withDefaults的时候使用新版解构方式会报错
|
||||
@ -45,6 +63,8 @@ const props = withDefaults(
|
||||
maxW?: boolean
|
||||
closeW?: boolean
|
||||
shrink?: boolean
|
||||
topWinLabel?: string
|
||||
currentLabel?: string
|
||||
shrinkStatus?: boolean
|
||||
}>(),
|
||||
{
|
||||
@ -55,9 +75,22 @@ const props = withDefaults(
|
||||
shrinkStatus: true
|
||||
}
|
||||
)
|
||||
const { minW, maxW, closeW, shrinkStatus } = toRefs(props)
|
||||
const windowMaximized = ref(false)
|
||||
const { minW, maxW, closeW, topWinLabel, shrinkStatus } = toRefs(props)
|
||||
const alwaysOnTopStore = alwaysOnTop()
|
||||
const { resizeWindow } = useWindow()
|
||||
// 窗口是否最大化状态
|
||||
const windowMaximized = ref(false)
|
||||
// 窗口是否置顶状态
|
||||
const alwaysOnTopStatus = computed(() => {
|
||||
if (topWinLabel.value === void 0) return false
|
||||
return alwaysOnTopStore.getWindowTop(topWinLabel.value)
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (alwaysOnTopStatus.value) {
|
||||
appWindow.setAlwaysOnTop(alwaysOnTopStatus.value as boolean)
|
||||
}
|
||||
})
|
||||
|
||||
// todo 放大的时候图个拖动了窗口,窗口会变回原来的大小,但是图标的状态没有改变
|
||||
// // 定义一个可能保存unlisten函数的变量
|
||||
@ -98,6 +131,15 @@ const shrinkWindow = async () => {
|
||||
await resizeWindow('home', 960, 700)
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置窗口置顶 */
|
||||
const handleAlwaysOnTop = async () => {
|
||||
if (topWinLabel.value !== void 0) {
|
||||
const isTop = !alwaysOnTopStatus.value
|
||||
alwaysOnTopStore.setWindowTop(topWinLabel.value, isTop)
|
||||
await appWindow.setAlwaysOnTop(isTop)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -105,6 +147,6 @@ const shrinkWindow = async () => {
|
||||
@apply w-28px h24px flex-center hover:bg-[--action-bar-icon-hover];
|
||||
}
|
||||
.action-close {
|
||||
@apply w-28px h24px flex-center cursor-pointer hover:bg-#c22b1c svg:hover:color-[#fff] rounded-tr-6px;
|
||||
@apply w-28px h24px flex-center cursor-pointer hover:bg-#c22b1c svg:hover:color-[#fff];
|
||||
}
|
||||
</style>
|
||||
|
@ -42,10 +42,17 @@ export const useWindow = () => {
|
||||
fileDropEnabled: isDrag
|
||||
})
|
||||
|
||||
// 首先检查是否已经存在同名窗口
|
||||
// TODO 这里如果主页刷新页面这样传递过来的label就没有了从而导致isExistsWinds为空的问题 (nyh -> 2024-03-06 06:33:38)
|
||||
const isExistsWinds = WebviewWindow.getByLabel(label)
|
||||
// TODO 页面刷新后很多状态会丢失,虽然上线打包后可以禁用刷新但难免会有些人会触发刷新,需要解决这个刷新后状态丢失问题 (nyh -> 2024-03-06 06:32:03)
|
||||
if (isExistsWinds) {
|
||||
// 如果窗口已存在,则给它焦点,使其置顶
|
||||
// 如果窗口已存在,首先检查是否最小化了
|
||||
const minimized = await webview.isMinimized()
|
||||
if (minimized) {
|
||||
// 如果已最小化,恢复窗口
|
||||
await webview.unminimize()
|
||||
}
|
||||
// 如果窗口已存在,则给它焦点,使其在最前面显示
|
||||
await webview.setFocus()
|
||||
} else {
|
||||
await webview.once('tauri://created', async () => {
|
||||
|
@ -9,11 +9,14 @@
|
||||
v-for="(item, index) in itemsTop"
|
||||
:key="index"
|
||||
@click="pageJumps(item.url)"
|
||||
class="top-action"
|
||||
:class="{ active: activeItem === item.url }">
|
||||
:class="[
|
||||
{ active: activeItem === item.url && item.url !== 'dynamic' },
|
||||
openWindowsList.has(item.url) ? 'p-[6px_8px] color-#059669' : 'top-action'
|
||||
]">
|
||||
<n-badge :value="item.badge" :max="99">
|
||||
<svg class="w-22px h-22px">
|
||||
<use :href="`#${activeItem === item.url && item.iconAction ? item.iconAction : item.icon}`"></use>
|
||||
<use
|
||||
:href="`#${activeItem === item.url || openWindowsList.has(item.url) ? item.iconAction : item.icon}`"></use>
|
||||
</svg>
|
||||
</n-badge>
|
||||
</div>
|
||||
@ -25,9 +28,9 @@
|
||||
v-for="(item, index) in itemsBottom"
|
||||
:key="index"
|
||||
@click="openContent(item.title, item.label)"
|
||||
class="bottom-action">
|
||||
:class="openWindowsList.has(item.url.substring(1)) ? 'p-[6px_8px] color-#059669' : 'bottom-action'">
|
||||
<svg class="w-22px h-22px">
|
||||
<use :href="`#${item.icon}`"></use>
|
||||
<use :href="`#${openWindowsList.has(item.url.substring(1)) ? item.iconAction : item.icon}`"></use>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@ -55,6 +58,7 @@ import { delay } from 'lodash-es'
|
||||
import { useWindow } from '@/hooks/useWindow.ts'
|
||||
import router from '@/router'
|
||||
import Mitt from '@/utils/Bus.ts'
|
||||
import { listenMsg } from '@/common/CrossTabMsg.ts'
|
||||
|
||||
type TopActive = {
|
||||
url: string
|
||||
@ -156,6 +160,8 @@ const menuList = ref<MenuList>([
|
||||
/*当前选中的元素 默认选中itemsTop的第一项*/
|
||||
const activeItem = ref<string>(itemsTop.value[0].url)
|
||||
const settingShow = ref(false)
|
||||
/* 已打开窗口的列表 */
|
||||
const openWindowsList = ref(new Set())
|
||||
const { createWebviewWindow } = useWindow()
|
||||
|
||||
watchEffect(() => {
|
||||
@ -166,6 +172,13 @@ watchEffect(() => {
|
||||
}
|
||||
})
|
||||
})
|
||||
listenMsg((msgInfo: any) => {
|
||||
if (msgInfo.content.payload !== void 0) {
|
||||
openWindowsList.value.delete(msgInfo.content.payload)
|
||||
} else if (!openWindowsList.value.has(msgInfo.content)) {
|
||||
openWindowsList.value.add(msgInfo.content)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
@ -173,13 +186,13 @@ watchEffect(() => {
|
||||
* @param url 跳转的路由
|
||||
* */
|
||||
const pageJumps = (url: string) => {
|
||||
activeItem.value = url
|
||||
// 判断是否是动态页面
|
||||
if (url === 'dynamic') {
|
||||
delay(async () => {
|
||||
await createWebviewWindow('动态', 'dynamic', 840, 800)
|
||||
}, 300)
|
||||
} else {
|
||||
activeItem.value = url
|
||||
router.push(`/${url}`)
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ const generateRandomString = (length: number, type: number) => {
|
||||
}
|
||||
|
||||
export const MockList = ref<MockItem[]>(
|
||||
Array.from({ length: 50 }, (_, i) => {
|
||||
Array.from({ length: 20 }, (_, i) => {
|
||||
const type = Math.round(Math.random()) + 1
|
||||
return {
|
||||
key: i,
|
||||
@ -43,6 +43,7 @@ export const dynamicList = Array.from({ length: 10 }, (_, i) => {
|
||||
id: i,
|
||||
avatar: `${avatars}?${i}`,
|
||||
user: `泰勒斯威夫特 ${i}`,
|
||||
img: imgList
|
||||
img: imgList,
|
||||
isAuth: i % 2 === 0
|
||||
}
|
||||
})
|
||||
|
22
src/stores/alwaysOnTop.ts
Normal file
22
src/stores/alwaysOnTop.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
interface AlwaysOnTopData {
|
||||
isAlwaysOnTop: boolean
|
||||
}
|
||||
|
||||
interface AlwaysOnTop {
|
||||
[key: string]: AlwaysOnTopData | boolean
|
||||
}
|
||||
|
||||
export const alwaysOnTop = defineStore('alwaysOnTop', {
|
||||
state: (): AlwaysOnTop => ({}),
|
||||
actions: {
|
||||
setWindowTop(key: string, data: AlwaysOnTopData | boolean) {
|
||||
this.$state[key] = data
|
||||
},
|
||||
getWindowTop(key: string) {
|
||||
return this.$state[key]
|
||||
}
|
||||
},
|
||||
persist: true
|
||||
})
|
@ -2,10 +2,10 @@
|
||||
<!-- TODO 项目初次启动然后初次打开窗口时候这个页面的样式没有渲染出来 (nyh -> 2024-03-04 05:30:15) -->
|
||||
<!-- TODO 我需要点击最左边侧边栏时候的选中样式(如果是淡出弹出窗口就把图标点亮但是不用设置背景颜色,然后如果关闭了对应窗口就把图标恢复原来的样式) (nyh -> 2024-03-04 05:32:03) -->
|
||||
<main class="wh-full bg-[--right-bg-color]">
|
||||
<ActionBar :shrink="false" :max-w="false" />
|
||||
<article class="flex flex-col items-center text-color-[--text-color] wh-full bg-[--right-bg-color]">
|
||||
<ActionBar :shrink="false" :max-w="false" :top-win-label="currentWindowLabel" :current-label="currentWindowLabel" />
|
||||
<article class="flex flex-col items-center text-[--text-color] wh-full bg-[--right-bg-color]">
|
||||
<n-scrollbar
|
||||
style="max-height: 100vh"
|
||||
style="max-height: calc(100vh - 20px)"
|
||||
class="w-650px bg-[--center-bg-color] h-full p-[10px_0] box-border rounded-4px">
|
||||
<n-flex justify="center">
|
||||
<!-- 动态内容框 -->
|
||||
@ -14,16 +14,18 @@
|
||||
v-for="item in dynamicList"
|
||||
:key="item.id"
|
||||
class="w-450px h-fit rounded-8px bg-[--right-bg-color] p-10px box-border">
|
||||
<n-flex>
|
||||
<n-flex align="center">
|
||||
<!-- 用户的头像和用户名以及个签 -->
|
||||
<img class="w-45px h-45px bg-#ccc rounded-50% select-none" :src="item.avatar" alt="" />
|
||||
<n-space vertical style="flex: 1">
|
||||
<n-flex justify="space-between" align="center">
|
||||
<label class="text-14px flex items-center gap-5px">
|
||||
<span>泰勒斯威夫特</span>
|
||||
<n-popover trigger="hover">
|
||||
<span :class="item.isAuth ? 'text-#059669' : ''">{{ item.user }}</span>
|
||||
<n-popover trigger="hover" v-if="item.isAuth">
|
||||
<template #trigger>
|
||||
<svg class="w-20px h-20px color-#059669 select-none outline-none"><use href="#auth"></use></svg>
|
||||
<svg class="w-20px h-20px color-#059669 select-none outline-none cursor-pointer">
|
||||
<use href="#auth"></use>
|
||||
</svg>
|
||||
</template>
|
||||
<span>著名歌手</span>
|
||||
</n-popover>
|
||||
@ -62,5 +64,27 @@
|
||||
</main>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { dynamicList } from '@/mock/index.ts'
|
||||
import { dynamicList } from '@/mock'
|
||||
import { appWindow, WebviewWindow } from '@tauri-apps/api/window'
|
||||
import { sendMsg } from '@/common/CrossTabMsg.ts'
|
||||
|
||||
// 获取当前窗口的标签
|
||||
const currentWindowLabel = computed(() => {
|
||||
return appWindow.label
|
||||
})
|
||||
const win = WebviewWindow.getByLabel(currentWindowLabel.value)
|
||||
|
||||
// TODO 可以尝试使用win.emit来取代自定义封装的跨标签页通信 (nyh -> 2024-03-05 07:15:42)
|
||||
watchEffect(() => {
|
||||
win?.listen('windowsClose', (e) => {
|
||||
sendMsg('windowsShow', e)
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const isShow = await win?.isVisible()
|
||||
if (isShow) {
|
||||
sendMsg('windowsShow', currentWindowLabel.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -1,10 +1,32 @@
|
||||
<template>
|
||||
<div class="wh-full bg-[--right-bg-color]">
|
||||
<ActionBar :shrink="false" />
|
||||
<ActionBar :shrink="false" :current-label="currentWindowLabel" />
|
||||
|
||||
<p>邮箱</p>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
import { appWindow, WebviewWindow } from '@tauri-apps/api/window'
|
||||
import { sendMsg } from '@/common/CrossTabMsg.ts'
|
||||
// 获取当前窗口的标签
|
||||
const currentWindowLabel = computed(() => {
|
||||
return appWindow.label
|
||||
})
|
||||
const win = WebviewWindow.getByLabel(currentWindowLabel.value)
|
||||
// TODO 可以尝试使用win.emit来取代自定义封装的跨标签页通信 (nyh -> 2024-03-05 07:15:42)
|
||||
|
||||
watchEffect(() => {
|
||||
win?.listen('windowsClose', (e) => {
|
||||
sendMsg('windowsShow', e)
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const isShow = await win?.isVisible()
|
||||
if (isShow) {
|
||||
sendMsg('windowsShow', currentWindowLabel.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@ -1,38 +1,40 @@
|
||||
<template>
|
||||
<!-- 消息列表 -->
|
||||
<!-- 可拖拽排序组件 -->
|
||||
<VueDraggable v-if="MockList.length > 0" ref="el" :animation="150" v-model="MockList">
|
||||
<VueDraggable v-if="MockList.length > 0" ref="el" target=".sort-target" :animation="150" v-model="MockList">
|
||||
<!-- 右键菜单组件 -->
|
||||
<ContextMenu
|
||||
@select="handleSelect($event.click(item.key))"
|
||||
@click="handleMsgClick(item)"
|
||||
:menu="menuList"
|
||||
:special-menu="specialMenuList"
|
||||
:class="{ active: activeItem === item.key }"
|
||||
class="msg-box w-full h-75px mb-5px"
|
||||
v-for="item in MockList"
|
||||
:key="item.key">
|
||||
<!-- 消息框,使用v-slide自定义指令来自动抉择右键菜单位置 -->
|
||||
<div v-slide class="flex items-center h-full pl-6px pr-8px gap-10px">
|
||||
<img class="w-44px h-44px rounded-50% bg-#fff" style="border: 1px solid #f1f1f1" :src="item.avatar" alt="" />
|
||||
<TransitionGroup type="transition" tag="div" name="fade" class="sort-target">
|
||||
<ContextMenu
|
||||
@select="handleSelect($event.click(item.key))"
|
||||
@click="handleMsgClick(item)"
|
||||
:menu="menuList"
|
||||
:special-menu="specialMenuList"
|
||||
:class="{ active: activeItem === item.key }"
|
||||
class="msg-box w-full h-75px mb-5px"
|
||||
v-for="item in MockList"
|
||||
:key="item.key">
|
||||
<!-- 消息框,使用v-slide自定义指令来自动抉择右键菜单位置 -->
|
||||
<div v-slide class="flex items-center h-full pl-6px pr-8px gap-10px">
|
||||
<img class="w-44px h-44px rounded-50% bg-#fff" style="border: 1px solid #f1f1f1" :src="item.avatar" alt="" />
|
||||
|
||||
<div class="h-38px flex flex-1 flex-col justify-between">
|
||||
<div class="flex-between-center">
|
||||
<span class="text-14px">{{ item.accountName }}</span>
|
||||
<span class="text text-10px">昨天</span>
|
||||
</div>
|
||||
<div class="h-38px flex flex-1 flex-col justify-between">
|
||||
<div class="flex-between-center">
|
||||
<span class="text-14px">{{ item.accountName }}</span>
|
||||
<span class="text text-10px">昨天</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-between-center">
|
||||
<p class="text w-135px text-12px" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis">
|
||||
说的很经典哈萨克的哈萨克看到贺卡上
|
||||
</p>
|
||||
<div class="flex-between-center">
|
||||
<p class="text w-135px text-12px" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis">
|
||||
说的很经典哈萨克的哈萨克看到贺卡上
|
||||
</p>
|
||||
|
||||
<!-- 消息提示 -->
|
||||
<n-badge :value="msgTotal" :max="99" />
|
||||
<!-- 消息提示 -->
|
||||
<n-badge :value="msgTotal" :max="99" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContextMenu>
|
||||
</ContextMenu>
|
||||
</TransitionGroup>
|
||||
</VueDraggable>
|
||||
|
||||
<!-- 暂无消息 -->
|
||||
@ -126,6 +128,9 @@ onMounted(() => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.msg-box {
|
||||
transition:
|
||||
background-color 0.3s ease,
|
||||
border-radius 0.3s ease;
|
||||
color: var(--text-color);
|
||||
.text {
|
||||
color: #808080;
|
||||
@ -150,6 +155,24 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/*! TransitionGroup过渡样式 */
|
||||
.fade-move,
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.01) translate(30px, 0);
|
||||
}
|
||||
|
||||
.fade-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
/*! end */
|
||||
|
||||
:deep(.n-badge .n-badge-sup) {
|
||||
font-weight: bold;
|
||||
font-size: 10px;
|
||||
|
@ -6,6 +6,7 @@ import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||
import { getRootPath, getSrcPath } from './build/config/getPath'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import unocss from '@unocss/vite'
|
||||
import terser from '@rollup/plugin-terser'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }: ConfigEnv) => {
|
||||
@ -50,8 +51,38 @@ export default defineConfig(({ mode }: ConfigEnv) => {
|
||||
dirs: ['src/components/**'], // 设置需要扫描的目录
|
||||
resolvers: [NaiveUiResolver()],
|
||||
dts: 'src/typings/components.d.ts'
|
||||
}),
|
||||
/* 压缩代码 */
|
||||
terser({
|
||||
format: {
|
||||
comments: false // 移除所有注释
|
||||
},
|
||||
compress: {
|
||||
drop_console: true, // 移除 console.log
|
||||
drop_debugger: true // 移除 debugger
|
||||
}
|
||||
})
|
||||
],
|
||||
build: {
|
||||
cssCodeSplit: true, // 启用 CSS 代码拆分
|
||||
minify: 'terser', // 指定使用哪种混淆器
|
||||
// chunk 大小警告的限制(kb)
|
||||
chunkSizeWarningLimit: 1200,
|
||||
// 分包配置
|
||||
rollupOptions: {
|
||||
output: {
|
||||
chunkFileNames: 'static/js/[name]-[hash].js', // 引入文件名的名称
|
||||
entryFileNames: 'static/js/[name]-[hash].js', // 包的入口文件名称
|
||||
assetFileNames: 'static/[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等
|
||||
// 最小化拆分包
|
||||
manualChunks(id) {
|
||||
if (id.includes('node_modules')) {
|
||||
return id.toString().split('node_modules/')[1].split('/')[0].toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user