card.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <template>
  2. <FlexCol :padding="30" gap="gap.lg">
  3. <!-- 卡片背景 -->
  4. <BackgroundBox
  5. color1="#eecaa0"
  6. color2="white"
  7. color2Position="85%"
  8. color3="white"
  9. radius="radius.lg"
  10. direction="column"
  11. :padding="[35,30]"
  12. gap="gap.lg"
  13. >
  14. <!-- 标题 -->
  15. <FlexRow justify="space-between" width="100%">
  16. <FlexCol gap="gap.md">
  17. <Text :text="villageInfoLoader.content.value?.title" fontConfig="primaryTitle" />
  18. <Text :text="villageInfoLoader.content.value?.address" fontConfig="contentText" />
  19. </FlexCol>
  20. <FlexRow center gap="gap.lg">
  21. <Button
  22. icon="https://xy.wenlvti.net/app_static/images/village/IconJoin.png"
  23. radius="radius.lgr"
  24. @click="isFollowed ? onUnFollow() : onFollow()"
  25. >
  26. {{ isFollowed ? '已关注' : '关注' }}
  27. </Button>
  28. <Button
  29. icon="https://xy.wenlvti.net/app_static/images/village/IconFollow.png"
  30. radius="radius.lgr"
  31. @click="handleGoJoin()"
  32. >
  33. {{ isJoined ? '已加入' : '加入' }}
  34. </Button>
  35. </FlexRow>
  36. </FlexRow>
  37. <!-- 状态与申请 -->
  38. <Construction text="暂无数据">
  39. <FlexRow
  40. backgroundColor="background.tertiary"
  41. radius="radius.md"
  42. padding="space.md"
  43. justify="space-between"
  44. >
  45. <FlexCol gap="gap.md">
  46. <Text :text="`${villageInfoLoader.content.value?.applyCount} 个乡源果可申请`" fontConfig="secondText" />
  47. <Text :text="`存储空间内存:${villageInfoLoader.content.value?.sizeText}`" fontConfig="secondText" />
  48. </FlexCol>
  49. <FlexCol gap="gap.md">
  50. <Button icon="https://xy.wenlvti.net/app_static/images/village/IconUser.png" radius="radius.lgr" :padding="[10, 30]" backgroundColor="white">申请管理者</Button>
  51. </FlexCol>
  52. </FlexRow>
  53. </Construction>
  54. <!-- 图片 -->
  55. <Construction text="暂无接口">
  56. <FlexRow v-if="villageInfoLoader.content.value" justify="space-between">
  57. <Image
  58. v-for="index of 3"
  59. :key="index"
  60. :src="villageInfoLoader.content.value.images[index - 1]"
  61. radius="radius.md"
  62. :width="200"
  63. :height="140"
  64. mode="aspectFill"
  65. defaultImage=""
  66. touchable
  67. >
  68. <template #empty>
  69. <Touchable
  70. direction="column"
  71. position="absolute"
  72. inset="0"
  73. center
  74. backgroundColor="background.primary"
  75. >
  76. <Icon name="add" size="fontSize.md" />
  77. <Text text="上传封面" fontConfig="secondText" />
  78. </Touchable>
  79. </template>
  80. <FlexCol
  81. v-if="index === 3 && villageInfoLoader.content.value.images.length > 3"
  82. position="absolute" inset="0"
  83. center
  84. backgroundColor="mask.default"
  85. >
  86. <Text :text="`+${villageInfoLoader.content.value.images.length - 3}`" fontSize="fontSize.lg" color="white" />
  87. </FlexCol>
  88. </Image>
  89. </FlexRow>
  90. </Construction>
  91. <!-- 地址 -->
  92. <FlexRow align="center" gap="gap.sm">
  93. <Icon name="https://xy.wenlvti.net/app_static/images/village/IconMap.png" size="fontSize.md" />
  94. <Text :text="villageInfoLoader.content.value?.address" fontConfig="contentText" />
  95. </FlexRow>
  96. <VillageMiniMap
  97. v-if="villageInfoLoader.content.value"
  98. :lonlat="{
  99. longitude: villageInfoLoader.content.value.longitude,
  100. latitude: villageInfoLoader.content.value.latitude
  101. }"
  102. />
  103. <FlexRow justify="space-between" align="center">
  104. <FlexRow center gap="gap.lg" flexBasis="50%">
  105. <Text text="村社排名" fontConfig="contentText" />
  106. <Text text="No." fontConfig="lightTitle" />
  107. <Text :text="villageInfoLoader.content.value?.rankText" fontConfig="primaryTitle" />
  108. </FlexRow>
  109. <FlexRow center gap="gap.lg" flexBasis="50%">
  110. <Text text="村社等级" fontConfig="contentText" />
  111. <Text :text="villageInfoLoader.content.value?.levelText" fontConfig="primaryTitle" />
  112. </FlexRow>
  113. </FlexRow>
  114. <FlexRow backgroundColor="background.tertiary" radius="radius.md" :padding="[30, 20]">
  115. <FlexCol center gap="gap.sm" flexBasis="25%">
  116. <Text text="乡源光" fontConfig="secondText" />
  117. <Text :text="villageInfoLoader.content.value?.light" fontConfig="importantTitle" />
  118. </FlexCol>
  119. <Divider type="vertical" />
  120. <FlexCol center gap="gap.sm" flexBasis="25%">
  121. <Text text="乡源人数" fontConfig="contentText" />
  122. <Text :text="villageInfoLoader.content.value?.memberCount" fontConfig="importantTitle" />
  123. </FlexCol>
  124. <Divider type="vertical" />
  125. <FlexCol center gap="gap.sm" flexBasis="25%">
  126. <Text text="关注人数" fontConfig="contentText" />
  127. <Text :text="villageInfoLoader.content.value?.followerCount" fontConfig="importantTitle" />
  128. </FlexCol>
  129. <Divider type="vertical" />
  130. <Button
  131. :padding="0"
  132. type="text"
  133. size="small"
  134. textColor="text.title"
  135. text="新手上路"
  136. rightIcon="arrow-right"
  137. @click="handleGoNew()"
  138. />
  139. </FlexRow>
  140. </BackgroundBox>
  141. <!-- 排行榜 -->
  142. <HomeTitle title="排行榜" showMore @moreClicked="navTo('/pages/home/village/rank/volunteer', {
  143. villageId: villageStore.currentVillage?.id ?? undefined,
  144. })" />
  145. <RoundTags v-model:active="rankActiveTag" :tags="['乡源果', '志愿者', '乡源光']" />
  146. <VillageUserRankList :list="villageUserRankListLoader.content.value ?? []" />
  147. <!-- 魅力乡源 -->
  148. <HomeTitle title="魅力乡源" showMore @moreClicked="handleGoCollect()">
  149. <template #right>
  150. <Touchable
  151. :padding="[15, 20]"
  152. :innerStyle="{
  153. marginRight: '20rpx'
  154. }"
  155. borderStyle="solid"
  156. borderColor="border.secondary"
  157. borderWidth="1px"
  158. backgroundColor="background.quaternary"
  159. radius="radius.md"
  160. direction="row"
  161. center
  162. gap="gap.sm"
  163. @click="handleGoCollect()"
  164. >
  165. <Icon name="https://xy.wenlvti.net/app_static/images/village/IconHistory.png" :size="30" />
  166. <Text text="共编村史" fontConfig="contentText" />
  167. </Touchable>
  168. </template>
  169. </HomeTitle>
  170. <ProvideVar :vars="{
  171. GridItemIconSize: 90,
  172. GridItemBackgroundColor: 'transparent',
  173. GridItemPaddingHorizontal: 0,
  174. GridItemPaddingVertical: 8,
  175. }">
  176. <Grid :borderGrid="false" :mainAxisCount="4">
  177. <GridItem title="村社概况" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeIntrod.png" touchable @click="handleGoCollect('overview')" />
  178. <GridItem title="自然风光" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeEnvirounment.png" touchable @click="handleGoCollect('environment', '自然环境')" />
  179. <GridItem title="历史沿革" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeHistory.png" touchable @click="handleGoCollect('history', '建村历史')" />
  180. <GridItem title="特色产业" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeIndustry.png" touchable @click="handleGoCollect('product')" />
  181. <GridItem title="文艺活动" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeActivity.png" touchable @click="handleGoCollect('custom')" />
  182. <GridItem title="非遗展示" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeShow.png" touchable @click="handleGoCollect('ich')" />
  183. <GridItem title="民俗风采" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeFolkloreVibe.png" touchable @click="handleGoCollect('custom')" />
  184. <GridItem title="乡贤故事" icon="https://xy.wenlvti.net/app_static/images/village/IconGoods.png" touchable @click="handleGoCollect('history', '历史人物')" />
  185. </Grid>
  186. </ProvideVar>
  187. <!-- 活力乡源 -->
  188. <HomeTitle title="活力乡源" />
  189. <ProvideVar :vars="{
  190. GridItemIconSize: 90,
  191. GridItemBackgroundColor: 'transparent',
  192. GridItemPaddingHorizontal: 0,
  193. GridItemPaddingVertical: 8,
  194. }">
  195. <Grid :borderGrid="false" :mainAxisCount="4">
  196. <GridItem title="乡源荣光" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeHornor.png" touchable @click="toast('暂未开放,敬请期待!')" />
  197. <GridItem title="乡源好物" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeGoods.png" touchable @click="navTo('/pages/home/village/goods/index')" />
  198. <GridItem title="乡源树" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeTree.png" touchable @click="emit('goTree')" />
  199. <GridItem title="政贤连心" icon="https://xy.wenlvti.net/app_static/images/village/IconGovAffairs.png" touchable @click="toast('暂未开放,敬请期待!')" />
  200. <GridItem title="互动游戏" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeGame.png" touchable @click="navTo('/pages/home/village/games/index')" />
  201. </Grid>
  202. </ProvideVar>
  203. <!-- 文脉乡源 -->
  204. <HomeTitle title="文脉乡源">
  205. <template #right>
  206. <BackgroundImageButton
  207. backgroundImage="https://xy.wenlvti.net/app_static/images/village/TagActive.png"
  208. :backgroundCutBorder="[10, 10, 10, 10]"
  209. :backgroundCutBorderSize="[10, 10, 10, 10]"
  210. :padding="[15, 20]"
  211. :innerStyle="{ marginRight: '20rpx' }"
  212. direction="row"
  213. center
  214. gap="gap.sm"
  215. @click="handleGoPublish()"
  216. >
  217. <Icon name="https://xy.wenlvti.net/app_static/images/village/IconLargeHistory.png" :size="30" />
  218. <Text text="去发布" fontConfig="contentText" color="white" />
  219. </BackgroundImageButton>
  220. </template>
  221. </HomeTitle>
  222. <RoundTags v-model:active="listActiveTag" :tags="['广场', '老味道', '老手艺', '老物件', '老故事']" />
  223. </FlexCol>
  224. <OfficialAccountPublishWrap
  225. :topic="recommendTagName"
  226. @publishsuccess="onPublishSuccess"
  227. />
  228. </template>
  229. <script setup lang="ts">
  230. import { computed, ref, watch } from 'vue';
  231. import { useUserTools } from '@/common/composeabe/UserTools';
  232. import { useOfficialAccount } from '../../composeabe/OfficialAccount';
  233. import { useAuthStore } from '@/store/auth';
  234. import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
  235. import { useVillageStore } from '@/store/village';
  236. import { useReqireLogin } from '@/common/composeabe/RequireLogin';
  237. import { useFollow } from '../composeabe/Follow';
  238. import { confirm, toast } from '@/components/utils/DialogAction';
  239. import { ArrayUtils } from '@imengyu/imengyu-utils';
  240. import { navTo } from '@/components/utils/PageAction';
  241. import HomeTitle from '@/common/components/parts/HomeTitle.vue';
  242. import Button from '@/components/basic/Button.vue';
  243. import Icon from '@/components/basic/Icon.vue';
  244. import Image from '@/components/basic/Image.vue';
  245. import Text from '@/components/basic/Text.vue';
  246. import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
  247. import Touchable from '@/components/feedback/Touchable.vue';
  248. import FlexCol from '@/components/layout/FlexCol.vue';
  249. import FlexRow from '@/components/layout/FlexRow.vue';
  250. import VillageMiniMap from '../../components/VillageMiniMap.vue';
  251. import Divider from '@/components/display/Divider.vue';
  252. import RoundTags from '@/common/components/parts/RoundTags.vue';
  253. import VillageUserRankList from '../../components/VillageUserRankList.vue';
  254. import BackgroundImageButton from '@/components/basic/BackgroundImageButton.vue';
  255. import ProvideVar from '@/components/theme/ProvideVar.vue';
  256. import Grid from '@/components/layout/grid/Grid.vue';
  257. import GridItem from '@/components/layout/grid/GridItem.vue';
  258. import LightVillageApi from '@/api/light/LightVillageApi';
  259. import Construction from '@/common/components/Construction.vue';
  260. import OfficialAccountPublishWrap from '@/common/components/OfficialAccountPublishWrap.vue';
  261. const authStore = useAuthStore();
  262. const { getIsVolunteer } = useUserTools();
  263. const { requireLogin } = useReqireLogin();
  264. const { onPublishSuccess } = useOfficialAccount();
  265. const { isFollowed, onFollow, onUnFollow } = useFollow();
  266. const villageStore = useVillageStore();
  267. const { getIsJoinedVillage } = useUserTools();
  268. const isJoined = ref(false);
  269. const villageInfoLoader = useSimpleDataLoader(async () => {
  270. const village = villageStore.currentVillage;
  271. if (village)
  272. isJoined.value = await getIsJoinedVillage(village.id);
  273. return {
  274. title: village?.name || '',
  275. desc: village?.desc || '',
  276. address: village?.address,
  277. applyCount: village?.applyCount || 0,
  278. sizeText: village?.sizeText || 0,
  279. levelText: village?.level.toString() || '',
  280. rankText: village?.rank.toString() || '',
  281. light: village?.light as number || 0,
  282. memberCount: village?.volunteerCount as number || 0,
  283. followerCount: village?.followCount as number || 0,
  284. images: village?.images || [],
  285. longitude: village?.longitude as number || 0,
  286. latitude: village?.latitude as number || 0,
  287. };
  288. });
  289. const emit = defineEmits<{
  290. (e: 'goTree'): void;
  291. }>();
  292. const rankActiveTag = ref('乡源果');
  293. const listActiveTag = ref('广场');
  294. const villageUserRankListLoader = useSimpleDataLoader(async () => {
  295. const res = (await LightVillageApi.getVolunteerRankList({
  296. num: 3,
  297. village_id: villageStore.currentVillage?.id ?? undefined,
  298. }))
  299. .map((item, i) => ({
  300. id: item.id,
  301. image: item.image ?? '',
  302. title: item.name,
  303. rank: i + 1,
  304. score: item.points,
  305. }));
  306. if (res.length >= 3) {
  307. //移动第一名到中间
  308. const first = res[0];
  309. ArrayUtils.removeAt(res, 0);
  310. ArrayUtils.insert(res, 1, first);
  311. }
  312. return res
  313. });
  314. watch(rankActiveTag, () => {
  315. villageUserRankListLoader.reload();
  316. });
  317. const recommendTagName = computed(() => {
  318. return '亮乡源·' + villageInfoLoader.content.value?.title + '·' + listActiveTag.value;
  319. });
  320. function handleGoJoin() {
  321. if (isJoined.value)
  322. return;
  323. navTo('/pages/home/light/submit', {
  324. villageId: villageStore.currentVillage?.id ?? undefined,
  325. unit: (villageStore.currentVillage?.city as string) + (villageStore.currentVillage?.district as string) + (villageStore.currentVillage?.township as string),
  326. regionId: villageStore.currentVillage?.region ?? undefined,
  327. });
  328. }
  329. function handleGoNew() {
  330. navTo('/pages/home/light/help/new');
  331. }
  332. function handleGoCollect(taskName?: string, taskFindCatalogName?: string) {
  333. if (!taskName) {
  334. navTo('/pages/dig/details', {
  335. villageId: villageStore.currentVillage?.id ?? undefined,
  336. });
  337. return;
  338. }
  339. navTo('/pages/dig/forms/list', {
  340. villageId: villageStore.currentVillage?.id ?? undefined,
  341. taskName: taskName,
  342. taskFindCatalogName: taskFindCatalogName,
  343. });
  344. }
  345. function handleGoPublish() {
  346. if (!authStore.isLogged) {
  347. confirm({
  348. title: '提示',
  349. content: '登录后就可以发布村社贴图了',
  350. confirmText: '去登录',
  351. }).then((res) => {
  352. if (res) {
  353. navTo('/pages/user/login');
  354. }
  355. });
  356. return;
  357. }
  358. navTo('/pages/chat/dependent/post/publish', {
  359. tag: listActiveTag.value,
  360. villageId: villageStore.currentVillage?.id ?? undefined,
  361. });
  362. }
  363. function handleGoPost(id: number) {
  364. navTo('/pages/home/post/detail', {
  365. id: id,
  366. });
  367. }
  368. watch(() => villageStore.currentVillage, () => {
  369. villageInfoLoader.reload();
  370. villageUserRankListLoader.reload();
  371. }, { immediate: true });
  372. </script>