Quellcode durchsuchen

📦 换肤功能基础架构

快乐的梦鱼 vor 3 Wochen
Ursprung
Commit
8938500db8

BIN
server/images/village/skin/1/A.jpg


BIN
server/images/village/skin/DefaultA.jpg


BIN
server/images/village/skin/DefaultB.jpg


+ 54 - 0
src/common/components/parts/IndexRoundSimpleItem.vue

@@ -0,0 +1,54 @@
+<template>
+  <Touchable
+    :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="emit('click', item)"
+  >
+    <Image :src="item.image" :width="400" :height="imageHeight" mode="aspectFill" />
+    <FlexCol position="absolute" :inset="{ b: -1, l: -1, r: -1 }" backgroundColor="background.primary" padding="padding.md" gap="gap.md">
+      <FlexRow justify="space-between" align="center">
+        <Text fontConfig="contentSpeicalText" fontFamily="SongtiSCBlack" :lines="1" :wrap="false">{{ item.title }}</Text>
+        <slot name="titleRight" />
+      </FlexRow>
+      <Text fontSize="fontSize.sm" :text="item.desc" :lines="1" :maxWidth="400" />
+      <slot />
+    </FlexCol>
+  </Touchable>
+</template>
+
+<script setup lang="ts">
+import Image from '@/components/basic/Image.vue';
+import Text from '@/components/basic/Text.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+
+const props = withDefaults(defineProps<{
+  item: {
+    id?: string|number;
+    title?: string;
+    desc?: string;
+    image?: string;
+  },
+  imageHeight?: number;
+}>(), {
+  imageHeight: 270,
+})
+
+const emit = defineEmits(['click'])
+
+defineOptions({
+  options: {
+    virtualHost: true,
+  }
+})
+</script>

+ 21 - 0
src/pages.json

@@ -329,6 +329,27 @@
       }
     },
     {
+      "path": "pages/home/village/introd/config",
+      "style": {
+        "navigationBarTitleText": "主页换肤",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/home/village/introd/skin/skin",
+      "style": {
+        "navigationBarTitleText": "皮肤中心",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/home/village/introd/skin/details",
+      "style": {
+        "navigationBarTitleText": "皮肤详情",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/home/discover/details",
       "style": {
         "navigationBarTitleText": "发现详情页",

+ 5 - 0
src/pages/home/components/LightMap.vue

@@ -280,6 +280,11 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
     && p.longitude > -180 && p.longitude < 180
     && p.latitude > -90 && p.latitude < 90
   );
+
+  //如果小地图则只显示200个
+  if (props.small && list.length > 200)
+    list.splice(200, list.length - 200);
+
   asyncAddMarkers(list);
 
   if (res.length == 1) {

+ 4 - 18
src/pages/home/index.vue

@@ -74,27 +74,12 @@
       <HomeTitle title="今日推荐" showMore :showTopMargin="false" @moreClicked="handleGoRecommendMore" />
       <SimplePageContentLoader :loader="recommendLoader">
         <FlexRow gap="gap.lg" align="stretch" justify="space-between">
-          <Touchable 
+          <IndexRoundSimpleItem
             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%"
+            :item="item"
             @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>
       </SimplePageContentLoader>
 
@@ -351,6 +336,7 @@ import Touchable from '@/components/feedback/Touchable.vue';
 import Icon from '@/components/basic/Icon.vue';
 import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
 import { useTaskActions } from './village/composeabe/TaskActions';
+import IndexRoundSimpleItem from '@/common/components/parts/IndexRoundSimpleItem.vue';
 
 const props = defineProps({
   currentScrollTop: {

+ 7 - 1
src/pages/home/village/introd/card.vue

@@ -614,6 +614,11 @@ function handleGoGallery() {
     maxCount: villageStore.currentVillage?.imageLimit || 3,
   });
 }
+function handleGoSkin() {
+  navTo('/pages/home/village/introd/config', { 
+    villageId: villageStore.currentVillage?.id ?? 0 ,
+  });
+}
 
 const staffInfoRef = ref<InstanceType<typeof StaffInfoDialog>>();
 
@@ -685,6 +690,7 @@ const changeNickRef = ref<InstanceType<typeof ChangeNickDialog>>();
 
 const pageScriptGlobal = reactive({
   handleGoGallery,
+  handleGoSkin,
   handleGoEdit,
   handleGoDigManage,
   handleGoCollect,
@@ -770,7 +776,7 @@ const page : DynamicXPage = {
                     text: "主页换肤",
                   },
                   events: {
-                    click: 'globalContext.handleGoDigManage()',
+                    click: 'globalContext.handleGoSkin()',
                   },
                 },
               ],

+ 0 - 35
src/pages/home/village/introd/card/dynamic/BlockGames.vue

@@ -1,35 +0,0 @@
-<template>
-  <!-- 活力乡源 -->
-  <HomeTitle :title="node.props?.title" />
-  <ProvideVar :vars="{
-    GridItemIconSize: 90,
-    GridItemBackgroundColor: 'transparent',
-    GridItemPaddingHorizontal: 0,
-    GridItemPaddingVertical: 8,
-  }">
-    <Grid :borderGrid="false" :mainAxisCount="4">
-      <GridItem
-        v-for="item in node.props?.items" 
-        :key="item.title"
-        :title="item.title"
-        :icon="item.icon"
-        touchable 
-        @click="() => context.getScriptEngine().execute(item.handler, node.getNodeScriptContext?.())" 
-      />
-    </Grid>
-  </ProvideVar>
-</template>
-
-<script lang="ts" setup>
-import HomeTitle from '@/common/components/parts/HomeTitle.vue';
-import type { DynamicXNode, DynamicXNodeContext } from '@/components/dynamic/DynamicX';
-import Grid from '@/components/layout/grid/Grid.vue';
-import GridItem from '@/components/layout/grid/GridItem.vue';
-import ProvideVar from '@/components/theme/ProvideVar.vue';
-
-const props = withDefaults(defineProps<{
-  node: DynamicXNode;
-  context: DynamicXNodeContext;
-}>(), {
-})
-</script>

+ 8 - 0
src/pages/home/village/introd/card/index.vue

@@ -0,0 +1,8 @@
+<template>
+
+</template>
+
+<script setup lang="ts">
+
+
+</script>

+ 88 - 0
src/pages/home/village/introd/config.vue

@@ -0,0 +1,88 @@
+<template>
+  <CommonTopBanner title="主页换肤">
+    <FlexCol padding="padding.md" gap="gap.lg">
+            
+      <HomeTitle title="热门皮肤" showMore :showTopMargin="false" @moreClicked="handleGoRecommendMore" />
+      <SimplePageContentLoader :loader="recommendLoader">
+        <FlexRow gap="gap.lg" align="stretch" justify="space-between">
+          <IndexRoundSimpleItem
+            v-for="item in recommendLoader.content.value"
+            :key="item.id"
+            :item="item"
+            :imageHeight="400"
+            @click="handleGoRecommendDetails(item)"
+          />
+        </FlexRow>
+      </SimplePageContentLoader>
+
+      <HomeTitle title="主页配置" />
+      <Touchable 
+        direction="column" 
+        center
+        padding="padding.lg"
+        radius="radius.md"
+        backgroundColor="background.tertiary"
+        @click="handleGoConfigDetails"
+      >
+        <Icon icon="https://xy.wenlvti.net/app_static/images/tabs/TabIconDigActive.png" :size="120" />
+        <Text>管理主页栏目显示与顺序</Text>
+      </Touchable>
+
+      <Touchable 
+        direction="column" 
+        center
+        padding="padding.lg"
+        radius="radius.md"
+        backgroundColor="background.tertiary"
+        @click="handleGoConfigDetails"
+      >
+        <Icon icon="https://xy.wenlvti.net/app_static/images/tabs/TabIconMineActive.png" :size="120" />
+        <Text>管理贴图话题列表</Text>
+      </Touchable>
+
+    </FlexCol>
+  </CommonTopBanner>
+</template>
+
+<script setup lang="ts">
+import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+import HomeTitle from '@/common/components/parts/HomeTitle.vue';
+import IndexRoundSimpleItem from '@/common/components/parts/IndexRoundSimpleItem.vue';
+import Icon from '@/components/basic/Icon.vue';
+import Image from '@/components/basic/Image.vue';
+import Text from '@/components/basic/Text.vue';
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import Touchable from '@/components/feedback/Touchable.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
+import { navTo } from '@/components/utils/PageAction';
+
+const recommendLoader = useSimpleDataLoader(async () => {
+  return [
+    {
+      id: 1,
+      title: '默认皮肤',
+      desc: '默认的村社名片',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+    },
+    {
+      id: 2,
+      title: '古风主题',
+      desc: '古风凸显村社的特色',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/1/A.jpg',
+    },
+  ];
+});
+
+const handleGoRecommendDetails = (item: { id: number }) => {
+  navTo('/pages/home/village/introd/skin/details', { id: item.id })
+}
+const handleGoRecommendMore = () => {
+  navTo('/pages/home/village/introd/skin/skin');
+}
+const handleGoConfigDetails = () => {
+  navTo('/pages/home/village/introd/config/details');
+}
+
+</script>

+ 79 - 0
src/pages/home/village/introd/skin/details.vue

@@ -0,0 +1,79 @@
+<template>
+  <CommonTopBanner title="皮肤详情">
+    <SimplePageContentLoader :loader="skinLoader">
+      <FlexCol v-if="skin" padding="padding.md">
+        
+        <scroll-view scroll-x>
+          <FlexRow :width="IMAGE_WIDTH * skin.images.length" :gap="30" align="stretch">
+            <Image 
+              v-for="item in skin.images" 
+              :key="item" 
+              :src="item" 
+              :width="IMAGE_WIDTH"
+              radius="radius.lg"
+              mode="widthFix"
+              :innerStyle="{
+                border: '1px solid #944329',
+              }"
+            />
+          </FlexRow>
+        </scroll-view>
+
+        <FlexCol padding="padding.md">
+          <FlexRow justify="space-between" align="center">
+            <Text fontConfig="contentSpeicalText" fontFamily="SongtiSCBlack" :lines="1" :wrap="false">{{ skin.title }}</Text>
+            <FlexCol backgroundColor="text.gold" radius="10" padding="10">
+              <Text v-if="skin.price > 0" fontSize="fontSize.sm" fontWeight="bold" color="white" :text="`${skin.price}乡源果`" />
+              <Text v-else fontSize="fontSize.sm" color="white" text="免费" />
+            </FlexCol>
+          </FlexRow>
+          <Text fontSize="fontSize.sm" :text="skin.desc" :lines="1" :maxWidth="400" />
+        </FlexCol>
+
+        <FlexCol padding="padding.md">
+          <PrimaryButton v-if="!skin.isBought" radius="radius.xl" text="购买" @click="handleBuy" />
+          <PrimaryButton v-else radius="radius.xl" text="去使用" @click="handleUse" />
+        </FlexCol>
+      </FlexCol>
+
+    </SimplePageContentLoader>
+  </CommonTopBanner>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue';
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Image from '@/components/basic/Image.vue';
+import Text from '@/components/basic/Text.vue';
+import PrimaryButton from '@/common/components/PrimaryButton.vue';
+
+const IMAGE_WIDTH = 330;
+
+const skinLoader = useSimpleDataLoader(async () => {
+  return {
+    id: 1,
+    title: '默认皮肤',
+    desc: '默认的村社名片',
+    image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+    images: [
+      'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+      'https://xy.wenlvti.net/app_static/images/village/skin/DefaultB.jpg',
+    ],
+    price: 0,
+    isBought: false,
+  };
+})
+const skin = computed(() => skinLoader.content.value);
+
+function handleBuy() {
+  //TODO
+}
+
+function handleUse() {
+  //TODO
+}
+</script>

+ 116 - 0
src/pages/home/village/introd/skin/skin.vue

@@ -0,0 +1,116 @@
+<template>
+  <CommonTopBanner title="皮肤中心">
+    <FlexCol padding="padding.md">
+      <SimplePageContentLoader :loader="skinLoader">
+        <FlexRow gap="gap.lg" align="stretch" justify="space-between" wrap>
+          <IndexRoundSimpleItem
+            v-for="item in skinLoader.content.value"
+            :key="item.id"
+            :item="item"
+            :imageHeight="400"
+            @click="handleGoSkinDetails(item)"
+          >
+            <template #titleRight>
+              <FlexCol backgroundColor="text.gold" radius="10" padding="10">
+                <Text v-if="item.price > 0" fontSize="fontSize.sm" fontWeight="bold" color="white" :text="`${item.price}乡源果`" />
+                <Text v-else fontSize="fontSize.sm" color="white" text="免费" />
+              </FlexCol>
+            </template>
+          </IndexRoundSimpleItem>
+        </FlexRow>
+      </SimplePageContentLoader>
+    </FlexCol>
+  </CommonTopBanner>
+</template>
+
+<script setup lang="ts">
+import { useSimpleDataLoader } from '@/components/composeabe/loader/SimpleDataLoader';
+import { navTo } from '@/components/utils/PageAction';
+import CommonTopBanner from '@/common/components/CommonTopBanner.vue';
+import IndexRoundSimpleItem from '@/common/components/parts/IndexRoundSimpleItem.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import SimplePageContentLoader from '@/components/loader/SimplePageContentLoader.vue';
+import Text from '@/components/basic/Text.vue';
+
+const skinLoader = useSimpleDataLoader(async () => {
+  return [
+    {
+      id: 1,
+      title: '默认皮肤',
+      desc: '默认的村社名片',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+      price: 0,
+    },
+    {
+      id: 2,
+      title: '古风主题',
+      desc: '古风凸显村社的特色',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/1/A.jpg',
+      price: 99,
+    },
+    {
+      id: 3,
+      title: '默认皮肤',
+      desc: '默认的村社名片',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+      price: 199,
+    },
+    {
+      id: 4,
+      title: '古风主题',
+      desc: '古风凸显村社的特色',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/1/A.jpg',
+      price: 189,
+    },
+    {
+      id: 5,
+      title: '默认皮肤',
+      desc: '默认的村社名片',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+      price: 199,
+    },
+    {
+      id: 6,
+      title: '古风主题',
+      desc: '古风凸显村社的特色',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/1/A.jpg',
+      price: 189,
+    },
+    {
+      id: 7,
+      title: '默认皮肤',
+      desc: '默认的村社名片',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+      price: 199,
+    },
+    {
+      id: 8,
+      title: '古风主题',
+      desc: '古风凸显村社的特色',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/1/A.jpg',
+      price: 189,
+    },
+    {
+      id: 9,
+      title: '默认皮肤',
+      desc: '默认的村社名片',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/DefaultA.jpg',
+      price: 199,
+    },
+    {
+      id: 10,
+      title: '古风主题',
+      desc: '古风凸显村社的特色',
+      image: 'https://xy.wenlvti.net/app_static/images/village/skin/1/A.jpg',
+      price: 189,
+    },
+  ];
+});
+
+const handleGoSkinDetails = (item: { id: number }) => {
+  navTo('/pages/home/village/introd/skin/details', { id: item.id })
+}
+
+
+</script>