Jelajahi Sumber

Update 优化主页架构,方便扩展

Yue 1 Minggu lalu
induk
melakukan
484c4b3ad8

+ 1 - 1
UI/VAP_V3.VUE/src/router/_staticRouter.ts

@@ -53,7 +53,7 @@ export const staticRouter: RouteRecordRaw[] = [
 			{
 				path: "/m-home",
 				name: "m-home",
-				component: () => import("@/views/mobile-home.vue"),
+				component: () => import("@/views/home/m-home.vue"),
 				meta: {
 					title: "移动端首页"
 				}

+ 1 - 1
UI/VAP_V3.VUE/src/router/index.ts

@@ -26,7 +26,7 @@ export const constantRoutes: RouteRecordRaw[] = [
 			{
 				path: "/home",
 				name: "home",
-				component: () => import("@/views/home.vue"),
+				component: () => import("@/views/home/index.vue"),
 				meta: {
 					title: "首页",
 					affix: true

+ 1 - 1
UI/VAP_V3.VUE/src/router/menuMap/index.ts

@@ -7,7 +7,7 @@ export const MenuRouteMaps: MenuRouteMap[] = [
 	},
 	{
 		path: "/home/index",
-		component: () => import("@v/home.vue")
+		component: () => import("@v/home/index.vue")
 	},
 	...system
 ]

+ 1 - 19
UI/VAP_V3.VUE/src/views/home.vue → UI/VAP_V3.VUE/src/views/home/_home.vue

@@ -1,26 +1,8 @@
 <script setup lang="ts">
 import appStore from "@s"
-import { onMounted, onBeforeUnmount, ref } from "vue"
-import { useRouter } from "vue-router"
 
-const name = ref(appStore.appConfigStore.getPlatformName() || "玮博科技")
-const router = useRouter()
-
-onMounted(() => {
-	// 如果是移动设备,跳转到移动端主页
-	if (isMobile()) {
-		router.replace("/m-home")
-		return
-	}
-
-	document.querySelector("body")?.classList.add("is-home")
-})
-
-onBeforeUnmount(() => {
-	document.querySelector("body")?.classList.remove("is-home")
-})
+const name = ref(appStore.appConfigStore.getPlatformName())
 </script>
-
 <template>
 	<div class="home-page">
 		<div class="inner-header">

+ 24 - 0
UI/VAP_V3.VUE/src/views/home/index.vue

@@ -0,0 +1,24 @@
+<script setup lang="ts">
+import { useRouter } from "vue-router"
+import Home from "./_home.vue"
+
+const router = useRouter()
+
+onMounted(() => {
+	// 如果是移动设备,跳转到移动端主页
+	if (isMobile()) {
+		router.replace("/m-home")
+		return
+	}
+	document.querySelector("body")?.classList.add("is-home")
+})
+onBeforeUnmount(() => {
+	document.querySelector("body")?.classList.remove("is-home")
+})
+</script>
+
+<template>
+	<div>
+		<Home />
+	</div>
+</template>

+ 554 - 0
UI/VAP_V3.VUE/src/views/home/m-home.vue

@@ -0,0 +1,554 @@
+<script setup lang="ts">
+import appStore from "@s"
+import { useDebounceFn, useResizeObserver } from "@vueuse/core"
+import { ref, onBeforeUnmount, onMounted, computed } from "vue"
+import VbQrScan from "@/components/qrcode/VbQrScan.vue"
+import router from "@r"
+
+const pageRef = ref<HTMLDivElement | null>(null)
+const name = ref(appStore.appConfigStore.getPlatformName())
+const showScanner = ref(false)
+const scanResult = ref("")
+const currentTime = ref(new Date())
+
+const copyright = computed(() => {
+	return appStore.appConfigStore.getConfig().copyrightYear || "2020"
+})
+
+// 更新当前时间
+onMounted(() => {
+	const timer = setInterval(() => {
+		currentTime.value = new Date()
+	}, 1000)
+
+	onBeforeUnmount(() => {
+		clearInterval(timer)
+		document.querySelector("body")?.classList.remove("is-mobile-home")
+	})
+})
+
+function handleScan() {
+	showScanner.value = true
+}
+
+function handleScanResult(result: string) {
+	scanResult.value = result
+	if (result.startsWith("vb@")) {
+		const arr = result.split("@")
+		if (arr.length > 2) {
+			router.push(arr[2])
+			return
+		}
+	}
+	message.msgError("无效二维码")
+}
+
+function handleCloseScanner() {
+	showScanner.value = false
+}
+
+// 格式化时间显示
+const formattedTime = computed(() => {
+	return currentTime.value.toLocaleTimeString("zh-CN", {
+		hour: "2-digit",
+		minute: "2-digit"
+	})
+})
+
+const formattedDate = computed(() => {
+	return currentTime.value.toLocaleDateString("zh-CN", {
+		year: "numeric",
+		month: "long",
+		day: "numeric",
+		weekday: "long"
+	})
+})
+
+// 问候语
+const greeting = computed(() => {
+	const hour = currentTime.value.getHours()
+	if (hour < 6) return "夜深了"
+	if (hour < 9) return "早上好"
+	if (hour < 12) return "上午好"
+	if (hour < 14) return "中午好"
+	if (hour < 18) return "下午好"
+	if (hour < 22) return "晚上好"
+	return "夜深了"
+})
+
+function handleResize() {
+	if (pageRef.value) {
+		pageRef.value.style.width = window.innerWidth + "px"
+		pageRef.value.style.height = window.innerHeight + "px"
+	}
+}
+
+onMounted(() => {
+	handleResize()
+	useResizeObserver(pageRef.value, useDebounceFn(handleResize, 200, { maxWait: 500 }))
+})
+</script>
+
+<template>
+	<div ref="pageRef" class="mobile-home-page">
+		<!-- 动态背景 -->
+		<div class="background-decoration">
+			<div class="circle circle-1"></div>
+			<div class="circle circle-2"></div>
+			<div class="circle circle-3"></div>
+		</div>
+
+		<div class="home-content">
+			<!-- 头部区域 -->
+			<header class="header-section">
+				<div class="time-display">
+					<div class="time">{{ formattedTime }}</div>
+					<div class="date">{{ formattedDate }}</div>
+				</div>
+				<div class="company-name">
+					<h1>{{ name }}</h1>
+					<p class="tagline">移动工作平台</p>
+				</div>
+			</header>
+
+			<!-- 主要内容区 -->
+			<main class="main-content">
+				<!-- 用户信息卡片 -->
+				<div class="user-card">
+					<div class="user-avatar">
+						<vb-symbol
+							:text="appStore.authStore.user.nickName"
+							:src="appStore.authStore.user.avatar"
+							:size="80"
+							shape="circle" />
+					</div>
+					<div class="user-info">
+						<div class="greeting">{{ greeting }}</div>
+						<div class="nickname">{{ appStore.authStore.user.nickName }}</div>
+						<div class="status">在线工作中</div>
+					</div>
+				</div>
+
+				<!-- 快捷操作区 -->
+				<div class="quick-actions">
+					<div class="action-card primary" @click="handleScan">
+						<div class="action-icon">
+							<i class="bi bi-qr-code-scan"></i>
+						</div>
+						<div class="action-info">
+							<div class="action-title">扫一扫</div>
+							<div class="action-desc">扫描二维码快速访问</div>
+						</div>
+						<div class="action-arrow">
+							<i class="bi bi-chevron-right"></i>
+						</div>
+					</div>
+
+					<!-- <div class="action-card">
+						<div class="action-icon">
+							<i class="bi bi-calendar-check"></i>
+						</div>
+						<div class="action-info">
+							<div class="action-title">工作台</div>
+							<div class="action-desc">查看待办事项</div>
+						</div>
+						<div class="action-arrow">
+							<i class="bi bi-chevron-right"></i>
+						</div>
+					</div>
+
+					<div class="action-card">
+						<div class="action-icon">
+							<i class="bi bi-bell"></i>
+						</div>
+						<div class="action-info">
+							<div class="action-title">消息通知</div>
+							<div class="action-desc">查看系统消息</div>
+						</div>
+						<div class="action-badge">3</div>
+						<div class="action-arrow">
+							<i class="bi bi-chevron-right"></i>
+						</div>
+					</div> -->
+				</div>
+
+				<!-- 数据统计区 -->
+				<!-- <div class="stats-section">
+					<h3 class="section-title">今日概览</h3>
+					<div class="stats-grid">
+						<div class="stat-item">
+							<div class="stat-value">12</div>
+							<div class="stat-label">待处理</div>
+						</div>
+						<div class="stat-item">
+							<div class="stat-value">8</div>
+							<div class="stat-label">已完成</div>
+						</div>
+						<div class="stat-item">
+							<div class="stat-value">3</div>
+							<div class="stat-label">进行中</div>
+						</div>
+						<div class="stat-item">
+							<div class="stat-value">1</div>
+							<div class="stat-label">预警</div>
+						</div>
+					</div>
+				</div> -->
+			</main>
+
+			<!-- 底部区域 -->
+			<footer class="footer-section">
+				<p>Copyright ©{{ copyright }}-{{ new Date().getFullYear() }} VAP All Rights Reserved.</p>
+			</footer>
+		</div>
+
+		<!-- 扫码组件 -->
+		<VbQrScan
+			v-if="showScanner"
+			:enable-torch="true"
+			:continuous="false"
+			@close="handleCloseScanner"
+			@scanSuccess="handleScanResult" />
+	</div>
+</template>
+
+<style lang="scss" scoped>
+.mobile-home-page {
+	width: 100%;
+	height: 100vh;
+	overflow: hidden;
+	position: relative;
+	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+
+	.background-decoration {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		overflow: hidden;
+		pointer-events: none;
+
+		.circle {
+			position: absolute;
+			border-radius: 50%;
+			background: rgba(255, 255, 255, 0.05);
+			animation: float 20s infinite ease-in-out;
+
+			&.circle-1 {
+				width: 300px;
+				height: 300px;
+				top: -100px;
+				right: -100px;
+				animation-delay: 0s;
+			}
+
+			&.circle-2 {
+				width: 200px;
+				height: 200px;
+				bottom: -50px;
+				left: -50px;
+				animation-delay: 5s;
+			}
+
+			&.circle-3 {
+				width: 150px;
+				height: 150px;
+				top: 50%;
+				left: 50%;
+				animation-delay: 10s;
+			}
+		}
+	}
+}
+
+.home-content {
+	width: 100%;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	position: relative;
+	z-index: 1;
+}
+
+.header-section {
+	padding: 40px 20px 20px;
+	text-align: center;
+
+	.time-display {
+		margin-bottom: 20px;
+
+		.time {
+			font-size: 3rem;
+			font-weight: 200;
+			color: rgba(255, 255, 255, 0.95);
+			letter-spacing: 2px;
+			text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
+		}
+
+		.date {
+			font-size: 0.9rem;
+			color: rgba(255, 255, 255, 0.7);
+			margin-top: 5px;
+			letter-spacing: 1px;
+		}
+	}
+
+	.company-name {
+		h1 {
+			font-size: 1.8rem;
+			font-weight: 700;
+			color: white;
+			margin-bottom: 5px;
+			text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+		}
+
+		.tagline {
+			font-size: 0.9rem;
+			color: rgba(255, 255, 255, 0.8);
+			letter-spacing: 3px;
+		}
+	}
+}
+
+.main-content {
+	flex: 1;
+	overflow-y: auto;
+	padding: 0 20px 20px;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
+}
+
+.user-card {
+	background: rgba(255, 255, 255, 0.15);
+	backdrop-filter: blur(10px);
+	border-radius: 20px;
+	padding: 25px;
+	display: flex;
+	align-items: center;
+	gap: 20px;
+	margin-bottom: 25px;
+	box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
+	border: 1px solid rgba(255, 255, 255, 0.2);
+
+	.user-avatar {
+		flex-shrink: 0;
+		border: 3px solid rgba(255, 255, 255, 0.3);
+		border-radius: 50%;
+		background: rgba(255, 255, 255, 0.1);
+
+		:deep(.symbol) {
+			border-radius: 50%;
+		}
+	}
+
+	.user-info {
+		flex: 1;
+
+		.greeting {
+			font-size: 0.85rem;
+			color: rgba(255, 255, 255, 0.7);
+			margin-bottom: 5px;
+		}
+
+		.nickname {
+			font-size: 1.4rem;
+			font-weight: 600;
+			color: white;
+			margin-bottom: 5px;
+			text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+		}
+
+		.status {
+			font-size: 0.75rem;
+			color: #4ade80;
+			display: flex;
+			align-items: center;
+			gap: 5px;
+
+			&::before {
+				content: "";
+				width: 6px;
+				height: 6px;
+				background: #4ade80;
+				border-radius: 50%;
+				display: inline-block;
+				animation: pulse 2s infinite;
+			}
+		}
+	}
+}
+
+.quick-actions {
+	margin-bottom: 25px;
+}
+
+.action-card {
+	background: rgba(255, 255, 255, 0.15);
+	backdrop-filter: blur(10px);
+	border-radius: 16px;
+	padding: 18px;
+	display: flex;
+	align-items: center;
+	gap: 15px;
+	margin-bottom: 12px;
+	cursor: pointer;
+	transition: all 0.3s ease;
+	border: 1px solid rgba(255, 255, 255, 0.1);
+	position: relative;
+
+	&:active {
+		transform: scale(0.98);
+		background: rgba(255, 255, 255, 0.2);
+	}
+
+	&.primary {
+		background: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.1));
+		border-color: rgba(255, 255, 255, 0.3);
+	}
+
+	.action-icon {
+		width: 45px;
+		height: 45px;
+		border-radius: 12px;
+		background: rgba(255, 255, 255, 0.2);
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 1.5rem;
+		color: white;
+		flex-shrink: 0;
+		i {
+			font-size: 1.5rem;
+			color: #eee;
+		}
+	}
+
+	.action-info {
+		flex: 1;
+
+		.action-title {
+			font-size: 1rem;
+			font-weight: 600;
+			color: white;
+			margin-bottom: 3px;
+		}
+
+		.action-desc {
+			font-size: 0.8rem;
+			color: rgba(255, 255, 255, 0.6);
+		}
+	}
+
+	.action-arrow {
+		color: rgba(255, 255, 255, 0.5);
+		font-size: 1.2rem;
+	}
+
+	.action-badge {
+		position: absolute;
+		top: 10px;
+		right: 45px;
+		background: #ef4444;
+		color: white;
+		font-size: 0.7rem;
+		font-weight: 600;
+		padding: 2px 8px;
+		border-radius: 10px;
+		animation: bounce 2s infinite;
+	}
+}
+
+.stats-section {
+	background: rgba(255, 255, 255, 0.1);
+	backdrop-filter: blur(10px);
+	border-radius: 16px;
+	padding: 20px;
+	border: 1px solid rgba(255, 255, 255, 0.1);
+
+	.section-title {
+		font-size: 1rem;
+		font-weight: 600;
+		color: white;
+		margin-bottom: 15px;
+		text-align: left;
+	}
+
+	.stats-grid {
+		display: grid;
+		grid-template-columns: repeat(4, 1fr);
+		gap: 15px;
+
+		.stat-item {
+			text-align: center;
+
+			.stat-value {
+				font-size: 1.6rem;
+				font-weight: 700;
+				color: #fbbf24;
+				margin-bottom: 5px;
+				text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+			}
+
+			.stat-label {
+				font-size: 0.75rem;
+				color: rgba(255, 255, 255, 0.6);
+			}
+		}
+	}
+}
+
+.footer-section {
+	padding: 20px;
+	text-align: center;
+
+	p {
+		font-size: 0.75rem;
+		color: rgba(255, 255, 255, 0.5);
+	}
+}
+
+@keyframes shine {
+	0% {
+		background-position: 0% 50%;
+	}
+	100% {
+		background-position: 200% 50%;
+	}
+}
+
+@keyframes float {
+	0%,
+	100% {
+		transform: translate(0, 0) rotate(0deg);
+	}
+	33% {
+		transform: translate(30px, -30px) rotate(120deg);
+	}
+	66% {
+		transform: translate(-20px, 20px) rotate(240deg);
+	}
+}
+
+@keyframes pulse {
+	0%,
+	100% {
+		opacity: 1;
+	}
+	50% {
+		opacity: 0.5;
+	}
+}
+
+@keyframes bounce {
+	0%,
+	100% {
+		transform: translateY(0);
+	}
+	50% {
+		transform: translateY(-5px);
+	}
+}
+</style>

+ 0 - 204
UI/VAP_V3.VUE/src/views/mobile-home.vue

@@ -1,204 +0,0 @@
-<script setup lang="ts">
-import appStore from "@s"
-import { ref, onBeforeUnmount } from "vue"
-import VbQrScan from "@/components/qrcode/VbQrScan.vue"
-
-const name = ref("中科轼峰")
-const showScanner = ref(false)
-const scanResult = ref("")
-
-onBeforeUnmount(() => {
-	document.querySelector("body")?.classList.remove("is-mobile-home")
-})
-
-function handleScan() {
-	// 使用新创建的扫码组件
-	showScanner.value = true
-}
-
-function handleScanResult(result: string) {
-	scanResult.value = result
-	alert("扫描结果: " + result)
-}
-
-function handleCloseScanner() {
-	showScanner.value = false
-}
-</script>
-
-<template>
-	<div class="mobile-home-page">
-		<div class="home-page">
-			<div class="header">
-				<h1 class="animated-title">{{ name }}</h1>
-				<div class="subtitle">移动工作平台</div>
-			</div>
-
-			<div class="content">
-				<div class="avatar-wrapper">
-					<vb-symbol
-						:text="appStore.authStore.user.nickName"
-						:src="appStore.authStore.user.avatar"
-						:size="100"
-						shape="circle" />
-				</div>
-
-				<div class="welcome-card">
-					<div class="welcome-message">
-						<h2>欢迎您,{{ appStore.authStore.user.nickName }} !</h2>
-						<p class="welcome-text">祝您使用愉快</p>
-					</div>
-				</div>
-
-				<div class="actions">
-					<el-button type="primary" round style="" @click="handleScan">
-						<i class="bi bi-qr-code-scan me-3 text-white fs-2"></i>
-						<span class="fs-2">扫一扫</span>
-					</el-button>
-				</div>
-			</div>
-
-			<div class="footer">
-				<p>&copy; {{ new Date().getFullYear() }} 中科轼峰. 保留所有权利.</p>
-			</div>
-		</div>
-		<!-- 扫码组件 -->
-		<VbQrScan
-			v-if="showScanner"
-			:enable-torch="true"
-			:continuous="false"
-			@close="handleCloseScanner"
-			@scanSuccess="handleScanResult" />
-	</div>
-</template>
-
-<style lang="scss" scoped>
-.mobile-home-page {
-	width: 100%;
-	height: 100vh;
-	overflow: hidden;
-	background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-	display: flex;
-	justify-content: center;
-}
-.home-page {
-	width: 100%;
-	max-width: 500px;
-	height: 100%;
-	color: white;
-	display: flex;
-	flex-direction: column;
-	position: relative;
-
-	&::before {
-		content: "";
-		position: absolute;
-		top: -50%;
-		left: -50%;
-		width: 200%;
-		height: 200%;
-		background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 70%);
-		transform: rotate(30deg);
-		z-index: 0;
-	}
-
-	.header {
-		text-align: center;
-		padding: 30px 20px 10px;
-		position: relative;
-		z-index: 1;
-
-		.animated-title {
-			font-size: 2.2rem;
-			font-weight: 800;
-			margin-bottom: 5px;
-			background: linear-gradient(45deg, #ffffff, #ffd700, #ffffff);
-			background-size: 200% auto;
-			-webkit-background-clip: text;
-			-webkit-text-fill-color: transparent;
-			animation: shine 3s linear infinite;
-			text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
-			letter-spacing: 1px;
-		}
-
-		.subtitle {
-			font-size: 1.1rem;
-			opacity: 0.9;
-			letter-spacing: 2px;
-			font-weight: 300;
-		}
-	}
-
-	.content {
-		flex: 1;
-		display: flex;
-		flex-direction: column;
-		justify-content: center;
-		align-items: center;
-		margin-top: -20%;
-		padding: 10px 20px;
-		position: relative;
-		z-index: 1;
-
-		.avatar-wrapper {
-			margin-bottom: -40px;
-			z-index: 2;
-			border: 4px solid rgba(255, 255, 255, 0.3);
-			border-radius: 50%;
-			background: rgba(255, 255, 255, 0.1);
-			backdrop-filter: blur(5px);
-
-			:deep(.symbol) {
-				border-radius: 50%;
-				box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
-			}
-		}
-
-		.welcome-card {
-			background: rgba(255, 255, 255, 0.15);
-			backdrop-filter: blur(10px);
-			border-radius: 20px;
-			padding: 60px 20px 30px;
-			text-align: center;
-			margin-bottom: 50px;
-			width: 90%;
-			box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
-			border: 1px solid rgba(255, 255, 255, 0.18);
-
-			.welcome-message {
-				h2 {
-					font-size: 1.6rem;
-					margin-bottom: 10px;
-					font-weight: 600;
-					text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
-				}
-
-				.welcome-text {
-					font-size: 1.1rem;
-					opacity: 0.9;
-					letter-spacing: 1px;
-				}
-			}
-		}
-
-		.actions {
-			width: 90%;
-
-			:deep(.el-button) {
-				width: 100%;
-				height: 45px;
-				font-size: 1rem;
-			}
-		}
-	}
-
-	.footer {
-		text-align: center;
-		padding: 15px;
-		font-size: 0.8rem;
-		opacity: 0.7;
-		position: relative;
-		z-index: 1;
-	}
-}
-</style>