feat(component): 新增是否启用界面阴影功能、收缩页面按钮功能

This commit is contained in:
nongyehong
2024-07-16 17:37:25 +08:00
parent 866ba89b93
commit 085a773967
53 changed files with 246 additions and 140 deletions

View File

@@ -7,4 +7,4 @@ VITE_APP_TITLE="HuLa—IM"
# 项目名称
VITE_APP_NAME="HuLa-IM-Tauri"
# gitee token
VITE_GITEE_TOKEN="xxxxxxxxxxxxxxx"
VITE_GITEE_TOKEN="0312a213a6b6882beb96f487e75661a6"

View File

@@ -7,7 +7,7 @@
<title>HuLa</title>
<!--引入iconpark图标库-->
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_111.4a4828943d33d677b9abb7a0669ca950.js"></script>
<script defer src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_30895_113.b759c39620e6fa93d145499865d57e99.js"></script>
</head>
<body>

View File

@@ -18,7 +18,7 @@ import router from '@/router'
const settingStore = setting()
const OLStatusStore = onlineStatus()
const { themes, lockScreen } = storeToRefs(settingStore)
const { themes, lockScreen, page } = storeToRefs(settingStore)
/** 不需要锁屏的页面 */
const LockExclusion = new Set(['/login', '/tray', '/qrCode', '/about', '/onlineStatus'])
const isLock = computed(() => {
@@ -34,6 +34,15 @@ const preventDrag = (e: MouseEvent) => {
}
}
/** 控制阴影 */
watch(
() => page.value.shadow,
(val) => {
document.documentElement.style.setProperty('--shadow-enabled', val ? '0' : '1')
},
{ immediate: true }
)
onMounted(() => {
// initWebSocket()
// /**! 使用msi或者其他安装包安装后才会显示应用的名字和图标 */

BIN
src/assets/img/lock_bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -84,7 +84,7 @@ import { CacheUserItem, MockItem } from '@/services/types.ts'
import { emit, listen } from '@tauri-apps/api/event'
import { setting } from '@/stores/setting.ts'
import { storeToRefs } from 'pinia'
import { sendOptions } from '@/views/home-window/more/settings/config.ts'
import { sendOptions } from '@/views/homeWindow/more/settings/config.ts'
import { useMsgInput } from '@/hooks/useMsgInput.ts'
import { useCommon } from '@/hooks/useCommon.ts'
import { onKeyStroke } from '@vueuse/core'

View File

@@ -1,8 +1,8 @@
<template>
<!-- 底部栏 -->
<main
class="size-full relative z-10 bg-[--right-bg-color] color-[--icon-color]"
style="box-shadow: 0 -4px 4px var(--box-shadow-color)">
style="box-shadow: var(--shadow-enabled) -4px 4px var(--box-shadow-color)"
class="size-full relative z-10 bg-[--right-bg-color] border-t-(1px solid [--line-color]) color-[--icon-color]">
<!-- 输入框顶部选项栏 -->
<n-flex align="center" justify="space-between" class="p-[10px_22px_5px] select-none">
<n-flex align="center" :size="0" class="input-options">

View File

@@ -1,8 +1,8 @@
<template>
<!-- 顶部操作栏和显示用户名 -->
<main
style="box-shadow: 0 4px 4px var(--box-shadow-color)"
class="relative z-30 flex-y-center justify-between p-[8px_20px_12px] select-none">
style="box-shadow: var(--shadow-enabled) 4px 4px var(--box-shadow-color)"
class="relative z-30 flex-y-center border-b-(1px solid [--line-color]) justify-between p-[6px_20px_12px] select-none">
<n-flex align="center">
<span class="color-[--text-color]">{{ activeItem.name }}</span>
<svg v-if="activeItem.hot_Flag === IsAllUserEnum.Yes" class="size-20px color-#13987f select-none outline-none">

View File

@@ -15,8 +15,8 @@
style="max-height: calc(100vh - 260px)"
:class="{ 'right-1px': activeItem.type === RoomTypeEnum.SINGLE }"
class="relative h-100vh"
:ignore-item-resize="true"
:item-resizable="true"
ignore-item-resize
item-resizable
:padding-top="10"
:item-size="itemSize"
:items="chatMessageList">
@@ -32,7 +32,7 @@
]">
<!-- 信息间隔时间 -->
<span
class="text-(12px #909090) select-none bg-[--time-color] p-4px rounded-6px shadow-md"
class="text-(12px #909090) select-none bg-[--time-color] p-4px rounded-6px custom-shadow"
v-if="item.timeBlock">
{{ item.timeBlock }}
</span>
@@ -239,7 +239,7 @@
:size="6"
v-if="item.message.body.reply"
@click="jumpToReplyMsg(item.message.body.reply.id)"
class="reply-bubble relative w-fit shadow-md">
class="reply-bubble relative w-fit custom-shadow">
<svg class="size-14px"><use href="#to-top"></use></svg>
<span>{{ `${item.message.body.reply.username}` }}</span>
<!-- 当回复消息为图片时渲染 -->

View File

@@ -1,6 +1,21 @@
<template>
<!--! 这里最好不要使用n-flex,滚动高度会有问题 -->
<main v-if="isGroup" style="border-left: 1px solid var(--line-color)" class="item-box shadow-md">
<main
v-if="isGroup"
:class="
isCollapsed ? 'w-180px border-l-(1px solid [--line-color]) p-[12px_0_12px_6px] custom-shadow' : 'w-0 pr-1px'
"
class="item-box">
<!-- 收缩按钮 -->
<div
@click.stop="isCollapsed = !isCollapsed"
style="border-radius: 18px 0 0 18px"
class="contraction transition-all duration-600 ease-in-out absolute top-35% left--14px cursor-pointer opacity-0 bg-#c8c8c833 h-60px w-14px">
<svg :class="isCollapsed ? 'rotate-180' : 'rotate-0'" class="size-16px color-#909090 absolute top-38%">
<use href="#left-arrow"></use>
</svg>
</div>
<n-flex v-if="!isSearch" align="center" justify="space-between" class="pr-8px pl-8px h-42px">
<span class="text-14px">群聊成员&nbsp;{{ userList.length }}</span>
<svg @click="handleSearch" class="size-14px"><use href="#search"></use></svg>
@@ -103,6 +118,7 @@ const searchRef = ref('')
/** 手动触发Popover显示 */
const infoPopover = ref(false)
const inputInstRef = ref<InputInst | null>(null)
const isCollapsed = ref(true)
const { optionsList, report, selectKey } = useChatMain()
const { handlePopoverUpdate } = usePopover(selectKey, 'image-chat-sidebar')

View File

@@ -8,7 +8,7 @@
<!-- 输入框和操作列表 -->
<ChatFooter class="flex-1" />
</n-flex>
<ChatSidebar :active-item="activeItemRef" />
<ChatSidebar />
</n-flex>
</template>
<script setup lang="ts">

View File

@@ -184,8 +184,8 @@ export const useMsgInput = (messageInputDom: Ref) => {
})
}
// 判断文本信息是否超过限制
if (msg.type === MsgEnum.TEXT && msg.content.length > 2000) {
window.$message.info('消息内容超过限制2000删减内容')
if (msg.type === MsgEnum.TEXT && msg.content.length > 500) {
window.$message.info('消息内容超过限制500分段发送')
return
}
// TODO 当输入的类型是混合类型如输入文本加上图片的类型需要处理 (nyh -> 2024-02-28 06:32:13)

View File

@@ -1,6 +1,19 @@
<template>
<main id="center" class="resizable select-none flex flex-col shadow-inner" :style="{ width: `${initWidth}px` }">
<div class="resize-handle" @mousedown="initDrag"></div>
<main
id="center"
class="resizable select-none flex flex-col border-r-(1px solid [--line-color])"
:style="{ width: `${initWidth}px` }">
<!-- 分隔条 -->
<div v-if="!shrinkStatus" class="resize-handle transition-all duration-600 ease-in-out" @mousedown="initDrag">
<div :class="{ 'opacity-100': isDragging }" class="transition-all duration-600 ease-in-out opacity-0 drag-icon">
<div style="border-radius: 8px 0 0 8px" class="bg-#c8c8c833 h-60px w-14px absolute top-40% right-0 drag-icon">
<svg class="size-16px absolute top-1/2 right--2px transform -translate-y-1/2 color-#909090">
<use href="#sliding"></use>
</svg>
</div>
</div>
</div>
<ActionBar
class="absolute right-0 w-full"
v-if="shrinkStatus"
@@ -10,8 +23,8 @@
<!-- 顶部搜索栏 -->
<header
style="box-shadow: 0 2px 4px var(--box-shadow-color)"
class="mt-30px w-full h-38px flex flex-col items-center">
style="box-shadow: var(--shadow-enabled) 4px 4px var(--box-shadow-color)"
class="mt-30px w-full h-40px flex flex-col items-center border-b-(1px solid [--line-color])">
<div class="flex-center gap-5px w-full pr-16px pl-16px box-border">
<n-input
id="search"
@@ -96,6 +109,7 @@ const addPanels = ref({
const startX = ref()
const startWidth = ref()
const shrinkStatus = ref(false)
const isDragging = ref(false)
watchEffect(() => {
if (width.value >= 310 && width.value < 800) {
@@ -149,6 +163,7 @@ const initDrag = (e: MouseEvent) => {
if (!isDrag.value) return
startX.value = e.clientX
startWidth.value = initWidth.value
isDragging.value = true
document.addEventListener('mousemove', doDrag, false)
document.addEventListener('mouseup', stopDrag, false)
}
@@ -156,6 +171,12 @@ const initDrag = (e: MouseEvent) => {
const stopDrag = () => {
document.removeEventListener('mousemove', doDrag, false)
document.removeEventListener('mouseup', stopDrag, false)
isDragging.value = false
setTimeout(() => {
// 移除 hover 样式
const resizeHandle = document.querySelector('.resize-handle') as HTMLElement
resizeHandle.classList.remove('hover')
}, 1000)
}
onMounted(async () => {

View File

@@ -10,10 +10,15 @@
right: 0;
top: 0;
bottom: 0;
width: 1px;
width: 6px;
cursor: ew-resize;
z-index: 9999;
background-color: var(--split-color);
background: transparent;
&:hover {
.drag-icon {
opacity: 1;
}
}
}
.add-item {

View File

@@ -1,5 +1,5 @@
<template>
<main class="flex-1 bg-[--right-bg-color] h-full w-100vw min-w-600px shadow-inner">
<main class="flex-1 bg-[--right-bg-color] h-full w-100vw min-w-600px">
<ActionBar :current-label="appWindow.label" />
<!-- 需要判断当前路由是否是信息详情界面 -->
<ChatBox :active-item="activeItem" v-if="msgBoxShow && isChat && activeItem !== -1" />

View File

@@ -7,12 +7,12 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login-window/Login.vue')
component: () => import('@/views/loginWindow/Login.vue')
},
{
path: '/qrCode',
name: 'qrCode',
component: () => import('@/views/login-window/QRCode.vue')
component: () => import('@/views/loginWindow/QRCode.vue')
},
{
path: '/tray',
@@ -27,7 +27,7 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/message',
name: 'message',
component: () => import('@/views/home-window/message/index.vue')
component: () => import('@/views/homeWindow/message/index.vue')
},
{
path: '/friendsList',
@@ -37,29 +37,29 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/searchDetails',
name: 'searchDetails',
component: () => import('@/views/home-window/SearchDetails.vue')
component: () => import('@/views/homeWindow/SearchDetails.vue')
}
]
},
{
path: '/robot',
name: 'robot',
component: () => import('@/views/home-window/robot/index.vue'),
component: () => import('@/views/homeWindow/robot/index.vue'),
children: [
{
path: '/welcome',
name: 'welcome',
component: () => import('@/views/home-window/robot/views/Welcome.vue')
component: () => import('@/views/homeWindow/robot/views/Welcome.vue')
},
{
path: '/chat',
name: 'chat',
component: () => import('@/views/home-window/robot/views/Chat.vue')
component: () => import('@/views/homeWindow/robot/views/Chat.vue')
},
{
path: '/chatSettings',
name: 'chatSettings',
component: () => import('@/views/home-window/robot/views/chatSettings/index.vue')
component: () => import('@/views/homeWindow/robot/views/chatSettings/index.vue')
}
]
},
@@ -81,32 +81,32 @@ const routes: Array<RouteRecordRaw> = [
{
path: '/about',
name: 'about',
component: () => import('@/views/home-window/more/About.vue')
component: () => import('@/views/homeWindow/more/About.vue')
},
{
path: '/alone',
name: 'alone',
component: () => import('@/views/home-window/message/Alone.vue')
component: () => import('@/views/homeWindow/message/Alone.vue')
},
{
path: '/sharedScreen',
name: 'sharedScreen',
component: () => import('@/views/home-window/SharedScreen.vue')
component: () => import('@/views/homeWindow/SharedScreen.vue')
},
{
path: '/settings',
name: 'settings',
component: () => import('@/views/home-window/more/settings/index.vue'),
component: () => import('@/views/homeWindow/more/settings/index.vue'),
children: [
{
path: '/general',
name: 'general',
component: () => import('@/views/home-window/more/settings/General.vue')
component: () => import('@/views/homeWindow/more/settings/General.vue')
},
{
path: '/loginSetting',
name: 'loginSetting',
component: () => import('@/views/home-window/more/settings/LoginSetting.vue')
component: () => import('@/views/homeWindow/more/settings/LoginSetting.vue')
}
]
}

View File

@@ -1,8 +1,8 @@
import Dynamic from '@/views/home-window/Dynamic.vue'
import Mail from '@/views/home-window/Mail.vue'
import OnlineStatus from '@/views/home-window/onlineStatus/index.vue'
import Dynamic from '@/views/homeWindow/Dynamic.vue'
import Mail from '@/views/homeWindow/Mail.vue'
import OnlineStatus from '@/views/homeWindow/onlineStatus/index.vue'
import Tray from '@/views/Tray.vue'
import Layout from '@/layout/index.vue'
import FriendsList from '@/views/home-window/FriendsList.vue'
import FriendsList from '@/views/homeWindow/FriendsList.vue'
export { Dynamic, Mail, OnlineStatus, Tray, Layout, FriendsList }

View File

@@ -1,6 +1,6 @@
import { defineStore } from 'pinia'
import { StoresEnum } from '@/enums'
import { statusItem } from '@/views/home-window/onlineStatus/config.ts'
import { statusItem } from '@/views/homeWindow/onlineStatus/config.ts'
import Colorthief from 'colorthief'
const colorthief = new Colorthief()

View File

@@ -45,6 +45,10 @@ export const setting = defineStore(StoresEnum.SETTING, {
sendKey: 'Enter',
/** 是否双击打开独立会话窗口 */
isDouble: true
},
/** 界面设置 */
page: {
shadow: false
}
}),
actions: {

View File

@@ -14,7 +14,7 @@
}
.setting-item {
@apply border-(solid 1px [--line-color]) shadow-md;
@apply border-(solid 1px [--line-color]) custom-shadow;
&:first-child {
margin-top: 0;
}
@@ -51,7 +51,7 @@
}
.sidebar {
@apply flex flex-col absolute top-44px right-0 bg-[--bg-chat-drawer] p-22px box-border w-320px h-100vh shadow-[0_14px_14px_rgba(0,0,0,0.35)];
@apply flex flex-col absolute top-42px right-0 bg-[--bg-chat-drawer] p-22px box-border w-320px h-100vh shadow-[0_14px_14px_rgba(0,0,0,0.35)];
}
/**! 使用vue内置transition做过渡效果 */

View File

@@ -1,6 +1,6 @@
/** 气泡样式 */
@mixin bubble {
@apply w-fit max-w-35vw min-h-1em p-[8px_12px] text-15px line-height-22px bg-[--bg-bubble] rounded-[2px_18px_18px] shadow-md;
@apply w-fit max-w-35vw min-h-1em p-[8px_12px] text-15px line-height-22px bg-[--bg-bubble] rounded-[2px_18px_18px] custom-shadow;
word-break: break-all; /** 强制连续文本换行 */
&.active {
background-color: var(--bg-bubble-active);
@@ -24,6 +24,7 @@
}
/**! 气泡动画 */
.bubble-animation {
@apply transform-gpu;
animation: bubble-twinkle 0.4s ease-out forwards;
}
.photo-wall {
@@ -75,7 +76,7 @@
}
/** emoji回复气泡的样式 */
.emoji-reply-bubble {
@apply relative rounded-50px p-[4px_8px] cursor-pointer select-none bg-#13987F66 text-14px w-fit border-(1px solid #13987F) shadow-md;
@apply relative rounded-50px p-[4px_8px] cursor-pointer select-none bg-#13987F66 text-14px w-fit border-(1px solid #13987F) custom-shadow;
}
/** 跳转到回复内容时候显示的样式 */
.active-reply {

View File

@@ -1,5 +1,5 @@
.item-box {
@apply flex flex-col w-180px h-100vh p-[12px_0_12px_6px] box-border select-none text-[--text-color];
@apply relative flex flex-col h-100vh box-border select-none text-[--text-color];
.item {
height: 42px;
padding: 0 4px;
@@ -11,6 +11,11 @@
background-color: var(--bg-group-hover);
}
}
&:hover {
.contraction {
opacity: 1;
}
}
}
/**! 修改naive-ui虚拟列表滚动条的宽度 */
:deep(

View File

@@ -59,8 +59,6 @@
--reply-color: #909090;
--reply-hover: #505050;
--bg-reply-img-count: #e3e3e3;
// 主页面面板分割线样式
--split-color: #f1f1f1;
// 编辑资料背景颜色
--bg-edit: #f0f0f0;
// 聊天框时间戳样式
@@ -71,6 +69,9 @@
--chat-right-bg: #f1f1f1;
--chat-text-color: #505050;
--chat-hover-color: #e3e3e3;
// 是否启用阴影
--shadow-enabled: 1;
}
html[data-theme='dark'] {
@@ -133,8 +134,6 @@ html[data-theme='dark'] {
--reply-color: #e3e3e3;
--reply-hover: #b1b1b1;
--bg-reply-img-count: #505050;
// 主页面面板分割线样式
--split-color: #3b3b3b;
// 编辑资料背景颜色
--bg-edit: #262626;
// 聊天框时间戳样式

View File

@@ -7,59 +7,59 @@ export {}
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']
ChatSidebar: (typeof import('./../components/rightBox/chatBox/ChatSidebar.vue'))['default']
ContextMenu: (typeof import('./../components/common/ContextMenu.vue'))['default']
Details: (typeof import('./../components/rightBox/Details.vue'))['default']
Emoji: (typeof import('./../components/rightBox/emoji/index.vue'))['default']
Image: (typeof import('./../components/rightBox/renderMessage/Image.vue'))['default']
InfoPopover: (typeof import('./../components/common/InfoPopover.vue'))['default']
MsgInput: (typeof import('./../components/rightBox/MsgInput.vue'))['default']
NaiveProvider: (typeof import('./../components/common/NaiveProvider.vue'))['default']
NAlert: (typeof import('naive-ui'))['NAlert']
NAvatar: (typeof import('naive-ui'))['NAvatar']
NAvatarGroup: (typeof import('naive-ui'))['NAvatarGroup']
NBadge: (typeof import('naive-ui'))['NBadge']
NButton: (typeof import('naive-ui'))['NButton']
NButtonGroup: (typeof import('naive-ui'))['NButtonGroup']
NCheckbox: (typeof import('naive-ui'))['NCheckbox']
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']
ChatSidebar: typeof import('./../components/rightBox/chatBox/ChatSidebar.vue')['default']
ContextMenu: typeof import('./../components/common/ContextMenu.vue')['default']
Details: typeof import('./../components/rightBox/Details.vue')['default']
Emoji: typeof import('./../components/rightBox/emoji/index.vue')['default']
Image: typeof import('./../components/rightBox/renderMessage/Image.vue')['default']
InfoPopover: typeof import('./../components/common/InfoPopover.vue')['default']
MsgInput: typeof import('./../components/rightBox/MsgInput.vue')['default']
NaiveProvider: typeof import('./../components/common/NaiveProvider.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NAvatar: typeof import('naive-ui')['NAvatar']
NAvatarGroup: typeof import('naive-ui')['NAvatarGroup']
NBadge: typeof import('naive-ui')['NBadge']
NButton: typeof import('naive-ui')['NButton']
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCode: (typeof import('naive-ui'))['NCode']
NCollapse: (typeof import('naive-ui'))['NCollapse']
NCollapseItem: (typeof import('naive-ui'))['NCollapseItem']
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']
NImage: (typeof import('naive-ui'))['NImage']
NImageGroup: (typeof import('naive-ui'))['NImageGroup']
NInput: (typeof import('naive-ui'))['NInput']
NLoadingBarProvider: (typeof import('naive-ui'))['NLoadingBarProvider']
NMessageProvider: (typeof import('naive-ui'))['NMessageProvider']
NModal: (typeof import('naive-ui'))['NModal']
NModalProvider: (typeof import('naive-ui'))['NModalProvider']
NNotificationProvider: (typeof import('naive-ui'))['NNotificationProvider']
NPopover: (typeof import('naive-ui'))['NPopover']
NPopselect: (typeof import('naive-ui'))['NPopselect']
NQrCode: (typeof import('naive-ui'))['NQrCode']
NRadio: (typeof import('naive-ui'))['NRadio']
NScrollbar: (typeof import('naive-ui'))['NScrollbar']
NSelect: (typeof import('naive-ui'))['NSelect']
NSkeleton: (typeof import('naive-ui'))['NSkeleton']
NSwitch: (typeof import('naive-ui'))['NSwitch']
NTabPane: (typeof import('naive-ui'))['NTabPane']
NTabs: (typeof import('naive-ui'))['NTabs']
NTooltip: (typeof import('naive-ui'))['NTooltip']
NVirtualList: (typeof import('naive-ui'))['NVirtualList']
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']
NCollapse: typeof import('naive-ui')['NCollapse']
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
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']
NImage: typeof import('naive-ui')['NImage']
NImageGroup: typeof import('naive-ui')['NImageGroup']
NInput: typeof import('naive-ui')['NInput']
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
NModal: typeof import('naive-ui')['NModal']
NModalProvider: typeof import('naive-ui')['NModalProvider']
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopover: typeof import('naive-ui')['NPopover']
NPopselect: typeof import('naive-ui')['NPopselect']
NQrCode: typeof import('naive-ui')['NQrCode']
NRadio: typeof import('naive-ui')['NRadio']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSkeleton: typeof import('naive-ui')['NSkeleton']
NSwitch: typeof import('naive-ui')['NSwitch']
NTabPane: typeof import('naive-ui')['NTabPane']
NTabs: typeof import('naive-ui')['NTabs']
NTooltip: typeof import('naive-ui')['NTooltip']
NVirtualList: typeof import('naive-ui')['NVirtualList']
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']
}
}

View File

@@ -45,15 +45,28 @@ declare namespace STO {
/** 是否双击打开独立会话窗口 */
isDouble: boolean
}
/** 界面设置 */
page: {
/** 是否开启阴影 */
shadow: boolean
}
}
/** 置顶 */
/** 置顶窗口列表 */
type AlwaysOnTop = {
/** 是否置顶窗口列表 */
[key: string]: boolean
}
/** 隐藏窗口列表 */
type HideWindow = {
/** 是否隐藏窗口列表 */
[key: string]: boolean
}
/** 历史内容 */
type History = {
/** emoji列表 */
emoji: string[]
}
}

View File

@@ -1,5 +1,6 @@
<template>
<!-- 锁屏页面 -->
<!-- // TODO 锁屏时隐藏其他窗口,解锁后再显示 (nyh -> 2024-07-14 01:39:01) -->
<div
data-tauri-drag-region
class="lock-bg select-none absolute top-0 left-0 w-full h-full z-9999 transition-all duration-300 ease-in-out">
@@ -56,8 +57,8 @@
ref="inputInstRef"
style="
width: 320px;
border: 2px solid rgba(255, 255, 255, 0.4);
border-bottom-color: rgba(19, 152, 127, 0.8);
border: 2px solid rgba(255, 255, 255, 0.1);
border-bottom-color: rgba(19, 152, 127, 1);
background-color: #404040;
color: #fff;
"
@@ -212,7 +213,10 @@ onUnmounted(() => {
</script>
<style scoped lang="scss">
.lock-bg {
background-image: url('@/assets/img/lock_bg.png');
background-image: url('@/assets/img/lock_bg.jpg');
background-size: cover; // 或者使用 contain取决于你想要的效果
background-position: center; // 确保图片居中
background-repeat: no-repeat; // 防止图片重复
}
.options {

View File

@@ -51,7 +51,7 @@
import { useWindow } from '@/hooks/useWindow.ts'
import { invoke } from '@tauri-apps/api/tauri'
import { exit } from '@tauri-apps/api/process'
import { statusItem } from './home-window/onlineStatus/config.ts'
import { statusItem } from '@/views/homeWindow/onlineStatus/config.ts'
import { onlineStatus } from '@/stores/onlineStatus.ts'
import { appWindow } from '@tauri-apps/api/window'
import { listen } from '@tauri-apps/api/event'

View File

@@ -40,7 +40,7 @@
vertical
v-for="item in dynamicList"
:key="item.id"
class="w-450px h-fit border-(solid 1px [--line-color]) shadow-md rounded-8px bg-[--right-bg-color] p-10px box-border">
class="w-450px h-fit border-(solid 1px [--line-color]) custom-shadow rounded-8px bg-[--right-bg-color] p-10px box-border">
<n-flex align="center">
<!-- 用户的头像和用户名以及个签 -->
<img class="size-45px bg-#ccc rounded-50% select-none" :src="item.avatar" alt="" />

View File

@@ -13,14 +13,13 @@
<!-- 用户框 多套一层div来移除默认的右键事件然后覆盖掉因为margin空隙而导致右键可用 -->
<div @contextmenu.stop="$event.preventDefault()">
<n-flex
v-slide
:size="10"
@click="handleClick(item.uid, RoomTypeEnum.SINGLE)"
:class="{ active: activeItem === item.uid }"
class="user-box w-full h-75px mb-5px"
v-for="item in contactStore.contactsList"
:key="item.uid">
<n-flex v-slide align="center" :size="10" class="h-75px pl-6px pr-8px flex-1 truncate">
<n-flex align="center" :size="10" class="h-75px pl-6px pr-8px flex-1 truncate">
<n-avatar
round
bordered

View File

@@ -17,7 +17,7 @@
<template v-for="(item, _index) in historyList" :key="_index">
<n-flex align="center" :size="14" class="p-6px cursor-pointer rounded-8px hover:bg-[--bg-group-hover]">
<n-avatar :size="38" round bordered :src="item.avatar" />
<p class="text-(16px [--text-color])">{{ item.name }}</p>
<p class="text-(14px [--text-color])">{{ item.name }}</p>
</n-flex>
</template>
</n-flex>

View File

@@ -16,8 +16,7 @@
@click="onSelectSelectSession(item, item.type)"
@dblclick="handleMsgDblclick(item)"
@select="$event.click(item)">
<!-- 消息框使用v-slide自定义指令来自动抉择右键菜单位置 -->
<n-flex v-slide :size="10" align="center" class="h-75px pl-6px pr-8px flex-1">
<n-flex :size="10" align="center" class="h-75px pl-6px pr-8px flex-1">
<n-avatar :color="'#fff'" :size="44" :src="item.avatar" bordered fallback-src="/logo.png" round />
<n-flex class="h-fit flex-1 truncate" justify="space-between" vertical>

View File

@@ -14,7 +14,7 @@
:key="index">
<div
@click="handleTheme(item.code)"
class="size-full rounded-8px cursor-pointer shadow-md"
class="size-full rounded-8px cursor-pointer custom-shadow"
:class="{ 'outline outline-2 outline-#13987f outline-offset': activeItem === item.code }">
<component :is="item.model" />
</div>
@@ -84,6 +84,20 @@
</n-flex>
</n-flex>
</n-flex>
<!-- 界面设置 -->
<n-flex vertical class="text-(14px [--text-color])" :size="16">
<span class="pl-10px">界面</span>
<n-flex class="item" :size="15" vertical>
<!-- 发送信息 -->
<n-flex align="center" justify="space-between">
<span>是否开启阴影</span>
<n-switch size="small" v-model:value="page.shadow" />
</n-flex>
</n-flex>
</n-flex>
</n-flex>
</template>
<script setup lang="tsx">
@@ -94,7 +108,7 @@ import { topicsList } from './model.tsx'
import { sendOptions } from './config.ts'
const settingStore = setting()
const { themes, tips, escClose, chat } = storeToRefs(settingStore)
const { themes, tips, escClose, chat, page } = storeToRefs(settingStore)
const activeItem = ref<string>(themes.value.pattern)
/** 切换主题 */
@@ -106,6 +120,6 @@ const handleTheme = (code: string) => {
<style scoped lang="scss">
.item {
@apply bg-[--bg-setting-item] rounded-12px size-full p-12px box-border border-(solid 1px [--line-color]) shadow-md;
@apply bg-[--bg-setting-item] rounded-12px size-full p-12px box-border border-(solid 1px [--line-color]) custom-shadow;
}
</style>

View File

@@ -47,6 +47,6 @@ const clearInfo = () => {
<style scoped lang="scss">
.item-box {
@apply text-14px text-[--text-color] bg-[--bg-setting-item] rounded-8px p-10px border-(solid 1px [--line-color]) shadow-md;
@apply text-14px text-[--text-color] bg-[--bg-setting-item] rounded-8px p-10px border-(solid 1px [--line-color]) custom-shadow;
}
</style>

View File

@@ -13,10 +13,12 @@
</section>
<!-- 右边内容 -->
<section class="bg-[--right-bg-color] flex-1 shadow-md">
<section class="bg-[--right-bg-color] flex-1 custom-shadow border-l-(1px solid [--line-color])">
<ActionBar :shrink="false" :max-w="false" />
<header class="header" style="box-shadow: 0 4px 4px var(--box-shadow-color)">{{ title }}</header>
<header class="header" style="box-shadow: var(--shadow-enabled) 4px 4px var(--box-shadow-color)">
{{ title }}
</header>
<div class="flex-1 p-24px"><router-view /></div>
</section>
@@ -80,6 +82,6 @@ onMounted(() => {
}
.header {
@apply w-full h-42px flex items-center pl-40px select-none text-18px color-[--text-color];
@apply w-full h-42px flex items-center pl-40px select-none text-18px color-[--text-color] border-b-(1px solid [--line-color]);
}
</style>

View File

@@ -79,14 +79,14 @@
<n-flex :size="12" align="center">
<div
@click="jump"
class="bg-[--chat-bt-color] color-[--chat-text-color] size-fit p-[8px_9px] rounded-8px shadow-md cursor-pointer">
class="bg-[--chat-bt-color] border-(1px solid [--line-color]) color-[--chat-text-color] size-fit p-[8px_9px] rounded-8px custom-shadow cursor-pointer">
<svg class="size-18px"><use href="#settings"></use></svg>
</div>
<a
target="_blank"
rel="noopener noreferrer"
href="https://github.com/nongyehong/HuLa-IM-Tauri"
class="bg-[--chat-bt-color] color-[--chat-text-color] size-fit p-[8px_9px] rounded-8px shadow-md cursor-pointer">
class="bg-[--chat-bt-color] border-(1px solid [--line-color]) color-[--chat-text-color] size-fit p-[8px_9px] rounded-8px custom-shadow cursor-pointer">
<svg class="size-18px"><use href="#github"></use></svg>
</a>
</n-flex>
@@ -95,7 +95,7 @@
:size="4"
align="center"
@click="add"
class="bg-[--chat-bt-color] select-none text-(14px [--chat-text-color]) size-fit p-8px rounded-8px shadow-md cursor-pointer">
class="bg-[--chat-bt-color] border-(1px solid [--line-color]) select-none text-(14px [--chat-text-color]) size-fit p-8px rounded-8px custom-shadow cursor-pointer">
<svg class="size-18px"><use href="#plus"></use></svg>
<p>新的聊天</p>
</n-flex>
@@ -250,12 +250,12 @@ onMounted(() => {
@apply bg-clip-text text-transparent bg-gradient-to-r from-#38BDF8 to-#13987F text-20px font-800;
}
.plugins {
@apply size-fit bg-[--chat-bt-color] rounded-8px shadow-md p-[8px_14px]
@apply size-fit bg-[--chat-bt-color] rounded-8px custom-shadow p-[8px_14px]
flex items-center gap-10px select-none cursor-pointer
text-14px color-[--chat-text-color];
text-14px color-[--chat-text-color] border-(1px solid [--line-color]);
}
.chat-item {
@apply relative bg-[--chat-bt-color] cursor-pointer shadow-md rounded-8px w-full h-65px;
@apply relative bg-[--chat-bt-color] border-(1px solid [--line-color]) cursor-pointer custom-shadow rounded-8px w-full h-65px;
&:hover {
@apply bg-[--chat-hover-color];
svg {

View File

@@ -1,5 +1,8 @@
<template>
<n-flex vertical :size="0" class="flex-1 truncate shadow-md select-none text-[--text-color]">
<n-flex
vertical
:size="0"
class="flex-1 truncate border-l-(1px solid [--line-color]) custom-shadow select-none text-[--text-color]">
<!-- 右上角操作栏 -->
<ActionBar class="w-full" :shrink="false" :current-label="appWindow.label" :top-win-label="appWindow.label" />

View File

@@ -2,7 +2,9 @@
<!-- 主体内容 -->
<!-- // TODO 使 (nyh -> 2024-07-01 10:44:14)-->
<main>
<div class="flex truncate p-[14px_20px] justify-between items-center gap-50px">
<div
style="box-shadow: var(--shadow-enabled) 4px 4px var(--box-shadow-color)"
class="flex truncate p-[14px_20px] justify-between items-center gap-50px">
<n-flex :size="10" vertical class="truncate">
<p
v-if="!isEdit"
@@ -38,7 +40,7 @@
<div class="h-1px bg-[--line-color]"></div>
<!-- 聊天信息框 -->
<div class="w-full shadow-inner p-[28px_16px] box-border" style="height: calc(100vh - 300px)">
<div class="w-full p-[28px_16px] box-border" style="height: calc(100vh - 300px)">
<n-flex :size="6">
<n-avatar
class="rounded-8px"
@@ -61,7 +63,7 @@
<n-flex
vertical
:size="6"
style="box-shadow: 0 -4px 4px 0 rgba(0, 0, 0, 0.05)"
style="box-shadow: var(--shadow-enabled) -4px 4px 0 rgba(0, 0, 0, 0.05)"
class="size-full p-[14px_22px] box-border">
<n-flex :size="26" class="options">
<n-popover v-for="(item, index) in features" :key="index" trigger="hover" :show-arrow="false" placement="top">
@@ -136,7 +138,7 @@ onMounted(() => {
<style scoped lang="scss">
@import '@/styles/scss/chat-main';
.right-btn {
@apply size-fit cursor-pointer bg-[--chat-bt-color] color-[--chat-text-color] rounded-8px shadow-md p-[10px_11px];
@apply size-fit border-(1px solid [--line-color]) cursor-pointer bg-[--chat-bt-color] color-[--chat-text-color] rounded-8px custom-shadow p-[10px_11px];
svg {
@apply size-18px;
}

View File

@@ -129,7 +129,7 @@ const examplesList: Example = [
<style lang="scss">
.examples {
@apply w-300px h-fit rounded-12px p-10px box-border cursor-pointer border-(solid 1px [--line-color]) shadow-md;
@apply w-300px h-fit rounded-12px p-10px box-border cursor-pointer border-(solid 1px [--line-color]) custom-shadow;
&:hover {
.search-item:not(:hover) {
@apply blur-md scale-94;

View File

@@ -1,5 +1,7 @@
<template>
<div class="flex truncate p-[14px_20px] justify-between items-center gap-50px">
<div
style="box-shadow: var(--shadow-enabled) 4px 4px var(--box-shadow-color)"
class="flex border-b-(1px solid [--line-color]) truncate p-[14px_20px] justify-between items-center gap-50px">
<n-flex :size="10" vertical class="truncate">
<p class="text-(22px [--chat-text-color]) truncate font-bold">设置</p>
<p class="text-(14px #707070)">所有设置选项</p>
@@ -11,15 +13,14 @@
</div>
</n-flex>
</div>
<div class="h-1px bg-[--line-color]"></div>
<!-- 设置的主体内容 -->
<n-scrollbar style="max-height: calc(100vh - 104px)">
<n-flex vertical :size="20" class="p-[20px_0] shadow-inner">
<n-flex vertical :size="20" class="p-[20px_0]">
<div v-for="(key, index) in content" :key="index" class="flex flex-1 p-[0_20px]">
<n-flex
vertical
class="w-full h-fit bg-[--bg-setting-item] border-(solid 1px [--line-color]) shadow-md rounded-8px p-10px">
class="w-full h-fit bg-[--bg-setting-item] border-(solid 1px [--line-color]) custom-shadow rounded-8px p-10px">
<n-flex vertical justify="center" v-for="(item, index) in key" :key="index">
<n-flex justify="space-between" :size="0" align="center" class="p-8px">
<n-flex vertical :size="4">
@@ -52,7 +53,7 @@ const handleClose = () => {
</script>
<style scoped lang="scss">
.right-btn {
@apply size-fit cursor-pointer bg-[--chat-bt-color] color-[--chat-text-color] rounded-8px shadow-md p-[10px_11px];
@apply size-fit border-(1px solid [--line-color]) cursor-pointer bg-[--chat-bt-color] color-[--chat-text-color] rounded-8px custom-shadow p-[10px_11px];
svg {
@apply size-18px;
}

View File

@@ -11,6 +11,15 @@ export default defineConfig({
},
presets: [presetUno({ dark: 'class' })],
transformers: [transformerDirectives(), transformerVariantGroup()],
/** 自定义规则 */
rules: [
[
/^custom-shadow$/,
() => ({
'box-shadow': 'var(--shadow-enabled) 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
})
]
],
/**
* 快捷键命名标准
* @default '布局样式 - 水平样式 - 垂直样式'