快乐的梦鱼 3 settimane fa
parent
commit
04416ad8f6

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

@@ -4,7 +4,7 @@
       <Image :src="icon" :width="75" :height="46" mode="widthFix" />
     </slot>
     <Width :width="15" />
-    <Text :text="title" fontConfig="h4" :color="active ? '#55989a' : '#5f3f2c'" fontFamily="SongtiSCBlack" />
+    <Text :text="title" fontConfig="h4" :color="active ? 'text.titleLight' : 'text.title'" fontFamily="SongtiSCBlack" />
   </Touchable>
 </template>
 

+ 23 - 31
src/common/components/parts/RoundTags.vue

@@ -1,52 +1,44 @@
 <template>
-  <FlexRow wrap :gap="10">
+  <FlexRow wrap gap="gap.lg">
     <template
       v-for="(tag, k) in tags"
       :key="k" 
     >
-      <Tag 
-        v-if="tag"
-        :innerStyle="{
-          maxWidth: '160rpx',
-          overflow: 'hidden',
-          textOverflow: 'ellipsis',
-          whiteSpace: 'nowrap',
-        }"
-        :size="small ? 'small' : 'medium'"
-        :text="tag"
-        scheme="light"
-      />
+      <Touchable @click="emit('update:active', tag)">
+        <BackgroundBox 
+          v-if="tag"
+          :backgroundImage="tag === active ? 
+            'https://xy.wenlvti.net/app_static/images/village/TagActive.png' : 
+            'https://xy.wenlvti.net/app_static/images/village/TagNormal.png'"
+          :backgroundCutBorder="[10, 10, 10, 10]"
+          :backgroundCutBorderSize="[10, 10, 10, 10]"
+          :padding="[20, 30]"
+          :text="tag"
+        >
+          <Text :text="tag" fontConfig="contentText" :color="tag === active ? 'white' : 'text.content'" />
+        </BackgroundBox>
+      </Touchable>
     </template>
   </FlexRow>
 </template>
 
 <script setup lang="ts">
-import { computed, type PropType } from 'vue';
+import { type PropType } from 'vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
-import Tag from '@/components/display/Tag.vue';
+import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
+import Text from '@/components/basic/Text.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
 
 const props = defineProps({
   tags: {
     type: Array as PropType<string[]>,
     default: null,
   },
-  tags2: {
-    type: Array as PropType<string[]>,
-    default: null,
+  active: {
+    type: String,
+    default: '',
   },
-  small: {
-    type: Boolean,
-    default: false, 
-  }
 })
 
-const tagss = computed(() => {
-  if (props.tags && props.tags.length > 0) {
-    return props.tags;
-  } else if (props.tags2) {
-    return props.tags2;
-  } else {
-    return [];
-  }
-})
+const emit = defineEmits(['update:active']);
 </script>

+ 15 - 3
src/common/config/Theme.ts

@@ -13,6 +13,7 @@ export function configAppTheme() {
     theme.colorConfigs.text.title = '#62422f';
     theme.colorConfigs.text.content = '#7b5e49';
     theme.colorConfigs.text.second = '#95755a';
+    theme.colorConfigs.text.titleLight = '#55989a';
 
     theme.varOverrides.radius.smaller = '5rpx';
     theme.varOverrides.radius.small = '10rpx';
@@ -23,16 +24,27 @@ export function configAppTheme() {
     theme.textConfigs.primaryTitle = {
       color: 'text.title',
       fontFamily: 'SongtiSCBlack',
-      fontSize: '22px',
+      fontSize: '40rpx',
+      fontWeight: 'bold',
+    };
+    theme.textConfigs.lightTitle = {
+      color: 'text.titleLight',
+      fontFamily: 'SongtiSCBlack',
+      fontSize: '40rpx',
+      fontWeight: 'bold',
+    };
+    theme.textConfigs.importantTitle = {
+      color: 'text.title',
+      fontSize: '36rpx',
       fontWeight: 'bold',
     };
     theme.textConfigs.contentText = {
       color: 'text.content',
-      fontSize: '13px',
+      fontSize: '26rpx',
     };
     theme.textConfigs.secondText = {
       color: 'text.second',
-      fontSize: '12px',
+      fontSize: '24rpx',
     };
 
 

+ 16 - 19
src/components/basic/Button.vue

@@ -10,6 +10,7 @@
     v-bind="viewProps"
     :pressedColor="finalPressedColor"
     :touchable="touchable && !loading"
+    :gap="iconMargin"
     @state="(v) => state = v"
     @click="(e) => emit('click', e)"
   >
@@ -18,18 +19,12 @@
         v-if="loading"
         :size="selectStyleType(size, 'medium', FonstSizes)"
         :color="themeContext.resolveThemeColor(loadingColor) || currentStyle.color"
-        :innerStyle="{
-          marginRight: iconMargin ? '10rpx': undefined,
-        }"
       />
       <Icon
         v-else-if="icon"
         :icon="icon"
         :size="selectStyleType(size, 'medium', FonstSizes)"
         :color="currentStyle.color"
-        :innerStyle="{
-          marginRight: iconMargin ? '10rpx': undefined,
-        }"
         v-bind="iconProps"
       />
     </slot>
@@ -48,9 +43,6 @@
         :icon="rightIcon"
         :size="selectStyleType(size, 'medium', FonstSizes)"
         :color="currentStyle.color"
-        :innerStyle="{
-          marginLeft: iconMargin ? '10rpx' : undefined,
-        }"
         v-bind="rightIconProps"
       />
     </slot>
@@ -59,7 +51,7 @@
 
 <script setup lang="ts">
 import { computed, inject, ref } from 'vue';
-import { useTheme, type ViewStyle } from '../theme/ThemeDefine';
+import { propGetThemeVar, useTheme, type ViewStyle } from '../theme/ThemeDefine';
 import { configPadding, DynamicColor, DynamicSize, selectStyleType } from '../theme/ThemeTools';
 import type { IconProps } from './Icon.vue';
 import type { FlexProps } from '../layout/FlexView.vue';
@@ -122,6 +114,11 @@ export interface ButtonProp {
    */
   iconProps?: IconProps;
   /**
+   * 图标与文字之间的间距
+   * @default 10
+   */
+  iconMargin?: number|string;
+  /**
    * 右侧图标。支持 Icon 组件里的所有图标,也可以传入图标的图片 URL(http/https)。
    */
   rightIcon?: string,
@@ -212,14 +209,15 @@ const themeContext = useTheme();
 const props = withDefaults(defineProps<ButtonProp>(), {
   touchable: true,
   loading: false,
-  color: 'primary',
-  pressedColor: 'pressed.primary',
-  disabledColor: 'grey',
-  type: 'default',
-  size: 'medium',
-  block: false,
-  radius: 16,
-  shape: "round",
+  color: () => propGetThemeVar('ButtonColor', 'primary'),
+  pressedColor: () => propGetThemeVar('ButtonPressedColor', 'pressed.primary'),
+  disabledColor: () => propGetThemeVar('ButtonDisabledColor', 'grey'),
+  type: () => propGetThemeVar('ButtonType', 'default'),
+  size: () => propGetThemeVar('ButtonSize', 'medium'),
+  block: () => propGetThemeVar('ButtonBlock', false),
+  radius: () => propGetThemeVar('ButtonRadius', 16),
+  iconMargin: () => propGetThemeVar('ButtonIconMargin', 10),
+  shape: () => propGetThemeVar('ButtonShape', "round"),
 });
 
 const FonstSizes = computed(() => ({
@@ -423,7 +421,6 @@ const currentStyle = computed(() => {
 const state = ref('');
 
 const currentText =  computed(() => (props.loading ? (props.loadingText || props.text) : props.text));
-const iconMargin = computed(() => Boolean(currentText.value));
 const textColorFinal = computed(() => (
   state.value === 'active' ?
     themeContext.resolveThemeColor(props.pressedTextColor) :

+ 32 - 2
src/components/display/block/BackgroundBox.vue

@@ -56,6 +56,18 @@ export interface BackgroundBoxProps extends FlexProps {
    */
   color2?: string;
   /**
+   * 背景颜色(3)。
+   * 
+   * 格式:字符串格式或主题中定义的颜色预设。
+   */
+  color3?: string;
+  /**
+   * 背景颜色(2)位置。
+   * 
+   * 格式:50% 表示颜色(2)位置为50%,即颜色(2)在背景中间。
+   */
+  color2Position?: string;
+  /**
    * 圆角。
    */
   radius?: string | number;
@@ -135,8 +147,26 @@ const style = computed(() => {
   if (props.radius) {
     o.borderRadius = theme.resolveThemeSize(props.radius);
   }
-  if (props.color1 !== undefined && props.color2 !== undefined) {
-    o.background = `linear-gradient(${props.gradientAngle || 180}deg, ${theme.resolveThemeColor(props.color1)}, ${theme.resolveThemeColor(props.color2)})`;
+  if (props.color1 !== undefined && (props.color2 !== undefined || props.color3 !== undefined)) {
+    // 支持 color2Position, color3
+    if (props.color3 !== undefined) {
+      // 当有 color3 时,支持三色渐变
+      const colorStops = [
+        `${theme.resolveThemeColor(props.color1)} 0%`,
+        props.color2Position !== undefined
+          ? `${theme.resolveThemeColor(props.color2)} ${props.color2Position}`
+          : `${theme.resolveThemeColor(props.color2)} 50%`,
+        `${theme.resolveThemeColor(props.color3)} 100%`
+      ];
+      o.background = `linear-gradient(${props.gradientAngle || 180}deg, ${colorStops.join(', ')})`;
+    } else {
+      // 仅 color1/color2 时,支持 color2Position
+      if (props.color2Position !== undefined) {
+        o.background = `linear-gradient(${props.gradientAngle || 180}deg, ${theme.resolveThemeColor(props.color1)} 0%, ${theme.resolveThemeColor(props.color2)} ${props.color2Position})`;
+      } else {
+        o.background = `linear-gradient(${props.gradientAngle || 180}deg, ${theme.resolveThemeColor(props.color1)}, ${theme.resolveThemeColor(props.color2)})`;
+      }
+    }
 
   } else if (props.backgroundImage) {
     const b = props.backgroundCutBorder;

+ 81 - 2
src/pages/home/village/introd/card.vue

@@ -1,9 +1,12 @@
 <template>
-  <FlexCol>
+  <FlexCol gap="gap.lg">
 
+    <!-- 卡片背景 -->
     <BackgroundBox 
       color1="#eecaa0"
       color2="white"
+      color2Position="45%"
+      color3="white"
       radius="radius.large"
       direction="column"
       :padding="[35,30]"
@@ -14,7 +17,7 @@
       <FlexRow justify="space-between" width="100%">
         <FlexCol gap="gap.md">
           <Text :text="villageInfoLoader.content.value?.title" fontConfig="primaryTitle" />
-          <Text :text="villageInfoLoader.content.value?.address" fontConfig="secondText" />
+          <Text :text="villageInfoLoader.content.value?.address" fontConfig="contentText" />
         </FlexCol>
         <FlexCol gap="gap.md">
           <Button icon="https://xy.wenlvti.net/app_static/images/village/IconUser.png" radius="radius.larger" :padding="[10, 30]" backgroundColor="white">申请管理者</Button>
@@ -58,14 +61,77 @@
       </FlexRow>
 
       <!-- 地址 -->
+      <FlexRow align="center" gap="gap.sm">
+        <Icon name="https://xy.wenlvti.net/app_static/images/village/IconMap.png" size="fontSize.medium" />
+        <Text :text="villageInfoLoader.content.value?.address" fontConfig="contentText" />
+      </FlexRow>
+      <VillageMiniMap />
 
+      <FlexRow center gap="gap.lg">
+        <Button 
+          icon="https://xy.wenlvti.net/app_static/images/village/IconJoin.png" 
+          radius="radius.larger"
+          size="large"
+          :innerStyle="{ flexBasis: '20%' }"
+        >
+          关注
+        </Button>
+        <Button 
+          icon="https://xy.wenlvti.net/app_static/images/village/IconFollow.png" 
+          size="large"
+          radius="radius.larger"
+          :innerStyle="{ flexBasis: '20%' }"
+        >
+          加入
+        </Button>
+      </FlexRow>
 
+      <FlexRow justify="space-between" align="center">
+        <FlexRow center gap="gap.lg" flexBasis="50%">
+          <Text text="村社排名" fontConfig="contentText" />
+          <Text text="No." fontConfig="lightTitle" />
+          <Text :text="villageInfoLoader.content.value?.rankText" fontConfig="primaryTitle" />
+        </FlexRow>
+        <FlexRow center gap="gap.lg" flexBasis="50%">
+          <Text text="村社等级" fontConfig="contentText" />
+          <Text :text="villageInfoLoader.content.value?.levelText" fontConfig="primaryTitle" />
+        </FlexRow>
+      </FlexRow>
+
+      <FlexRow backgroundColor="background.tertiary" radius="radius.medium" :padding="[30, 20]">
+        <FlexCol center gap="gap.sm" flexBasis="25%">
+          <Text text="乡源光" fontConfig="secondText" />
+          <Text :text="villageInfoLoader.content.value?.light" fontConfig="importantTitle" />
+        </FlexCol>
+        <Divider type="vertical" />
+        <FlexCol center gap="gap.sm" flexBasis="25%">
+          <Text text="乡源人数" fontConfig="contentText" />
+          <Text :text="villageInfoLoader.content.value?.memberCount" fontConfig="importantTitle" />
+        </FlexCol>
+        <Divider type="vertical" />
+        <FlexCol center gap="gap.sm" flexBasis="25%">
+          <Text text="关注人数" fontConfig="contentText" />
+          <Text :text="villageInfoLoader.content.value?.followerCount" fontConfig="importantTitle" />
+        </FlexCol>
+        <Divider type="vertical" />
+        <Button :padding="0" type="text" size="small" textColor="text.title" text="新手上路" rightIcon="arrow-right" />
+      </FlexRow>
     </BackgroundBox>
 
+    <!-- 排行榜 -->
     <HomeTitle title="排行榜" />
+    <RoundTags v-model:active="rankActiveTag" :tags="['乡源果', '志愿者', '乡源光']" />
+    <VillageUserRankList />
 
+    <!-- 魅力乡源 -->
     <HomeTitle title="魅力乡源" />
 
+    <!-- 活力乡源 -->
+    <HomeTitle title="活力乡源" />
+
+    <!-- 文脉乡源 -->
+    <HomeTitle title="文脉乡源" />
+
 
   </FlexCol>
 
@@ -83,6 +149,12 @@ import BackgroundBox from '@/components/display/block/BackgroundBox.vue';
 import Touchable from '@/components/feedback/Touchable.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import FlexRow from '@/components/layout/FlexRow.vue';
+import VillageMiniMap from '../../components/VillageMiniMap.vue';
+import Divider from '@/components/display/Divider.vue';
+import VillageRankList from '../../components/VillageRankList.vue';
+import RoundTags from '@/common/components/parts/RoundTags.vue';
+import VillageUserRankList from '../../components/VillageUserRankList.vue';
+import { ref } from 'vue';
 
 const villageInfoLoader = useSimpleDataLoader(async () => {
   return {
@@ -90,6 +162,11 @@ const villageInfoLoader = useSimpleDataLoader(async () => {
     desc: '',
     address: '福建省厦门市同安区高浦社区',
     applyCount: 100,
+    levelText: '一级',
+    rankText: '1',
+    light: 0,
+    memberCount: 0,
+    followerCount: 0,
     images: [
       'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest1.jpg',
       'https://mn.wenlvti.net/app_static/minnan/images/test/ImageTest2.jpg',
@@ -99,4 +176,6 @@ const villageInfoLoader = useSimpleDataLoader(async () => {
     ],
   };
 });
+
+const rankActiveTag = ref('乡源果');
 </script>