| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- <template>
- <FlexCol v-if="currentVillage">
- <FlexCol :margin="[10,0,0,0]">
- <Text textAlign="center" text="一人添果,全村增光;乡源树茂,故土名扬" fontConfig="primaryTitle" fontSize="35rpx" />
- </FlexCol>
- <VillageTree
- ref="villageTreeRef"
- :treeImage="currentVillage.treeImage"
- :treeName="currentVillage.treeName"
- :treeAnimProps="currentVillage.treeImageAnimProps"
- @fruitPick="handlePick"
- />
- <FlexCol :padding="30">
- <FlexCol>
- <FlexRow center>
- <Progress
- :value="treeProgress"
- :backgroundStyle="{
- background: 'linear-gradient(to bottom, #280502, #a44e17)',
- }"
- :progressStyle="{
- borderImageSource: 'url(https://xy.wenlvti.net/app_static/images/village/ImageProgress.png)',
- borderImageSlice: `5 25 5 25 fill`,
- borderImageWidth: `0px 15px`,
- borderImageRepeat: 'stretch',
- borderRadius: '10px',
- overflow: 'hidden',
- }"
- :width="300"
- :height="30"
- />
- </FlexRow>
- <Height height="space.lg" />
- <FlexRow center gap="gap.md">
- <Icon icon="https://xy.wenlvti.net/app_static/images/village/IconLight.png" :size="50" />
- <Text :text="`乡源光 ${currentVillage?.lightTotal || 0} ${currentVillage.treeName}`" fontConfig="contentText" fontSize="30rpx" color="#E79412" />
- </FlexRow>
- <Height height="space.md" />
- <Text textAlign="center" :text="`还差 ${treeNextLevelLight} 乡源光即可升级至 ${currentVillage?.nextTreeName || ''}`" fontConfig="secondText" />
- </FlexCol>
- <Height height="space.xl" />
- <FlexRow justify="space-around" :padding="[0, 30]">
- <Touchable center direction="column" flexBasis="22%" @click="handlePick">
- <Image src="https://xy.wenlvti.net/app_static/images/village/IconCollect.png" :width="130" mode="widthFix" />
- <Text text="拾果" fontConfig="contentText" />
- </Touchable>
- <Touchable center direction="column" flexBasis="22%" @click="handleFertilize">
- <Image src="https://xy.wenlvti.net/app_static/images/village/IconFertilization.png" :width="130" mode="widthFix" />
- <Text text="施肥" fontConfig="contentText" />
- </Touchable>
- <Touchable center direction="column" flexBasis="22%" @click="handleWater">
- <Image src="https://xy.wenlvti.net/app_static/images/village/IconWatering.png" :width="130" mode="widthFix" />
- <Text text="浇水" fontConfig="contentText" textAlign="center" />
- </Touchable>
- <Touchable center direction="column" flexBasis="22%" @click="handleGoBless">
- <Image src="https://xy.wenlvti.net/app_static/images/village/IconBlessing.png" :width="130" mode="widthFix" />
- <Text text="赐福" fontConfig="contentText" textAlign="center" />
- </Touchable>
- </FlexRow>
- <HomeTitle title="最新动态" />
- <SimplePageContentLoader :loader="activityLoader" :emptyView="{ 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"
- defaultAvatar="https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteerNew.png"
- :size="80"
- radius="50%"
- />
- <FlexCol flex="1">
- <Text :text="item.nickname" fontConfig="contentText" :innerStyle="{ flex: 1 }" />
- <Text :text="item.content" fontConfig="contentText" :innerStyle="{ flex: 1 }" />
- </FlexCol>
- <BackgroundBox
- backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagNormal.png"
- :backgroundCutBorder="[10, 10, 10, 10]"
- :backgroundCutBorderSize="[10, 10, 10, 10]"
- :padding="[15, 30]"
- >
- <Text :text="item.levelText" fontConfig="contentText" />
- </BackgroundBox>
- </FlexRow>
- </FlexCol>
- </SimplePageContentLoader>
- <HomeTitle
- title="乡源赐福"
- showMore
- moreText="我的订单"
- @moreClicked="handleGoBlessOrders"
- />
- <FlexRow wrap>
- <Touchable
- v-for="(item, index) in blessingInfoLoader.content.value"
- :key="item.id"
- center
- flexBasis="30%"
- gap="gap.sm"
- direction="column"
- :margin="[10,0,0,0]"
- @click="handleBuyBless(item)"
- >
- <BackgroundBox
- backgroundImage="https://xy.wenlvti.net/app_static/images/village/ImageBlessingCount.png"
- backgroundPosition="center"
- :padding="[10, 20]"
- >
- <Text :text="index + 1" color="white" />
- </BackgroundBox>
- <Image :src="item.image" :width="210" :height="250" radius="radius.md" mode="aspectFill" />
- <BackgroundBox
- backgroundImage="https://xy.wenlvti.net/app_static/images/village/ImageBlessingBar.png"
- :padding="10"
- :width="210"
- >
- <Text textAlign="center" :text="item.name" fontConfig="contentText" fontFamily="SongtiSCBlack" color="white" />
- </BackgroundBox>
- </Touchable>
- </FlexRow>
- </FlexCol>
- </FlexCol>
- <BlessBuyDialog
- ref="blessBuyDialogRef"
- :currentBless="currentBless"
- @buyBless="handleBuyBlessConfirm"
- />
- <BlessSuccessDialog
- ref="blessSuccessDialogRef"
- />
- </template>
- <script setup lang="ts">
- import { computed, onBeforeMount, onMounted, ref, watch } from 'vue';
- import { useVillageStore } from '@/store/village';
- import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
- import { useRequireLogin } from '@/common/composeabe/RequireLogin';
- import { showError } from '@/common/composeabe/ErrorDisplay';
- import { toast } from '@/components/utils/DialogAction';
- import { navTo } from '@/components/utils/PageAction';
- import { RequestApiError, SimpleTimer, waitTimeOut } from '@imengyu/imengyu-utils';
- import { useAuthStore } from '@/store/auth';
- import HomeTitle from '@/common/components/parts/HomeTitle.vue';
- import Text from '@/components/basic/Text.vue';
- import FlexCol from '@/components/layout/FlexCol.vue';
- import VillageTree from '../components/VillageTree.vue';
- import FlexRow from '@/components/layout/FlexRow.vue';
- import Icon from '@/components/basic/Icon.vue';
- import Touchable from '@/components/feedback/Touchable.vue';
- import Image from '@/components/basic/Image.vue';
- import Height from '@/components/layout/space/Height.vue';
- import Avatar from '@/components/display/Avatar.vue';
- import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
- import Progress from '@/components/display/Progress.vue';
- import TreeApi, { type BlessPackageItem, type GrowthLogFeedItem } from '@/api/light/TreeApi';
- import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
- import BlessBuyDialog from '../dialogs/BlessBuyDialog.vue';
- import BlessSuccessDialog from '../dialogs/BlessSuccessDialog.vue';
- const GROWTH_FEED_COUNT = 6;
- const DEFAULT_AVATAR = 'https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteerNew.png';
- const villageStore = useVillageStore();
- const authStore = useAuthStore();
- const { requireLoginAsync } = useRequireLogin();
- const blessBuyDialogRef = ref<InstanceType<typeof BlessBuyDialog>>();
- const blessSuccessDialogRef = ref<InstanceType<typeof BlessSuccessDialog>>();
- const villageTreeRef = ref<InstanceType<typeof VillageTree>>();
- const currentBless = ref<BlessPackageItem>();
- const activityLoader = useSimpleDataLoader(async () => {
- const villageId = villageStore.currentVillage?.id;
- if (!villageId) return [];
- function mapGrowthFeedToInfoItem(feed: GrowthLogFeedItem) {
- const { item, logType } = feed;
- let content = item.remark?.trim() || '';
- let levelText = '';
- if (logType === 'task') {
- levelText = item.taskName || '任务';
- if (!content) {
- content = item.taskName ? `完成任务「${item.taskName}」` : '任务动态';
- }
- } else if (logType === 'fruit') {
- levelText = item.typeText || item.type || '乡源果';
- if (!content) {
- const delta = item.fruit > 0 ? `+${item.fruit}` : `${item.fruit}`;
- content = `${levelText} ${delta} 乡源果`;
- }
- } else {
- levelText = item.typeText || item.type || '乡源光';
- if (!content) {
- const delta = item.light > 0 ? `+${item.light}` : `${item.light}`;
- content = `${levelText} ${delta} 乡源光`;
- }
- }
- return {
- id: `${logType}-${item.id}`,
- head: DEFAULT_AVATAR,
- nickname: item.nickname,
- content,
- levelText,
- };
- }
- const { list } = await TreeApi.getRandomGrowthLogFeed(GROWTH_FEED_COUNT, { villageId });
- const res = list.map(mapGrowthFeedToInfoItem);
- if (res.length > 0)
- return res;
- return [
- {
- id: 'default',
- head: DEFAULT_AVATAR,
- content: '冷冷清清,等你来添光加彩',
- nickname: '',
- levelText: '第一条',
- }
- ];
- });
- watch(() => villageStore.currentVillage?.id, () => {
- activityLoader.reload();
- getFruits();
- });
- const blessingInfoLoader = useSimpleDataLoader(async () => {
- const res = await TreeApi.getBlessList({ page: 1, pageSize: 18 });
- return res.list;
- });
- const currentVillage = computed(() => {
- return villageStore.currentVillage;
- });
- const treeProgress = computed(() => {
- if (!villageStore.currentVillage)
- return 0;
- const v = villageStore.currentVillage;
- return Math.floor(v.lightTotal - v.treeLight) / (v.nextTreeLight - v.treeLight) * 100;
- });
- const treeNextLevelLight = computed(() => {
- if (!villageStore.currentVillage)
- return 0;
- const v = villageStore.currentVillage;
- return v.nextTreeLight - v.treeLight;
- });
- function handleBuyBless(bless: BlessPackageItem) {
- currentBless.value = bless;
- blessBuyDialogRef.value?.show();
- }
- async function handleGoBlessOrders() {
- if (!await requireLoginAsync('登录后查看我的赐福订单'))
- return;
- navTo('/pages/home/village/bless/my-orders', {
- villageId: villageStore.currentVillage?.id
- });
- }
- async function handleBuyBlessConfirm() {
- if (!currentBless.value || !villageStore.currentVillage?.id)
- return;
- if (!await requireLoginAsync('登录后为村社赐福,留下你的大名吧'))
- return;
- try {
- uni.showLoading({
- title: '请稍后...',
- });
- const payInfo = await TreeApi.createBlessOrder(villageStore.currentVillage?.id, currentBless.value.id, 1);
- if (payInfo) {
- uni.requestPayment({
- provider: 'wxpay',
- appId: payInfo.pay.appId,
- timeStamp: payInfo.pay.timeStamp,
- nonceStr: payInfo.pay.nonceStr,
- package: payInfo.pay.package,
- signType: payInfo.pay.signType,
- paySign: payInfo.pay.paySign,
- success: () => {
- blessSuccessDialogRef.value?.show();
- handleBlessPaySuccessRefresh();
- },
- fail: (err) => {
- showError(err);
- },
- });
- }
- } catch (error) {
- showError(error);
- } finally {
- uni.hideLoading();
- }
- }
- function handleGoBless() {
- uni.pageScrollTo({
- scrollTop: 1000,
- duration: 300,
- });
- }
- async function handleBlessPaySuccessRefresh() {
- refreshVillageTreeInfo();
- await waitTimeOut(600);
- authStore.refreshUserInfo();
- await waitTimeOut(2000);
- activityLoader.reload();
- }
- function handleFertilize() {
- handlePickOrWaterOrFertilize('fertilize');
- }
- function handlePick() {
- handlePickOrWaterOrFertilize('pick');
- }
- function handleWater() {
- handlePickOrWaterOrFertilize('water');
- }
- async function handlePickOrWaterOrFertilize(action: 'pick' | 'water' | 'fertilize') {
- if (!villageStore.currentVillage?.id)
- return;
- switch (action) {
- case 'pick':
- if (!await requireLoginAsync('登录后就可以为乡源树拾果了'))
- return;
- break;
- case 'water':
- if (!await requireLoginAsync('登录后就可以为乡源树浇水了'))
- return;
- break;
- case 'fertilize':
- if (!await requireLoginAsync('登录后就可以为乡源树施肥了'))
- return;
- break;
- }
- try {
- uni.showLoading({
- title: '请稍后...',
- });
- switch (action) {
- case 'pick':
- const res = await TreeApi.pick(villageStore.currentVillage.id);
- villageTreeRef.value?.playStateAnimation('collect');
- toast(res);
- break;
- case 'water':
- await TreeApi.water(villageStore.currentVillage.id);
- villageTreeRef.value?.playStateAnimation('water');
- toast('浇水成功!感谢您的贡献');
- break;
- case 'fertilize':
- await TreeApi.fertilize(villageStore.currentVillage.id);
- villageTreeRef.value?.playStateAnimation('fertilize');
- toast('施肥成功!感谢您的贡献');
- break;
- }
- refreshVillageTreeInfo();
- uni.hideLoading();
- } catch (e) {
- uni.hideLoading();
- if (e instanceof RequestApiError && typeof e.data === 'string') {
- toast(e.data);
- return;
- }
- showError(e);
- }
- }
- async function getFruits() {
- if (!villageStore.currentVillage?.id)
- return;
- const res = await TreeApi.dropFruit(villageStore.currentVillage.id);
- if (res) {
- villageTreeRef.value?.createFruits(res.fruitRemain);
- }
- }
- async function refreshVillageTreeInfo() {
- await villageStore.reloadVillageInfo();
- }
- const refreshFruitTimer = new SimpleTimer(undefined, () => getFruits(), 15000);
- onMounted(() => {
- refreshFruitTimer.start();
- });
- onBeforeMount(() => {
- refreshFruitTimer.stop();
- });
- defineExpose({
- onPageBack: (name: string, data: Record<string, unknown>) => {
- if (name === 'blessPaySuccessRefresh') {
- handleBlessPaySuccessRefresh();
- }
- },
- });
- </script>
|