Ver código fonte

📦 按要求优化首页效果

快乐的梦鱼 3 semanas atrás
pai
commit
485b9fa2a9

BIN
server/images/home/BannerHomeNewSmall1.png


+ 1 - 1
src/common/components/parts/HomeTitle.vue

@@ -3,7 +3,7 @@
     :title="title" 
     :moreText="moreText"
     :showMore="showMore" 
-    :padding="[25, 0, 20, 0]"
+    :padding="[25, 0, 5, 0]"
     @moreClicked="emit('moreClicked')"
   >
     <template #icon>

+ 1 - 0
src/common/config/Theme.ts

@@ -1,4 +1,5 @@
 import { configTheme } from "@/components/theme/ThemeDefine";
+import { DefaultTheme } from "@/components/theme/Theme";
 
 export function configAppTheme() {
   //修改默认主题颜色

+ 127 - 26
src/pages/home/index.vue

@@ -10,11 +10,12 @@
     }">
       <FlexCol position="absolute" :left="30" :top="0" :right="30">
         <StatusBarSpace />
-        <FlexRow justify="space-between" align="center">
+        <FlexRow justify="space-between" align="flex-start">
           <Button 
             @click="showCityPopup = true"
             icon="https://xy.wenlvti.net/app_static/images/home/IconSwitch.png"
             size="medium"
+            :radius="40"
             :text="currentCity"
           />
           <Image
@@ -38,24 +39,8 @@
         </template>
       </SimpleTransition>
 
-      <Height height="150px" />
+      <Height height="80px" />
       <FlexCol :gap="20" align="center">
-        <FlexRow justify="space-between" align="center" width="100%">
-          <SearchBar 
-            v-model="searchKeywords" 
-            placeholder="搜索" 
-            :innerStyle="{
-              backgroundColor: 'white',
-              borderRadius: '20rpx',
-              borderWidth: '1px',
-              borderStyle: 'solid',
-              borderColor: themeContext.resolveThemeColor('primary'),
-              width: '630rpx',//490rpx
-            }" 
-            @search="handleSearch"
-          />
-          <!-- <Button icon="ai-thinking" @click="handleGoAI" text="问AI" /> -->
-        </FlexRow>
         <ImageSwiper 
           :images="bannerLoader.content.value || []"
           :height="appConfiguration?.banners.home.height"
@@ -69,6 +54,50 @@
         />
       </FlexCol>
 
+      <FlexRow justify="space-between" align="center" width="100%">
+        <SearchBar 
+          v-model="searchKeywords" 
+          placeholder="搜索村社" 
+          :innerStyle="{
+            backgroundColor: 'white',
+            borderRadius: '20rpx',
+            borderWidth: '1px',
+            borderStyle: 'solid',
+            borderColor: themeContext.resolveThemeColor('primary'),
+            width: '630rpx',//490rpx
+          }" 
+          @search="handleSearch"
+        />
+        <!-- <Button icon="ai-thinking" @click="handleGoAI" text="问AI" /> -->
+      </FlexRow>
+
+      <HomeTitle title="今日推荐" showMore @moreClicked="emit('goDiscover')" />
+      <FlexRow gap="gap.lg" align="stretch" justify="space-between">
+        <Touchable 
+          v-for="item in recommendLoader.content.value"
+          :key="item.id"
+          position="relative"
+          radius="radius.md"
+          shadow="default"
+          gap="gap.lg"
+          direction="row"
+          align="center"
+          overflow="hidden"
+          border="1px solid #fff"
+          :flexShrink="0"
+          width="48%"
+          @click="handleGoRecommendDetails(item)"
+        >
+          <Image :src="item.image" :width="400" :height="270" />
+          <FlexCol position="absolute" :inset="{ b: -1, l: -1, r: -1 }" backgroundColor="background.primary" padding="padding.md" gap="gap.md">
+            <Text fontConfig="contentSpeicalText" fontFamily="SongtiSCBlack" :lines="1" :wrap="false">{{ item.title }}</Text>
+            <Text fontSize="fontSize.sm" :text="item.desc" :lines="1" :maxWidth="200" />
+          </FlexCol>
+        </Touchable>
+      </FlexRow>
+
+      <Height :height="10" />
+
       <LightMap
         small
         :city="currentCity"
@@ -107,7 +136,7 @@
           radius="radius.lg" 
           :padding="[10, 30]"
           text="切换城市"
-          :textProps="{ fontSize: 'fontSize.sm' }"
+          :textProps="{ wrap: false }"
           @click="showCityPopup = true"
         />
         <Button 
@@ -115,7 +144,7 @@
           radius="radius.lg" :padding="[10, 30]" 
           @click="showMyFollowPopup = true"
           text="我的关注"
-          :textProps="{ fontSize: 'fontSize.sm' }"
+          :textProps="{ wrap: false }"
         />
         <BubbleTip
           v-model:show="showLightTip"
@@ -129,13 +158,72 @@
           <Button 
             icon="https://xy.wenlvti.net/app_static/images/home/IconLight.png" 
             radius="radius.lg" :padding="[10, 30]" 
-            :textProps="{ fontSize: 'fontSize.sm' }"
+            :textProps="{ wrap: false }"
             text="点亮村社"
             @click="handleLightVillage"
           />
         </BubbleTip>
       </FlexRow>
 
+      <HomeTitle title="今日共建任务" showMore @moreClicked="navTo('/pages/home/village/task/index')" />
+      <scroll-view scroll-x>
+        <FlexRow gap="gap.lg" align="stretch">
+          <Touchable 
+            v-for="item in taskListLoader.content.value"
+            :key="item.id"
+            backgroundColor="white"
+            radius="radius.md"
+            padding="padding.md"
+            shadow="default"
+            gap="gap.lg"
+            direction="row"
+            align="center"
+            :flexShrink="0"
+            :innerStyle="{ minWidth: '400rpx' }"
+          >
+            <Icon :icon="item.image || 'space'" :size="100" />
+            <FlexCol gap="gap.md">
+              <Text fontConfig="contentSpeicalText" fontFamily="SongtiSCBlack">{{ item.name }}</Text>
+              <Text fontSize="fontSize.sm" :text="item.desc" :lines="1" :maxWidth="400" />
+              <Text v-if="item.points > 0" fontConfig="secondText">+{{ item.points }}文化积分</Text>
+              <FlexRow>
+                <Button v-if="item.rewardFruit > 0" icon="https://xy.wenlvti.net/app_static/images/village/IconFruit.png" size="mini" :text="`+${item.rewardFruit}乡源果`" />
+                <Button v-if="item.rewardLight > 0" icon="https://xy.wenlvti.net/app_static/images/village/IconLight.png" size="mini" :text="`+${item.rewardLight}乡源光`" />
+                <Button v-if="item.rewardWater > 0" icon="https://xy.wenlvti.net/app_static/images/village/IconWatering.png" size="mini" :text="`+${item.rewardWater}浇水次数`" />
+              </FlexRow>
+            </FlexCol>
+          </Touchable>
+        </FlexRow>
+      </scroll-view>
+
+
+      <HomeTitle title="最新动态" />
+      <FlexCol gap="gap.lg">
+        <FlexRow 
+          v-for="item in noticeListLoader.content.value?.slice(0, 3) || [] "
+          :key="item.item.id"
+          backgroundColor="background.tertiary"
+          radius="radius.md"
+          :padding="[20, 30]"
+          gap="gap.lg"
+          align="center"
+        > 
+          <Avatar 
+            :src="(item.item.avatar as string)"
+            :size="80"
+            :round="false"
+            :radius="10"
+          />
+          <FlexCol>
+            <Text :text="item.item.title" fontConfig="contentText" />
+            <Text 
+              :text="`来自${item.item.villageName}`" 
+              fontConfig="secondText"
+            />
+          </FlexCol>
+        </FlexRow>
+      </FlexCol>
+
       <HomeTitle title="乡村排名" showMore @moreClicked="navTo('/pages/home/village/rank/village', {})" />
       <VillageRankList :list="villageRankListLoader.content.value ?? []" :jumpToSingle="false" @goDetails="handleGoVillageDetails" />
 
@@ -213,7 +301,7 @@ import { useAuthStore } from '@/store/auth';
 import { useRequireLogin } from '@/common/composeabe/RequireLogin';
 import { useUserTools } from '@/common/composeabe/UserTools';
 import { useGetNotice } from './village/composeabe/GetNotice';
-import { waitTimeOut } from '@imengyu/imengyu-utils';
+import { DateUtils, waitTimeOut } from '@imengyu/imengyu-utils';
 import { toast, loading, hideLoading, confirm } from '@/components/utils/DialogAction';
 import { navTo } from '@/components/utils/PageAction';
 import { injectAppConfiguration } from '@/api/system/useAppConfiguration';
@@ -237,7 +325,7 @@ import Width from '@/components/layout/space/Width.vue';
 import IntroClamTip from './village/dialogs/IntroClamTip.vue';
 import MemoryTimeOut from '@/components/composeabe/MemoryTimeOut';
 import ImageSwiper from '@/common/components/parts/ImageSwiper.vue';
-import { type CommonInfoModel } from '@/api/inhert/VillageInfoApi';
+import VillageInfoApi, { type CommonInfoModel } from '@/api/inhert/VillageInfoApi';
 import PostIndex from './village/dialogs/PostIndex.vue';
 import FrameButton from '@/common/components/FrameButton.vue';
 import type { RegionItem } from '@/api/map/RegionApi';
@@ -250,6 +338,11 @@ import RecommendTab from './recommend/RecommendTab.vue';
 import GoodsTab from './recommend/GoodsTab.vue';
 import TravelTab from './recommend/TravelTab.vue';
 import CulturalTab from './recommend/CulturalTab.vue';
+import Avatar from '@/components/display/Avatar.vue';
+import Text from '@/components/basic/Text.vue';
+import TreeApi from '@/api/light/TreeApi';
+import Touchable from '@/components/feedback/Touchable.vue';
+import Icon from '@/components/basic/Icon.vue';
 
 const props = defineProps({
   currentScrollTop: {
@@ -257,7 +350,7 @@ const props = defineProps({
     default: 0,
   },
 });
-const emit = defineEmits(['goVillage','goDig']);
+const emit = defineEmits(['goVillage','goDig','goDiscover']);
 const authStore = useAuthStore();
 const villageStore = useVillageStore();
 const themeContext = useTheme();
@@ -282,7 +375,7 @@ const currentLocation = useGetCurrentLocation({
   },
 });
 
-const { currentNoticeContent } = useGetNotice();
+const { currentNoticeContent, noticeListLoader } = useGetNotice();
 
 const bannerLoader = useSimpleDataLoader(async () => {
   return (await BannerApi.getBannerList({
@@ -313,7 +406,15 @@ const villageUserRankListLoader = useSimpleDataLoader(async () => {
     }));
   return res
 });
-
+const taskListLoader = useSimpleDataLoader(async () => {
+  return (await TreeApi.getTaskList({
+    page: 1,
+    pageSize: 5,
+  })).list;
+});
+const recommendLoader = useSimpleDataLoader(async () => {
+  return (await VillageInfoApi.getListForDiscover({ page: 1, pageSize: 2 })).list;
+});
 
 const showLightTipTimeout = new MemoryTimeOut('LightTip', 1000 * 3600 * 2);//2h
 const showLightTip = ref(false);

+ 16 - 11
src/pages/home/village/composeabe/GetNotice.ts

@@ -1,43 +1,48 @@
 import TreeApi from "@/api/light/TreeApi";
 import { useSimpleDataLoader } from "@/components/composeabe/loader/SimpleDataLoader";
 import { SimpleTimer } from "@imengyu/imengyu-utils";
-import { onBeforeMount, ref } from "vue";
+import { computed, onBeforeMount, onMounted, ref } from "vue";
 
 export function useGetNotice(getVillageId?: () => number) {
   const currentNoticeContent = ref('');
   const currentNoticeIndex = ref(0);
   const noticeListLoader = useSimpleDataLoader(async () => {
-    const arr = (await TreeApi.getRandomGrowthLogFeed(10, {
+    const res = (await TreeApi.getRandomGrowthLogFeed(10, {
       villageId: getVillageId?.(),
-    })).list.map((item) => (item.item.title));
-    currentNoticeIndex.value = 0;
-    currentNoticeContent.value = arr[0];
-    noticeTimer.start();
-    return arr
+    })).list;
+    console.log(res);
+    return res;
   });
+  const noticeList = computed(() => noticeListLoader.content.value?.map((item) => (item.item.title)) || []);
 
   let prevSkipped = false;
   const noticeTimer = new SimpleTimer(undefined, () => {
-    if (!noticeListLoader.content.value)
+    if (!noticeList.value.length)
       return;
-    const current = noticeListLoader.content.value[currentNoticeIndex.value]; 
+    const current = noticeList.value[currentNoticeIndex.value]; 
     if (current.length > 50 && !prevSkipped) {
       prevSkipped = true;
       return;
     }
     prevSkipped = false;
     currentNoticeIndex.value ++;
-    if (currentNoticeIndex.value >= noticeListLoader.content.value.length)
+    if (currentNoticeIndex.value >= noticeList.value.length)
       currentNoticeIndex.value = 0;
-    currentNoticeContent.value = noticeListLoader.content.value[currentNoticeIndex.value]; 
+    currentNoticeContent.value = noticeList.value[currentNoticeIndex.value]; 
   }, 10000);
 
+  onMounted(() => {
+    currentNoticeIndex.value = 0;
+    currentNoticeContent.value = '';
+    noticeTimer.start();
+  });
   onBeforeMount(() => {
     noticeTimer.stop();
   });
 
   return {
     currentNoticeContent,
+    noticeList,
     noticeListLoader,
   }
 }

+ 1 - 0
src/pages/index.vue

@@ -30,6 +30,7 @@
         showSwitch
         @goHome="tabIndex=0" 
         @goMap="homeIndex?.goMap()"
+        @goDiscover="tabIndex = 3"
       />
       <DigIndex v-show="tabIndex === 2" />
       <DiscoverIndex v-show="tabIndex === 3" @goVillage="tabIndex = 1" />