Pārlūkot izejas kodu

🎨 探索文化地图页面

快乐的梦鱼 2 nedēļas atpakaļ
vecāks
revīzija
c506138330

+ 1 - 0
src/App.vue

@@ -33,6 +33,7 @@ configTheme(false, (theme, darkTheme) => {
   theme.colorConfigs.default.primary = '#d9492e';
   theme.colorConfigs.pressed.primary = '#882d1d';
   theme.colorConfigs.background.primary = '#ffcfc6';
+  theme.colorConfigs.background.page = '#f6f2e7';
   return [theme, darkTheme];
 });
 </script>

+ 1 - 1
src/api/introduction/CustomContent.ts

@@ -3,7 +3,7 @@ import { CommonContentApi } from '../CommonContent';
 export class CustomContentApi extends CommonContentApi {
 
   constructor() {
-    super(undefined, 4, "闽南文化-民间习俗");
+    super(undefined, 4, "闽南文化-民间习俗", 245);
   }
 }
 

+ 1 - 1
src/api/introduction/FeatureContent.ts

@@ -3,7 +3,7 @@ import { CommonContentApi } from '../CommonContent';
 export class FeatureContentApi extends CommonContentApi {
 
   constructor() {
-    super(undefined, 3, "闽南文化-艺术特色");
+    super(undefined, 3, "闽南文化-艺术特色", 243);
   }
 }
 

+ 35 - 0
src/components/display/Divider.vue

@@ -21,6 +21,10 @@
     </template>
     <view v-else class="line" :style="lineStyle">
       <view class="bar" :style="barStyle" />
+      <view v-if="centerDot" class="dot" :style="{
+        ...dotStyle,
+        ...centerDotStyle,
+      }" />
     </view>
   </view>
 </template>
@@ -63,6 +67,20 @@ export interface DividerProps {
    */
   type?: 'horizontal' | 'vertical';
   /**
+   * 是否在线中间添加一个点
+   * @default false
+   */
+  centerDot?: boolean;
+  /**
+   * 分割线中间点的大小
+   * @default 26
+   */
+  centerDotSize?: number;
+  /**
+   * 分割线中间点的样式
+   */
+  centerDotStyle?: object,
+  /**
    * 分割线上面的文字(仅水平状态有效)
    */
   text?: string,
@@ -89,6 +107,7 @@ const props = withDefaults(defineProps<DividerProps>(), {
   backgroundColor: () => propGetThemeVar('DividerBackgroundColor', undefined)!,
   width: () => propGetThemeVar('DividerWidth', 2),
   size: () => propGetThemeVar('DividerSize', 36),
+  centerDotSize: () => propGetThemeVar('DividerCenterDotSize', 26),
   type: 'horizontal',
   orientation: 'center',
 });
@@ -98,6 +117,13 @@ const outStyle = computed(() => {
     backgroundColor: theme.resolveThemeColor(props.backgroundColor),
   }
 })
+const dotStyle = computed(() => {
+  return {
+    width: theme.resolveThemeSize(props.centerDotSize),
+    height: theme.resolveThemeSize(props.centerDotSize),
+    backgroundColor: theme.resolveThemeColor(props.color),
+  }
+})
 const lineStyle = computed(() => {
   return {
     fontSize: '22rpx',
@@ -125,6 +151,14 @@ const barStyle = computed(() => {
   align-items: center;
   align-self: stretch;
 
+  .dot {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    border-radius: 50%;
+  }
+  
   .line {
     position: relative;
     display: flex;
@@ -152,6 +186,7 @@ const barStyle = computed(() => {
 
   &.vertical {
     flex-direction: column;
+    height: 100%;
   }
   &.horizontal {
     flex-direction: row;

+ 71 - 9
src/components/display/TextEllipsis.vue

@@ -1,15 +1,57 @@
 <script setup lang="ts">
-import { computed, ref } from 'vue';
+import { computed, popScopeId, ref } from 'vue';
 import type { TextProps } from '../basic/Text.vue';
 import Text from '../basic/Text.vue';
 import FlexCol from '../layout/FlexCol.vue';
+import BackgroundBox from './block/BackgroundBox.vue';
 
 export interface TextEllipsisProps extends TextProps {
+  /**
+   * 显示的行数
+   * @default 1
+   */
   lines?: number;
+  /**
+   * 是否可展开
+   * @default true
+   */
   expandable?: boolean;
+  /**
+   * 是否默认展开
+   * @default false
+   */
   startOpen?: boolean;
+  /**
+   * 展开按钮文字
+   * @default '展开'
+   */
   openText?: string;
+  /**
+   * 收起按钮文字
+   * @default '收起'
+   */ 
   closeText?: string;
+  /**
+   * 是否显示遮罩层,当定义了该属性时,折叠时将会显示一个遮罩层。
+   * @default false
+   */
+  showMask?: boolean;
+  /**
+   * 遮罩层颜色
+   * @default white
+   */
+  maskColor?: string;
+  /**
+   * 折叠时的高度
+   * @default 140
+   */
+  collapsedHeight?: number;
+  /**
+   * 是否自定义内容,当定义了该属性时,可以通过slot自定义内容渲染,
+   * 折叠时将会设置高度为collapsedHeight。
+   * @default false
+   */
+  customContent?: boolean;
 }
 
 
@@ -17,7 +59,11 @@ const emit = defineEmits(['expand', 'collapse']);
 const props = withDefaults(defineProps<TextEllipsisProps>(), {
   lines: 1,
   startOpen: false,
-  expandable: false,
+  expandable: true,
+  customContent: false,
+  showMask: false,
+  maskColor: 'white',
+  collapsedHeight: 140,
   openText: '展开',
   closeText: '收起',
 });
@@ -43,16 +89,32 @@ defineOptions({
 </script>
 
 <template>
-  <FlexCol>
-    <Text 
-      :ellipsis="true" 
-      :lines="currentLines"
-      v-bind="$attrs"
+  <FlexCol position="relative">
+    <FlexCol 
+      :height="customContent && !open ? collapsedHeight : undefined"
+      overflow="hidden"
     >
       <slot>
-        {{ text }}
+        <Text 
+          :ellipsis="true" 
+          :lines="currentLines"
+          v-bind="$attrs"
+          :text="text"
+        />
       </slot>
-    </Text>
+      <BackgroundBox 
+        v-if="!open && (showMask || customContent)"
+        :height="collapsedHeight"
+        color1="transparent"
+        :color2="maskColor"
+        :innerStyle="{ 
+          position: 'absolute',
+          bottom: '36rpx',
+          left: 0,
+          right: 0,
+        }"
+      />
+    </FlexCol>
     <slot v-if="expandable" name="button" :onClick="handleClick">
       <Text 
         innerClass="nana-text-ellipsis-expand"

+ 7 - 0
src/pages.json

@@ -34,6 +34,13 @@
       }
     },
     {
+      "path": "pages/introduction/map",
+      "style": {
+        "navigationBarTitleText": "探索文化地图",
+        "enablePullDownRefresh": true
+      }
+    },
+    {
       "path": "pages/introduction/character/list",
       "style": {
         "navigationBarTitleText": "历史人物列表",

+ 12 - 0
src/pages/article/common/CommonContent.ts

@@ -8,6 +8,18 @@ export interface IHomePageMiniCommonListGoMoreAndGoDetail {
   goList: () => void; 
 }
 
+export function navHomePageMiniCommonDetailGo(p: {
+  id: number,
+  title?: string,
+  mainBodyColumnId?: string|number|number[],
+  modelId?: number,
+}) {
+  navTo('/pages/article/details', {
+    mainBodyColumnId: p.mainBodyColumnId,
+    modelId: p.modelId,
+    id: p.id,
+  }) 
+}
 export function navHomePageMiniCommonListGo(p: {
   title?: string,
   mainBodyColumnId?: string|number|number[],

+ 1 - 1
src/pages/home.vue

@@ -31,7 +31,7 @@
           icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconMap.png"
           bg="https://mncdn.wenlvti.net/app_static/minnan/images/home/ButtonMapBg.png"
           large
-          @click="navTo('inhert/map/index', { tab: 0 })"
+          @click="navTo('/pages/introduction/map')"
         />
 
         <view class="position-relative d-flex flex-row flex-wrap justify-between mt-25 row-gap-sss">

+ 188 - 0
src/pages/introduction/map.vue

@@ -0,0 +1,188 @@
+<template>
+  <CommonRoot>
+    <FlexCol :padding="30" :gap="15" innerClass="bg-base">
+      <!-- 分类 -->
+      <template v-for="category in categoryDatas" :key="category.title">
+        <HomeTitle 
+          :title="category.title"
+          showMore 
+          moreText="更多"
+          @clickMore="category.morePage" 
+        />
+        <SimplePageContentLoader :loader="category.data" >
+          <FlexCol>
+            <Box2LineImageRightShadow
+              v-for="(item, i) in category.data.content.value"
+              titleColor="title-text"
+              fixSize
+              :key="i"
+              :title="item.title"
+              :desc="item.desc"
+              :image="item.image"
+              :tags="item.bottomTags"
+              @click="category.detailPage(item.id)"
+            />
+          </FlexCol>
+        </SimplePageContentLoader>  
+      </template>
+      <!-- 闽南文化概况 -->
+      <HomeTitle title="闽南文化概况(厦门市)" />
+      <SimplePageContentLoader :loader="introdData" >
+        <FlexCol>
+          <FlexRow
+            v-for="(item, i) in introdData .content.value"
+            :key="i"
+            position="relative"
+            align-items="stretch"
+          >
+            <Divider 
+              centerDot
+              type="vertical" 
+              color="primary" 
+            />
+            <Width :width="15" />
+            <Box2LineImageRightShadow
+              titleColor="title-text"
+              fixSize
+              :title="item.title"
+              :desc="item.desc"
+              :showImage="false"
+            >
+            <template #desc>
+              <TextEllipsis customContent maskColor="background.page">
+                <Parse :content="item.desc" />
+              </TextEllipsis>
+            </template>
+            </Box2LineImageRightShadow>
+          </FlexRow>
+        </FlexCol>
+      </SimplePageContentLoader>  
+    </FlexCol>
+  </CommonRoot>
+</template>
+
+<script setup lang="ts">
+import CommonRoot from '@/components/dialog/CommonRoot.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import HomeTitle from '../parts/HomeTitle.vue';
+import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
+import { navTo } from '@/components/utils/PageAction';
+import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import { GetColumListParams, GetContentListParams } from '@/api/CommonContent';
+import { navHomePageMiniCommonDetailGo, navHomePageMiniCommonListGo } from '../article/common/CommonContent';
+import CharacterContent from '@/api/introduction/CharacterContent';
+import Box2LineImageRightShadow from '../parts/Box2LineImageRightShadow.vue';
+import LanguageContent from '@/api/introduction/LanguageContent';
+import CustomContent from '@/api/introduction/CustomContent';
+import FeatureContent from '@/api/introduction/FeatureContent';
+import BulidingContent from '@/api/introduction/BulidingContent';
+import VictualsContent from '@/api/introduction/VictualsContent';
+import SeaContent from '@/api/introduction/SeaContent';
+import PolicyContent from '@/api/introduction/PolicyContent';
+import HistoryContent from '@/api/introduction/HistoryContent';
+import IndexContent from '@/api/introduction/IndexContent';
+import Divider from '@/components/display/Divider.vue';
+import Width from '@/components/layout/space/Width.vue';
+import Parse from '@/components/display/parse/Parse.vue';
+import TextEllipsis from '@/components/display/TextEllipsis.vue';
+
+const categoryDefine = [
+  {
+    title: '文化常识和闽南历史地理背景',
+    content: HistoryContent,
+  },
+  {
+    title: '历史人物',
+    content: CharacterContent,
+    detailPage: '/pages/introduction/character/details',
+    morePage: '/pages/introduction/character/list',
+  },
+  {
+    title: '语言文化',
+    content: LanguageContent,
+  },
+  {
+    title: '民间习俗',
+    content: CustomContent,
+    morePage: '/pages/introduction/custom/list',
+  },
+  {
+    title: '艺术特色',
+    content: FeatureContent,
+  },
+  {
+    title: '建筑文化',
+    content: BulidingContent,
+  },
+  {
+    title: '饮食文化',
+    content: VictualsContent,
+  },
+  {
+    title: '海洋文化',
+    content: SeaContent,
+  },
+  {
+    title: '政策法规',
+    content: PolicyContent,
+  },
+]
+const categoryDatas = categoryDefine.map(item => ({
+  ...item,
+  detailPage: (id: number) => {
+    if (item.detailPage) {
+      navTo(item.detailPage, { id });
+    } else {
+      navHomePageMiniCommonDetailGo({
+        id,
+        mainBodyColumnId: item.content.mainBodyColumnId,
+        modelId: item.content.modelId,
+      })
+    }
+  },
+  morePage: () => {
+    if (item.morePage) {
+      navTo(item.morePage, {});
+    } else {
+      navHomePageMiniCommonListGo({
+        title: item.title,
+        mainBodyColumnId: item.content.mainBodyColumnId,
+        modelId: item.content.modelId,
+        detailsPage: item.detailPage,
+      })
+    }
+  },
+  data: useSimpleDataLoader(async () => 
+    (await item.content.getContentList(new GetContentListParams(), 1, 3)).list.map(p => ({
+      id: p.id,
+      title: p.title, 
+      desc: p.desc, 
+      image: p.thumbnail || p.image,
+      bottomTags: p.keywords as string[],
+    }))
+  ),
+}));
+const introdData = useSimpleDataLoader(async () => {
+  let i = 0;
+  const promises = [];
+  for (const item of categoryDefine) {
+    promises.push(IndexContent.getColumList(new GetColumListParams().setSelfValues({
+      modelId: item.content.modelId,
+      mainBodyColumnId: item.content.mainBodyColumnId,
+    })))
+  }
+  const result = await Promise.all(promises);
+  return result.map((data, i) => {
+    const res = data.list[0];
+    const item = categoryDefine[i];
+    return {
+      title: item.title,
+      subtitle: '',
+      date: '',
+      desc: res.overview as string,
+      index: i,
+    }
+  });
+});
+</script>

+ 15 - 1
src/pages/parts/Box2LineImageRightShadow.vue

@@ -11,6 +11,7 @@
   >
     <view class="d-flex flex-row w-100">
       <Image 
+        v-if="showImage"
         innerClass="flex-shrink-0"
         :width="wideImage ? '250' : '150'"
         :height="150"
@@ -27,7 +28,9 @@
             titleBox ? 'border-all-text' : '',
           ]">{{ title }}</text>
         </view> 
-        <text :class="'size-s color-second text-lines-3 mt-2' + (tags ? 'text-lines-2' : '')">{{ desc }}</text>
+        <slot name="desc">
+          <text :class="'size-s color-second text-lines-3 mt-2' + (tags ? 'text-lines-2' : '')">{{ desc }}</text>
+        </slot>
         <RoundTags v-if="tags" :tags="tags" small />
       </view>
     </view>
@@ -54,6 +57,10 @@ defineProps({
     type: Boolean,
     default: false,
   },
+  showImage: {
+    type: Boolean,
+    default: true,
+  },
   tags: {
     type: Array as PropType<string[]>,
     default: null
@@ -67,4 +74,11 @@ defineProps({
     default: false,
   }
 })
+
+defineOptions({
+  options: {
+    inheritAttrs: false,
+    virtualHost: true,
+  }
+})
 </script>

+ 5 - 1
src/pages/parts/HomeTitle.vue

@@ -8,7 +8,7 @@
   >
     <text>{{ title }}</text>
     <text v-if="showMore" class="more" @click="$emit('clickMore')">
-      查看全部
+      {{moreText}}
       <text class="iconfont icon-arrow-right ml-2" />
     </text>
   </view>
@@ -20,6 +20,10 @@ defineProps({
     type: String,
     default: '',
   },
+  moreText: {
+    type: String,
+    default: '查看全部',
+  },
   inWing: {
     type: Boolean,
     default: false,