u
This commit is contained in:
1 parent
bcc4722efc
commit
f48536774f
403 files changed
+34776
No files matched your search
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<Topbar></Topbar>
|
||||
<Center></Center>
|
||||
<!-- <Footer></Footer> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Topbar from "../topbar/index.vue";
|
||||
import Center from "../center/index.vue";
|
||||
// import Footer from "../footer/index.vue";
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="footer">footer</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer {
|
||||
width: 100%;
|
||||
padding-bottom: 20px;
|
||||
background: #efeff1;
|
||||
margin-top: 88px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<a-badge :count="imMessageStore.allNoReadNumber" :max-count="99" class="badge" @click="handleToIm">
|
||||
<div class="i-material-symbols:notifications-outline icon"></div>
|
||||
</a-badge>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useImMessageStore } from "@/store/imMessageStore";
|
||||
import { getAllNoReadNumberApi } from "@/service/message";
|
||||
import { WebSocketType } from "@/types/websocket";
|
||||
import { useWebSocketStore } from "@/store/webSocketStore";
|
||||
|
||||
const router = useRouter();
|
||||
const imMessageStore = useImMessageStore();
|
||||
const webSocketStore = useWebSocketStore();
|
||||
|
||||
const unlisten = ref<() => void>(); // 类型为 undefined | (() => void)
|
||||
|
||||
onMounted(() => {
|
||||
getAllNoReadNumberApi({}).then((res) => {
|
||||
imMessageStore.setAllNoReadNumber(res.data.noReadNumber);
|
||||
});
|
||||
unlisten.value = webSocketStore.onMessage((data) => {
|
||||
if (data.type === WebSocketType.AcceptMessageEnums.ONE) {
|
||||
imMessageStore.setAllNoReadNumber(imMessageStore.allNoReadNumber + 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
unlisten.value?.();
|
||||
});
|
||||
|
||||
function handleToIm() {
|
||||
router.push("/im");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
font-size: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<a-badge :count="imMessageStore.allNoReadNumber" :max-count="99" class="badge" @click="handleToMessages">
|
||||
<div class="i-material-symbols:notifications-outline icon"></div>
|
||||
</a-badge>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useImMessageStore } from "@/store/imMessageStore";
|
||||
import { getAllNoReadNumberApi } from "@/service/message";
|
||||
import { WebSocketType } from "@/types/websocket";
|
||||
import { useWebSocketStore } from "@/store/webSocketStore";
|
||||
|
||||
const router = useRouter();
|
||||
const imMessageStore = useImMessageStore();
|
||||
const webSocketStore = useWebSocketStore();
|
||||
|
||||
const unlisten = ref<() => void>(); // 类型为 undefined | (() => void)
|
||||
|
||||
onMounted(() => {
|
||||
getAllNoReadNumberApi({}).then((res) => {
|
||||
imMessageStore.setAllNoReadNumber(res.data.noReadNumber);
|
||||
});
|
||||
unlisten.value = webSocketStore.onMessage((data) => {
|
||||
if (data.type === WebSocketType.AcceptMessageEnums.ONE) {
|
||||
imMessageStore.setAllNoReadNumber(imMessageStore.allNoReadNumber + 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
unlisten.value?.();
|
||||
});
|
||||
|
||||
function handleToMessages() {
|
||||
router.push("/messages");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<a-button v-if="!userStore.isLogined" type="primary" shape="round" @click="handleToLogin()">登录/注册</a-button>
|
||||
<a-dropdown v-else>
|
||||
<div class="user-message">
|
||||
<a-avatar class="user-message-avatar">
|
||||
<img src="/RightAvatar.png" />
|
||||
</a-avatar>
|
||||
<div class="user-message-name">{{ userStore.userInfo.name }}</div>
|
||||
</div>
|
||||
<template #content>
|
||||
<a-doption @click="handleToUser">
|
||||
<template #icon>
|
||||
<SvgIcon icon="material-symbols:settings-outline-rounded"></SvgIcon>
|
||||
</template>
|
||||
个人设置
|
||||
</a-doption>
|
||||
<a-doption @click="handleToLogin()">
|
||||
<template #icon>
|
||||
<SvgIcon icon="material-symbols:exit-to-app"></SvgIcon>
|
||||
</template>
|
||||
退出登录
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import { useUserStore } from "@/store/user";
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
function handleToLogin() {
|
||||
userStore.clearUserInfo();
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
function handleToUser() {
|
||||
router.push("/user");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-message {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
.user-message:hover {
|
||||
background-color: #ececed;
|
||||
}
|
||||
.user-message-avatar {
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<a-button v-if="!userStore.isLogined" type="primary" shape="round" @click="handleToLogin()">登录/注册</a-button>
|
||||
<a-dropdown v-else>
|
||||
<div class="user-message">
|
||||
<SafeImage :url="userStore.userInfo.avatar" class="user-message-avatar" />
|
||||
<div class="user-message-name">{{ userStore.userInfo.name }}</div>
|
||||
</div>
|
||||
<template #content>
|
||||
<a-doption @click="handleToUser">
|
||||
<template #icon>
|
||||
<SvgIcon icon="material-symbols:settings-outline-rounded"></SvgIcon>
|
||||
</template>
|
||||
个人设置
|
||||
</a-doption>
|
||||
<a-doption @click="handleToLogin()">
|
||||
<template #icon>
|
||||
<SvgIcon icon="material-symbols:exit-to-app"></SvgIcon>
|
||||
</template>
|
||||
退出登录
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SafeImage from "@/components/safe-image/index.vue";
|
||||
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
function handleToLogin() {
|
||||
userStore.clearUserInfo();
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
function handleToUser() {
|
||||
router.push("/settings");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.user-message {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
.user-message:hover {
|
||||
background-color: #ececed;
|
||||
}
|
||||
.user-message-avatar {
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="topbar" :class="{ 'at-top': isAtTop }">
|
||||
<div class="left">
|
||||
<img src="/logo.png" class="logo" alt="Logo" @click="handleToPath('/home')" />
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="nav">
|
||||
<div class="nav-item" :class="{ active: activeNavItem === '/home' }" @click="handleToPath('/home')">
|
||||
首页
|
||||
<div class="underline"></div>
|
||||
</div>
|
||||
<div class="nav-item" :class="{ active: activeNavItem.startsWith('/company') }" @click="handleToPath('/company')">
|
||||
公司列表
|
||||
<div class="underline"></div>
|
||||
</div>
|
||||
<div class="nav-item" :class="{ active: activeNavItem.startsWith('/forum') }" @click="handleToPath('/forum')">
|
||||
论坛中心
|
||||
<div class="underline"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="actions">
|
||||
<div class="action-item" :class="{ active: activeNavItem.startsWith('/messages') }">
|
||||
<Messages v-if="userStore.isLogined" />
|
||||
<div class="underline"></div>
|
||||
</div>
|
||||
<div class="action-item" :class="{ active: activeNavItem.startsWith('/settings') }">
|
||||
<User />
|
||||
<div class="underline"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "@/store/user";
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import Messages from "./components/messages.vue";
|
||||
import User from "./components/user.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const activeNavItem = ref("");
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(newVal) => {
|
||||
activeNavItem.value = newVal;
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
const isAtTop = ref(true);
|
||||
|
||||
const handleScroll = () => {
|
||||
const scrollTop = window.scrollY; // 获取页面滚动的高度
|
||||
isAtTop.value = scrollTop === 0; // 如果滚动到顶部,isAtTop 为 true
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("scroll", handleScroll); // 添加滚动事件监听
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("scroll", handleScroll); // 移除滚动事件监听
|
||||
});
|
||||
function handleToPath(path: string) {
|
||||
router.push(path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.topbar {
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: rgba(0, 0, 0, 0.04) 0px 2px 8px -2px;
|
||||
padding: 0px 200px;
|
||||
}
|
||||
.at-top {
|
||||
background-color: #ffffff;
|
||||
box-shadow: none;
|
||||
}
|
||||
.left {
|
||||
height: 100%;
|
||||
width: 120px;
|
||||
}
|
||||
.center {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 100%;
|
||||
width: 160px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
height: 100%;
|
||||
margin: 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: flex-end;
|
||||
font-size: 14px;
|
||||
.action-item {
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.action-item,
|
||||
.nav-item {
|
||||
position: relative;
|
||||
|
||||
.underline {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: #007bff;
|
||||
transform: translateX(-50%) scaleX(0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.3s ease; // 仅在需要动画时应用过渡效果
|
||||
}
|
||||
&.active {
|
||||
color: #007bff;
|
||||
}
|
||||
&.active .underline,
|
||||
&:hover .underline {
|
||||
width: 100%;
|
||||
transform: translateX(-50%) scaleX(1);
|
||||
}
|
||||
|
||||
// 默认状态下不应用过渡效果
|
||||
&:not(.active):not(:hover) .underline {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user