Просмотр исходного кода

feat(api): 添加搜索词条接口定义和类型

- 添加 xsbSearchTerm 接口类型定义
- 添加 SearchTerm API 接口配置
- 添加对应的全局类型声明

fix(router): 修复未授权跳转逻辑

- 修改路由跳转方式从 replaceAll 为 replace
- 优化重定向路径存储逻辑
- 修复登录过期跳转问题

feat(index): 优化首页导航功能

- 为导航图标添加"敬请期待"遮罩层
- 添加点击未实现功能的提示
- 调整导航网格布局间距

feat(search): 实现搜索功能增强

- 添加热门搜索词显示
- 支持页面传参搜索
- 集成搜索词条API数据

feat(login): 优化登录流程

- 修改token格式为Bearer认证
- 添加返回首页功能
- 优化登录成功后跳转逻辑

refactor(store): 调整用户状态管理

- 添加用户信息状态存储
- 优化token存储格式
- 移除不必要的tabbar显示状态

refactor(config): 更新开发环境配置

- 调整开发环境服务器地址
- 移除过时的tabbar配置
zhangtao 5 дней назад
Родитель
Сommit
e45d062af1

+ 41 - 0
src/api/api.type.d.ts

@@ -225,5 +225,46 @@ namespace Api {
     status?: number
     [property: string]: any
   }
+  interface xsbSearchTerm {
+    /**
+     * 创建时间
+     */
+    createTime?: string
+    /**
+     * 有效时间
+     */
+    effectiveTime?: string
+    /**
+     * 截至时间
+     */
+    endTime?: string
+    id?: number
+    /**
+     * 跳转参数
+     */
+    jumpUrl?: string
+    /**
+     * 落地页名称
+     */
+    reachName?: string
+    /**
+     * 搜索名称
+     */
+    searchName: string
+    /**
+     * 状态 1正常 0关闭
+     */
+    status?: number
+    /**
+     * 类型 1-关键词 2-热门搜索词 3-推荐搜索词
+     */
+    type?: number
+    /**
+     * 创建时间
+     * 修改时间
+     */
+    updateTime?: string
+    [property: string]: any
+  }
 
 }

+ 1 - 0
src/api/apiDefinitions.ts

@@ -33,4 +33,5 @@ export default {
   'xsb.findUserPoints':['GET', '/smqjh-system/app-api/v1/points/findUserPoints'],
   'xsb.getSearchProductList':['POST', '/smqjh-pms/app-api/v1/spu/getSearchProductList'],
   'xsb.appAdvertInfo':['GET', '/smqjh-system/app-api/v1/appAdvertInfo'],
+  'xsb.SearchTerm':['GET', '/smqjh-system/app-api/v1/SearchTerm/find'],
 };

+ 7 - 3
src/api/core/handlers.ts

@@ -31,14 +31,15 @@ export async function handleAlovaResponse(
   const globalToast = useGlobalToast()
   // Extract status code and data from UniApp response
   const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult
-
   // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
   if ((statusCode === 401 || statusCode === 403)) {
+    const { redirectName } = storeToRefs(useUserStore())
     // 如果是未授权错误,清除用户信息并跳转到登录页
     globalToast.error({ msg: '登录已过期,请重新登录!', duration: 2000 })
+    redirectName.value = getCurrentPath()
     const timer = setTimeout(() => {
       clearTimeout(timer)
-      router.replaceAll({ name: 'smqjh-login' })
+      router.replace({ name: 'smqjh-login' })
     }, 2000)
 
     throw new ApiError('登录已过期,请重新登录!', statusCode, data)
@@ -71,11 +72,14 @@ export function handleAlovaError(error: any, method: Method) {
 
   // 处理401/403错误(如果不是在handleAlovaResponse中处理的)
   if (error instanceof ApiError && (error.code === 401 || error.code === 403)) {
+    // 如果是未授权错误,清除用户信息并跳转到登录页
+    const { redirectName } = storeToRefs(useUserStore())
     // 如果是未授权错误,清除用户信息并跳转到登录页
     globalToast.error({ msg: '登录已过期,请重新登录!', duration: 2000 })
+    redirectName.value = getCurrentPath()
     const timer = setTimeout(() => {
       clearTimeout(timer)
-      router.replaceAll({ name: 'smqjh-login' })
+      router.replace({ name: 'smqjh-login' })
     }, 2000)
     throw new ApiError('登录已过期,请重新登录!', error.code, error.data)
   }

+ 9 - 0
src/api/globals.d.ts

@@ -202,6 +202,15 @@ declare global {
       >(
         config: Config
       ): Alova2Method<Api.xsbAdvertInfo[], 'xsb.appAdvertInfo', Config>;
+      SearchTerm<
+        Config extends Alova2MethodConfig<Api.xsbSearchTerm[]> & {
+          data:{
+            type:number
+          }
+        }
+      >(
+        config: Config
+      ): Alova2Method<Api.xsbSearchTerm[], 'xsb.SearchTerm', Config>;
     }
 
   }

+ 1 - 0
src/auto-imports.d.ts

@@ -183,6 +183,7 @@ declare global {
   const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
   const useCloned: typeof import('@vueuse/core')['useCloned']
   const useColorMode: typeof import('@vueuse/core')['useColorMode']
+  const useConcurrentRequests: typeof import('./composables/useConcurrentRequests')['useConcurrentRequests']
   const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
   const useCounter: typeof import('@vueuse/core')['useCounter']
   const useCssModule: typeof import('vue')['useCssModule']

+ 3 - 13
src/config/index.ts

@@ -11,9 +11,10 @@ const mapEnvVersion = {
   /**
    * 开发版
    */
-  // develop: 'http://192.168.1.166:8080', // 张
-  develop: 'http://192.168.1.101:8080',
+  develop: 'http://192.168.1.166:8080', // 张
+  // develop: 'http://192.168.1.101:8080',
   // develop: 'http://192.168.0.157:8080',
+  // develop: 'http://192.168.1.242:8080',
   /**
    * 体验版
    */
@@ -51,14 +52,3 @@ export const BASE_URL
  */
 
 export const StaticUrl = mapEnvStaticVersion[uni.getAccountInfoSync().miniProgram.envVersion]
-
-/**
- * 是否存在这些页面,存在则显示tabbar
- * 防止tabbar闪烁
- */
-export const xsbTabbarName = [
-  'xsb-home',
-  'xsb-classfiy',
-  'xsb-my',
-  'xsb-cart',
-]

+ 22 - 9
src/pages/index/index.vue

@@ -13,19 +13,20 @@ definePage({
     backgroundColorTop: '#9ED605',
   },
 })
+const { show } = useGlobalToast()
 
 const addressStore = useAddressStore()
 const { ScrollDown, statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
 const { name } = storeToRefs(addressStore)
 const navList = ref([
   { icon: `${VITE_OSS_BASE_URL}2025/11/4dabcf9b8d794d3c99aa6b49be34f205.png`, title: '星闪豹', name: 'xsb-homeTabbar' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/40cb38e287234a83885d68f30c9c39bc.png`, title: '充电' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/9981d979739b4ae6b4eec941b7d2c9b0.png`, title: '电影演出' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/f2b15ec1048e4b5689fe1ba26f6058e1.png`, title: '视频权益' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/0e971577095c406a88c5ea10af419246.png`, title: '大牌点餐' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/95e2ea622dbd498a8a36ab74f00209f3.png`, title: '加油' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/f5178ca02e3e4ebb9072d9e129bb3fd4.png`, title: '酒店民宿' },
-  { icon: `${VITE_OSS_BASE_URL}2025/11/d1f4d36d6fcc442a841f4f4f4927df19.png`, title: '代驾' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/40cb38e287234a83885d68f30c9c39bc.png`, title: '充电', name: '' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/9981d979739b4ae6b4eec941b7d2c9b0.png`, title: '电影演出', name: '' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/f2b15ec1048e4b5689fe1ba26f6058e1.png`, title: '视频权益', name: '' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/0e971577095c406a88c5ea10af419246.png`, title: '大牌点餐', name: '' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/95e2ea622dbd498a8a36ab74f00209f3.png`, title: '加油', name: '' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/f5178ca02e3e4ebb9072d9e129bb3fd4.png`, title: '酒店民宿', name: '' },
+  { icon: `${VITE_OSS_BASE_URL}2025/11/d1f4d36d6fcc442a841f4f4f4927df19.png`, title: '代驾', name: '' },
 ])
 onMounted(() => {
   addressStore.getLocation()
@@ -40,6 +41,10 @@ onPageScroll((e) => {
   }
 })
 function handleClick(name: string) {
+  if (!name) {
+    show({ msg: '敬请期待' })
+    return
+  }
   router.push({ name })
 }
 </script>
@@ -79,12 +84,17 @@ function handleClick(name: string) {
       </view>
     </view>
     <view class="px24rpx -mt260rpx">
-      <view class="grid grid-cols-4 mt24rpx rounded-16rpx bg-white py24rpx">
+      <view class="grid grid-cols-4 mt24rpx gap12rpx rounded-16rpx bg-white py24rpx">
         <view
           v-for="item in navList" :key="item.icon" class="flex flex-col items-center justify-center"
           @click="handleClick(String(item.name))"
         >
-          <image :src="item.icon" class="h120rpx w120rpx" />
+          <view class="relative h120rpx w120rpx">
+            <image :src="item.icon" class="h120rpx w120rpx" />
+            <view v-if="item.title != '星闪豹'" class="linebg absolute left-0 top-0 h-full w-full flex items-center justify-center rounded-32rpx text-24rpx text-white font-semibold">
+              敬请期待
+            </view>
+          </view>
           <view class="text-24rpx">
             {{ item.title }}
           </view>
@@ -368,4 +378,7 @@ function handleClick(name: string) {
   background: url('https://zswl-shop.oss-cn-chengdu.aliyuncs.com/2025/11/771b8a09633448d8b62c0004a8928054.png') no-repeat;
   background-size: contain;
 }
+.linebg{
+  background: rgba(0,0,0,.3);
+}
 </style>

+ 9 - 4
src/pages/login/index.vue

@@ -23,15 +23,20 @@ async function handleGetPhone(e: UniHelper.ButtonOnGetphonenumberDetail) {
     success: async (res) => {
       uni.hideLoading()
       await send(res.code, e.code)
-      token.value = data.value.access_token
+      token.value = `Bearer ${data.value.access_token}`
       useGlobalToast().show({ msg: '登录成功' })
       setTimeout(() => {
-        router.replace({ name: redirectName.value })
+        router.replace({ path: redirectName.value })
         useUserStore().getUserInfo()
       }, 2000)
     },
   })
 }
+function goBack() {
+  uni.switchTab({
+    url: '/pages/index/index',
+  })
+}
 </script>
 
 <template>
@@ -41,8 +46,8 @@ async function handleGetPhone(e: UniHelper.ButtonOnGetphonenumberDetail) {
         手机号快捷登录
       </wd-button>
       <view class="mt20rpx">
-        <wd-button block size="large" type="info">
-          手机号快捷登录
+        <wd-button block size="large" type="info" @click="goBack">
+          暂不登录
         </wd-button>
       </view>
     </view>

+ 0 - 1
src/pages/my/index.vue

@@ -11,7 +11,6 @@ definePage({
     navigationStyle: 'custom',
   },
 })
-
 const tabList = ref([
   { title: '待支付', icon: `${StaticUrl}/1.png`, name: 'smqjh-order' },
   { title: '待收货', icon: `${StaticUrl}/2.png`, name: 'smqjh-order' },

+ 2 - 1
src/router/index.ts

@@ -34,7 +34,7 @@ router.beforeEach((to, from, next) => {
   const { token, redirectName } = storeToRefs(useUserStore())
   if (!whitePathName().includes(to.name) && !token.value) {
     const { confirm: showConfirm } = useGlobalMessage()
-    redirectName.value = String(to.name)
+
     return new Promise<void>((resolve, reject) => {
       showConfirm({
         title: '警告',
@@ -42,6 +42,7 @@ router.beforeEach((to, from, next) => {
         confirmButtonText: '登录',
         cancelButtonText: '取消',
         success() {
+          redirectName.value = String(to.path)
           // console.log('✅ 用户确认访问,允许导航')
           router.push({ name: 'smqjh-login' })
           // resolve()

+ 0 - 5
src/store/sys.ts

@@ -10,17 +10,12 @@ interface SysState {
    * 胶囊按钮高度
    */
   MenuButtonHeight: number
-  /**
-   * 是否显示tabbar
-   */
-  showXsbTabbar: boolean
 }
 export const useSysStore = defineStore('system', {
   state: (): SysState => ({
     ScrollDown: false,
     statusBarHeight: 0,
     MenuButtonHeight: 0,
-    showXsbTabbar: false,
   }),
   actions: {
     getSystemData() {

+ 1 - 1
src/store/themeStore.ts

@@ -1,5 +1,5 @@
-import type { SystemThemeState, ThemeMode } from '@/composables/types/theme'
 import { defineStore } from 'pinia'
+import type { SystemThemeState, ThemeMode } from '@/composables/types/theme'
 import { themeColorOptions } from '@/composables/types/theme'
 
 /**

+ 9 - 2
src/store/user.ts

@@ -6,17 +6,24 @@ interface userStroe {
    * 重定向路由名称
    */
   redirectName: string
+  /**
+   * 用户登录信息
+   */
+  userInfo: Api.userInfo
 
 }
 export const useUserStore = defineStore('user', {
   state: (): userStroe => ({
     token: '',
     redirectName: '',
+    userInfo: {},
   }),
   actions: {
     async getUserInfo() {
-      const res = await api.sys.userInfo({})
-      console.log(res, '用户信息')
+      if (this.token) {
+        const res = await api.sys.userInfo({})
+        this.userInfo = res
+      }
     },
   },
 })

+ 2 - 2
src/subPack-xsb/commonTab/components/classfiy.vue

@@ -3,7 +3,7 @@ import type { LoadMoreState } from 'wot-design-uni/components/wd-loadmore/types'
 import { StaticUrl } from '@/config'
 import router from '@/router'
 
-const props = defineProps<{ categoryList: Api.xsbCategories[] }>()
+const props = defineProps<{ categoryList: Api.xsbCategories[], hotText: Api.xsbSearchTerm[] }>()
 const { statusBarHeight, MenuButtonHeight } = storeToRefs(useSysStore())
 const { topNavActive, leftActive } = storeToRefs(useSysXsbStore())
 const classfiylist = computed(() => props.categoryList)
@@ -141,7 +141,7 @@ onMounted(() => {
               <wd-icon name="search" size="14" color="#ccc" />
               <view class="search ml12rpx h-full w-full overflow-hidden">
                 <wd-notice-bar
-                  :text="['霸王茶姬', '牛奶', '洗衣机']" custom-class="notice-bar" color="#ccc"
+                  :text="hotText.map((it) => it.searchName)" custom-class="notice-bar" color="#ccc"
                   background-color="#fff" direction="vertical"
                 />
               </view>

+ 6 - 5
src/subPack-xsb/commonTab/components/index.vue

@@ -2,7 +2,7 @@
 import { StaticUrl, VITE_OSS_BASE_URL } from '@/config'
 import router from '@/router'
 
-const props = defineProps<{ categoryList: Api.xsbCategories[], swiper: Api.xsbAdvertInfo[] }>()
+const props = defineProps<{ categoryList: Api.xsbCategories[], swiper: Api.xsbAdvertInfo[], hotText: Api.xsbSearchTerm[], recommendText: Api.xsbSearchTerm[] }>()
 
 const emit = defineEmits(['changeNav'])
 
@@ -88,7 +88,7 @@ function handleGo(e: { index: number, item: Api.xsbAdvertInfo }) {
             <wd-icon name="search" size="14" color="#ccc" />
             <view class="search ml12rpx h-full w-full overflow-hidden">
               <wd-notice-bar
-                :text="['霸王茶姬', '牛奶', '洗衣机']" custom-class="notice-bar" color="#ccc"
+                :text="hotText.map((it) => it.searchName)" custom-class="notice-bar" color="#ccc"
                 background-color="#fff" direction="vertical"
               />
             </view>
@@ -102,10 +102,11 @@ function handleGo(e: { index: number, item: Api.xsbAdvertInfo }) {
         <scroll-view scroll-x class="mb20rpx mt20rpx h44rpx whitespace-nowrap">
           <view class="h-full flex items-center">
             <view
-              v-for="item in 10" :key="item"
-              class="mr32rpx h-full flex items-center justify-center rounded-22rpx bg-[rgba(255,255,255,.5)] px16rpx text-24rpx"
+              v-for="item in recommendText"
+              :key="item.id" class="mr32rpx h-full flex items-center justify-center rounded-22rpx bg-[rgba(255,255,255,.5)] px16rpx text-24rpx"
+              @click="router.push({ name: 'xsb-search', params: { text: item.searchName } })"
             >
-              {{ item }}苹果
+              {{ item.searchName }}
             </view>
           </view>
         </scroll-view>

+ 12 - 3
src/subPack-xsb/commonTab/index.vue

@@ -32,6 +32,8 @@ const tabbarItems = ref([
 ])
 const tabbarName = ref('xsb-home')
 const swiperList = ref<Api.xsbAdvertInfo[]>([])
+const hotText = ref<Api.xsbSearchTerm[]>([])
+const recommendText = ref<Api.xsbSearchTerm[]>([])
 function handleTabbarChange({ value }: { value: string }) {
   setTabbarItemActive(value)
   tabbarName.value = value
@@ -66,10 +68,15 @@ async function getCurrentImg() {
   console.log(res, '但是')
   swiperList.value = res
 }
+async function getSearchData(type: number) {
+  return await Apis.xsb.SearchTerm({ data: { type } })
+}
 
-onMounted(() => {
+onMounted(async () => {
   getCurrentImg()
   getCategories()
+  hotText.value = await getSearchData(2)
+  recommendText.value = await getSearchData(3)
 })
 </script>
 
@@ -78,13 +85,15 @@ onMounted(() => {
     <indexHome
       v-if="tabbarName == 'xsb-home'" :category-list="commonCategoryData"
       :swiper="swiperList"
+      :hot-text="hotText"
+      :recommend-text="recommendText"
       @change-nav="tabbarName = 'xsb-classfiy'"
     />
     <cart v-if="tabbarName == 'xsb-cart'" />
-    <classfiy v-if="tabbarName == 'xsb-classfiy'" :category-list="commonCategoryData" />
+    <classfiy v-if="tabbarName == 'xsb-classfiy'" :category-list="commonCategoryData" :hot-text="hotText" />
     <my v-if="tabbarName == 'xsb-my'" />
     <wd-tabbar
-      :model-value="tabbarName" placeholder safe-area-inset-bottom fixed :bordered="false"
+      :model-value="tabbarName" safe-area-inset-bottom placeholder fixed :bordered="false"
       custom-class="custom-tab"
       :custom-style="`box-shadow:${tabbarName == 'xsb-cart' || tabbarName == 'xsb-classfiy' ? '' : ' 0rpx -6rpx 12rpx 2rpx rgba(0, 0, 0, 0.09)'}`"
       @change="handleTabbarChange"

+ 20 - 1
src/subPack-xsb/search/index.vue

@@ -32,6 +32,16 @@ const { data, send, isLastPage, page } = usePagination((pageNum, pageSize) => Ap
   append: true,
 })
 const isSearch = ref(false)
+const hotText = ref<Api.xsbSearchTerm[]>([])
+
+onLoad((query: any) => {
+  if (query.text) {
+    searchText.value = query.text
+    send()
+    isSearch.value = true
+  }
+})
+
 function handleSearch() {
   data.value = []
   isSearch.value = true
@@ -67,6 +77,10 @@ function handleClearnSeachLocaData() {
     },
   })
 }
+async function getSearchData() {
+  hotText.value = await Apis.xsb.SearchTerm({ data: { type: 2 } })
+}
+getSearchData()
 function handleSearchText(text: string) {
   searchText.value = text
   handleSearch()
@@ -104,10 +118,15 @@ function handleSearchText(text: string) {
             </view>
           </view>
         </view>
-        <view class="mt24rpx">
+        <view v-if="hotText.length" class="mt24rpx">
           <view class="text-28rpx font-semibold">
             热门搜索
           </view>
+          <view class="mt20rpx flex flex-wrap items-center">
+            <view v-for="item in hotText" :key="item.id" class="mr16rpx box-border flex items-center justify-center rounded-30rpx bg-#F6F6F6 px24rpx py10rpx text-28rpx" @click="handleSearchText(item.searchName)">
+              {{ item.searchName }}
+            </view>
+          </view>
         </view>
       </view>
       <view v-if="isSearch">