2 次代码提交 371fd32215 ... 7b4aa5cae2

作者 SHA1 备注 提交日期
  haiyang 7b4aa5cae2 Merge branch 'dev' of http://39.101.143.165:8090/haiyang/couponCenter_mpapp into dev 1 周之前
  haiyang 1ead3b5cba 初始化,登录拦截 1 周之前

+ 2 - 2
env/.env

@@ -1,8 +1,8 @@
-VITE_APP_TITLE = 'unibest'
+VITE_APP_TITLE = '券中心'
 VITE_APP_PORT = 9000
 
 VITE_UNI_APPID = '__UNI__D1E5001'
-VITE_WX_APPID = 'wx6196e8e71383a3b0'
+VITE_WX_APPID = 'wx23776edbfe90d642'
 
 # h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
 # https://uniapp.dcloud.net.cn/collocation/manifest.html#h5-router

+ 23 - 12
src/App.vue

@@ -1,26 +1,37 @@
 <script setup lang="ts">
 import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
+import { LOGIN_PAGE } from '@/router/config'
 import { navigateToInterceptor } from '@/router/interceptor'
 
 onLaunch((options) => {
-  console.log('App.vue onLaunch', options)
+    console.log('App.vue onLaunch', options)
 })
 onShow((options) => {
-  console.log('App.vue onShow', options)
-  // 处理直接进入页面路由的情况:如h5直接输入路由、微信小程序分享后进入等
-  // https://github.com/unibest-tech/unibest/issues/192
-  if (options?.path) {
-    navigateToInterceptor.invoke({ url: `/${options.path}`, query: options.query })
-  }
-  else {
-    navigateToInterceptor.invoke({ url: '/' })
-  }
+    console.log('App.vue onShow', options)
+    // 获取当前页面路径
+    const pages = getCurrentPages()
+    const currentPage = pages[pages.length - 1]
+    const currentPath = `/${currentPage.route}`
+
+    // 如果当前已经是登录页,不触发拦截器
+    if (currentPath === LOGIN_PAGE) {
+        return
+    }
+
+    // 处理直接进入页面路由的情况:如h5直接输入路由、微信小程序分享后进入等
+    // https://github.com/unibest-tech/unibest/issues/192
+    if (options?.path) {
+        navigateToInterceptor.invoke({ url: `/${options.path}`, query: options.query })
+    }
+    else {
+        navigateToInterceptor.invoke({ url: '/' })
+    }
 })
 onHide(() => {
-  console.log('App Hide')
+    console.log('App Hide')
 })
 </script>
 
 <style lang="scss">
-  @import 'uview-plus/index.scss';
+@import 'uview-plus/index.scss';
 </style>

+ 9 - 0
src/api/coupon.ts

@@ -0,0 +1,9 @@
+import { API_DOMAINS, http } from '@/http/alova'
+
+export function getCouponList() {
+    return http.Get('/couponCenter/APP/couponTemplate/queryByType', {
+        meta: {
+            ignoreAuth: true,
+        },
+    })
+}

+ 0 - 43
src/api/foo.ts

@@ -1,43 +0,0 @@
-import { http } from '@/http/http'
-
-export interface IFoo {
-  id: number
-  name: string
-}
-
-export function foo() {
-  return http.Get<IFoo>('/foo', {
-    params: {
-      name: '菲鸽',
-      page: 1,
-      pageSize: 10,
-    },
-  })
-}
-
-export interface IFooItem {
-  id: string
-  name: string
-}
-
-/** GET 请求 */
-export async function getFooAPI(name: string) {
-  return await http.get<IFooItem>('/foo', { name })
-}
-/** GET 请求;支持 传递 header 的范例 */
-export function getFooAPI2(name: string) {
-  return http.get<IFooItem>('/foo', { name }, { 'Content-Type-100': '100' })
-}
-
-/** POST 请求 */
-export function postFooAPI(name: string) {
-  return http.post<IFooItem>('/foo', { name })
-}
-/** POST 请求;需要传递 query 参数的范例;微信小程序经常有同时需要query参数和body参数的场景 */
-export function postFooAPI2(name: string) {
-  return http.post<IFooItem>('/foo', { name }, { a: 1, b: 2 })
-}
-/** POST 请求;支持 传递 header 的范例 */
-export function postFooAPI3(name: string) {
-  return http.post<IFooItem>('/foo', { name }, { a: 1, b: 2 }, { 'Content-Type-100': '100' })
-}

+ 14 - 16
src/api/login.ts

@@ -1,5 +1,6 @@
 import type { IAuthLoginRes, ICaptcha, IDoubleTokenRes, IUpdateInfo, IUpdatePassword, IUserInfoRes } from './types/login'
-import { http } from '@/http/http'
+// import { http } from '@/http/http'
+import { API_DOMAINS, http } from '@/http/alova'
 
 /**
  * 登录表单
@@ -14,7 +15,7 @@ export interface ILoginForm {
  * @returns ICaptcha 验证码
  */
 export function getCode() {
-    return http.get<ICaptcha>('/user/getCode')
+    return http.Get<ICaptcha>('/user/getCode')
 }
 
 /**
@@ -22,7 +23,7 @@ export function getCode() {
  * @param loginForm 登录表单
  */
 export function login(loginForm: ILoginForm) {
-    return http.post<IAuthLoginRes>('/auth/login', loginForm)
+    return http.Post<IAuthLoginRes>('/auth/login', loginForm)
 }
 
 /**
@@ -30,35 +31,28 @@ export function login(loginForm: ILoginForm) {
  * @param refreshToken 刷新token
  */
 export function refreshToken(refreshToken: string) {
-    return http.post<IDoubleTokenRes>('/auth/refreshToken', { refreshToken })
+    return http.Post<IDoubleTokenRes>('/auth/refreshToken', { refreshToken })
 }
 
 /**
  * 获取用户信息
  */
 export function getUserInfo() {
-    return http.get<IUserInfoRes>('/user/info')
+    return http.Get<IUserInfoRes>('/user/info')
 }
 
 /**
  * 退出登录
  */
 export function logout() {
-    return http.get<void>('/auth/logout')
-}
-
-/**
- * 修改用户信息
- */
-export function updateInfo(data: IUpdateInfo) {
-    return http.post('/user/updateInfo', data)
+    return http.Get<void>('/auth/logout')
 }
 
 /**
  * 修改用户密码
  */
 export function updateUserPassword(data: IUpdatePassword) {
-    return http.post('/user/updatePassword', data)
+    return http.Post('/user/updatePassword', data)
 }
 
 /**
@@ -77,7 +71,7 @@ export function getWxCode() {
 
 export function getUserProfile() {
     return new Promise<UniApp.GetUserProfileRes>((resolve, reject) => {
-        uni.getUserInfo({
+        uni.getUserProfile({
             provider: 'weixin',
             desc: '用于获取登录用户信息',
             success: res => resolve(res),
@@ -92,5 +86,9 @@ export function getUserProfile() {
  * @returns Promise 包含登录结果
  */
 export function wxLogin(code: string) {
-    return http.post<IAuthLoginRes>('/couponCenter/APP/wx/login', { code })
+    return http.Post<IAuthLoginRes>('/couponCenter/APP/wx/login', { code }, {
+        meta: {
+            ignoreAuth: true,
+        },
+    })
 }

+ 0 - 0
src/api/types/coupon.ts


+ 1 - 0
src/api/types/login.ts

@@ -10,6 +10,7 @@ export interface ISingleTokenRes {
 export interface ISingleWXTokenRes {
     openId: string
     token: string
+    expiresIn: number // 有效期(秒)
 }
 
 // 双Token响应类型

+ 12 - 3
src/http/alova.ts

@@ -4,6 +4,7 @@ import AdapterUniapp from '@alova/adapter-uniapp'
 import { createAlova } from 'alova'
 import { createServerTokenAuthentication } from 'alova/client'
 import VueHook from 'alova/vue'
+import { useTokenStore } from '@/store/token'
 import { toLoginPage } from '@/utils/toLoginPage'
 import { ContentTypeEnum, ResultEnum, ShowMessage } from './tools/enum'
 
@@ -31,7 +32,8 @@ const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthenticati
             }
             catch (error) {
                 // 切换到登录页
-                toLoginPage({ mode: 'reLaunch' })
+                // toLoginPage({ mode: 'reLaunch' })
+                console.log('刷新token失败', error)
                 throw error
             }
         },
@@ -51,6 +53,9 @@ const alovaInstance = createAlova({
         // 设置默认 Content-Type
         method.config.headers = {
             ContentType: ContentTypeEnum.JSON,
+            // #ifndef MP-WEIXIN
+            responseType: 'json',
+            // #endif
             Accept: 'application/json, text/plain, */*',
             ...method.config.headers,
         }
@@ -60,10 +65,14 @@ const alovaInstance = createAlova({
         console.log('ignoreAuth===>', ignoreAuth)
         // 处理认证信息   自行处理认证问题
         if (ignoreAuth) {
-            const token = 'getToken()'
+            const tokenStore = useTokenStore()
+            const token = tokenStore.validToken
+            // const token = 'getToken()'
             if (!token) {
                 throw new Error('[请求错误]:未登录')
             }
+            method.config.headers['X-Access-Token'] = `${token}`
+            method.config.headers.AppType = '7'
             // method.config.headers.token = token;
         }
 
@@ -100,7 +109,7 @@ const alovaInstance = createAlova({
         }
 
         // 处理业务逻辑错误
-        const { code, message, data } = rawData as IResponse
+        const { code, message, result: data } = rawData as IResponse
         // 0和200当做成功都很普遍,这里直接兼容两者,见 ResultEnum
         if (code !== ResultEnum.Success0 && code !== ResultEnum.Success200) {
             if (config.meta?.toast !== false) {

+ 54 - 53
src/http/interceptor.ts

@@ -8,62 +8,63 @@ const baseUrl = getEnvBaseUrl()
 
 // 拦截器配置
 const httpInterceptor = {
-  // 拦截前触发
-  invoke(options: CustomRequestOptions) {
-    // 如果您使用了alova,则请把下面的代码放开注释
-    // alova 执行流程:alova beforeRequest --> 本拦截器 --> alova responded
-    // return options
+    // 拦截前触发
+    invoke(options: CustomRequestOptions) {
+        // 如果您使用了alova,则请把下面的代码放开注释
+        // alova 执行流程:alova beforeRequest --> 本拦截器 --> alova responded
+        return options
 
-    // 非 alova 请求,正常执行
-    // 接口请求支持通过 query 参数配置 queryString
-    if (options.query) {
-      const queryStr = stringifyQuery(options.query)
-      if (options.url.includes('?')) {
-        options.url += `&${queryStr}`
-      }
-      else {
-        options.url += `?${queryStr}`
-      }
-    }
-    // 非 http 开头需拼接地址
-    if (!options.url.startsWith('http')) {
-      // #ifdef H5
-      if (JSON.parse(import.meta.env.VITE_APP_PROXY_ENABLE)) {
-        // 自动拼接代理前缀
-        options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
-      }
-      else {
-        options.url = baseUrl + options.url
-      }
-      // #endif
-      // 非H5正常拼接
-      // #ifndef H5
-      options.url = baseUrl + options.url
-      // #endif
-      // TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
-    }
-    // 1. 请求超时
-    options.timeout = 60000 // 60s
-    // 2. (可选)添加小程序端请求头标识
-    options.header = {
-      ...options.header,
-    }
-    // 3. 添加 token 请求头标识
-    const tokenStore = useTokenStore()
-    const token = tokenStore.validToken
+        // 非 alova 请求,正常执行
+        // 接口请求支持通过 query 参数配置 queryString
+        if (options.query) {
+            const queryStr = stringifyQuery(options.query)
+            if (options.url.includes('?')) {
+                options.url += `&${queryStr}`
+            }
+            else {
+                options.url += `?${queryStr}`
+            }
+        }
+        // 非 http 开头需拼接地址
+        if (!options.url.startsWith('http')) {
+            // #ifdef H5
+            if (JSON.parse(import.meta.env.VITE_APP_PROXY_ENABLE)) {
+                // 自动拼接代理前缀
+                options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
+            }
+            else {
+                options.url = baseUrl + options.url
+            }
+            // #endif
+            // 非H5正常拼接
+            // #ifndef H5
+            options.url = baseUrl + options.url
+            // #endif
+            // TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
+        }
+        // 1. 请求超时
+        options.timeout = 60000 // 60s
+        // 2. (可选)添加小程序端请求头标识
+        options.header = {
+            ...options.header,
+        }
+        // 3. 添加 token 请求头标识
+        const tokenStore = useTokenStore()
+        const token = tokenStore.validToken
 
-    if (token) {
-      options.header.Authorization = `Bearer ${token}`
-    }
-    return options
-  },
+        if (token) {
+            options.header['X-Access-Token'] = `${token}`
+            options.header.AppType = '7'
+        }
+        return options
+    },
 }
 
 export const requestInterceptor = {
-  install() {
-    // 拦截 request 请求
-    uni.addInterceptor('request', httpInterceptor)
-    // 拦截 uploadFile 文件上传
-    uni.addInterceptor('uploadFile', httpInterceptor)
-  },
+    install() {
+        // 拦截 request 请求
+        uni.addInterceptor('request', httpInterceptor)
+        // 拦截 uploadFile 文件上传
+        uni.addInterceptor('uploadFile', httpInterceptor)
+    },
 }

+ 18 - 18
src/http/tools/queryString.ts

@@ -5,25 +5,25 @@
  * @returns 序列化后的查询字符串
  */
 export function stringifyQuery(obj: Record<string, any>): string {
-  if (!obj || typeof obj !== 'object' || Array.isArray(obj))
-    return ''
+    if (!obj || typeof obj !== 'object' || Array.isArray(obj))
+        return ''
 
-  return Object.entries(obj)
-    .filter(([_, value]) => value !== undefined && value !== null)
-    .map(([key, value]) => {
-      // 对键进行编码
-      const encodedKey = encodeURIComponent(key)
+    return Object.entries(obj)
+        .filter(([_, value]) => value !== undefined && value !== null)
+        .map(([key, value]) => {
+            // 对键进行编码
+            const encodedKey = encodeURIComponent(key)
 
-      // 处理数组类型
-      if (Array.isArray(value)) {
-        return value
-          .filter(item => item !== undefined && item !== null)
-          .map(item => `${encodedKey}=${encodeURIComponent(item)}`)
-          .join('&')
-      }
+            // 处理数组类型
+            if (Array.isArray(value)) {
+                return value
+                    .filter(item => item !== undefined && item !== null)
+                    .map(item => `${encodedKey}=${encodeURIComponent(item)}`)
+                    .join('&')
+            }
 
-      // 处理基本类型
-      return `${encodedKey}=${encodeURIComponent(value)}`
-    })
-    .join('&')
+            // 处理基本类型
+            return `${encodedKey}=${encodeURIComponent(value)}`
+        })
+        .join('&')
 }

+ 0 - 30
src/http/vue-query.ts

@@ -1,30 +0,0 @@
-import type { CustomRequestOptions } from '@/http/types'
-import { http } from './http'
-
-/*
- * openapi-ts-request 工具的 request 跨客户端适配方法
- */
-export default function request<T extends { data?: any }>(
-  url: string,
-  options: Omit<CustomRequestOptions, 'url'> & {
-    params?: Record<string, unknown>
-    headers?: Record<string, unknown>
-  },
-) {
-  const requestOptions = {
-    url,
-    ...options,
-  }
-
-  if (options.params) {
-    requestOptions.query = requestOptions.params
-    delete requestOptions.params
-  }
-
-  if (options.headers) {
-    requestOptions.header = options.headers
-    delete requestOptions.headers
-  }
-
-  return http<T['data']>(requestOptions)
-}

+ 109 - 14
src/pages-fg/login/login.vue

@@ -1,44 +1,139 @@
 <script lang="ts" setup>
 import { useTokenStore } from '@/store/token'
+import { isPageTabbar } from '@/tabbar/store'
+import { HOME_PAGE } from '@/utils/index'
 
+// 使用自定义导航栏样式
 definePage({
     style: {
-        navigationBarTitleText: '登录',
+        navigationBarTitleText: '',
+        navigationStyle: 'custom',
+        backgroundColor: '#f7f7f7',
     },
 })
 
 const tokenStore = useTokenStore()
+
 async function doLogin() {
     if (tokenStore.hasLogin) {
         uni.navigateBack()
         return
     }
+
     try {
-        // 调用登录接口
-        await tokenStore.login({
-            username: '菲鸽',
-            password: '123456',
+        uni.showLoading({
+            title: '登录中...',
         })
-        uni.navigateBack()
+        // 调用登录接口
+        await tokenStore.wxLogin()
+
+        // 登录成功后,检查是否有重定向参数
+        const pages = getCurrentPages()
+        const currentPage = pages[pages.length - 1]
+        const redirect = currentPage?.options?.redirect
+        uni.hideLoading()
+        if (redirect) {
+            const redirectUrl = decodeURIComponent(redirect)
+            if (isPageTabbar(redirectUrl)) {
+                uni.switchTab({ url: redirectUrl })
+            }
+            else {
+                uni.redirectTo({ url: redirectUrl })
+            }
+        }
+        else {
+            // 默认跳转到首页
+            if (isPageTabbar(HOME_PAGE)) {
+                uni.switchTab({ url: HOME_PAGE })
+            }
+            else {
+                uni.redirectTo({ url: HOME_PAGE })
+            }
+        }
     }
     catch (error) {
+        uni.hideLoading()
         console.log('登录失败', error)
+        uni.showToast({
+            title: '登录失败,请稍后重试',
+            icon: 'none',
+        })
     }
 }
 </script>
 
 <template>
-    <view class="login">
-        <!-- 本页面是非MP的登录页,主要用于 h5 和 APP -->
-        <view class="text-center">
-            登录页
+    <view class="login-container">
+        <!-- Logo区域 -->
+        <view class="logo-section">
+            <image class="logo" src="~@/static/images/index/logo.jpg" mode="aspectFit" />
+        </view>
+
+        <!-- 登录按钮区域 -->
+        <view class="login-btn-section">
+            <button class="login-btn" open-type="getUserInfo" @click="doLogin">
+                <text class="btn-text">微信快捷登录</text>
+            </button>
         </view>
-        <button class="mt-4 w-40 text-center" @click="doLogin">
-            点击模拟登录
-        </button>
     </view>
 </template>
 
 <style lang="scss" scoped>
-//
+.login-container {
+    display: flex;
+    flex-direction: column;
+    height: 100vh;
+    background-color: #f7f7f7;
+    padding: 0 30px;
+}
+
+.logo-section {
+    flex: 1;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    .logo {
+        width: 160px;
+        height: 160px;
+        border-radius: 20px;
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+    }
+}
+
+.login-btn-section {
+    margin-bottom: 390rpx;
+
+    .login-btn {
+        background-color: #07c160;
+        color: white;
+        font-size: 16px;
+        font-weight: 500;
+        width: 100%;
+        height: 48px;
+        line-height: 48px;
+        border-radius: 24px;
+        border: none;
+        box-shadow: 0 2px 8px rgba(7, 193, 96, 0.3);
+
+        &:active {
+            background-color: #06b356;
+        }
+    }
+}
+
+.agreement-section {
+    text-align: center;
+    padding-bottom: 30px;
+
+    .agreement-text {
+        font-size: 12px;
+        color: #999;
+
+        .link {
+            color: #07c160;
+            text-decoration: underline;
+        }
+    }
+}
 </style>

+ 13 - 5
src/pages/index/index.vue

@@ -2,10 +2,13 @@
 import couponBg from '@img/index/coupon-bg.png'
 import indexBg from '@img/index/index-bg.png'
 import { pathToBase64 } from 'image-tools'
-import { ref } from 'vue'
+import { storeToRefs } from 'pinia'
+import { ref, watch } from 'vue'
 import DiscountCoupon from '@/components/discountCoupon.vue'
 import SpendAndSaveCoupon from '@/components/spendAndSaveCoupon.vue'
+import { useCouponStore } from '@/store/coupon'
 import { useTokenStore } from '@/store/token'
+import { toLoginPage } from '@/utils/toLoginPage'
 
 defineOptions({
     name: 'Home',
@@ -21,11 +24,13 @@ definePage({
 })
 const couponBgBase64 = ref(couponBg)
 const indexBgBase64 = ref(indexBg)
+const isLogin = ref(false)
 
 const tokenStore = useTokenStore()
-const { hasLogin, wxLogin } = tokenStore
+const { hasLogin } = storeToRefs(tokenStore)
 
-console.log('hasLogin:', hasLogin)
+// 获取优惠券
+const couponStore = useCouponStore()
 
 // 将图片路径转换为base64格式
 async function convertImageToBase64() {
@@ -44,13 +49,16 @@ async function convertImageToBase64() {
 }
 
 onLoad(async () => {
-    console.log('测试 uni API 自动引入: onLoad')
     await convertImageToBase64()
+    // 获取优惠券
+    couponStore.getCouponListByType()
 })
 
 // #ifdef MP-WEIXIN
 async function login() {
-    await tokenStore.wxLogin()
+    const currentPage = getCurrentPages()[0]
+    const redirectUrl = `/${currentPage.route}`
+    toLoginPage({ queryString: `?redirect=${encodeURIComponent(redirectUrl)}` })
 }
 // #endif
 

+ 8 - 7
src/router/config.ts

@@ -1,11 +1,11 @@
 import { getAllPages } from '@/utils'
 
 export const LOGIN_STRATEGY_MAP = {
-  DEFAULT_NO_NEED_LOGIN: 0, // 黑名单策略,默认可以进入APP
-  DEFAULT_NEED_LOGIN: 1, // 白名单策略,默认不可以进入APP,需要强制登录
+    DEFAULT_NO_NEED_LOGIN: 0, // 黑名单策略,默认可以进入APP
+    DEFAULT_NEED_LOGIN: 1, // 白名单策略,默认不可以进入APP,需要强制登录
 }
 // TODO: 1/3 登录策略,默认使用`无需登录策略`,即默认不需要登录就可以访问
-export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.DEFAULT_NO_NEED_LOGIN
+export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
 export const isNeedLoginMode = LOGIN_STRATEGY === LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
 
 export const LOGIN_PAGE = '/pages-fg/login/login'
@@ -20,12 +20,13 @@ export const excludeLoginPathList = getAllPages('excludeLoginPath').map(page =>
 // 排除在外的列表,白名单策略指白名单列表,黑名单策略指黑名单列表
 // TODO: 2/3 在 definePage 配置 excludeLoginPath,或者在下面配置 EXCLUDE_LOGIN_PATH_LIST
 export const EXCLUDE_LOGIN_PATH_LIST = [
-  '/pages/xxx/index', // 示例值
-  '/pages-sub/xxx/index', // 示例值
-  ...excludeLoginPathList, // 都是以 / 开头的 path
+    LOGIN_PAGE,
+    '/pages/xxx/index', // 示例值
+    '/pages-sub/xxx/index', // 示例值
+    ...excludeLoginPathList, // 都是以 / 开头的 path
 ]
 
 // 在小程序里面是否使用H5的登录页,默认为 false
 // 如果为 true 则复用 h5 的登录逻辑
 // TODO: 3/3 确定自己的登录页是否需要在小程序里面使用
-export const LOGIN_PAGE_ENABLE_IN_MP = false
+export const LOGIN_PAGE_ENABLE_IN_MP = true

+ 123 - 123
src/router/interceptor.ts

@@ -13,158 +13,158 @@ export const FG_LOG_ENABLE = false
 
 // 系统内部页面白名单(不需要检查路由存在性)
 const SYSTEM_INTERNAL_PATHS = [
-  '/__uniappchooselocation', // 选择位置页面
-  '/__uniapproute', // 路由页面
-  '/__uniappfilepicker', // 文件选择器
-  '/__uniappimagepicker', // 图片选择器
+    '/__uniappchooselocation', // 选择位置页面
+    '/__uniapproute', // 路由页面
+    '/__uniappfilepicker', // 文件选择器
+    '/__uniappimagepicker', // 图片选择器
 ]
 
 export function judgeIsExcludePath(path: string) {
-  const isDev = import.meta.env.DEV
-  if (!isDev) {
-    return EXCLUDE_LOGIN_PATH_LIST.includes(path)
-  }
-  const allExcludeLoginPages = getAllPages('excludeLoginPath') // dev 环境下,需要每次都重新获取,否则新配置就不会生效
-  return EXCLUDE_LOGIN_PATH_LIST.includes(path) || (isDev && allExcludeLoginPages.some(page => page.path === path))
+    const isDev = import.meta.env.DEV
+    if (!isDev) {
+        return EXCLUDE_LOGIN_PATH_LIST.includes(path)
+    }
+    const allExcludeLoginPages = getAllPages('excludeLoginPath') // dev 环境下,需要每次都重新获取,否则新配置就不会生效
+    return EXCLUDE_LOGIN_PATH_LIST.includes(path) || (isDev && allExcludeLoginPages.some(page => page.path === path))
 }
 
 // 检查是否为系统内部页面
 export function isSystemInternalPath(path: string): boolean {
-  return SYSTEM_INTERNAL_PATHS.includes(path)
+    return SYSTEM_INTERNAL_PATHS.includes(path)
 }
 
 // 检查路由是否存在
 export function isRouteExists(path: string): boolean {
-  // 系统内部页面始终认为存在
-  if (isSystemInternalPath(path)) {
-    return true
-  }
+    // 系统内部页面始终认为存在
+    if (isSystemInternalPath(path)) {
+        return true
+    }
 
-  const allPages = getAllPages()
-  return allPages.some(page => page.path === path) || path === '/'
+    const allPages = getAllPages()
+    return allPages.some(page => page.path === path) || path === '/'
 }
 
 export const navigateToInterceptor = {
-  // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
-  // 增加对相对路径的处理,BY 网友 @ideal
-  invoke({ url, query }: { url: string, query?: Record<string, string> }) {
-    if (url === undefined) {
-      return
-    }
-    let { path, query: _query } = parseUrlToObj(url)
-
-    FG_LOG_ENABLE && console.log('\n\n路由拦截器:-------------------------------------')
-    FG_LOG_ENABLE && console.log('路由拦截器 1: url->', url, ', query ->', query)
-    const myQuery = { ..._query, ...query }
-    // /pages/route-interceptor/index?name=feige&age=30
-    FG_LOG_ENABLE && console.log('路由拦截器 2: path->', path, ', _query ->', _query)
-    FG_LOG_ENABLE && console.log('路由拦截器 3: myQuery ->', myQuery)
-
-    // 处理相对路径
-    if (!path.startsWith('/')) {
-      const currentPath = getLastPage()?.route || ''
-      const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
-      const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
-      path = `${baseDir}/${path}`
-    }
+    // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
+    // 增加对相对路径的处理,BY 网友 @ideal
+    invoke({ url, query }: { url: string, query?: Record<string, string> }) {
+        if (url === undefined) {
+            return
+        }
+        let { path, query: _query } = parseUrlToObj(url)
+
+        FG_LOG_ENABLE && console.log('\n\n路由拦截器:-------------------------------------')
+        FG_LOG_ENABLE && console.log('路由拦截器 1: url->', url, ', query ->', query)
+        const myQuery = { ..._query, ...query }
+        // /pages/route-interceptor/index?name=feige&age=30
+        FG_LOG_ENABLE && console.log('路由拦截器 2: path->', path, ', _query ->', _query)
+        FG_LOG_ENABLE && console.log('路由拦截器 3: myQuery ->', myQuery)
+
+        // 处理相对路径
+        if (!path.startsWith('/')) {
+            const currentPath = getLastPage()?.route || ''
+            const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
+            const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
+            path = `${baseDir}/${path}`
+        }
 
-    // 系统内部页面直接放行(不检查路由存在性,不进行登录拦截)
-    if (isSystemInternalPath(path)) {
-      FG_LOG_ENABLE && console.log('系统内部页面,直接放行:', path)
-      return true
-    }
+        // 系统内部页面直接放行(不检查路由存在性,不进行登录拦截)
+        if (isSystemInternalPath(path)) {
+            FG_LOG_ENABLE && console.log('系统内部页面,直接放行:', path)
+            return true
+        }
 
-    // 处理路由不存在的情况
-    if (!isRouteExists(path)) {
-      console.warn('路由不存在:', path)
-      uni.navigateTo({ url: NOT_FOUND_PAGE })
-      return false // 明确表示阻止原路由继续执行
-    }
+        // 处理路由不存在的情况
+        if (!isRouteExists(path)) {
+            console.warn('路由不存在:', path)
+            uni.navigateTo({ url: NOT_FOUND_PAGE })
+            return false // 明确表示阻止原路由继续执行
+        }
 
-    // 处理直接进入路由非首页时,tabbarIndex 不正确的问题
-    tabbarStore.setAutoCurIdx(path)
+        // 处理直接进入路由非首页时,tabbarIndex 不正确的问题
+        tabbarStore.setAutoCurIdx(path)
 
-    // 小程序里面使用平台自带的登录,则不走下面的逻辑
-    if (isMp && !LOGIN_PAGE_ENABLE_IN_MP) {
-      return true // 明确表示允许路由继续执行
-    }
+        // 小程序里面使用平台自带的登录,则不走下面的逻辑
+        if (isMp && !LOGIN_PAGE_ENABLE_IN_MP) {
+            return true // 明确表示允许路由继续执行
+        }
 
-    const tokenStore = useTokenStore()
-    FG_LOG_ENABLE && console.log('tokenStore.hasLogin:', tokenStore.hasLogin)
-
-    // 不管黑白名单,登录了就直接去吧(但是当前不能是登录页)
-    if (tokenStore.hasLogin) {
-      if (path !== LOGIN_PAGE) {
-        return true // 明确表示允许路由继续执行
-      }
-      else {
-        console.log('已经登录,但是还在登录页', myQuery.redirect)
-        const url = myQuery.redirect || HOME_PAGE
-        if (isPageTabbar(url)) {
-          uni.switchTab({ url })
+        const tokenStore = useTokenStore()
+        FG_LOG_ENABLE && console.log('tokenStore.hasLogin:', tokenStore.hasLogin)
+
+        // 不管黑白名单,登录了就直接去吧(但是当前不能是登录页)
+        if (tokenStore.hasLogin) {
+            if (path !== LOGIN_PAGE) {
+                return true // 明确表示允许路由继续执行
+            }
+            else {
+                console.log('已经登录,但是还在登录页', myQuery.redirect)
+                const url = myQuery.redirect || HOME_PAGE
+                if (isPageTabbar(url)) {
+                    uni.switchTab({ url })
+                }
+                else {
+                    uni.navigateTo({ url })
+                }
+                return false // 明确表示阻止原路由继续执行
+            }
         }
-        else {
-          uni.navigateTo({ url })
+        let fullPath = path
+
+        if (Object.keys(myQuery).length) {
+            fullPath += `?${Object.keys(myQuery).map(key => `${key}=${myQuery[key]}`).join('&')}`
         }
-        return false // 明确表示阻止原路由继续执行
-      }
-    }
-    let fullPath = path
+        const redirectUrl = `${LOGIN_PAGE}?redirect=${encodeURIComponent(fullPath)}`
+
+        // #region 1/2 默认需要登录的情况(白名单策略) ---------------------------
+        if (isNeedLoginMode) {
+            // 需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示白名单,可以直接通过
+            if (judgeIsExcludePath(path)) {
+                return true // 明确表示允许路由继续执行
+            }
+            // 否则需要重定向到登录页
+            else {
+                if (path === LOGIN_PAGE) {
+                    return true // 明确表示允许路由继续执行
+                }
+                FG_LOG_ENABLE && console.log('1 isNeedLogin(白名单策略) redirectUrl:', redirectUrl)
+                uni.navigateTo({ url: redirectUrl })
+                return false // 明确表示阻止原路由继续执行
+            }
+        }
+        // #endregion 1/2 默认需要登录的情况(白名单策略) ---------------------------
 
-    if (Object.keys(myQuery).length) {
-      fullPath += `?${Object.keys(myQuery).map(key => `${key}=${myQuery[key]}`).join('&')}`
-    }
-    const redirectUrl = `${LOGIN_PAGE}?redirect=${encodeURIComponent(fullPath)}`
-
-    // #region 1/2 默认需要登录的情况(白名单策略) ---------------------------
-    if (isNeedLoginMode) {
-      // 需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示白名单,可以直接通过
-      if (judgeIsExcludePath(path)) {
-        return true // 明确表示允许路由继续执行
-      }
-      // 否则需要重定向到登录页
-      else {
-        if (path === LOGIN_PAGE) {
-          return true // 明确表示允许路由继续执行
+        // #region 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
+        else {
+            // 不需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示黑名单,需要重定向到登录页
+            if (judgeIsExcludePath(path)) {
+                FG_LOG_ENABLE && console.log('2 isNeedLogin(黑名单策略) redirectUrl:', redirectUrl)
+                uni.navigateTo({ url: redirectUrl })
+                return false // 修改为false,阻止原路由继续执行
+            }
+            return true // 明确表示允许路由继续执行
         }
-        FG_LOG_ENABLE && console.log('1 isNeedLogin(白名单策略) redirectUrl:', redirectUrl)
-        uni.navigateTo({ url: redirectUrl })
-        return false // 明确表示阻止原路由继续执行
-      }
-    }
-    // #endregion 1/2 默认需要登录的情况(白名单策略) ---------------------------
-
-    // #region 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
-    else {
-      // 不需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示黑名单,需要重定向到登录页
-      if (judgeIsExcludePath(path)) {
-        FG_LOG_ENABLE && console.log('2 isNeedLogin(黑名单策略) redirectUrl:', redirectUrl)
-        uni.navigateTo({ url: redirectUrl })
-        return false // 修改为false,阻止原路由继续执行
-      }
-      return true // 明确表示允许路由继续执行
-    }
-    // #endregion 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
-  },
+        // #endregion 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
+    },
 }
 
 // 针对 chooseLocation 的特殊处理
 export const chooseLocationInterceptor = {
-  invoke(options: any) {
-    // 直接放行 chooseLocation 调用
-    FG_LOG_ENABLE && console.log('chooseLocation 调用,直接放行:', options)
-    return true
-  },
+    invoke(options: any) {
+        // 直接放行 chooseLocation 调用
+        FG_LOG_ENABLE && console.log('chooseLocation 调用,直接放行:', options)
+        return true
+    },
 }
 
 export const routeInterceptor = {
-  install() {
-    uni.addInterceptor('navigateTo', navigateToInterceptor)
-    uni.addInterceptor('reLaunch', navigateToInterceptor)
-    uni.addInterceptor('redirectTo', navigateToInterceptor)
-    uni.addInterceptor('switchTab', navigateToInterceptor)
-
-    // 添加 chooseLocation 的拦截器,确保直接放行
-    uni.addInterceptor('chooseLocation', chooseLocationInterceptor)
-  },
+    install() {
+        uni.addInterceptor('navigateTo', navigateToInterceptor)
+        uni.addInterceptor('reLaunch', navigateToInterceptor)
+        uni.addInterceptor('redirectTo', navigateToInterceptor)
+        uni.addInterceptor('switchTab', navigateToInterceptor)
+
+        // 添加 chooseLocation 的拦截器,确保直接放行
+        uni.addInterceptor('chooseLocation', chooseLocationInterceptor)
+    },
 }

+ 0 - 0
src/service/coupon.ts


二进制
src/static/images/index/logo.jpg


+ 15 - 0
src/store/coupon.ts

@@ -0,0 +1,15 @@
+import { defineStore } from 'pinia'
+import { getCouponList } from '@/api/coupon'
+
+export const useCouponStore = defineStore('coupon', {
+    state: () => ({
+        discountVoucherList: [], // 满减券
+        couponList: [], // 折扣券
+    }),
+    actions: {
+        async getCouponListByType() {
+            const res = await getCouponList()
+            console.log(res)
+        }
+    }
+})

+ 36 - 36
src/store/token.ts

@@ -55,8 +55,8 @@ export const useTokenStore = defineStore(
         }
 
         /**
-                                                     * 判断token是否过期
-                                                     */
+                                                                     * 判断token是否过期
+                                                                     */
         const isTokenExpired = computed(() => {
             if (!tokenInfo.value) {
                 return true
@@ -71,8 +71,8 @@ export const useTokenStore = defineStore(
         })
 
         /**
-                                                     * 判断refreshToken是否过期
-                                                     */
+                                                                     * 判断refreshToken是否过期
+                                                                     */
         const isRefreshTokenExpired = computed(() => {
             if (!isDoubleTokenMode)
                 return true
@@ -86,9 +86,9 @@ export const useTokenStore = defineStore(
         })
 
         /**
-                                                     * 登录成功后处理逻辑
-                                                     * @param tokenInfo 登录返回的token信息
-                                                     */
+                                                                     * 登录成功后处理逻辑
+                                                                     * @param tokenInfo 登录返回的token信息
+                                                                     */
         async function _postLogin(tokenInfo: IAuthLoginRes) {
             setTokenInfo(tokenInfo)
             // const userStore = useUserStore()
@@ -96,12 +96,12 @@ export const useTokenStore = defineStore(
         }
 
         /**
-                                                     * 用户登录
-                                                     * 有的时候后端会用一个接口返回token和用户信息,有的时候会分开2个接口,一个获取token,一个获取用户信息
-                                                     * (各有利弊,看业务场景和系统复杂度),这里使用2个接口返回的来模拟
-                                                     * @param loginForm 登录参数
-                                                     * @returns 登录结果
-                                                     */
+                                                                     * 用户登录
+                                                                     * 有的时候后端会用一个接口返回token和用户信息,有的时候会分开2个接口,一个获取token,一个获取用户信息
+                                                                     * (各有利弊,看业务场景和系统复杂度),这里使用2个接口返回的来模拟
+                                                                     * @param loginForm 登录参数
+                                                                     * @returns 登录结果
+                                                                     */
         const login = async (loginForm: ILoginForm) => {
             try {
                 const res = await _login(loginForm)
@@ -124,11 +124,11 @@ export const useTokenStore = defineStore(
         }
 
         /**
-                                                     * 微信登录
-                                                     * 有的时候后端会用一个接口返回token和用户信息,有的时候会分开2个接口,一个获取token,一个获取用户信息
-                                                     * (各有利弊,看业务场景和系统复杂度),这里使用2个接口返回的来模拟
-                                                     * @returns 登录结果
-                                                     */
+                                                                     * 微信登录
+                                                                     * 有的时候后端会用一个接口返回token和用户信息,有的时候会分开2个接口,一个获取token,一个获取用户信息
+                                                                     * (各有利弊,看业务场景和系统复杂度),这里使用2个接口返回的来模拟
+                                                                     * @returns 登录结果
+                                                                     */
         const wxLogin = async () => {
             try {
                 // 获取用户信息
@@ -157,8 +157,8 @@ export const useTokenStore = defineStore(
         }
 
         /**
-                                                     * 退出登录 并 删除用户信息
-                                                     */
+                                                                     * 退出登录 并 删除用户信息
+                                                                     */
         const logout = async () => {
             try {
                 // TODO 实现自己的退出登录逻辑
@@ -181,9 +181,9 @@ export const useTokenStore = defineStore(
         }
 
         /**
-                                                     * 刷新token
-                                                     * @returns 刷新结果
-                                                     */
+                                                                     * 刷新token
+                                                                     * @returns 刷新结果
+                                                                     */
         const refreshToken = async () => {
             if (!isDoubleTokenMode) {
                 console.error('单token模式不支持刷新token')
@@ -209,16 +209,16 @@ export const useTokenStore = defineStore(
         }
 
         /**
-                                                     * 获取有效的token
-                                                     * 注意:在computed中不直接调用异步函数,只做状态判断
-                                                     * 实际的刷新操作应由调用方处理
-                                                     */
+                                                                     * 获取有效的token
+                                                                     * 注意:在computed中不直接调用异步函数,只做状态判断
+                                                                     * 实际的刷新操作应由调用方处理
+                                                                     */
         const getValidToken = computed(() => {
             // token已过期,返回空
             if (isTokenExpired.value) {
                 return ''
             }
-
+            console.log('getValidToken', tokenInfo.value)
             if (!isDoubleTokenMode) {
                 return isSingleTokenRes(tokenInfo.value) ? tokenInfo.value.token : ''
             }
@@ -228,8 +228,8 @@ export const useTokenStore = defineStore(
         })
 
         /**
-                                                     * 检查是否有登录信息(不考虑token是否过期)
-                                                     */
+                                                                     * 检查是否有登录信息(不考虑token是否过期)
+                                                                     */
         const hasLoginInfo = computed(() => {
             if (!tokenInfo.value) {
                 return false
@@ -243,17 +243,17 @@ export const useTokenStore = defineStore(
         })
 
         /**
-                                                     * 检查是否已登录且token有效
-                                                     */
+                     * 检查是否已登录且token有效
+                     */
         const hasValidLogin = computed(() => {
-            console.log('hasValidLogin', hasLoginInfo.value, !isTokenExpired.value)
+            console.log('hasValidLogin', hasLoginInfo.value && !isTokenExpired.value, hasLoginInfo.value, !isTokenExpired.value)
             return hasLoginInfo.value && !isTokenExpired.value
         })
 
         /**
-                                                     * 尝试获取有效的token,如果过期且可刷新,则刷新token
-                                                     * @returns 有效的token或空字符串
-                                                     */
+                 * 尝试获取有效的token,如果过期且可刷新,则刷新token
+                 * @returns 有效的token或空字符串
+                 */
         const tryGetValidToken = async (): Promise<string> => {
             if (!getValidToken.value && isDoubleTokenMode && !isRefreshTokenExpired.value) {
                 try {

+ 26 - 26
src/utils/toLoginPage.ts

@@ -2,20 +2,20 @@ import { getLastPage } from '@/utils'
 import { debounce } from '@/utils/debounce'
 
 interface ToLoginPageOptions {
-  /**
-   * 跳转模式, uni.navigateTo | uni.reLaunch
-   * @default 'navigateTo'
-   */
-  mode?: 'navigateTo' | 'reLaunch'
-  /**
-   * 查询参数
-   * @example '?redirect=/pages/home/index'
-   */
-  queryString?: string
+    /**
+     * 跳转模式, uni.navigateTo | uni.reLaunch
+     * @default 'navigateTo'
+     */
+    mode?: 'navigateTo' | 'reLaunch'
+    /**
+     * 查询参数
+     * @example '?redirect=/pages/home/index'
+     */
+    queryString?: string
 }
 
 // TODO: 自己增加登录页
-const LOGIN_PAGE = '/pages/login/index'
+const LOGIN_PAGE = '/pages-fg/login/login'
 
 /**
  * 跳转到登录页, 带防抖处理
@@ -23,22 +23,22 @@ const LOGIN_PAGE = '/pages/login/index'
  * 如果要立即跳转,不做延时,可以使用 `toLoginPage.flush()` 方法
  */
 export const toLoginPage = debounce((options: ToLoginPageOptions = {}) => {
-  const { mode = 'navigateTo', queryString = '' } = options
+    const { mode = 'navigateTo', queryString = '' } = options
 
-  const url = `${LOGIN_PAGE}${queryString}`
+    const url = `${LOGIN_PAGE}${queryString}`
 
-  // 获取当前页面路径
-  const currentPage = getLastPage()
-  const currentPath = `/${currentPage.route}`
-  // 如果已经在登录页,则不跳转
-  if (currentPath === LOGIN_PAGE) {
-    return
-  }
+    // 获取当前页面路径
+    const currentPage = getLastPage()
+    const currentPath = `/${currentPage.route}`
+    // 如果已经在登录页,则不跳转
+    if (currentPath === LOGIN_PAGE) {
+        return
+    }
 
-  if (mode === 'navigateTo') {
-    uni.navigateTo({ url })
-  }
-  else {
-    uni.reLaunch({ url })
-  }
+    if (mode === 'navigateTo') {
+        uni.navigateTo({ url })
+    }
+    else {
+        uni.reLaunch({ url })
+    }
 }, 500)