|
|
@@ -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>
|