| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- <template>
- <FlexCol>
- <FlexCol :gap="20" :padding="[30,30,0,30]" :innerStyle="{
- marginTop: '-80px',
- backgroundImage: `url(${appConfiguration?.banners.homeTop})`,
- backgroundSize: '100% auto',
- backgroundRepeat: 'no-repeat',
- backgroundPosition: 'top center',
- backgroundColor: themeContext.resolveThemeColor('background.primary'),
- }">
- <FlexCol position="absolute" :left="30" :top="0" :right="30">
- <StatusBarSpace />
- <FlexRow justify="space-between" align="center">
- <Button
- @click="showCityPopup = true"
- icon="https://xy.wenlvti.net/app_static/images/home/IconSwitch.png"
- size="small"
- :text="currentCity"
- />
- <Image
- :src="appConfiguration?.banners.homeTitle"
- :width="140"
- :height="75"
- />
- <Width :width="150" />
- </FlexRow>
- </FlexCol>
- <Height height="150px" />
- <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="appConfiguration?.banners.home.images"
- :height="appConfiguration?.banners.home.height"
- :width="700"
- radius="radius.md"
- :innerStyle="{
- border: '1px solid #fff',
- overflow: 'hidden',
- clipPath: 'ellipse(100% 90% at 50% 0%)'
- }"
- />
- </FlexCol>
- <LightMap
- small
- :city="currentCity"
- :cityCode="currentCityCode"
- v-model:district="currentDistrict"
- :lonlat="currentLocation.currentLonlat.value"
- @getCurrentLonlat="currentLocation.getCurrentExactLocation"
- @selectVillage="handleGoVillageDetails"
- @changeCity="showCityPopup=true"
- >
- <NoticeBar
- v-if="currentNoticeContent"
- :content="currentNoticeContent"
- :innerStyle="{
- position: 'absolute',
- top: '20rpx',
- left: '20rpx',
- right: '20rpx',
- zIndex: 100,
- borderRadius: '30rpx',
- }"
- :textStyle="{
- fontSize: '26rpx',
- }"
- icon="https://xy.wenlvti.net/app_static/images/home/IconLightActive.png"
- :iconProps="{
- size: 34,
- }"
- textColor="#C9211F"
- backgroundColor="#D9492E10"
- />
- </LightMap>
- <FlexRow justify="space-between" :padding="[10, 16]" gap="gap.md">
- <Button
- icon="https://xy.wenlvti.net/app_static/images/home/IconSwitch.png"
- radius="radius.lg"
- :padding="[10, 30]"
- @click="showCityPopup = true"
- >
- 切换城市
- </Button>
- <Button
- icon="https://xy.wenlvti.net/app_static/images/home/IconFollow.png"
- radius="radius.lg" :padding="[10, 30]"
- @click="showMyFollowPopup = true"
- >
- 我的关注
- </Button>
- <Button
- icon="https://xy.wenlvti.net/app_static/images/home/IconLight.png"
- radius="radius.lg" :padding="[10, 30]"
- @click="handleLightVillage"
- >
- 点亮村社
- </Button>
- </FlexRow>
- <!-- <HomeTitle title="最新动态" />
- <Construction text="测试数据,没有接口!">
- <FlexCol gap="gap.lg">
- <FlexRow
- v-for="item in activityLoader.content.value" :key="item.id"
- backgroundColor="background.tertiary"
- radius="radius.md"
- :padding="[20, 30]"
- gap="gap.lg"
- align="center"
- >
- <Avatar
- :url="item.head"
- :size="80"
- :round="false"
- :radius="10"
- />
- <Text :text="item.content" fontConfig="contentText" :innerStyle="{ flex: 1 }" />
- </FlexRow>
- </FlexCol>
- </Construction> -->
- <HomeTitle title="乡村排名" showMore @moreClicked="navTo('/pages/home/village/rank/village', {})" />
- <VillageRankList :list="villageRankListLoader.content.value ?? []" :jumpToSingle="false" @goDetails="handleGoVillageDetails" />
- <HomeTitle title="志愿者排名" showMore :lightCount="3" @moreClicked="navTo('/pages/home/village/rank/volunteer', {})" />
- <VillageUserRankList
- :list="villageUserRankListLoader.content.value ?? []"
- scoreSuffix="积分"
- @goDetails="navTo('/pages/home/village/volunteer/detail', { id: $event.id })"
- />
- <HomeTitle title="精选记忆">
- <template #right>
- <Touchable
- :padding="[15, 20]"
- :innerStyle="{ marginRight: '20rpx' }"
- direction="row"
- center
- gap="gap.md"
- @click="handleGoPublish()"
- >
- <Icon name="https://xy.wenlvti.net/app_static/images/village/IconLargeHistory.png" :size="30" />
- <Text text="我也来写" fontConfig="contentText" />
- </Touchable>
- </template>
- </HomeTitle>
- <SimplePageListLoader :loader="recommendLoader">
- <MasonryGrid>
- <MasonryGridItem
- v-for="(item, i) in recommendLoader.list.value"
- :key="i"
- :width="340"
- >
- <IndexCommonImageItem
- :image="item.image"
- :title="item.title"
- :desc="item.content ?? ''"
- :userName="item.villageVolunteerName ?? ''"
- :likes="0"
- :isLike="false"
- @click="handleGoRecommendDetails(item)"
- />
- </MasonryGridItem>
- </MasonryGrid>
- </SimplePageListLoader>
- </FlexCol>
- <Height :height="200" />
- <Popup
- v-model:show="showCityPopup"
- closeable
- position="top"
- size="60vh"
- >
- <CitySelect @selectCity="handleSelectCity" />
- </Popup>
- <Popup
- v-model:show="showMyFollowPopup"
- closeable
- position="bottom"
- round
- size="80vh"
- >
- <VillageMyFollow @goDetails="handleGoVillageDetails" />
- </Popup>
- <PostIndex
- ref="postIndexRef"
- @goDig="emit('goDig')"
- />
- <IntroClamTip ref="introClamTipRef" @apply="handleLightVillage" />
- </FlexCol>
- </template>
- <script setup lang="ts">
- import { onBeforeMount, onMounted, ref, watch } from 'vue';
- import { useTheme } from '@/components/theme/ThemeDefine';
- import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
- import { useStorageVar } from '@/components/composeabe/StorageVar';
- import { useVillageStore } from '@/store/village';
- import { useGetCurrentLocation } from './composeabe/GetCurrentLocation';
- import { useAuthStore } from '@/store/auth';
- import { useRequireLogin } from '@/common/composeabe/RequireLogin';
- import { useUserTools } from '@/common/composeabe/UserTools';
- import { ArrayUtils, waitTimeOut } from '@imengyu/imengyu-utils';
- import { toast } from '@/components/utils/DialogAction';
- import { navTo } from '@/components/utils/PageAction';
- import { injectAppConfiguration } from '@/api/system/useAppConfiguration';
- import Image from '@/components/basic/Image.vue';
- import FlexCol from '@/components/layout/FlexCol.vue';
- import FlexRow from '@/components/layout/FlexRow.vue';
- import Height from '@/components/layout/space/Height.vue';
- import SearchBar from '@/components/form/SearchBar.vue';
- import Button from '@/components/basic/Button.vue';
- import HomeTitle from '@/common/components/parts/HomeTitle.vue';
- import VillageRankList from './components/VillageRankList.vue';
- import VillageUserRankList from './components/VillageUserRankList.vue';
- import Popup from '@/components/dialog/Popup.vue';
- import CitySelect from './components/CitySelect.vue';
- import VillageMyFollow from './components/VillageMyFollow.vue';
- import MapApi from '@/api/map/MapApi';
- import LightMap from './components/LightMap.vue';
- import NoticeBar from '@/components/display/NoticeBar.vue';
- import StatusBarSpace from '@/components/layout/space/StatusBarSpace.vue';
- import LightVillageApi, { VillageListItem } from '@/api/light/LightVillageApi';
- import type { RegionItem } from '@/api/map/RegionApi';
- import Width from '@/components/layout/space/Width.vue';
- import MasonryGrid from '@/components/layout/masonry/MasonryGrid.vue';
- import MasonryGridItem from '@/components/layout/masonry/MasonryGridItem.vue';
- import IndexCommonImageItem from '@/common/components/parts/IndexCommonImageItem.vue';
- import IntroClamTip from './village/dialogs/IntroClamTip.vue';
- import MemoryTimeOut from '@/components/composeabe/MemoryTimeOut';
- import Touchable from '@/components/feedback/Touchable.vue';
- import Icon from '@/components/basic/Icon.vue';
- import Text from '@/components/basic/Text.vue';
- import ImageSwiper from '@/common/components/parts/ImageSwiper.vue';
- import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePageListLoader';
- import VillageInfoApi, { type CommonInfoModel } from '@/api/inhert/VillageInfoApi';
- import SimplePageListLoader from '@/components/loader/SimplePageListLoader.vue';
- import PostIndex from './village/dialogs/PostIndex.vue';
- import TreeApi from '@/api/light/TreeApi';
- import { SimpleTimer } from '@/components/utils/Timer';
- import { useGetNotice } from './village/composeabe/GetNotice';
- const emit = defineEmits(['goVillage','goDig']);
- const authStore = useAuthStore();
- const villageStore = useVillageStore();
- const themeContext = useTheme();
- const { requireLogin } = useRequireLogin();
- const { getIsVolunteer } = useUserTools();
- const searchKeywords = ref('');
- const showCityPopup = ref(false);
- const showMyFollowPopup = ref(false);
- const introClamTipRef = ref();
- const introClamTipTimeout = new MemoryTimeOut('IntroClamTip', 1000 * 3600 * 30);//30h
- const postIndexRef = ref<InstanceType<typeof PostIndex>>();
- const appConfiguration = injectAppConfiguration();
- const { value: currentCity } = useStorageVar('currentCityName', '');
- const { value: currentCityCode } = useStorageVar('currentCityCode', 0);
- const { value: currentDistrict } = useStorageVar('currentDistrict', '');
- const currentLocation = useGetCurrentLocation({
- onCityChanged: (city, code, district) => {
- currentCity.value = city;
- currentCityCode.value = code;
- currentDistrict.value = district || '';
- },
- });
- const { currentNoticeContent } = useGetNotice();
- const villageRankListLoader = useSimpleDataLoader(async () => {
- const res = await LightVillageApi.getVillageRankList({ num: 3 });
- return res.map((item, i) => ({
- image: item.image ?? '',
- title: item.name,
- rank: i + 1,
- id: item.id,
- }));
- });
- const villageUserRankListLoader = useSimpleDataLoader(async () => {
- const res = (await LightVillageApi.getVolunteerRankList({ num: 3 }))
- .map((item, i) => ({
- id: item.id,
- image: item.image ?? '',
- title: item.name,
- rank: i + 1,
- score: item.points,
- }));
- if (res.length >= 3) {
- //移动第一名到中间
- const first = res[0];
- ArrayUtils.removeAt(res, 0);
- ArrayUtils.insert(res, 1, first);
- }
- return res
- });
- const activityLoader = useSimpleDataLoader(async () => {
- return [
- {
- id: 1,
- head: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest1.png',
- content: '测试数据,没有接口!:福泽乡里 为全村加成+10%乡源果,可持续24小时',
- levelText: '一级',
- },
- {
- id: 2,
- head: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest2.png',
- content: '福泽乡里 为全村加成+10%乡源果,可持续24小时',
- levelText: '五级',
- },
- {
- id: 3,
- head: 'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest3.png',
- content: '福泽乡里 为全村加成+10%乡源果,可持续24小时',
- levelText: '十级',
- },
- ];
- });
- const recommendLoader = useSimplePageListLoader(20, async (page, pageSize) => {
- return await VillageInfoApi.getListForDiscover(page, pageSize);
- }, true);
- function handleGoRecommendDetails(item: CommonInfoModel) {
- navTo(`/pages/home/discover/details`, { id: item.id });
- }
- async function handleGoVillageDetails(item: VillageListItem) {
- showMyFollowPopup.value = false;
- const details = await LightVillageApi.getVillageDetails(item.id);
- villageStore.setCurrentVillage(details);
- await waitTimeOut(100);
- emit('goVillage')
- }
- function handleSelectCity(city: RegionItem) {
- currentCity.value = city.name;
- currentCityCode.value = city.areaCode;
- showCityPopup.value = false;
- currentLocation.currentLonlat.value = {
- longitude: Number(city.longitude),
- latitude: Number(city.latitude),
- };
- }
- function handleGoAI() {
- requireLogin(async () => {
- navTo('/pages/chat/index');
- }, '暂时需要登录后才能使用AI助手');
- }
- async function handleGoPublish() {
- requireLogin(async () => {
- postIndexRef.value?.show();
- }, '欢迎您发布内容,登录后以便使用更多功能哦!');
- }
- async function handleLightVillage() {
- requireLogin(async () => navTo('/pages/home/light/submit-map', {
- city: currentCity.value,
- cityCode: currentCityCode.value,
- district: currentDistrict.value,
- }), '登录后才能点亮村社哦!');
- }
- function handleSearch() {
- if (!searchKeywords.value) {
- toast('请输入搜索关键词');
- return;
- }
- navTo('/pages/home/search/index', {
- city: currentCity.value,
- cityCode: currentCityCode.value,
- searchValue: searchKeywords.value,
- });
- }
- async function loadInfo() {
- //如果不是志愿者,则显示认领村庄提示
- const isVolunteer = await getIsVolunteer();
- if (!isVolunteer) {
- if (introClamTipTimeout.isTimeout()) {
- introClamTipRef.value?.show();
- introClamTipTimeout.recordTime();
- }
- }
- //加载我的关注村庄
- try {
- await villageStore.loadMyFollowVillages();
- await villageStore.loadMyJoinedVillages();
- } catch (error) {
- console.error(error);
- }
- if (villageStore.myFollowVillages.length > 0) {
- const currentVillage = villageStore.loadCurrentVillage();
- const id = (villageStore.myFollowVillages.find(p => p.id === currentVillage) as VillageListItem || villageStore.myFollowVillages[0]).id;
- const res = await LightVillageApi.getVillageDetails(id)
- villageStore.setCurrentVillage(res);
- }
- }
- watch(() => authStore.isLogged, async (newVal) => {
- if (newVal) {
- await loadInfo();
- }
- });
- onMounted(async () => {
- try {
- if (currentCity.value) {
- await currentLocation.setCurrentLocationWithCity(currentCity.value, currentDistrict.value);
- } else {
- await currentLocation.getCurrentFuzzyLocation();
- }
- } catch (error) {
- console.error(error);
- toast('获取当前位置失败,您可以手动选择城市');
- }
- });
- defineExpose({
- onPageBack: (name: string, data: Record<string, unknown>) => {
- if (name === 'changeCurrentCity') {
- currentCity.value = data.city as string;
- currentCityCode.value = data.code as number;
- currentDistrict.value = '';
- currentLocation.currentLonlat.value = {
- longitude: Number(data.longitude),
- latitude: Number(data.latitude),
- };
- }
- }
- })
- </script>
|