From bfa7d3b23e526809b04299d9c2719c3449aba3be Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期二, 28 四月 2026 10:32:42 +0800
Subject: [PATCH] 小程序图片压缩

---
 yuyueMiniProgram/src/static/mine/bg.png              |    0 
 yuyueMiniProgram/src/static/mine/record.png          |    0 
 yuyueMiniProgram/src/static/appointment/woman.png    |    0 
 yuyueMiniProgram/src/static/review/contentbg.png     |    0 
 yuyueMiniProgram/src/static/appointment/company.png  |    0 
 yuyueMiniProgram/src/static/appointment/delete.png   |    0 
 yuyueMiniProgram/src/static/review/pass.png          |    0 
 yuyueMiniProgram/src/static/home/download.png        |    0 
 yuyueMiniProgram/src/static/home/chevron_left.png    |    0 
 yuyueMiniProgram/src/static/mine/edit.png            |    0 
 yuyueMiniProgram/src/static/appointment/man.png      |    0 
 yuyueMiniProgram/src/static/choosePerson/add.png     |    0 
 yuyueMiniProgram/src/static/choosePerson/bg@2x.png   |    0 
 yuyueMiniProgram/src/static/review/reduce.png        |    0 
 yuyueMiniProgram/src/static/home/wait.png            |    0 
 yuyueMiniProgram/src/static/review/greenpass.png     |    0 
 yuyueMiniProgram/src/router/index.ts                 |   15 
 yuyueMiniProgram/src/static/home/bg.png              |    0 
 yuyueMiniProgram/src/static/appointment/idCard.png   |    0 
 yuyueMiniProgram/src/static/home/record.png          |    0 
 yuyueMiniProgram/src/static/home/toDo.png            |    0 
 yuyueMiniProgram/src/static/mine/aboutUs.png         |    0 
 yuyueMiniProgram/src/static/review/close.png         |    0 
 yuyueMiniProgram/src/layouts/tabbar.vue              |   10 +
 yuyueMiniProgram/src/components.d.ts                 |    1 
 yuyueMiniProgram/src/static/review/Pending.png       |    0 
 yuyueMiniProgram/src/static/home/成都喜望科技有限公司.png      |    0 
 yuyueMiniProgram/src/static/review/arrow.png         |    0 
 yuyueMiniProgram/src/components/MiniLoginPrompt.vue  |  162 ++++++++++++++++
 yuyueMiniProgram/src/static/home/hi.png              |    0 
 yuyueMiniProgram/src/static/review/cancel.png        |    0 
 yuyueMiniProgram/src/types/auth.ts                   |    8 
 yuyueMiniProgram/src/static/home/left.png            |    0 
 yuyueMiniProgram/src/api/core/handlers.ts            |    2 
 yuyueMiniProgram/src/static/review/refueReview.png   |    0 
 yuyueMiniProgram/src/static/home/visitor.png         |    0 
 yuyueMiniProgram/src/static/review/applay.png        |    0 
 yuyueMiniProgram/src/static/review/cancelReview.png  |    0 
 yuyueMiniProgram/src/static/review/underReview.png   |    0 
 yuyueMiniProgram/src/static/review/fail.png          |    0 
 yuyueMiniProgram/src/static/review/circle.png        |    0 
 frontend-admin/src/layouts/MainLayout.vue            |   78 ++++---
 yuyueMiniProgram/src/static/review/bg-grey.png       |    0 
 yuyueMiniProgram/src/static/choosePerson/noData.png  |    0 
 yuyueMiniProgram/src/store/auth.ts                   |   67 ++++++
 yuyueMiniProgram/src/main.ts                         |   39 +++
 yuyueMiniProgram/src/static/mine/logout.png          |    0 
 yuyueMiniProgram/src/pages/index/index.vue           |  102 ++++++++-
 yuyueMiniProgram/src/static/mine/change.png          |    0 
 yuyueMiniProgram/src/static/home/wait_bg.png         |    0 
 yuyueMiniProgram/src/static/home/right.png           |    0 
 yuyueMiniProgram/src/static/viewCode/Download@2x.png |    0 
 yuyueMiniProgram/src/static/home/chevron_right.png   |    0 
 yuyueMiniProgram/src/static/review/success.png       |    0 
 yuyueMiniProgram/src/static/review/add.png           |    0 
 yuyueMiniProgram/src/static/home/share.png           |    0 
 yuyueMiniProgram/src/static/review/cancelIcon.png    |    0 
 yuyueMiniProgram/src/pages/mine/index.vue            |   11 +
 yuyueMiniProgram/src/static/mine/bgmask.png          |    0 
 yuyueMiniProgram/src/static/home/tips.png            |    0 
 yuyueMiniProgram/src/static/mine/review.png          |    0 
 yuyueMiniProgram/src/static/review/close@2x.png      |    0 
 yuyueMiniProgram/src/static/review/chapter.png       |    0 
 yuyueMiniProgram/src/static/review/refesh.png        |    0 
 yuyueMiniProgram/src/static/mine/header.png          |    0 
 yuyueMiniProgram/src/static/review/review.png        |    0 
 yuyueMiniProgram/src/static/choosePerson/edit.png    |    0 
 yuyueMiniProgram/src/static/review/passReview.png    |    0 
 yuyueMiniProgram/src/auto-imports.d.ts               |   18 +
 yuyueMiniProgram/src/static/appointment/plate.png    |    0 
 yuyueMiniProgram/src/static/review/bg-t.png          |    0 
 yuyueMiniProgram/src/static/appointment/bg.png       |    0 
 yuyueMiniProgram/src/static/viewCode/bg@2x.png       |    0 
 yuyueMiniProgram/src/utils/mini-login.ts             |   75 +++++++
 yuyueMiniProgram/src/static/home/process.png         |    0 
 yuyueMiniProgram/src/static/home/appointment.png     |    0 
 yuyueMiniProgram/src/static/home/toDo_bg.png         |    0 
 77 files changed, 518 insertions(+), 70 deletions(-)

diff --git a/frontend-admin/src/layouts/MainLayout.vue b/frontend-admin/src/layouts/MainLayout.vue
index 632a048..03fd19e 100644
--- a/frontend-admin/src/layouts/MainLayout.vue
+++ b/frontend-admin/src/layouts/MainLayout.vue
@@ -1,6 +1,6 @@
 <template>
   <el-container class="layout">
-    <el-aside width="248px" class="side-wrap">
+    <el-aside width="15.98vw" class="side-wrap">
       <div class="brand-block">
         <div class="brand-icon"></div>
         <div class="brand-name">运维运营管理平台</div>
@@ -11,9 +11,9 @@
         unique-opened
         router
         class="side-menu"
-        background-color="#2f63d4"
+        background-color="#245ECC"
         text-color="#dce8ff"
-        active-text-color="#2f63d4"
+        active-text-color="#245ECC"
       >
         <el-menu-item v-if="auth.hasPermission('dashboard:page')" index="/dashboard" class="home-item">
           <el-icon><House /></el-icon>
@@ -100,22 +100,19 @@
     <el-container class="main-shell">
       <div class="top-wrap">
         <el-header class="header">
-          <div class="header-left">
-            <div class="header-title">
-              <span class="header-dot"></span>
-              <span>运维运营管理平台</span>
-            </div>
-          </div>
+          <div class="header-spacer"></div>
           <div class="header-right">
             <el-button class="menu-trigger" text>
               <el-icon><Operation /></el-icon>
             </el-button>
             <div class="user-box">
-              <el-avatar v-if="auth.avatarUrl" :size="28" :src="auth.avatarUrl" class="hdr-av" />
-              <el-avatar v-else :size="28" class="hdr-av">{{ nameInitial }}</el-avatar>
+              <el-avatar v-if="auth.avatarUrl" :size="30" :src="auth.avatarUrl" class="hdr-av" />
+              <el-avatar v-else :size="30" class="hdr-av">{{ nameInitial }}</el-avatar>
               <span class="user-name">{{ auth.displayName }}</span>
+              <el-button class="logout-trigger" text @click="confirmLogout">
+                <el-icon><SwitchButton /></el-icon>
+              </el-button>
             </div>
-            <el-button type="primary" link @click="logout">退出</el-button>
           </div>
         </el-header>
 
@@ -161,7 +158,8 @@
 <script setup lang="ts">
 import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
 import { useRoute, useRouter } from "vue-router";
-import { Document, Grid, House, Operation, Setting, User, UserFilled, Wallet } from "@element-plus/icons-vue";
+import { ElMessageBox } from "element-plus";
+import { Document, Grid, House, Operation, Setting, SwitchButton, User, UserFilled, Wallet } from "@element-plus/icons-vue";
 import { useAuthStore } from "../stores/auth";
 import http from "../api/http";
 
@@ -175,6 +173,10 @@
 const router = useRouter();
 const auth = useAuthStore();
 const active = computed(() => route.path);
+const nameInitial = computed(() => {
+  const n = auth.displayName;
+  return n && n.length ? n.slice(0, 1) : "?";
+});
 const defaultOpeneds = computed(() => {
   const path = route.path;
   if (path.startsWith("/system/")) return ["system"];
@@ -210,11 +212,6 @@
   "/region/local-units": "属地单位管理",
 };
 const hiddenTabRouteNames = new Set(["staff-group-detail", "role-detail"]);
-const nameInitial = computed(() => {
-  const n = auth.displayName;
-  return n && n.length ? n.slice(0, 1) : "?";
-});
-
 function resolveRouteTitle() {
   if (typeof route.meta.title === "string" && route.meta.title) {
     return route.meta.title;
@@ -342,6 +339,19 @@
   auth.clear();
   router.push("/login");
 }
+
+async function confirmLogout() {
+  try {
+    await ElMessageBox.confirm("确认退出当前账号吗?", "退出确认", {
+      confirmButtonText: "确认",
+      cancelButtonText: "取消",
+      type: "warning",
+    });
+  } catch {
+    return;
+  }
+  await logout();
+}
 </script>
 
 <style scoped>
@@ -356,7 +366,7 @@
 }
 
 .side-wrap {
-  background: linear-gradient(180deg, #2f63d4 0%, #2d5fce 100%);
+  background: #245ECC;
   color: #fff;
   padding: 0 10px 12px;
   box-sizing: border-box;
@@ -411,7 +421,7 @@
 
 .side-menu :deep(.el-menu-item.is-active) {
   background: #ffffff !important;
-  color: #2f63d4 !important;
+  color: #245ECC !important;
 }
 
 .side-menu :deep(.el-sub-menu .el-menu-item) {
@@ -449,34 +459,23 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  height: 44px;
-  width: 100%;
+  height: 60px;
+  width: 84vw;
   padding: 0 12px 0 14px;
   background: #ffffff;
   border-bottom: 1px solid #e7edf8;
   box-sizing: border-box;
 }
 
-.header-left,
+.header-spacer,
 .header-right,
-.user-box,
-.header-title {
+.user-box {
   display: flex;
   align-items: center;
 }
 
-.header-title {
-  gap: 10px;
-  color: #3d5d9e;
-  font-size: 14px;
-  font-weight: 600;
-}
-
-.header-dot {
-  width: 18px;
-  height: 18px;
-  border-radius: 50%;
-  background: linear-gradient(135deg, #7ca9ff, #4f7dff);
+.header-spacer {
+  flex: 1 1 auto;
 }
 
 .header-right {
@@ -491,6 +490,11 @@
   gap: 8px;
 }
 
+.logout-trigger {
+  color: #5e6f95;
+  padding: 0;
+}
+
 .user-name {
   color: #47587c;
   font-size: 13px;
diff --git a/yuyueMiniProgram/src/api/core/handlers.ts b/yuyueMiniProgram/src/api/core/handlers.ts
index fe7e14d..90a47c6 100644
--- a/yuyueMiniProgram/src/api/core/handlers.ts
+++ b/yuyueMiniProgram/src/api/core/handlers.ts
@@ -38,8 +38,6 @@
   clearMiniAuthSession()
   if (!redirectingToLogin) {
     redirectingToLogin = true
-    const globalToast = useGlobalToast()
-    globalToast.error({ msg: message || '登录已过期,请重新登录!', duration: 1200 })
     const timer = setTimeout(() => {
       clearTimeout(timer)
       redirectingToLogin = false
diff --git a/yuyueMiniProgram/src/auto-imports.d.ts b/yuyueMiniProgram/src/auto-imports.d.ts
index 3dcfdb0..60676c4 100644
--- a/yuyueMiniProgram/src/auto-imports.d.ts
+++ b/yuyueMiniProgram/src/auto-imports.d.ts
@@ -9,8 +9,10 @@
   const $$userConfigMap: typeof import('./api/index')['$$userConfigMap']
   const Apis: typeof import('./api/index')['Apis']
   const CommonUtil: typeof import('wot-design-uni')['CommonUtil']
+  const EMPLOYEE_SUBSCRIBE_TEMPLATE_IDS: typeof import('./utils/mini-login')['EMPLOYEE_SUBSCRIBE_TEMPLATE_IDS']
   const EffectScope: typeof import('vue')['EffectScope']
   const MINI_AUTH_STORAGE_KEY: typeof import('./store/auth')['MINI_AUTH_STORAGE_KEY']
+  const MINI_PHONE_AUTH_REQUIRED: typeof import('./utils/mini-login')['MINI_PHONE_AUTH_REQUIRED']
   const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
   const alovaInstance: typeof import('./api/index')['alovaInstance']
   const api: typeof import('./api/index')['default']
@@ -67,13 +69,17 @@
   const fetchVisitReasons: typeof import('./api/lookup')['fetchVisitReasons']
   const fetchVisitRecords: typeof import('./api/appointment')['fetchVisitRecords']
   const fetchVisitorDetail: typeof import('./api/visitor')['fetchVisitorDetail']
+  const finishMiniLogin: typeof import('./utils/mini-login')['finishMiniLogin']
   const getActivePinia: typeof import('pinia')['getActivePinia']
+  const getApiErrorCode: typeof import('./utils/mini-login')['getApiErrorCode']
   const getCurrentInstance: typeof import('vue')['getCurrentInstance']
   const getCurrentPath: typeof import('./utils/index')['getCurrentPath']
   const getCurrentScope: typeof import('vue')['getCurrentScope']
   const getPersistedAuthState: typeof import('./store/auth')['getPersistedAuthState']
   const getPersistedToken: typeof import('./store/auth')['getPersistedToken']
+  const getWxLoginCode: typeof import('./utils/mini-login')['getWxLoginCode']
   const h: typeof import('vue')['h']
+  const hideMiniLoginPrompt: typeof import('./store/auth')['hideMiniLoginPrompt']
   const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
   const inject: typeof import('vue')['inject']
   const injectLocal: typeof import('@vueuse/core')['injectLocal']
@@ -153,6 +159,7 @@
   const refDefault: typeof import('@vueuse/core')['refDefault']
   const refThrottled: typeof import('@vueuse/core')['refThrottled']
   const refWithControl: typeof import('@vueuse/core')['refWithControl']
+  const requestEmployeeSubscribeAuthorization: typeof import('./utils/mini-login')['requestEmployeeSubscribeAuthorization']
   const requestMiniSubscribeMessages: typeof import('./utils/subscribeMessage')['requestMiniSubscribeMessages']
   const resolveComponent: typeof import('vue')['resolveComponent']
   const resolveRef: typeof import('@vueuse/core')['resolveRef']
@@ -161,9 +168,11 @@
   const searchVisitedUsersByExactName: typeof import('./api/lookup')['searchVisitedUsersByExactName']
   const setActivePinia: typeof import('pinia')['setActivePinia']
   const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
+  const setMiniPhoneAuthorizeRequired: typeof import('./store/auth')['setMiniPhoneAuthorizeRequired']
   const shallowReactive: typeof import('vue')['shallowReactive']
   const shallowReadonly: typeof import('vue')['shallowReadonly']
   const shallowRef: typeof import('vue')['shallowRef']
+  const showMiniLoginPrompt: typeof import('./store/auth')['showMiniLoginPrompt']
   const storeToRefs: typeof import('pinia')['storeToRefs']
   const submitGeneralApproval: typeof import('./api/appointment')['submitGeneralApproval']
   const submitHandlerApproval: typeof import('./api/appointment')['submitHandlerApproval']
@@ -401,8 +410,10 @@
     readonly $$userConfigMap: UnwrapRef<typeof import('./api/index')['$$userConfigMap']>
     readonly Apis: UnwrapRef<typeof import('./api/index')['Apis']>
     readonly CommonUtil: UnwrapRef<typeof import('wot-design-uni')['CommonUtil']>
+    readonly EMPLOYEE_SUBSCRIBE_TEMPLATE_IDS: UnwrapRef<typeof import('./utils/mini-login')['EMPLOYEE_SUBSCRIBE_TEMPLATE_IDS']>
     readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
     readonly MINI_AUTH_STORAGE_KEY: UnwrapRef<typeof import('./store/auth')['MINI_AUTH_STORAGE_KEY']>
+    readonly MINI_PHONE_AUTH_REQUIRED: UnwrapRef<typeof import('./utils/mini-login')['MINI_PHONE_AUTH_REQUIRED']>
     readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
     readonly alovaInstance: UnwrapRef<typeof import('./api/index')['alovaInstance']>
     readonly api: UnwrapRef<typeof import('./api/index')['default']>
@@ -458,13 +469,17 @@
     readonly fetchVisitReasons: UnwrapRef<typeof import('./api/lookup')['fetchVisitReasons']>
     readonly fetchVisitRecords: UnwrapRef<typeof import('./api/appointment')['fetchVisitRecords']>
     readonly fetchVisitorDetail: UnwrapRef<typeof import('./api/visitor')['fetchVisitorDetail']>
+    readonly finishMiniLogin: UnwrapRef<typeof import('./utils/mini-login')['finishMiniLogin']>
     readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
+    readonly getApiErrorCode: UnwrapRef<typeof import('./utils/mini-login')['getApiErrorCode']>
     readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
     readonly getCurrentPath: UnwrapRef<typeof import('./utils/index')['getCurrentPath']>
     readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
     readonly getPersistedAuthState: UnwrapRef<typeof import('./store/auth')['getPersistedAuthState']>
     readonly getPersistedToken: UnwrapRef<typeof import('./store/auth')['getPersistedToken']>
+    readonly getWxLoginCode: UnwrapRef<typeof import('./utils/mini-login')['getWxLoginCode']>
     readonly h: UnwrapRef<typeof import('vue')['h']>
+    readonly hideMiniLoginPrompt: UnwrapRef<typeof import('./store/auth')['hideMiniLoginPrompt']>
     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
     readonly inject: UnwrapRef<typeof import('vue')['inject']>
     readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
@@ -543,6 +558,7 @@
     readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']>
     readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
     readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
+    readonly requestEmployeeSubscribeAuthorization: UnwrapRef<typeof import('./utils/mini-login')['requestEmployeeSubscribeAuthorization']>
     readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
     readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
     readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
@@ -550,9 +566,11 @@
     readonly searchVisitedUsersByExactName: UnwrapRef<typeof import('./api/lookup')['searchVisitedUsersByExactName']>
     readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
     readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
+    readonly setMiniPhoneAuthorizeRequired: UnwrapRef<typeof import('./store/auth')['setMiniPhoneAuthorizeRequired']>
     readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
     readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
     readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
+    readonly showMiniLoginPrompt: UnwrapRef<typeof import('./store/auth')['showMiniLoginPrompt']>
     readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
     readonly submitGeneralApproval: UnwrapRef<typeof import('./api/appointment')['submitGeneralApproval']>
     readonly submitHandlerApproval: UnwrapRef<typeof import('./api/appointment')['submitHandlerApproval']>
diff --git a/yuyueMiniProgram/src/components.d.ts b/yuyueMiniProgram/src/components.d.ts
index adae7ac..6089e6a 100644
--- a/yuyueMiniProgram/src/components.d.ts
+++ b/yuyueMiniProgram/src/components.d.ts
@@ -11,6 +11,7 @@
     GlobalLoading: typeof import('./components/GlobalLoading.vue')['default']
     GlobalMessage: typeof import('./components/GlobalMessage.vue')['default']
     GlobalToast: typeof import('./components/GlobalToast.vue')['default']
+    MiniLoginPrompt: typeof import('./components/MiniLoginPrompt.vue')['default']
     PrivacyPopup: typeof import('./components/PrivacyPopup.vue')['default']
     WdButton: typeof import('wot-design-uni/components/wd-button/wd-button.vue')['default']
     WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
diff --git a/yuyueMiniProgram/src/components/MiniLoginPrompt.vue b/yuyueMiniProgram/src/components/MiniLoginPrompt.vue
new file mode 100644
index 0000000..b7f6d0d
--- /dev/null
+++ b/yuyueMiniProgram/src/components/MiniLoginPrompt.vue
@@ -0,0 +1,162 @@
+<script setup lang="ts">
+import { loginByWx } from '@/api/auth'
+import { useMiniAuthStore } from '@/store/auth'
+import { finishMiniLogin, getApiErrorCode, getWxLoginCode, MINI_PHONE_AUTH_REQUIRED } from '@/utils/mini-login'
+import { normalizeProfileDisplayName } from '@/types/auth'
+
+const emit = defineEmits<{
+  (e: 'login-success'): void
+}>()
+
+const authStore = useMiniAuthStore()
+const router = useRouter()
+const loginSubmitting = ref(false)
+
+const visible = computed(() => authStore.loginPromptVisible)
+const needPhoneAuthorize = computed(() => authStore.needPhoneAuthorize)
+const message = computed(() => {
+  if (needPhoneAuthorize.value) {
+    return '首次登录需授权手机号完成身份识别'
+  }
+  return authStore.authPromptMessage || '您还未登录,请先登录'
+})
+
+function hideLoginPrompt() {
+  authStore.hideLoginPrompt()
+}
+
+function shouldForceEditProfile() {
+  return !normalizeProfileDisplayName(authStore.profile?.displayName)
+}
+
+function openProfileEditorIfNeeded() {
+  if (!shouldForceEditProfile()) {
+    return false
+  }
+  uni.showToast({
+    title: '请先完善个人资料',
+    icon: 'none',
+  })
+  router.push({ name: 'profile' })
+  return true
+}
+
+async function handleStartLogin() {
+  if (loginSubmitting.value) {
+    return
+  }
+
+  loginSubmitting.value = true
+  try {
+    const wxCode = await getWxLoginCode()
+    const loginData = await loginByWx({ wxCode })
+    await finishMiniLogin(loginData)
+    hideLoginPrompt()
+    uni.$emit('mini-login-success')
+    emit('login-success')
+    if (openProfileEditorIfNeeded()) {
+      return
+    }
+    uni.showToast({
+      title: '登录成功',
+      icon: 'success',
+    })
+  }
+  catch (error) {
+    if (getApiErrorCode(error) === MINI_PHONE_AUTH_REQUIRED) {
+      authStore.setNeedPhoneAuthorize(true)
+      return
+    }
+    uni.showToast({
+      title: error instanceof Error ? error.message : '登录失败',
+      icon: 'none',
+    })
+  }
+  finally {
+    loginSubmitting.value = false
+  }
+}
+
+async function handlePhoneAuthorize(event: any) {
+  if (loginSubmitting.value) {
+    return
+  }
+  const phoneCode = event?.detail?.code
+  if (!phoneCode) {
+    uni.showToast({
+      title: '未获取到手机号授权',
+      icon: 'none',
+    })
+    return
+  }
+
+  loginSubmitting.value = true
+  try {
+    const wxCode = await getWxLoginCode()
+    const loginData = await loginByWx({ wxCode, phoneCode })
+    await finishMiniLogin(loginData)
+    hideLoginPrompt()
+    uni.$emit('mini-login-success')
+    emit('login-success')
+    if (openProfileEditorIfNeeded()) {
+      return
+    }
+    uni.showToast({
+      title: '登录成功',
+      icon: 'success',
+    })
+  }
+  catch (error) {
+    uni.showToast({
+      title: error instanceof Error ? error.message : '登录失败',
+      icon: 'none',
+    })
+  }
+  finally {
+    loginSubmitting.value = false
+  }
+}
+</script>
+
+<template>
+  <view v-if="visible" class="fixed inset-0 z-40 bg-[rgba(0,0,0,0.55)]" @click="!needPhoneAuthorize && hideLoginPrompt()" />
+
+  <view
+    v-if="visible"
+    class="fixed left-[50%] top-[50%] z-50 w-[520rpx] translate-x-[-50%] translate-y-[-50%] rounded-[28rpx] bg-white px-[40rpx] pb-[36rpx] pt-[28rpx]"
+  >
+    <view class="mb-[16rpx] text-center text-[34rpx] text-[#1A1A1A] font-medium">
+      提示
+    </view>
+    <view class="flex flex-col items-center">
+      <image src="/static/home/tips.png" class="mb-[24rpx] h-[144rpx] w-[144rpx]" mode="aspectFit" />
+      <text class="mb-[44rpx] text-[28rpx] text-[#4A4A4A]">
+        {{ message }}
+      </text>
+    </view>
+    <view class="flex items-center justify-between gap-[20rpx]">
+      <view
+        class="h-[80rpx] flex flex-1 items-center justify-center rounded-full bg-[#F2F2F2] text-[30rpx] text-[#666666]"
+        @click="hideLoginPrompt"
+      >
+        再想想
+      </view>
+      <button
+        v-if="needPhoneAuthorize"
+        class="m-0 h-[80rpx] flex flex-1 items-center justify-center rounded-full border-none bg-[#0C6FFF] p-0 text-[30rpx] text-white after:border-none"
+        :disabled="loginSubmitting"
+        open-type="getPhoneNumber"
+        @getphonenumber="handlePhoneAuthorize"
+      >
+        {{ loginSubmitting ? '授权中...' : '授权手机号' }}
+      </button>
+      <view
+        v-else
+        class="h-[80rpx] flex flex-1 items-center justify-center rounded-full bg-[#0C6FFF] text-[30rpx] text-white"
+        @click="handleStartLogin"
+      >
+        {{ loginSubmitting ? '登录中...' : '去登录' }}
+      </view>
+    </view>
+  </view>
+</template>
diff --git a/yuyueMiniProgram/src/layouts/tabbar.vue b/yuyueMiniProgram/src/layouts/tabbar.vue
index f5e0f48..adf3a5b 100644
--- a/yuyueMiniProgram/src/layouts/tabbar.vue
+++ b/yuyueMiniProgram/src/layouts/tabbar.vue
@@ -2,10 +2,19 @@
 const router = useRouter()
 
 const route = useRoute()
+const authStore = useMiniAuthStore()
 
 const { activeTabbar, getTabbarItemValue, setTabbarItemActive, tabbarList } = useTabbar()
 
 function handleTabbarChange({ value }: { value: string }) {
+  if (value === 'mine' && !authStore.isLoggedIn) {
+    authStore.showLoginPrompt('您还未登录,请先登录')
+    setTabbarItemActive('home')
+    if (route.name !== 'home') {
+      router.replaceAll({ name: 'home' })
+    }
+    return
+  }
   setTabbarItemActive(value)
   router.pushTab({ name: value })
 }
@@ -34,6 +43,7 @@
 
 <template>
   <slot />
+  <MiniLoginPrompt />
   <wd-gap safe-area-bottom height="var(--wot-tabbar-height, 50px)" />
   <wd-tabbar
     :model-value="activeTabbar.name" active-color="#0068FF" inactive-color="#1A1A1A" bordered safe-area-inset-bottom fixed custom-class="custom-tabbar-bg"
diff --git a/yuyueMiniProgram/src/main.ts b/yuyueMiniProgram/src/main.ts
index dca7cf8..6172d55 100644
--- a/yuyueMiniProgram/src/main.ts
+++ b/yuyueMiniProgram/src/main.ts
@@ -3,6 +3,45 @@
 import router from './router'
 import 'uno.css'
 
+if (typeof globalThis.TextEncoder === 'undefined') {
+  class MiniTextEncoder {
+    encode(input = '') {
+      const bytes: number[] = []
+      for (const char of input) {
+        const codePoint = char.codePointAt(0) ?? 0
+        if (codePoint <= 0x7F) {
+          bytes.push(codePoint)
+          continue
+        }
+        if (codePoint <= 0x7FF) {
+          bytes.push(
+            0xC0 | (codePoint >> 6),
+            0x80 | (codePoint & 0x3F),
+          )
+          continue
+        }
+        if (codePoint <= 0xFFFF) {
+          bytes.push(
+            0xE0 | (codePoint >> 12),
+            0x80 | ((codePoint >> 6) & 0x3F),
+            0x80 | (codePoint & 0x3F),
+          )
+          continue
+        }
+        bytes.push(
+          0xF0 | (codePoint >> 18),
+          0x80 | ((codePoint >> 12) & 0x3F),
+          0x80 | ((codePoint >> 6) & 0x3F),
+          0x80 | (codePoint & 0x3F),
+        )
+      }
+      return new Uint8Array(bytes)
+    }
+  }
+
+  globalThis.TextEncoder = MiniTextEncoder as typeof TextEncoder
+}
+
 const pinia = createPinia()
 pinia.use(persistPlugin)
 export function createApp() {
diff --git a/yuyueMiniProgram/src/pages/index/index.vue b/yuyueMiniProgram/src/pages/index/index.vue
index 1c21593..b3be601 100644
--- a/yuyueMiniProgram/src/pages/index/index.vue
+++ b/yuyueMiniProgram/src/pages/index/index.vue
@@ -3,6 +3,7 @@
 import { fetchMyApprovalTodos, fetchMyVisitorCodes, fetchVisitRecords } from '@/api/appointment'
 import { useMiniAuthStore } from '@/store/auth'
 import type { MiniMyVisitorCodeOrderItem, MiniMyVisitorCodeTicketItem } from '@/types/appointment'
+import { getApiErrorCode } from '@/utils/mini-login'
 
 interface VisitorCodeDisplayTicket extends MiniMyVisitorCodeTicketItem {
   qrCode: string
@@ -27,9 +28,12 @@
 const visitRecordCount = ref(0)
 
 const profile = computed(() => authStore.profile)
+const isLoggedIn = computed(() => authStore.isLoggedIn)
 const isEmployee = computed(() => profile.value?.userType === 'EMPLOYEE')
+const showEmployeeDashboard = computed(() => !isLoggedIn.value || isEmployee.value)
+const welcomeName = computed(() => profile.value?.displayName || '欢迎使用,请登录')
 const userRoleText = computed(() => {
-  if (!profile.value) {
+  if (!profile.value || !isLoggedIn.value) {
     return ''
   }
   if (profile.value.userType === 'EMPLOYEE') {
@@ -38,7 +42,7 @@
   return '普通访客'
 })
 const secondaryText = computed(() => {
-  if (!profile.value) {
+  if (!profile.value || !isLoggedIn.value) {
     return ''
   }
   if (profile.value.userType === 'EMPLOYEE') {
@@ -46,6 +50,8 @@
   }
   return profile.value.companyName || ''
 })
+const approvalTodoDisplay = computed(() => isLoggedIn.value ? String(approvalTodoCount.value) : '-')
+const visitRecordDisplay = computed(() => isLoggedIn.value ? String(visitRecordCount.value) : '-')
 const currentOrder = computed(() => visitorOrders.value[currentOrderIndex.value] || null)
 const currentTicket = computed(() => {
   if (!currentOrder.value) {
@@ -102,6 +108,10 @@
   return order.visitTimeText || '暂无有效期'
 }
 
+function isUnauthorizedError(error: unknown) {
+  return getApiErrorCode(error) === 401 || getApiErrorCode(error) === 403
+}
+
 function formatDateTime(value?: string | null) {
   if (!value) {
     return ''
@@ -121,6 +131,13 @@
 }
 
 async function loadVisitorCodes() {
+  if (!isLoggedIn.value) {
+    visitorOrders.value = []
+    ticketIndexMap.value = {}
+    currentOrderIndex.value = 0
+    loadingVisitorCodes.value = false
+    return
+  }
   loadingVisitorCodes.value = true
   const currentToken = qrRenderToken.value + 1
   qrRenderToken.value = currentToken
@@ -145,6 +162,9 @@
   catch (error) {
     visitorOrders.value = []
     ticketIndexMap.value = {}
+    if (isUnauthorizedError(error)) {
+      return
+    }
     uni.showToast({
       title: error instanceof Error ? error.message : '加载访客码失败',
       icon: 'none',
@@ -158,7 +178,7 @@
 }
 
 async function loadEmployeeDashboardCounts() {
-  if (!isEmployee.value) {
+  if (!isLoggedIn.value || !isEmployee.value) {
     approvalTodoCount.value = 0
     visitRecordCount.value = 0
     return
@@ -335,14 +355,50 @@
   })
 }
 
+function showLoginPrompt(message?: string) {
+  authStore.showLoginPrompt(message)
+}
+
+function hideLoginPrompt() {
+  authStore.hideLoginPrompt()
+}
+
+function ensureLoggedIn(action?: () => void, message?: string) {
+  if (isLoggedIn.value) {
+    action?.()
+    return true
+  }
+  showLoginPrompt(message)
+  return false
+}
+
+async function reloadHomeData() {
+  if (!isLoggedIn.value) {
+    visitorOrders.value = []
+    ticketIndexMap.value = {}
+    currentOrderIndex.value = 0
+    approvalTodoCount.value = 0
+    visitRecordCount.value = 0
+    return
+  }
+  await Promise.all([
+    loadVisitorCodes(),
+    loadEmployeeDashboardCounts(),
+  ])
+}
+
 function handleOpenApprovalTodo() {
-  uni.navigateTo({ url: '/pages/reviewList/index' })
+  ensureLoggedIn(() => {
+    uni.navigateTo({ url: '/pages/reviewList/index' })
+  }, '您还未登录,请先登录')
 }
 
 function handleOpenVisitRecords() {
-  uni.navigateTo({
-    url: '/pages/reception/index',
-  })
+  ensureLoggedIn(() => {
+    uni.navigateTo({
+      url: '/pages/reception/index',
+    })
+  }, '您还未登录,请先登录')
 }
 
 function buildVisitorCodeSharePath(orderUuid?: string | null) {
@@ -361,8 +417,15 @@
 
 onShow(() => {
   authStore.restoreSession()
-  void loadVisitorCodes()
-  void loadEmployeeDashboardCounts()
+  void reloadHomeData()
+})
+
+onMounted(() => {
+  uni.$on('mini-login-success', reloadHomeData)
+})
+
+onUnmounted(() => {
+  uni.$off('mini-login-success', reloadHomeData)
 })
 
 definePage({
@@ -385,19 +448,19 @@
         </text>
       </view>
 
-      <view class="mb-[36rpx] flex items-center pl-[26rpx] pr-[22rpx]">
+      <view class="mb-[36rpx] flex items-center pl-[26rpx] pr-[22rpx]" @click="!isLoggedIn && showLoginPrompt('您还未登录,请先登录')">
         <image src="/static/home/hi.png" class="mr-[24rpx] h-[96rpx] w-[212rpx]" mode="aspectFit" />
         <view class="flex flex-col">
           <view class="welcome-title flex items-center text-[56rpx] text-white font-[600]">
-            <text>{{ profile?.displayName || '用户' }}</text>
+            <text>{{ welcomeName }}</text>
           </view>
-          <text class="mt-[8rpx] text-[24rpx] text-white/90">
+          <text v-if="isLoggedIn && userRoleText" class="mt-[8rpx] text-[24rpx] text-white/90">
             {{ userRoleText }}<template v-if="secondaryText"> · {{ secondaryText }}</template>
           </text>
         </view>
       </view>
 
-      <view v-if="isEmployee" class="mb-[32rpx] w-full pl-[26rpx] pr-[22rpx]" style="box-sizing: border-box;">
+      <view v-if="showEmployeeDashboard" class="mb-[32rpx] w-full pl-[26rpx] pr-[22rpx]" style="box-sizing: border-box;">
         <view
           class="rounded-[16rpx] p-[24rpx] pb-[26rpx]"
           style="background: linear-gradient(180deg, #CCE4FE 0%, #FAFCFD 40%, #FDFDFD 100%); box-shadow: 0 4rpx 8rpx 0 rgba(0, 104, 255, 0.2);border: 1rpx solid #fff;"
@@ -416,7 +479,7 @@
                   待办
                 </text>
                 <text class="text-[32rpx] text-[#056DFF] font-bold">
-                  {{ approvalTodoCount }}
+                  {{ approvalTodoDisplay }}
                 </text>
               </view>
             </view>
@@ -433,7 +496,7 @@
                   待接待
                 </text>
                 <text class="text-[32rpx] text-[#F86911] font-bold">
-                  {{ visitRecordCount }}
+                  {{ visitRecordDisplay }}
                 </text>
               </view>
             </view>
@@ -445,7 +508,7 @@
         <view
           class="h-[240rpx] w-[344rpx] flex flex-1 flex-col items-center justify-center rounded-[20rpx]"
           style="background: linear-gradient(180deg, #DEF4FF 0%, #FFFFFF 100%);border: 1rpx solid #fff;box-shadow: 0 4rpx 10rpx 0 rgba(0, 104, 255, 0.16)"
-          @click="() => { router.push({ name: 'appointment' }) }"
+          @click="ensureLoggedIn(() => { router.push({ name: 'appointment' }) })"
         >
           <image src="/static/home/appointment.png" class="mb-[12rpx] h-[96rpx] w-[96rpx]" />
           <text class="text-[28rpx] text-[#1a1a1a]">
@@ -456,7 +519,7 @@
           <view
             class="flex flex-1 items-center rounded-[20rpx] px-[40rpx]"
             style="background: linear-gradient(180deg, #FFF7E0 0%, #FFFFFF 100%);box-shadow: 0 4rpx 10rpx 0 rgba(0, 104, 255, 0.16)"
-            @click="() => { router.push({ name: 'record' }) }"
+            @click="ensureLoggedIn(() => { router.push({ name: 'record' }) })"
           >
             <image src="/static/home/record.png" class="mr-[16rpx] h-[64rpx] w-[64rpx]" />
             <text class="text-[28rpx] text-[#000000]">
@@ -466,7 +529,7 @@
           <view
             class="flex flex-1 items-center rounded-[20rpx] px-[40rpx]"
             style="background: linear-gradient(180deg, #E0FAEE 0%, #FFFFFF 100%);border: 1rpx solid #fff;box-shadow: 0 4rpx 10rpx 0 rgba(0, 104, 255, 0.16)"
-            @click="uni.navigateTo({ url: '/pages/choosePerson/index?mode=manage' })"
+            @click="ensureLoggedIn(() => { uni.navigateTo({ url: '/pages/choosePerson/index?mode=manage' }) })"
           >
             <image src="/static/home/visitor.png" class="mr-[16rpx] h-[64rpx] w-[64rpx]" />
             <text class="text-[28rpx] text-[#000000]">
@@ -476,7 +539,7 @@
         </view>
       </view>
 
-      <view class="mt-[32rpx] px-[24rpx]">
+      <view v-if="isLoggedIn" class="mt-[32rpx] px-[24rpx]">
         <view class="mb-[20rpx] flex items-center justify-between">
           <text class="text-[34rpx] text-[#1a1a1a]">
             我的访客码
@@ -610,6 +673,7 @@
     </view>
 
     <image
+      v-if="isLoggedIn"
       src="/static/home/process.png"
       class="fixed right-[-42rpx] z-5 h-[128rpx] w-[128rpx]"
       :style="{ bottom: `${parkFlowBottomPx}px` }"
diff --git a/yuyueMiniProgram/src/pages/mine/index.vue b/yuyueMiniProgram/src/pages/mine/index.vue
index 2ef937c..8ed955b 100644
--- a/yuyueMiniProgram/src/pages/mine/index.vue
+++ b/yuyueMiniProgram/src/pages/mine/index.vue
@@ -24,6 +24,7 @@
 const navBarHeight = 44
 
 const profile = computed(() => authStore.profile)
+const isLoggedIn = computed(() => authStore.isLoggedIn)
 const userRole = computed<'employee' | 'normal'>(() => profile.value?.userType === 'EMPLOYEE' ? 'employee' : 'normal')
 const roleLabel = computed(() => {
   if (userRole.value === 'employee') {
@@ -49,6 +50,10 @@
 const visibleMenus = computed(() => menuList.value.filter(item => item.showFor.includes(userRole.value)))
 
 function handleEditProfile() {
+  if (!isLoggedIn.value) {
+    authStore.showLoginPrompt('您还未登录,请先登录')
+    return
+  }
   uni.navigateTo({ url: '/pages/profile/index' })
 }
 
@@ -61,11 +66,15 @@
   }
   finally {
     authStore.clearSession()
-    router.replaceAll({ name: 'login' })
+    router.replaceAll({ name: 'home' })
   }
 }
 
 function handleMenuClick(item: MenuItem) {
+  if (!isLoggedIn.value) {
+    authStore.showLoginPrompt('您还未登录,请先登录')
+    return
+  }
   if (item.title === '退出登录') {
     void handleLogout()
     return
diff --git a/yuyueMiniProgram/src/router/index.ts b/yuyueMiniProgram/src/router/index.ts
index c888450..d0f4898 100644
--- a/yuyueMiniProgram/src/router/index.ts
+++ b/yuyueMiniProgram/src/router/index.ts
@@ -2,7 +2,7 @@
 import { pages, subPackages } from 'virtual:uni-pages'
 import { fetchMiniMe } from '@/api/auth'
 import { setUnauthorizedRedirectHandler } from './authRedirect'
-import { clearMiniAuthSession, getPersistedAuthState, updateMiniAuthProfile } from '@/store/auth'
+import { clearMiniAuthSession, getPersistedAuthState, showMiniLoginPrompt, updateMiniAuthProfile } from '@/store/auth'
 import { normalizeMeProfile } from '@/types/auth'
 
 function generateRoutes() {
@@ -27,10 +27,11 @@
 })
 
 setUnauthorizedRedirectHandler(() => {
-  router.replaceAll({ name: 'login' })
+  showMiniLoginPrompt('登录已失效,请重新登录')
+  router.replaceAll({ name: 'home' })
 })
 
-const PUBLIC_ROUTE_NAMES = new Set(['login', 'visitorCode'])
+const PUBLIC_ROUTE_NAMES = new Set(['home', 'login', 'visitorCode'])
 const HOME_ROUTE_NAME = 'home'
 
 function isPublicRoute(name?: string) {
@@ -48,7 +49,8 @@
       next()
       return
     }
-    next({ name: 'login' })
+    showMiniLoginPrompt('您还未登录,请先登录')
+    next({ name: HOME_ROUTE_NAME })
     return
   }
 
@@ -64,16 +66,17 @@
     catch (error) {
       console.warn('mini auth guard fetch /me failed', error)
       clearMiniAuthSession()
+      showMiniLoginPrompt('登录已失效,请重新登录')
       return false
     }
   }
 
   return Promise.resolve(ensureProfile()).then((ok) => {
     if (!ok) {
-      next({ name: 'login' })
+      next({ name: HOME_ROUTE_NAME })
       return
     }
-    if (publicRoute) {
+    if (to.name === 'login') {
       next({ name: HOME_ROUTE_NAME })
       return
     }
diff --git a/yuyueMiniProgram/src/static/appointment/bg.png b/yuyueMiniProgram/src/static/appointment/bg.png
index a0b42ad..c88e130 100644
--- a/yuyueMiniProgram/src/static/appointment/bg.png
+++ b/yuyueMiniProgram/src/static/appointment/bg.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/appointment/company.png b/yuyueMiniProgram/src/static/appointment/company.png
index 6692833..144829c 100644
--- a/yuyueMiniProgram/src/static/appointment/company.png
+++ b/yuyueMiniProgram/src/static/appointment/company.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/appointment/delete.png b/yuyueMiniProgram/src/static/appointment/delete.png
index 82920d7..6e9610a 100644
--- a/yuyueMiniProgram/src/static/appointment/delete.png
+++ b/yuyueMiniProgram/src/static/appointment/delete.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/appointment/idCard.png b/yuyueMiniProgram/src/static/appointment/idCard.png
index e21cf2e..d912169 100644
--- a/yuyueMiniProgram/src/static/appointment/idCard.png
+++ b/yuyueMiniProgram/src/static/appointment/idCard.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/appointment/man.png b/yuyueMiniProgram/src/static/appointment/man.png
index 534ed0b..c074d89 100644
--- a/yuyueMiniProgram/src/static/appointment/man.png
+++ b/yuyueMiniProgram/src/static/appointment/man.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/appointment/plate.png b/yuyueMiniProgram/src/static/appointment/plate.png
index d9fa83c..e0ef63a 100644
--- a/yuyueMiniProgram/src/static/appointment/plate.png
+++ b/yuyueMiniProgram/src/static/appointment/plate.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/appointment/woman.png b/yuyueMiniProgram/src/static/appointment/woman.png
index d4c4772..956d909 100644
--- a/yuyueMiniProgram/src/static/appointment/woman.png
+++ b/yuyueMiniProgram/src/static/appointment/woman.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/choosePerson/add.png b/yuyueMiniProgram/src/static/choosePerson/add.png
index fad42ac..43a26e9 100644
--- a/yuyueMiniProgram/src/static/choosePerson/add.png
+++ b/yuyueMiniProgram/src/static/choosePerson/add.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/choosePerson/bg@2x.png b/yuyueMiniProgram/src/static/choosePerson/bg@2x.png
index fd65fbd..d69f6e1 100644
--- a/yuyueMiniProgram/src/static/choosePerson/bg@2x.png
+++ b/yuyueMiniProgram/src/static/choosePerson/bg@2x.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/choosePerson/edit.png b/yuyueMiniProgram/src/static/choosePerson/edit.png
index edabf2b..fcb9719 100644
--- a/yuyueMiniProgram/src/static/choosePerson/edit.png
+++ b/yuyueMiniProgram/src/static/choosePerson/edit.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/choosePerson/noData.png b/yuyueMiniProgram/src/static/choosePerson/noData.png
index 9cf1b79..64260ab 100644
--- a/yuyueMiniProgram/src/static/choosePerson/noData.png
+++ b/yuyueMiniProgram/src/static/choosePerson/noData.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/appointment.png b/yuyueMiniProgram/src/static/home/appointment.png
index befa562..ea4aeeb 100644
--- a/yuyueMiniProgram/src/static/home/appointment.png
+++ b/yuyueMiniProgram/src/static/home/appointment.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/bg.png b/yuyueMiniProgram/src/static/home/bg.png
index 4c46072..1ca836a 100644
--- a/yuyueMiniProgram/src/static/home/bg.png
+++ b/yuyueMiniProgram/src/static/home/bg.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/chevron_left.png b/yuyueMiniProgram/src/static/home/chevron_left.png
index 9707c38..a3c9225 100644
--- a/yuyueMiniProgram/src/static/home/chevron_left.png
+++ b/yuyueMiniProgram/src/static/home/chevron_left.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/chevron_right.png b/yuyueMiniProgram/src/static/home/chevron_right.png
index 2698ffa..c35d183 100644
--- a/yuyueMiniProgram/src/static/home/chevron_right.png
+++ b/yuyueMiniProgram/src/static/home/chevron_right.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/download.png b/yuyueMiniProgram/src/static/home/download.png
index 4f95376..e018277 100644
--- a/yuyueMiniProgram/src/static/home/download.png
+++ b/yuyueMiniProgram/src/static/home/download.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/hi.png b/yuyueMiniProgram/src/static/home/hi.png
index ccfd23f..276d957 100644
--- a/yuyueMiniProgram/src/static/home/hi.png
+++ b/yuyueMiniProgram/src/static/home/hi.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/left.png b/yuyueMiniProgram/src/static/home/left.png
index 896a407..f29508b 100644
--- a/yuyueMiniProgram/src/static/home/left.png
+++ b/yuyueMiniProgram/src/static/home/left.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/process.png b/yuyueMiniProgram/src/static/home/process.png
index 01f5a49..35b8f2d 100644
--- a/yuyueMiniProgram/src/static/home/process.png
+++ b/yuyueMiniProgram/src/static/home/process.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/record.png b/yuyueMiniProgram/src/static/home/record.png
index 0016e14..e6149c3 100644
--- a/yuyueMiniProgram/src/static/home/record.png
+++ b/yuyueMiniProgram/src/static/home/record.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/right.png b/yuyueMiniProgram/src/static/home/right.png
index e317163..e6ea05e 100644
--- a/yuyueMiniProgram/src/static/home/right.png
+++ b/yuyueMiniProgram/src/static/home/right.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/share.png b/yuyueMiniProgram/src/static/home/share.png
index dd8904b..43bc9bf 100644
--- a/yuyueMiniProgram/src/static/home/share.png
+++ b/yuyueMiniProgram/src/static/home/share.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/tips.png b/yuyueMiniProgram/src/static/home/tips.png
index 39ece9a..e195ecb 100644
--- a/yuyueMiniProgram/src/static/home/tips.png
+++ b/yuyueMiniProgram/src/static/home/tips.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/toDo.png b/yuyueMiniProgram/src/static/home/toDo.png
index ecdeeb4..3ce78aa 100644
--- a/yuyueMiniProgram/src/static/home/toDo.png
+++ b/yuyueMiniProgram/src/static/home/toDo.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/toDo_bg.png b/yuyueMiniProgram/src/static/home/toDo_bg.png
index 2334b14..d0180e8 100644
--- a/yuyueMiniProgram/src/static/home/toDo_bg.png
+++ b/yuyueMiniProgram/src/static/home/toDo_bg.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/visitor.png b/yuyueMiniProgram/src/static/home/visitor.png
index 3eb3bad..8b2615a 100644
--- a/yuyueMiniProgram/src/static/home/visitor.png
+++ b/yuyueMiniProgram/src/static/home/visitor.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/wait.png b/yuyueMiniProgram/src/static/home/wait.png
index 44a6dbf..1283053 100644
--- a/yuyueMiniProgram/src/static/home/wait.png
+++ b/yuyueMiniProgram/src/static/home/wait.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/home/wait_bg.png b/yuyueMiniProgram/src/static/home/wait_bg.png
index c3c4252..fd15c65 100644
--- a/yuyueMiniProgram/src/static/home/wait_bg.png
+++ b/yuyueMiniProgram/src/static/home/wait_bg.png
Binary files differ
diff --git "a/yuyueMiniProgram/src/static/home/\346\210\220\351\203\275\345\226\234\346\234\233\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png" "b/yuyueMiniProgram/src/static/home/\346\210\220\351\203\275\345\226\234\346\234\233\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png"
index 85eb53c..803301f 100644
--- "a/yuyueMiniProgram/src/static/home/\346\210\220\351\203\275\345\226\234\346\234\233\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png"
+++ "b/yuyueMiniProgram/src/static/home/\346\210\220\351\203\275\345\226\234\346\234\233\347\247\221\346\212\200\346\234\211\351\231\220\345\205\254\345\217\270.png"
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/aboutUs.png b/yuyueMiniProgram/src/static/mine/aboutUs.png
index 9f49742..d928f17 100644
--- a/yuyueMiniProgram/src/static/mine/aboutUs.png
+++ b/yuyueMiniProgram/src/static/mine/aboutUs.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/bg.png b/yuyueMiniProgram/src/static/mine/bg.png
index 8a5e7d9..86da02a 100644
--- a/yuyueMiniProgram/src/static/mine/bg.png
+++ b/yuyueMiniProgram/src/static/mine/bg.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/bgmask.png b/yuyueMiniProgram/src/static/mine/bgmask.png
index 94e052d..083eec1 100644
--- a/yuyueMiniProgram/src/static/mine/bgmask.png
+++ b/yuyueMiniProgram/src/static/mine/bgmask.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/change.png b/yuyueMiniProgram/src/static/mine/change.png
index f50f16f..f0914a1 100644
--- a/yuyueMiniProgram/src/static/mine/change.png
+++ b/yuyueMiniProgram/src/static/mine/change.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/edit.png b/yuyueMiniProgram/src/static/mine/edit.png
index 0ea3abd..63d4bd9 100644
--- a/yuyueMiniProgram/src/static/mine/edit.png
+++ b/yuyueMiniProgram/src/static/mine/edit.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/header.png b/yuyueMiniProgram/src/static/mine/header.png
index a03d8d1..a977f22 100644
--- a/yuyueMiniProgram/src/static/mine/header.png
+++ b/yuyueMiniProgram/src/static/mine/header.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/logout.png b/yuyueMiniProgram/src/static/mine/logout.png
index 04ef7d7..8651a98 100644
--- a/yuyueMiniProgram/src/static/mine/logout.png
+++ b/yuyueMiniProgram/src/static/mine/logout.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/record.png b/yuyueMiniProgram/src/static/mine/record.png
index 5a1ac49..56c5208 100644
--- a/yuyueMiniProgram/src/static/mine/record.png
+++ b/yuyueMiniProgram/src/static/mine/record.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/mine/review.png b/yuyueMiniProgram/src/static/mine/review.png
index 1bf23ad..948b905 100644
--- a/yuyueMiniProgram/src/static/mine/review.png
+++ b/yuyueMiniProgram/src/static/mine/review.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/Pending.png b/yuyueMiniProgram/src/static/review/Pending.png
index 32fe6d2..1be987a 100644
--- a/yuyueMiniProgram/src/static/review/Pending.png
+++ b/yuyueMiniProgram/src/static/review/Pending.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/add.png b/yuyueMiniProgram/src/static/review/add.png
index fd395c1..dad7d33 100644
--- a/yuyueMiniProgram/src/static/review/add.png
+++ b/yuyueMiniProgram/src/static/review/add.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/applay.png b/yuyueMiniProgram/src/static/review/applay.png
index df22956..533a0a0 100644
--- a/yuyueMiniProgram/src/static/review/applay.png
+++ b/yuyueMiniProgram/src/static/review/applay.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/arrow.png b/yuyueMiniProgram/src/static/review/arrow.png
index cc1dbbc..2d4bdf0 100644
--- a/yuyueMiniProgram/src/static/review/arrow.png
+++ b/yuyueMiniProgram/src/static/review/arrow.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/bg-grey.png b/yuyueMiniProgram/src/static/review/bg-grey.png
index 4554c18..63d1320 100644
--- a/yuyueMiniProgram/src/static/review/bg-grey.png
+++ b/yuyueMiniProgram/src/static/review/bg-grey.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/bg-t.png b/yuyueMiniProgram/src/static/review/bg-t.png
index 788eddb..0267265 100644
--- a/yuyueMiniProgram/src/static/review/bg-t.png
+++ b/yuyueMiniProgram/src/static/review/bg-t.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/cancel.png b/yuyueMiniProgram/src/static/review/cancel.png
index 9ea31cd..e2788ee 100644
--- a/yuyueMiniProgram/src/static/review/cancel.png
+++ b/yuyueMiniProgram/src/static/review/cancel.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/cancelIcon.png b/yuyueMiniProgram/src/static/review/cancelIcon.png
index bd3c766..eafcbf0 100644
--- a/yuyueMiniProgram/src/static/review/cancelIcon.png
+++ b/yuyueMiniProgram/src/static/review/cancelIcon.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/cancelReview.png b/yuyueMiniProgram/src/static/review/cancelReview.png
index cd75787..d723574 100644
--- a/yuyueMiniProgram/src/static/review/cancelReview.png
+++ b/yuyueMiniProgram/src/static/review/cancelReview.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/chapter.png b/yuyueMiniProgram/src/static/review/chapter.png
index cbc4a88..c43e3da 100644
--- a/yuyueMiniProgram/src/static/review/chapter.png
+++ b/yuyueMiniProgram/src/static/review/chapter.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/circle.png b/yuyueMiniProgram/src/static/review/circle.png
index 200b17f..777fa38 100644
--- a/yuyueMiniProgram/src/static/review/circle.png
+++ b/yuyueMiniProgram/src/static/review/circle.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/close.png b/yuyueMiniProgram/src/static/review/close.png
index 4d2e756..108c446 100644
--- a/yuyueMiniProgram/src/static/review/close.png
+++ b/yuyueMiniProgram/src/static/review/close.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/close@2x.png b/yuyueMiniProgram/src/static/review/close@2x.png
index bef3083..74db53c 100644
--- a/yuyueMiniProgram/src/static/review/close@2x.png
+++ b/yuyueMiniProgram/src/static/review/close@2x.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/contentbg.png b/yuyueMiniProgram/src/static/review/contentbg.png
index 94e052d..083eec1 100644
--- a/yuyueMiniProgram/src/static/review/contentbg.png
+++ b/yuyueMiniProgram/src/static/review/contentbg.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/fail.png b/yuyueMiniProgram/src/static/review/fail.png
index 0fc75a6..4209f5e 100644
--- a/yuyueMiniProgram/src/static/review/fail.png
+++ b/yuyueMiniProgram/src/static/review/fail.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/greenpass.png b/yuyueMiniProgram/src/static/review/greenpass.png
index d4ae026..85eb235 100644
--- a/yuyueMiniProgram/src/static/review/greenpass.png
+++ b/yuyueMiniProgram/src/static/review/greenpass.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/pass.png b/yuyueMiniProgram/src/static/review/pass.png
index d85fc15..3a3fc55 100644
--- a/yuyueMiniProgram/src/static/review/pass.png
+++ b/yuyueMiniProgram/src/static/review/pass.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/passReview.png b/yuyueMiniProgram/src/static/review/passReview.png
index 021e345..492fe7b 100644
--- a/yuyueMiniProgram/src/static/review/passReview.png
+++ b/yuyueMiniProgram/src/static/review/passReview.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/reduce.png b/yuyueMiniProgram/src/static/review/reduce.png
index 254d7e5..880fff8 100644
--- a/yuyueMiniProgram/src/static/review/reduce.png
+++ b/yuyueMiniProgram/src/static/review/reduce.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/refesh.png b/yuyueMiniProgram/src/static/review/refesh.png
index 1b4c0d5..87bc799 100644
--- a/yuyueMiniProgram/src/static/review/refesh.png
+++ b/yuyueMiniProgram/src/static/review/refesh.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/refueReview.png b/yuyueMiniProgram/src/static/review/refueReview.png
index 0180b0f..63cc989 100644
--- a/yuyueMiniProgram/src/static/review/refueReview.png
+++ b/yuyueMiniProgram/src/static/review/refueReview.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/review.png b/yuyueMiniProgram/src/static/review/review.png
index 9199f5c..075e271 100644
--- a/yuyueMiniProgram/src/static/review/review.png
+++ b/yuyueMiniProgram/src/static/review/review.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/success.png b/yuyueMiniProgram/src/static/review/success.png
index d97e862..8d9bb36 100644
--- a/yuyueMiniProgram/src/static/review/success.png
+++ b/yuyueMiniProgram/src/static/review/success.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/review/underReview.png b/yuyueMiniProgram/src/static/review/underReview.png
index fc4c446..1bd02bd 100644
--- a/yuyueMiniProgram/src/static/review/underReview.png
+++ b/yuyueMiniProgram/src/static/review/underReview.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/viewCode/Download@2x.png b/yuyueMiniProgram/src/static/viewCode/Download@2x.png
index 83d6a86..13d3891 100644
--- a/yuyueMiniProgram/src/static/viewCode/Download@2x.png
+++ b/yuyueMiniProgram/src/static/viewCode/Download@2x.png
Binary files differ
diff --git a/yuyueMiniProgram/src/static/viewCode/bg@2x.png b/yuyueMiniProgram/src/static/viewCode/bg@2x.png
index fd65fbd..d69f6e1 100644
--- a/yuyueMiniProgram/src/static/viewCode/bg@2x.png
+++ b/yuyueMiniProgram/src/static/viewCode/bg@2x.png
Binary files differ
diff --git a/yuyueMiniProgram/src/store/auth.ts b/yuyueMiniProgram/src/store/auth.ts
index 2320da0..5846b14 100644
--- a/yuyueMiniProgram/src/store/auth.ts
+++ b/yuyueMiniProgram/src/store/auth.ts
@@ -8,10 +8,26 @@
   profile: MiniUserProfile | null
 }
 
+export interface MiniAuthUiState {
+  loginPromptVisible: boolean
+  needPhoneAuthorize: boolean
+  authPromptMessage: string
+}
+
+type MiniAuthStoreState = MiniAuthState & MiniAuthUiState
+
 function createInitialState(): MiniAuthState {
   return {
     token: '',
     profile: null,
+  }
+}
+
+function createInitialUiState(): MiniAuthUiState {
+  return {
+    loginPromptVisible: false,
+    needPhoneAuthorize: false,
+    authPromptMessage: '',
   }
 }
 
@@ -38,7 +54,7 @@
   uni.setStorageSync(MINI_AUTH_STORAGE_KEY, state)
 }
 
-function patchActiveStore(state: MiniAuthState) {
+function patchActiveStore(state: Partial<MiniAuthStoreState>) {
   const pinia = getActivePinia()
   if (!pinia) {
     return
@@ -50,7 +66,10 @@
 export function clearMiniAuthSession() {
   const state = createInitialState()
   uni.removeStorageSync(MINI_AUTH_STORAGE_KEY)
-  patchActiveStore(state)
+  patchActiveStore({
+    ...state,
+    ...createInitialUiState(),
+  })
 }
 
 export function updateMiniAuthProfile(profile: MiniUserProfile) {
@@ -62,8 +81,32 @@
   patchActiveStore(state)
 }
 
+export function showMiniLoginPrompt(message = '') {
+  patchActiveStore({
+    loginPromptVisible: true,
+    authPromptMessage: message,
+  })
+}
+
+export function hideMiniLoginPrompt() {
+  patchActiveStore({
+    loginPromptVisible: false,
+    authPromptMessage: '',
+    needPhoneAuthorize: false,
+  })
+}
+
+export function setMiniPhoneAuthorizeRequired(required: boolean) {
+  patchActiveStore({
+    needPhoneAuthorize: required,
+  })
+}
+
 export const useMiniAuthStore = defineStore('miniAuth', {
-  state: (): MiniAuthState => createInitialState(),
+  state: (): MiniAuthStoreState => ({
+    ...createInitialState(),
+    ...createInitialUiState(),
+  }),
   getters: {
     isLoggedIn: state => Boolean(state.token),
   },
@@ -71,6 +114,9 @@
     setSession(payload: MiniAuthState) {
       this.token = payload.token
       this.profile = payload.profile
+      this.loginPromptVisible = false
+      this.needPhoneAuthorize = false
+      this.authPromptMessage = ''
       writePersistedAuthState({
         token: payload.token,
         profile: payload.profile,
@@ -86,6 +132,9 @@
     clearSession() {
       this.token = ''
       this.profile = null
+      this.loginPromptVisible = false
+      this.needPhoneAuthorize = false
+      this.authPromptMessage = ''
       uni.removeStorageSync(MINI_AUTH_STORAGE_KEY)
     },
     restoreSession() {
@@ -94,5 +143,17 @@
       this.profile = state.profile
       return state
     },
+    showLoginPrompt(message = '') {
+      this.loginPromptVisible = true
+      this.authPromptMessage = message
+    },
+    hideLoginPrompt() {
+      this.loginPromptVisible = false
+      this.needPhoneAuthorize = false
+      this.authPromptMessage = ''
+    },
+    setNeedPhoneAuthorize(required: boolean) {
+      this.needPhoneAuthorize = required
+    },
   },
 })
diff --git a/yuyueMiniProgram/src/types/auth.ts b/yuyueMiniProgram/src/types/auth.ts
index 85847e0..0396ac9 100644
--- a/yuyueMiniProgram/src/types/auth.ts
+++ b/yuyueMiniProgram/src/types/auth.ts
@@ -43,6 +43,10 @@
   avatarUrl?: string | null
 }
 
+export function normalizeProfileDisplayName(value?: string | null) {
+  return (value || '').trim()
+}
+
 export function normalizeLoginProfile(data: MiniLoginResponse): MiniUserProfile {
   return {
     mpUserId: data.mpUserId,
@@ -51,7 +55,7 @@
     deptName: data.deptName ?? null,
     deptLeaderFlag: Boolean(data.deptLeader),
     avatarUrl: data.avatarUrl ?? null,
-    displayName: data.displayName,
+    displayName: normalizeProfileDisplayName(data.displayName),
     mobile: null,
     gender: null,
     companyName: data.companyName ?? null,
@@ -66,7 +70,7 @@
     deptName: data.deptName ?? null,
     deptLeaderFlag: Boolean(data.deptLeaderFlag),
     avatarUrl: data.avatarUrl ?? null,
-    displayName: data.nickName,
+    displayName: normalizeProfileDisplayName(data.nickName),
     mobile: data.mobile ?? null,
     gender: data.gender ?? null,
     companyName: data.companyName ?? null,
diff --git a/yuyueMiniProgram/src/utils/mini-login.ts b/yuyueMiniProgram/src/utils/mini-login.ts
new file mode 100644
index 0000000..bf01296
--- /dev/null
+++ b/yuyueMiniProgram/src/utils/mini-login.ts
@@ -0,0 +1,75 @@
+import type { ApiError } from '@/api/core/handlers'
+import { fetchMiniMe, loginByWx } from '@/api/auth'
+import { normalizeLoginProfile, normalizeMeProfile } from '@/types/auth'
+import { useMiniAuthStore } from '@/store/auth'
+
+export const EMPLOYEE_SUBSCRIBE_TEMPLATE_IDS = [
+  'BX49OBHvRcoYiCw0ZLqx04BiWQ9KaEQwuVth_Lh_M2A',
+  'GC1S3T2qHCbz8ow130u7elRczuqW6Z8q54mKRCYE-xo',
+]
+
+export const MINI_PHONE_AUTH_REQUIRED = 10008
+
+export function getApiErrorCode(error: unknown) {
+  return typeof error === 'object' && error != null && 'code' in error
+    ? Number((error as ApiError).code)
+    : NaN
+}
+
+export async function getWxLoginCode() {
+  // #ifdef MP-WEIXIN
+  return await new Promise<string>((resolve, reject) => {
+    uni.login({
+      provider: 'weixin',
+      success: (res) => {
+        if (res.code) {
+          resolve(res.code)
+          return
+        }
+        reject(new Error('获取微信登录凭证失败'))
+      },
+      fail: () => reject(new Error('获取微信登录凭证失败')),
+    })
+  })
+  // #endif
+  // #ifndef MP-WEIXIN
+  throw new Error('当前环境暂不支持微信授权登录')
+  // #endif
+}
+
+export async function requestEmployeeSubscribeAuthorization(userType?: string | null) {
+  if (userType !== 'EMPLOYEE') {
+    return
+  }
+  // #ifdef MP-WEIXIN
+  try {
+    await new Promise<void>((resolve) => {
+      uni.requestSubscribeMessage({
+        tmplIds: EMPLOYEE_SUBSCRIBE_TEMPLATE_IDS,
+        complete: () => resolve(),
+      })
+    })
+  }
+  catch (error) {
+    console.warn('request employee subscribe message failed', error)
+  }
+  // #endif
+}
+
+export async function finishMiniLogin(loginData: Awaited<ReturnType<typeof loginByWx>>) {
+  const authStore = useMiniAuthStore()
+  authStore.setSession({
+    token: loginData.token,
+    profile: normalizeLoginProfile(loginData),
+  })
+
+  try {
+    const profile = await fetchMiniMe(loginData.token)
+    authStore.setProfile(normalizeMeProfile(profile))
+  }
+  catch (error) {
+    console.warn('load mini profile failed after login', error)
+  }
+
+  await requestEmployeeSubscribeAuthorization(authStore.profile?.userType)
+}

--
Gitblit v1.7.1