feat(setting): 新增关闭窗口提示

This commit is contained in:
nongyehong
2024-03-19 23:41:41 +08:00
parent 60939bfd77
commit 183a2e786f
30 changed files with 403 additions and 222 deletions

View File

@@ -7,7 +7,7 @@
<title>Tauri + Vue + TS</title>
<!--引入iconpark图标库-->
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_66.e0a87d61eb42b8ddd45a67754a1dc86a.js"></script>
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_70.224ae5c926a3c7f59fc8f9cbe5bfaa9c.js"></script>
</head>
<body>

View File

@@ -34,7 +34,7 @@
},
"dependencies": {
"@tauri-apps/api": "^1.5.3",
"axios": "^1.6.7",
"axios": "^1.6.8",
"colorthief": "^2.4.0",
"dayjs": "^1.11.10",
"lodash-es": "^4.17.21",

47
pnpm-lock.yaml generated
View File

@@ -9,8 +9,8 @@ dependencies:
specifier: ^1.5.3
version: 1.5.3
axios:
specifier: ^1.6.7
version: 1.6.7
specifier: ^1.6.8
version: 1.6.8
colorthief:
specifier: ^2.4.0
version: 2.4.0
@@ -589,18 +589,18 @@ packages:
dev: true
optional: true
/@commitlint/load@19.0.3(@types/node@20.11.7)(typescript@5.4.2):
resolution: {integrity: sha512-18Tk/ZcDFRKIoKfEcl7kC+bYkEQ055iyKmGsYDoYWpKf6FUvBrP9bIWapuy/MB+kYiltmP9ITiUx6UXtqC9IRw==}
/@commitlint/load@19.2.0(@types/node@20.11.7)(typescript@5.4.2):
resolution: {integrity: sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==}
engines: {node: '>=v18'}
requiresBuild: true
dependencies:
'@commitlint/config-validator': 19.0.3
'@commitlint/execute-rule': 19.0.0
'@commitlint/resolve-extends': 19.0.3
'@commitlint/resolve-extends': 19.1.0
'@commitlint/types': 19.0.3
chalk: 5.3.0
cosmiconfig: 8.3.6(typescript@5.4.2)
cosmiconfig-typescript-loader: 5.0.0(@types/node@20.11.7)(cosmiconfig@8.3.6)(typescript@5.4.2)
cosmiconfig: 9.0.0(typescript@5.4.2)
cosmiconfig-typescript-loader: 5.0.0(@types/node@20.11.7)(cosmiconfig@9.0.0)(typescript@5.4.2)
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
lodash.uniq: 4.5.0
@@ -610,8 +610,8 @@ packages:
dev: true
optional: true
/@commitlint/resolve-extends@19.0.3:
resolution: {integrity: sha512-18BKmta8OC8+Ub+Q3QGM9l27VjQaXobloVXOrMvu8CpEwJYv62vC/t7Ka5kJnsW0tU9q1eMqJFZ/nN9T/cOaIA==}
/@commitlint/resolve-extends@19.1.0:
resolution: {integrity: sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==}
engines: {node: '>=v18'}
requiresBuild: true
dependencies:
@@ -2073,10 +2073,10 @@ packages:
resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==}
dev: false
/axios@1.6.7:
resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==}
/axios@1.6.8:
resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==}
dependencies:
follow-redirects: 1.15.5
follow-redirects: 1.15.6
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
@@ -2505,7 +2505,7 @@ packages:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
dev: false
/cosmiconfig-typescript-loader@5.0.0(@types/node@20.11.7)(cosmiconfig@8.3.6)(typescript@5.4.2):
/cosmiconfig-typescript-loader@5.0.0(@types/node@20.11.7)(cosmiconfig@9.0.0)(typescript@5.4.2):
resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==}
engines: {node: '>=v16'}
requiresBuild: true
@@ -2515,14 +2515,14 @@ packages:
typescript: '>=4'
dependencies:
'@types/node': 20.11.7
cosmiconfig: 8.3.6(typescript@5.4.2)
cosmiconfig: 9.0.0(typescript@5.4.2)
jiti: 1.21.0
typescript: 5.4.2
dev: true
optional: true
/cosmiconfig@8.3.6(typescript@5.4.2):
resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==}
/cosmiconfig@9.0.0(typescript@5.4.2):
resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
engines: {node: '>=14'}
requiresBuild: true
peerDependencies:
@@ -2531,10 +2531,10 @@ packages:
typescript:
optional: true
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.0
js-yaml: 4.1.0
parse-json: 5.2.0
path-type: 4.0.0
typescript: 5.4.2
dev: true
optional: true
@@ -2593,7 +2593,7 @@ packages:
longest: 2.0.1
word-wrap: 1.2.5
optionalDependencies:
'@commitlint/load': 19.0.3(@types/node@20.11.7)(typescript@5.4.2)
'@commitlint/load': 19.2.0(@types/node@20.11.7)(typescript@5.4.2)
transitivePeerDependencies:
- '@types/node'
- typescript
@@ -2783,6 +2783,13 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
/env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
requiresBuild: true
dev: true
optional: true
/error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
dependencies:
@@ -3315,8 +3322,8 @@ packages:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
dev: true
/follow-redirects@1.15.5:
resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==}
/follow-redirects@1.15.6:
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'

41
src-tauri/Cargo.lock generated
View File

@@ -1255,7 +1255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
[[package]]
name = "hula-im-tauri"
name = "hula"
version = "0.0.0"
dependencies = [
"serde",
@@ -1289,6 +1289,16 @@ dependencies = [
"cc",
]
[[package]]
name = "ico"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd"
dependencies = [
"byteorder",
"png",
]
[[package]]
name = "ico"
version = "0.3.0"
@@ -2447,6 +2457,7 @@ version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"indexmap 2.1.0",
"itoa 1.0.10",
"ryu",
"serde",
@@ -2782,13 +2793,14 @@ checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
[[package]]
name = "tauri"
version = "1.5.3"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d563b672acde8d0cc4c1b1f5b855976923f67e8d6fe1eba51df0211e197be2"
checksum = "f078117725e36d55d29fafcbb4b1e909073807ca328ae8deb8c0b3843aac0fed"
dependencies = [
"anyhow",
"cocoa 0.24.1",
"dirs-next",
"dunce",
"embed_plist",
"encoding_rs",
"flate2",
@@ -2798,6 +2810,7 @@ dependencies = [
"gtk",
"heck 0.4.1",
"http",
"ico 0.2.0",
"ignore",
"infer 0.9.0",
"objc",
@@ -2850,13 +2863,13 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "1.4.1"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb"
checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc"
dependencies = [
"base64 0.21.5",
"brotli",
"ico",
"ico 0.3.0",
"json-patch",
"plist",
"png",
@@ -2876,9 +2889,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "1.4.2"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acea6445eececebd72ed7720cfcca46eee3b5bad8eb408be8f7ef2e3f7411500"
checksum = "277abf361a3a6993ec16bcbb179de0d6518009b851090a01adfea12ac89fa875"
dependencies = [
"heck 0.4.1",
"proc-macro2",
@@ -2890,9 +2903,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "0.14.1"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43"
checksum = "cf2d0652aa2891ff3e9caa2401405257ea29ab8372cce01f186a5825f1bd0e76"
dependencies = [
"gtk",
"http",
@@ -2911,9 +2924,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "0.14.2"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "803a01101bc611ba03e13329951a1bde44287a54234189b9024b78619c1bc206"
checksum = "067c56fc153b3caf406d7cd6de4486c80d1d66c0f414f39e94cb2f5543f6445f"
dependencies = [
"cocoa 0.24.1",
"gtk",
@@ -2931,9 +2944,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "1.5.1"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a52165bb340e6f6a75f1f5eeeab1bb49f861c12abe3a176067d53642b5454986"
checksum = "75ad0bbb31fccd1f4c56275d0a5c3abdf1f59999f72cb4ef8b79b4ed42082a21"
dependencies = [
"brotli",
"ctor",

View File

@@ -1,8 +1,8 @@
[package]
name = "hula-im-tauri"
name = "hula"
version = "0.0.0"
description = "A Tauri App"
authors = ["you"]
authors = ["nongyehong"]
license = ""
repository = ""
edition = "2021"
@@ -20,10 +20,10 @@ strip = true # 删除调试符号
tauri-build = { version = "1.5", features = [] }
[dependencies]
tauri = { version = "1.5", features = [ "macos-private-api", "fs-all", "window-all", "system-tray", "shell-open", "icon-png"] }
tauri = { version = "1.6.1", features = [ "macos-private-api", "fs-all", "window-all", "system-tray", "shell-open", "icon-png", "icon-ico"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
window-shadows = "0.2.1" # windows 阴影和圆角
window-shadows = "0.2.2" # windows 阴影和圆角
window-vibrancy = "0.4.3" # windows 磨砂背景
[features]

View File

@@ -1,11 +1,10 @@
use tauri::{AppHandle, CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu};
use tauri::{AppHandle, CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu};
/// 托盘菜单
pub fn menu() -> SystemTray {
let quit = CustomMenuItem::new("quit".to_string(), "退出");
let show = CustomMenuItem::new("show".to_string(), "打开主板");
let hide = CustomMenuItem::new("hide".to_string(), "隐藏");
let change_ico = CustomMenuItem::new("change_ico".to_string(), "更改图标").disabled();
let show = CustomMenuItem::new("show".to_string(), "打开主");
let change_ico = CustomMenuItem::new("change_ico".to_string(), "更改图标");
let tray_menu = SystemTrayMenu::new()
.add_submenu(SystemTraySubmenu::new(
"Language", // 语言菜单
@@ -15,27 +14,49 @@ pub fn menu() -> SystemTray {
.add_item(CustomMenuItem::new("lang_zh_HK".to_string(), "繁体中文")),
))
.add_native_item(SystemTrayMenuItem::Separator) // 分割线
.add_item(show)
.add_item(change_ico)
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(hide)
.add_item(show)
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(quit);
SystemTray::new().with_menu(tray_menu)
}
/// 打开主页
pub fn open_home(app: &AppHandle) {
let window = app.get_window("home").unwrap();
let hide = window.is_visible().unwrap();
let min = window.is_minimized().unwrap();
if !hide {
window.show().unwrap();
}
if min {
window.unminimize().unwrap();
}
window.set_focus().unwrap();
}
/// 托盘事件
pub fn handler(app: &AppHandle, event: SystemTrayEvent) {
match event {
SystemTrayEvent::LeftClick { .. } => {
open_home(app);
},
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
"change_ico" => { // 更新托盘图标
app.tray_handle()
.set_icon(tauri::Icon::Raw(
include_bytes!("../../icons/128x128.png").to_vec(),
include_bytes!("../../../public/status/weather_3x.png").to_vec(),
))
.unwrap();
}
"show" => {
open_home(app);
}
"quit" => {
let window = app.get_window("home").unwrap();
window.close().unwrap();
window.emit("exit", ()).unwrap()
}
lang if lang.contains("lang_") => { // 选择语言,匹配 id 前缀包含 `lang_` 的事件
Lang::new(
app,

View File

@@ -5,11 +5,12 @@ use window_shadows::set_shadow;
pub fn set_window_attribute<R: Runtime>(app: &tauri::App<R>) {
for (_, window) in app.windows() {
// 设置窗口阴影和圆角
set_shadow(&window, true).expect("Failed to set window shadow");
#[cfg(any(windows, target_os = "macos"))]
set_shadow(&window, true).unwrap();
// 设置窗口的磨砂背景
#[cfg(target_os = "macos")]
window_vibrancy::apply_acrylic(&window, Some((255, 255, 255, 1)))
window_vibrancy::apply_vibrancy(&window, NSVisualEffectMaterial::Sidebar)
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
#[cfg(target_os = "windows")]

View File

@@ -11,10 +11,11 @@ mod common;
#[tauri::command]
fn reset_set_window<R: Runtime>(app: tauri::AppHandle<R>, label: String) {
let window = app.get_window(&label).unwrap();
set_shadow(&window, true).expect("Unsupported platform!");
#[cfg(any(windows, target_os = "macos"))]
set_shadow(&window, true).unwrap();
#[cfg(target_os = "macos")]
window_vibrancy::apply_acrylic(&window, Some((255, 255, 255, 1)))
window_vibrancy::apply_vibrancy(&window, NSVisualEffectMaterial::Sidebar)
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
#[cfg(target_os = "windows")]

View File

@@ -70,7 +70,7 @@
}
],
"systemTray": {
"iconPath": "icons/icon.png",
"iconPath": "icons/icon.ico",
"iconAsTemplate": true
}
}

View File

@@ -13,7 +13,7 @@ import { onlineStatus } from '@/stores/onlineStatus.ts'
const settingStore = setting()
const OLStatusStore = onlineStatus()
const { themes } = storeToRefs(settingStore)
const { themes, tray } = storeToRefs(settingStore)
/* 禁止图片以及输入框的拖拽 */
const preventDrag = (e: MouseEvent) => {
@@ -25,6 +25,9 @@ const preventDrag = (e: MouseEvent) => {
}
onMounted(() => {
if (!tray.value.notTips) {
tray.value.tips = false
}
// 判断localStorage中是否有设置主题
if (!localStorage.getItem(StoresEnum.SETTING)) {
settingStore.init(ThemeEnum.LIGHT)

View File

@@ -1,6 +1,8 @@
import { appWindow, WebviewWindow } from '@tauri-apps/api/window'
import { EventEnum } from '@/enums'
import { emit } from '@tauri-apps/api/event'
import { setting } from '@/stores/setting.ts'
import { storeToRefs } from 'pinia'
/** 最小化 */
export const minimizeWindow = async () => {
@@ -20,10 +22,16 @@ export const maximizeWindow = async () => {
* @see EventEnum.WIN_CLOSE
*/
export const closeWindow = async (label: string) => {
if (label !== void 0) {
if (label !== void 0 && label !== 'home') {
await emit(EventEnum.WIN_CLOSE, label)
}
await appWindow.close()
if (label === 'home') {
const settingStore = setting()
const { tray } = storeToRefs(settingStore)
tray.value.tips = true
} else {
await appWindow.close()
}
}
/** 取消最大化 */

View File

@@ -10,7 +10,7 @@
left: `${pos.posX}px`,
top: `${pos.posY}px`
}">
<div v-resize="handleSize" class="menu-list">
<div v-resize="handleSize" v-if="menu.length > 0" class="menu-list">
<div v-for="(item, index) in menu as any[]" :key="index">
<!-- 禁止的菜单选项需要禁止点击事件 -->
<div class="menu-item-disabled" v-if="item.disabled" @click.prevent="$event.preventDefault()">

View File

@@ -20,18 +20,22 @@
:class="item.accountId === userId ? 'flex-row-reverse' : ''"
style="max-width: calc(100% - 54px)">
<!-- 头像 -->
<img
:class="item.accountId === userId ? '' : 'mr-10px'"
class="w-34px rounded-50% select-none"
:src="item.accountId === userId ? item.avatar : activeItem.avatar"
alt="" />
<ContextMenu :menu="optionsList" :special-menu="report">
<img
:class="item.accountId === userId ? '' : 'mr-10px'"
class="w-34px rounded-50% select-none"
:src="item.accountId === userId ? item.avatar : activeItem.avatar"
alt="" />
</ContextMenu>
<div
class="flex flex-col gap-8px color-[--text-color]"
:class="item.accountId === userId ? 'items-end mr-10px' : ''">
<span class="text-12px select-none color-#909090" v-if="activeItem.type === RoomTypeEnum.GROUP">
{{ item.accountId === userId ? item.value : activeItem.accountName }}
</span>
<!-- 右键菜单及其气泡样式 -->
<ContextMenu :menu="optionsList" :special-menu="report">
<span class="text-12px select-none color-#909090" v-if="activeItem.type === RoomTypeEnum.GROUP">
{{ item.accountId === userId ? item.value : activeItem.accountName }}
</span>
</ContextMenu>
<!-- 气泡样式 -->
<ContextMenu
:data-key="item.accountId === userId ? `U${item.key}` : `Q${item.key}`"
@select="$event.click(item.key)"
@@ -46,7 +50,6 @@
style="white-space: pre-wrap"
:class="[
{ active: activeBubble === item.key },
activeItem.type === RoomTypeEnum.GROUP ? '' : 'm-[10px_0]',
item.accountId === userId ? 'bubble-oneself' : 'bubble'
]">
<span v-html="item.content"></span>
@@ -54,10 +57,7 @@
<!-- 消息为为图片类型(不固定宽度和高度), 多张图片时渲染 -->
<n-image-group v-if="Array.isArray(item.content) && item.type === MsgEnum.IMAGE">
<n-space
class="photo-wall"
vertical
:class="activeItem.type === RoomTypeEnum.GROUP ? '' : 'm-[10px_0]'">
<n-space class="photo-wall" vertical>
<n-image
v-for="(src, index) in item.content"
:key="index"
@@ -74,7 +74,6 @@
:img-props="{ style: { maxWidth: '325px', maxHeight: '165px' } }"
show-toolbar-tooltip
style="border-radius: 8px"
:class="activeItem.type === RoomTypeEnum.GROUP ? '' : 'm-[10px_0]'"
:src="item.content"></n-image>
</ContextMenu>
</div>
@@ -188,7 +187,46 @@ const { activeItem } = defineProps<{
// })
// const message = computed(() => msg.value)
/* 右键菜单列表 */
/* 右键用户信息菜单(单聊的时候显示) */
const optionsList = computed(() => {
if (activeItem.type === RoomTypeEnum.GROUP) {
return [
{
label: '发送信息',
icon: 'message-action',
click: () => {}
},
{
label: 'TA',
icon: 'aite',
click: () => {}
},
{
label: '查看资料',
icon: 'notes',
click: () => {}
},
{
label: '添加好友',
icon: 'people-plus',
click: () => {}
}
]
}
})
/* 举报选项 */
const report = computed(() => {
if (activeItem.type === RoomTypeEnum.GROUP) {
return [
{
label: '举报',
icon: 'caution',
click: () => {}
}
]
}
})
/* 右键消息菜单列表 */
const menuList = ref<OPT.RightMenu[]>([
{
label: '复制',

View File

@@ -7,9 +7,9 @@
<ChatFooter />
</template>
<script setup lang="ts">
import ChatFooter from './chatFooter.vue'
import ChatHeader from './chatHeader.vue'
import ChatMain from './chatMain.vue'
import ChatFooter from './ChatFooter.vue'
import ChatHeader from './ChatHeader.vue'
import ChatMain from './ChatMain.vue'
import { MockItem } from '@/services/types.ts'
import { listen } from '@tauri-apps/api/event'
import { appWindow } from '@tauri-apps/api/window'

View File

@@ -4,8 +4,8 @@
<script setup lang="ts">
import { MsgEnum } from '@/enums'
import type { MsgType } from '@/services/types'
import Text from './text.vue'
import Image from './image.vue'
import Text from './Text.vue'
import Image from './Image.vue'
const componentMap = {
[MsgEnum.TEXT]: Text,

View File

@@ -43,6 +43,41 @@
<use href="#close"></use>
</svg>
</div>
<!-- 是否退到托盘提示框 -->
<n-modal v-model:show="tray.tips" class="w-350px border-rd-8px">
<div class="bg-[--bg-popover] w-360px h-full p-6px box-border flex flex-col">
<svg @click="tray.tips = false" class="w-12px h-12px ml-a cursor-pointer select-none">
<use href="#close"></use>
</svg>
<n-space vertical :size="20" class="p-[22px_10px_10px_22px] select-none">
<span class="text-16px">最小化还是直接退出程序?</span>
<label class="text-14px text-#707070 flex gap-6px lh-16px items-center">
<n-radio
:checked="trayRef.type === CloseBxEnum.HIDE"
:value="CloseBxEnum.HIDE"
@change="trayRef.type = CloseBxEnum.HIDE" />
<span>最小化到系统托盘</span>
</label>
<label class="text-14px text-#707070 flex gap-6px lh-16px items-center">
<n-radio
:checked="trayRef.type === CloseBxEnum.CLOSE"
:value="CloseBxEnum.CLOSE"
@change="trayRef.type = CloseBxEnum.CLOSE" />
<span>直接退出程序</span>
</label>
<label class="text-12px text-#909090 flex gap-6px justify-end items-center">
<n-checkbox size="small" v-model:checked="trayRef.notTips" />
<span>下次不出现此提示</span>
</label>
<n-flex justify="end">
<n-button @click="handleConfirm" class="w-78px" color="#059669">确定</n-button>
<n-button @click="tray.tips = false" class="w-78px" secondary>取消</n-button>
</n-flex>
</n-space>
</div>
</n-modal>
</div>
</template>
@@ -52,8 +87,10 @@ 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'
import { setting } from '@/stores/setting.ts'
import { emit, listen } from '@tauri-apps/api/event'
import { CloseBxEnum, EventEnum } from '@/enums'
import { storeToRefs } from 'pinia'
/**
* 新版defineProps可以直接结构 { minW, maxW, closeW } 如果需要使用默认值withDefaults的时候使用新版解构方式会报错
@@ -79,7 +116,13 @@ const props = withDefaults(
)
const { minW, maxW, closeW, topWinLabel, shrinkStatus } = toRefs(props)
const alwaysOnTopStore = alwaysOnTop()
const settingStore = setting()
const { tray } = storeToRefs(settingStore)
const { resizeWindow } = useWindow()
const trayRef = reactive({
type: tray.value.type,
notTips: tray.value.notTips
})
// 窗口是否最大化状态
const windowMaximized = ref(false)
// 窗口是否置顶状态
@@ -92,7 +135,7 @@ watchEffect(() => {
if (alwaysOnTopStatus.value) {
appWindow.setAlwaysOnTop(alwaysOnTopStatus.value as boolean)
}
listen(EventEnum.LOGOUT, async () => {
listen(EventEnum.EXIT, async () => {
/* 退出账号前把窗口全部关闭 */
if (appWindow.label !== 'login') {
await appWindow.close()
@@ -148,6 +191,21 @@ const handleAlwaysOnTop = async () => {
await appWindow.setAlwaysOnTop(isTop)
}
}
/* 点击确定时 */
const handleConfirm = async () => {
tray.value.type = trayRef.type
tray.value.notTips = trayRef.notTips
tray.value.tips = false
if (tray.value.type === CloseBxEnum.CLOSE) {
await emit(EventEnum.EXIT)
await appWindow.close()
} else {
await nextTick(() => {
appWindow.hide()
})
}
}
</script>
<style scoped lang="scss">
@@ -157,4 +215,8 @@ const handleAlwaysOnTop = async () => {
.action-close {
@apply w-28px h24px flex-center cursor-pointer hover:bg-#c22b1c svg:hover:color-[#fff];
}
.n-modal {
align-self: start;
margin: 60px auto;
}
</style>

View File

@@ -33,8 +33,8 @@ export enum EventEnum {
WIN_CLOSE = 'winClose',
/** 窗口显示 */
WIN_SHOW = 'winShow',
/** 退出账号 */
LOGOUT = 'logout',
/** 退出程序 */
EXIT = 'exit',
/** 设置在线状态 */
SET_OL_STS = 'setOnlineStatus'
}
@@ -88,3 +88,11 @@ export enum RoomTypeEnum {
/** 2单聊 */
SINGLE
}
/** 关闭窗口的行为 */
export enum CloseBxEnum {
/** 隐藏 */
HIDE = 'hide',
/** 关闭 */
CLOSE = 'close'
}

View File

@@ -18,7 +18,7 @@ const shrinkStatus = ref(false)
* event默认如果没有传递值就为true所以shrinkStatus的值为false就会发生值的变化
* 因为shrinkStatus的值为false所以v-if="!shrinkStatus" 否则right组件刚开始渲染的时候不会显示
* */
Mitt.on('shrinkWindow', async (event) => {
Mitt.on('shrinkWindow', (event) => {
shrinkStatus.value = event as boolean
})
</script>

View File

@@ -79,7 +79,7 @@ const moreList = ref<OPT.L.MoreList[]>([
/* 给一点延迟,不然创建登录窗口后还没有来得及设置阴影和圆角效果 */
delay(async () => {
/* 通知全部打开的窗口然后关闭 */
await emit(EventEnum.LOGOUT)
await emit(EventEnum.EXIT)
}, 300)
})
}

View File

@@ -1,5 +1,5 @@
<template>
<main class="left w-60px h-full p-[30px_6px_15px] box-border flex-col-center select-none">
<main data-tauri-drag-region class="left w-60px h-full p-[30px_6px_15px] box-border flex-col-center select-none">
<!-- 点击时头像内容框 -->
<n-popover
v-model:show="info.show"

View File

@@ -1,6 +1,6 @@
<template>
<main class="flex-1 bg-[--right-bg-color] h-full w-100vw">
<ActionBar />
<ActionBar :current-label="appWindow.label" />
<!-- 需要判断当前路由是否是信息详情界面 -->
<ChatBox :active-item="activeItem" v-if="msgBoxShow && isChat && activeItem !== -1" />
@@ -20,6 +20,7 @@ import { setting } from '@/stores/setting.ts'
import { storeToRefs } from 'pinia'
import { EventEnum, ThemeEnum } from '@/enums'
import { listen } from '@tauri-apps/api/event'
import { appWindow } from '@tauri-apps/api/window'
const settingStore = setting()
const { themes } = storeToRefs(settingStore)

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
import { StoresEnum, ThemeEnum } from '@/enums'
import { CloseBxEnum, StoresEnum, ThemeEnum } from '@/enums'
export const setting = defineStore(StoresEnum.SETTING, {
state: (): STO.Setting => ({
@@ -8,6 +8,12 @@ export const setting = defineStore(StoresEnum.SETTING, {
content: '',
pattern: ''
},
/* 系统托盘 */
tray: {
type: CloseBxEnum.HIDE,
tips: true,
notTips: false
},
/* 登录设置 */
login: {
autoLogin: false,

View File

@@ -9,12 +9,12 @@ declare module 'vue' {
export interface GlobalComponents {
ActionBar: typeof import('./../components/windows/ActionBar.vue')['default']
ChatBox: typeof import('./../components/rightBox/chatBox/index.vue')['default']
ChatFooter: typeof import('./../components/rightBox/chatBox/chatFooter.vue')['default']
ChatHeader: typeof import('./../components/rightBox/chatBox/chatHeader.vue')['default']
ChatMain: typeof import('./../components/rightBox/chatBox/chatMain.vue')['default']
ChatFooter: typeof import('./../components/rightBox/chatBox/ChatFooter.vue')['default']
ChatHeader: typeof import('./../components/rightBox/chatBox/ChatHeader.vue')['default']
ChatMain: typeof import('./../components/rightBox/chatBox/ChatMain.vue')['default']
ContextMenu: typeof import('./../components/common/ContextMenu.vue')['default']
Details: typeof import('./../components/rightBox/Details.vue')['default']
Image: typeof import('./../components/rightBox/renderMessage/image.vue')['default']
Image: typeof import('./../components/rightBox/renderMessage/Image.vue')['default']
MsgInput: typeof import('./../components/rightBox/MsgInput/index.vue')['default']
NaiveProvider: typeof import('./../components/common/NaiveProvider.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
@@ -42,6 +42,7 @@ declare module 'vue' {
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopover: typeof import('naive-ui')['NPopover']
NQrCode: typeof import('naive-ui')['NQrCode']
NRadio: typeof import('naive-ui')['NRadio']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSkeleton: typeof import('naive-ui')['NSkeleton']
NSpace: typeof import('naive-ui')['NSpace']
@@ -53,6 +54,6 @@ declare module 'vue' {
RenderMessage: typeof import('./../components/rightBox/renderMessage/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Text: typeof import('./../components/rightBox/renderMessage/text.vue')['default']
Text: typeof import('./../components/rightBox/renderMessage/Text.vue')['default']
}
}

View File

@@ -7,6 +7,13 @@ declare namespace STO {
content: string
pattern: string
}
/* 系统托盘 */
tray: {
type: string
tips: boolean
/* 不再显示提示 */
notTips: boolean
}
/* 登录设置 */
login: {
autoLogin: boolean

View File

@@ -24,139 +24,12 @@ import { setting } from '@/stores/setting.ts'
import { storeToRefs } from 'pinia'
import { EventEnum, ThemeEnum } from '@/enums'
import { emit } from '@tauri-apps/api/event'
import { titleList } from './model.tsx'
const settingStore = setting()
const { themes } = storeToRefs(settingStore)
const activeItem = ref<string>(themes.value.pattern)
const titleList = [
{
title: '白天模式',
code: ThemeEnum.LIGHT,
model: (() => (
<div class="wh-full flex">
<div class="bg-#f1f1f1 flex-[1] rounded-[6px_0_0_6px]"></div>
<div class="bg-#fff flex-[3.5] p-[8px_4px] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
</div>
<div class="bg-#f1f1f1 flex-[5.5] p-[8px_4px] rounded-[0_6px_6px_0] flex flex-col gap-8px">
<div class="flex-y-center gap-4px">
<div class="bg-#ccc w-6px h-6px rounded-50%"></div>
<div class="bg-#fff w-28px h-6px"></div>
</div>
<div class="flex-y-center gap-4px ml-a">
<div class="bg-#059669 w-28px h-6px"></div>
<div class="bg-#ccc w-6px h-6px rounded-50%"></div>
</div>
</div>
</div>
))()
},
{
title: '夜间模式',
code: ThemeEnum.DARK,
model: (() => (
<div class="wh-full flex">
<div class="bg-#454545 flex-[1] rounded-[6px_0_0_6px]"></div>
<div class="bg-#212121 flex-[3.5] p-[8px_4px] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
</div>
<div class="bg-#1a1a1a flex-[5.5] p-[8px_4px] rounded-[0_6px_6px_0] flex flex-col gap-8px">
<div class="flex-y-center gap-4px">
<div class="bg-#4d4d4d w-6px h-6px rounded-50%"></div>
<div class="bg-#4d4d4d w-28px h-6px"></div>
</div>
<div class="flex-y-center gap-4px ml-a">
<div class="bg-#4d4d4d w-28px h-6px"></div>
<div class="bg-#4d4d4d w-6px h-6px rounded-50%"></div>
</div>
</div>
</div>
))()
},
{
title: '跟随系统',
code: ThemeEnum.OS,
model: (() => (
<div class="wh-full flex">
<div class="bg-#f1f1f1 flex-[1] rounded-[6px_0_0_6px]"></div>
<div class="bg-#fff flex-[4.5] p-[8px_4px] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
</div>
<div class="bg-#454545 flex-[1]"></div>
<div class="bg-#212121 flex-[4.5] p-[8px_4px] rounded-[0_6px_6px_0] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
</div>
</div>
))()
}
]
/* 切换主题 */
const handleTheme = async (event: MouseEvent, code: string) => {
if (code === themes.value.pattern) return

View File

@@ -0,0 +1,131 @@
import { ThemeEnum } from '@/enums'
const titleList = [
{
title: '白天模式',
code: ThemeEnum.LIGHT,
model: (() => (
<div class="wh-full flex">
<div class="bg-#f1f1f1 flex-[1] rounded-[6px_0_0_6px]"></div>
<div class="bg-#fff flex-[3.5] p-[8px_4px] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
</div>
<div class="bg-#f1f1f1 flex-[5.5] p-[8px_4px] rounded-[0_6px_6px_0] flex flex-col gap-8px">
<div class="flex-y-center gap-4px">
<div class="bg-#ccc w-6px h-6px rounded-50%"></div>
<div class="bg-#fff w-28px h-6px"></div>
</div>
<div class="flex-y-center gap-4px ml-a">
<div class="bg-#059669 w-28px h-6px"></div>
<div class="bg-#ccc w-6px h-6px rounded-50%"></div>
</div>
</div>
</div>
))()
},
{
title: '夜间模式',
code: ThemeEnum.DARK,
model: (() => (
<div class="wh-full flex">
<div class="bg-#454545 flex-[1] rounded-[6px_0_0_6px]"></div>
<div class="bg-#212121 flex-[3.5] p-[8px_4px] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
</div>
<div class="bg-#1a1a1a flex-[5.5] p-[8px_4px] rounded-[0_6px_6px_0] flex flex-col gap-8px">
<div class="flex-y-center gap-4px">
<div class="bg-#4d4d4d w-6px h-6px rounded-50%"></div>
<div class="bg-#4d4d4d w-28px h-6px"></div>
</div>
<div class="flex-y-center gap-4px ml-a">
<div class="bg-#4d4d4d w-28px h-6px"></div>
<div class="bg-#4d4d4d w-6px h-6px rounded-50%"></div>
</div>
</div>
</div>
))()
},
{
title: '跟随系统',
code: ThemeEnum.OS,
model: (() => (
<div class="wh-full flex">
<div class="bg-#f1f1f1 flex-[1] rounded-[6px_0_0_6px]"></div>
<div class="bg-#fff flex-[4.5] p-[8px_4px] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#f1f1f1 w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#f1f1f1 w-16px h-4px"></div>
<div class="bg-#f1f1f1 w-24px h-4px"></div>
</div>
</div>
</div>
<div class="bg-#454545 flex-[1]"></div>
<div class="bg-#212121 flex-[4.5] p-[8px_4px] rounded-[0_6px_6px_0] box-border flex flex-col gap-8px">
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
<div class="flex gap-4px">
<div class="bg-#4d4d4d w-10px h-10px rounded-50%"></div>
<div class="flex flex-col gap-2px">
<div class="bg-#4d4d4d w-16px h-4px"></div>
<div class="bg-#4d4d4d w-24px h-4px"></div>
</div>
</div>
</div>
</div>
))()
}
]
export { titleList }