Преглед изворни кода

🎨 按要求修改问题

快乐的梦鱼 пре 1 недеља
родитељ
комит
291879de38

+ 3 - 2
src/api/CommonContent.ts

@@ -285,6 +285,7 @@ export class GetContentDetailItem extends DataModel<GetContentDetailItem> {
   images = [] as string[];
   audio = '';
   video = '';
+  archives = '';
   publishVideo?: string;
   desc = '';
   flag ?: string[];
@@ -527,8 +528,8 @@ export class CommonContentApi extends AppServerRequestModule<DataModel> {
         data: data,
         headers: {},
       }
-      if (this.config.requestInceptor) {
-        const { newReq, newUrl } = this.config.requestInceptor(url, req);
+      if (this.config.requestInterceptor) {
+        const { newReq, newUrl } = this.config.requestInterceptor(url, req);
         url = newUrl;
         data = newReq;
       }

+ 118 - 0
src/pages/article/common/CommonCategoryHome.vue

@@ -0,0 +1,118 @@
+<template>
+  <FlexCol width="100%">
+    <!-- 分类 -->
+    <template v-for="category in categoryDatas" :key="category.title">
+      <HomeTitle 
+        :title="category.title"
+        showMore 
+        moreText="更多"
+        @clickMore="category.morePage" 
+      />
+      <SimplePageContentLoader :loader="category.data" >
+        <FlexCol>
+          <template v-if="category.type !== 'article'">
+            <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 as string[])"
+              @click="category.detailPage(item.id)"
+            />
+          </template>
+          <template v-else>
+            <Box2LineRightShadow
+              v-for="(item, i) in category.data.content.value" 
+              :key="i" 
+              :title="item.title"
+              :desc="item.desc"
+              :tags="(item.bottomTags as string[])"
+              @click="category.detailPage(item.id)"
+            />
+          </template>
+        </FlexCol>
+      </SimplePageContentLoader>  
+    </template>
+  </FlexCol>
+</template>
+
+<script setup lang="ts">;
+import HomeTitle from '@/pages/parts/HomeTitle.vue';
+import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
+import Box2LineImageRightShadow from '@/pages/parts/Box2LineImageRightShadow.vue';
+import Box2LineRightShadow from '@/pages/parts/Box2LineRightShadow.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import type { PropType } from 'vue';
+import { CommonContentApi, GetContentListParams } from '@/api/CommonContent';
+import { navHomePageMiniCommonDetailGo, navHomePageMiniCommonListGo, resolveCommonContentFormData, type IHomeCommonCategoryBlock } from './CommonContent';
+import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import { navTo } from '@/components/utils/PageAction';
+import { DateUtils } from '@imengyu/imengyu-utils';
+
+export interface CategoryDefine {
+  title: string;
+  content: CommonContentApi|IHomeCommonCategoryBlock;
+  type?: 'article'|''|undefined;
+  detailPage?: string;
+  morePage?: string;
+  noFrom?: boolean;
+}
+
+const props = defineProps({
+  categoryDefine: {
+    type: Array as PropType<CategoryDefine[]>,
+    default: () => [],
+  }
+});
+
+const categoryDatas = props.categoryDefine.map(item => ({
+  ...item,
+  detailPage: (id: number) => {
+    if (item.content instanceof CommonContentApi) {
+      if (item.detailPage) {
+        navTo(item.detailPage, { id });
+      } else {
+        navHomePageMiniCommonDetailGo({
+          id,
+          mainBodyColumnId: item.content.mainBodyColumnId,
+          modelId: item.content.modelId,
+        })
+      }
+    } else {
+      item.content.goDetail(id);
+    }
+  },
+  morePage: () => {
+    if (item.content instanceof CommonContentApi) {
+      if (item.morePage) {
+        navTo(item.morePage, {});
+      } else {
+        navHomePageMiniCommonListGo({
+          title: item.title,
+          mainBodyColumnId: item.content.mainBodyColumnId,
+          modelId: item.content.modelId,
+          detailsPage: item.detailPage,
+        })
+      }
+    } else {
+      item.content.goList();
+    }
+  },
+  data: item.content instanceof CommonContentApi ? useSimpleDataLoader(async () => {
+    let res = (await (item.content as CommonContentApi)
+      .getContentList(new GetContentListParams(), 1, 3))
+      .list;
+    if (!item.noFrom)
+      res = resolveCommonContentFormData(res);
+    else
+      res.forEach(p => {
+        if (!p.desc)
+          p.desc = DateUtils.formatDate(p.publishAt, 'YYYY-MM-dd');
+      })
+    return res;
+  }) : item.content.loader,
+}));
+</script>

+ 21 - 7
src/pages/article/common/CommonContent.ts

@@ -1,8 +1,9 @@
 import CommonContent, { GetContentListItem, GetContentListParams } from "@/api/CommonContent";
 import { useSimpleDataLoader, type ISimpleDataLoader } from "@/common/composeabe/SimpleDataLoader";
 import { navTo } from "@/components/utils/PageAction";
+import { DateUtils } from "@imengyu/imengyu-utils";
 
-export interface IHomePageMiniCommonListGoMoreAndGoDetail {
+export interface IHomeCommonCategoryBlock {
   loader: ISimpleDataLoader<GetContentListItem[], any>;
   goDetail: (id: number) => void;
   goList: () => void; 
@@ -38,12 +39,21 @@ export function navHomePageMiniCommonListGo(p: {
   }) 
 }
 
+export function resolveCommonContentFormData(item: GetContentListItem[]) {
+  item.forEach(it => {
+    it.desc = `来源:${it.from || '暂无'}\n` 
+      + (it.desc || '');
+    it.bottomTags = it.keywords?.length ? it.keywords as string[] : [ it.mainBodyColumnName ];
+  })
+  return item;
+}
+
 /**
  * 专用于通用内容的首页小列表控制代码组合
  * @param p 
  * @returns 
  */
-export function useHomePageMiniCommonListGoMoreAndGoDetail(p: {
+export function useHomeCommonCategoryBlock(p: {
   title?: string,
   mainBodyColumnId?: number|number[],
   modelId?: number,
@@ -51,7 +61,8 @@ export function useHomePageMiniCommonListGoMoreAndGoDetail(p: {
   detailsPage: string,
   count?: number,
   params?: Record<string, any>,
-}) : IHomePageMiniCommonListGoMoreAndGoDetail {
+  resolveData?: (item: GetContentListItem[]) => GetContentListItem[],
+}) : IHomeCommonCategoryBlock {
   function goDetail(id: number) {
     navTo(p.detailsPage, {
       mainBodyColumnId: p.mainBodyColumnId,
@@ -71,13 +82,16 @@ export function useHomePageMiniCommonListGoMoreAndGoDetail(p: {
     }) 
   }
 
-  const loader = useSimpleDataLoader(async () => 
-    (await CommonContent.getContentList(new GetContentListParams().setSelfValues({
+  const loader = useSimpleDataLoader(async () => {
+    let res = (await CommonContent.getContentList(new GetContentListParams().setSelfValues({
       mainBodyColumnId: p.mainBodyColumnId,
       modelId: p.modelId,
       ...p.params,
-    }), 1, p.count ?? 4)).list
-  );
+    }), 1, p.count ?? 4)).list;
+    if (p.resolveData)
+      res = p.resolveData(res);
+    return res;
+  });
 
   return {
     loader,

+ 109 - 70
src/pages/article/common/CommonListPage.vue

@@ -18,7 +18,7 @@
       />
     </view>
     <!-- 搜索 -->
-    <view v-if="showSearch" class="d-flex flex-col">
+    <view v-if="showSearch && showList" class="d-flex flex-col">
       <SearchBar
         v-model="searchValue"
         :placeholder="`输入关键词搜索${title}`" 
@@ -58,77 +58,79 @@
     </view>
     
     <!-- 列表 -->
+    <slot name="list" :tabId="tabCurrentId" />
     <view class="position-relative d-flex flex-row flex-wrap justify-between align-stretch mt-3">
-      <slot name="list" />
-      <view
-        v-for="(item, i) in listLoader.list.value"
-        :key="item.id"
-        :class="[
-          'position-relative d-flex flex-grow-1',
-          itemType.endsWith('-2') ? 'width-1-2' : 'w-100'
-        ]"
-      >
-        <Box2LineLargeImageUserShadow 
-          v-if="itemType.startsWith('image-large')"
-          class="w-100"
-          titleColor="title-text"
-          :classNames="getItemClass(i)"
-          :image="getImage(item)"
-          :titleBox="item.titleBox"
-          :titlePrefix="item.titlePrefix"
-          :title="item.title"
-          :desc="item.desc"
-          :tags="item.bottomTags"
-          :badge="item.badge"
-          @click="goDetails(item, item.id)"
-        />
-        <Box2LineImageRightShadow 
-          v-else-if="itemType.startsWith('article-common')"
-          class="w-100"
-          titleColor="title-text"
-          :titleBox="item.titleBox"
-          :titlePrefix="item.titlePrefix"
-          :classNames="getItemClass(i)"
-          :image="getImage(item)"
-          :title="item.title"
-          :desc="item.desc"
-          :tags="item.bottomTags"
-          :badge="item.badge"
-          :wideImage="true"
-          @click="goDetails(item, item.id)"
-        />
-        <Box2LineImageRightShadow 
-          v-else-if="itemType.startsWith('article-character')"
-          class="w-100"
-          :classNames="getItemClass(i)"
-          :image="getImage(item)"
-          titleColor="title-text"
-          :title="item.title"
-          :titlePrefix="item.titlePrefix"
-          :titleBox="item.titleBox"
-          :tags="item.bottomTags || item.keywords"
-          :desc="item.desc"
-          :badge="item.badge"
-          @click="goDetails(item, item.id)"
-        />
-        <Box2LineImageRightShadow 
-          v-else-if="itemType.startsWith('simple-text')"
-          class="w-100"
-          :classNames="getItemClass(i)"
-          titleColor="title-text"
-          :border="false"
-          :showImage="false"
-          :title="item.title"
-          :titlePrefix="item.titlePrefix"
-          :titleBox="item.titleBox"
-          :tags="item.bottomTags || item.keywords"
-          :desc="item.desc"
-          :badge="item.badge"
-          @click="goDetails(item, item.id)"
-        />
+      <template v-if="showList">
+        <view
+          v-for="(item, i) in listLoader.list.value"
+          :key="item.id"
+          :class="[
+            'position-relative d-flex flex-grow-1',
+            itemType.endsWith('-2') ? 'width-1-2' : 'w-100'
+          ]"
+        >
+          <Box2LineLargeImageUserShadow 
+            v-if="itemType.startsWith('image-large')"
+            class="w-100"
+            titleColor="title-text"
+            :classNames="getItemClass(i)"
+            :image="getImage(item)"
+            :titleBox="item.titleBox"
+            :titlePrefix="item.titlePrefix"
+            :title="item.title"
+            :desc="item.desc"
+            :tags="item.bottomTags"
+            :badge="item.badge"
+            @click="goDetails(item, item.id)"
+          />
+          <Box2LineImageRightShadow 
+            v-else-if="itemType.startsWith('article-common')"
+            class="w-100"
+            titleColor="title-text"
+            :titleBox="item.titleBox"
+            :titlePrefix="item.titlePrefix"
+            :classNames="getItemClass(i)"
+            :image="getImage(item)"
+            :title="item.title"
+            :desc="item.desc"
+            :tags="item.bottomTags"
+            :badge="item.badge"
+            :wideImage="true"
+            @click="goDetails(item, item.id)"
+          />
+          <Box2LineImageRightShadow 
+            v-else-if="itemType.startsWith('article-character')"
+            class="w-100"
+            :classNames="getItemClass(i)"
+            :image="getImage(item)"
+            titleColor="title-text"
+            :title="item.title"
+            :titlePrefix="item.titlePrefix"
+            :titleBox="item.titleBox"
+            :tags="item.bottomTags || item.keywords"
+            :desc="item.desc"
+            :badge="item.badge"
+            @click="goDetails(item, item.id)"
+          />
+          <Box2LineImageRightShadow 
+            v-else-if="itemType.startsWith('simple-text')"
+            class="w-100"
+            :classNames="getItemClass(i)"
+            titleColor="title-text"
+            :border="false"
+            :showImage="false"
+            :title="item.title"
+            :titlePrefix="item.titlePrefix"
+            :titleBox="item.titleBox"
+            :tags="item.bottomTags || item.keywords"
+            :desc="item.desc"
+            :badge="item.badge"
+            @click="goDetails(item, item.id)"
+          />
 
-      </view>
-      <view v-if="itemType.endsWith('-2') && listLoader.list.value.length % 2 != 0" class="width-1-2" />
+        </view>
+        <view v-if="itemType.endsWith('-2') && listLoader.list.value.length % 2 != 0" class="width-1-2" />
+      </template>
     </view>
     <SimplePageListLoader :loader="listLoader" />
   </view>
@@ -220,6 +222,13 @@ const props = defineProps({
     default: false,
   },
   /**
+   * 显示列表的Tab ID
+   */
+  showListTabIds: {
+    type: Array as PropType<number[]>,
+    default: () => ([]),
+  },
+  /**
    * 下拉框选项控制
    */
   dropDownNames: {
@@ -259,12 +268,21 @@ const props = defineProps({
   },
   /**
    * 点击详情跳转页面路径
+   * 可以是字符串路径,也可以是对象数组,每个对象包含路径和参数
+   * * 特殊值:byContent 表示根据 detailsPageByContentCallback 函数返回值跳转。
    */
   detailsPage: {
     type: [String,Object],
     default: '/pages/article/details'
   },
   /**
+   * 根据内容项返回跳转路径的回调函数
+   */
+  detailsPageByContentCallback: {
+    type: Function as PropType<(item: any) => string>,
+    default: true,
+  },
+  /**
    * 详情跳转页面参数
    */
   detailsParams: {
@@ -307,6 +325,7 @@ const listLoader = useSimplePageListLoader(props.pageSize, async (page, pageSize
 });
 const tabCurrentIndex = ref(0)
 const tabCurrentId = computed(() => props.tabs?.[tabCurrentIndex.value]?.id ?? -1)
+const showList = computed(() => props.showListTabIds.length == 0 || props.showListTabIds.includes(tabCurrentId.value))
 
 function handleChangeDropDownValue(index: number, value: number) {
   dropDownValues.value[index] = value;
@@ -332,7 +351,25 @@ function goDetails(item: any, id: number) {
     emit('goCustomDetails', item, id)
     return;
   }
+  if (props.detailsPage == 'byContent') {
+    if (handleByContent())
+      return;
+  }
+  function handleByContent() {
+    const page = props.detailsPageByContentCallback(item);
+    if (page) {
+      navTo(page, { 
+        ...props.detailsParams, 
+        id 
+      })
+      return true; 
+    }
+    return false;
+  }
+
   if (typeof props.detailsPage === 'object' && typeof props.detailsPage[0] === 'string') {
+    if (props.detailsPage[tabCurrentIndex.value] == 'byContent' && handleByContent())
+      return;
     navTo(props.detailsPage[tabCurrentIndex.value], { 
       ...props.detailsParams, 
       id 
@@ -340,6 +377,8 @@ function goDetails(item: any, id: number) {
     return; 
   }
   if (typeof props.detailsPage == 'object' && typeof props.detailsPage[0] === 'object') {
+    if (props.detailsPage[tabCurrentIndex.value].page == 'byContent' && handleByContent())
+      return;
     const item = props.detailsPage[tabCurrentIndex.value];
     navTo(item.page, { 
       ...item.params, 

+ 8 - 2
src/pages/article/common/list.vue

@@ -3,7 +3,8 @@
     :title="querys.title || undefined"
     :load="loadData"
     :itemType="querys.itemType as any || undefined"
-    :detailsPage="querys.detailsPage || undefined"
+    :detailsPage="querys.detailsPage || 'byContent'"
+    :detailsPageByContentCallback="handleDetailsPageByContentCallback"
     :detailsParams="{
       mainBodyColumnId: querys.mainBodyColumnId || undefined,
       modelId: querys.modelId || undefined,
@@ -14,7 +15,7 @@
 <script setup lang="ts">
 import { useLoadQuerys, stringDotNumbersToNumbers } from '@/common/composeabe/LoadQuerys';
 import CommonListPage from './CommonListPage.vue';
-import CommonContent, { GetContentListParams } from '@/api/CommonContent';
+import CommonContent, { GetContentListItem, GetContentListParams } from '@/api/CommonContent';
 
 const { querys } = useLoadQuerys({
   mainBodyColumnId: '',
@@ -47,4 +48,9 @@ async function loadData(
 
   return res;
 }
+function handleDetailsPageByContentCallback(item: GetContentListItem) {
+  if (item.type === GetContentListParams.TYPE_VIDEO || item.video)
+    return '/pages/video/details';
+  return '/pages/article/details';
+}
 </script>

+ 54 - 1
src/pages/article/details.vue

@@ -21,6 +21,17 @@
               <text class="size-s color-text-content-second text-nowrap">{{ DataDateUtils.formatDate(loader.content.value.publishAt, 'YYYY-MM-dd') }}</text>
             </view>
           </view>
+          <view v-if="archiveInfo.hasArchive" class="mt-3">
+            <Box2LineImageRightShadow
+              class="w-100"
+              titleColor="title-text"
+              title2
+              :image="archiveInfo.archiveIcon"
+              :title="loader.content.value.title"
+              desc="点击查看完整文档"
+              @click="goArchive(loader.content.value.id)"
+            />
+          </view>
           <view class="p-3 radius-ll bg-light mt-3">
             <Parse
               v-if="loader.content.value.content"
@@ -80,6 +91,13 @@ import LikeFooter from "../parts/LikeFooter.vue";
 import Image from "@/components/basic/Image.vue";
 import ArticleCorrect from "../parts/ArticleCorrect.vue";
 import ImageSwiper from "../parts/ImageSwiper.vue";
+import IconExcel from '@/components/images/files/excel.png';
+import IconPowerpoint from '@/components/images/files/powerpoint.png';
+import IconUnknown from '@/components/images/files/unknown.png';
+import IconWord from '@/components/images/files/word.png';
+import IconPdf from '@/components/images/files/pdf.png';
+import { StringUtils } from "@imengyu/imengyu-utils";
+import Icon from "@/components/basic/Icon.vue";
 
 const loader = useSimplePageContentLoader<
   GetContentDetailItem, 
@@ -96,6 +114,37 @@ const loader = useSimplePageContentLoader<
 const { onPreviewImage } = useSwiperImagePreview(() => loader.content.value?.images || [])
 
 const emptyContent = computed(() => (loader.content.value?.content || '').trim() === '')
+const archiveInfo = computed(() => {
+  const hasArchive = Boolean(loader.content.value?.archives);
+  let fileIcon = IconUnknown;
+  const ext = StringUtils.path.getFileExt(loader.content.value?.archives || '');
+  switch (ext) {
+    case 'excel': 
+    case 'xls': 
+    case 'xlsx': 
+      fileIcon = IconExcel;
+      break;
+    case 'powerpoint': 
+    case 'ppt': 
+    case 'pptx': 
+      fileIcon = IconPowerpoint;
+      break;
+    case 'word': 
+    case 'docx': 
+    case 'doc': 
+    case 'txt': 
+    case 'rtf': 
+      fileIcon = IconWord;
+      break;
+    case 'pdf': 
+      fileIcon = IconPdf;
+      break;
+  }
+  return {
+    hasArchive,
+    archiveIcon: hasArchive ? fileIcon : IconUnknown,
+  }
+})
 
 const recommendListLoader = useSimpleDataLoader(async () => {
   if (!querys.value.modelId)
@@ -111,7 +160,11 @@ const recommendListLoader = useSimpleDataLoader(async () => {
     }));
 });
 
-
+function goArchive(id: number) {
+  navTo('/pages/document/details', { 
+    id,
+  });
+}
 function goDetails(id: number) {
   navTo('/pages/article/details', { 
     id, 

+ 6 - 10
src/pages/home/index.vue

@@ -16,12 +16,10 @@
         >
           <text class="title">世界闽南文化交流中心</text>
           <text>闽南文化生态保护区(厦门市)</text>
-          <view class="more">
-            <text>&nbsp;</text>
-          </view>
           <Image 
             innerClass="footer"
             src="https://mncdn.wenlvti.net/app_static/minnan/images/home/MainBanner2.png"
+            :width="280"
             mode="widthFix"
           />
         </view>
@@ -30,8 +28,6 @@
           <HomeButton
             title="文化常识"
             icon="https://mncdn.wenlvti.net/app_static/minnan/images/home/IconMap.png"
-            bg="https://mncdn.wenlvti.net/app_static/minnan/images/home/ButtonMapBg.png"
-            
             @click="navTo('/pages/introduction/explore')"
           />
           <HomeButton
@@ -436,10 +432,10 @@ onShareAppMessage(() => {
 .page-home {
 
   .main-banner {
-    top: -150rpx;
+    top: 0rpx;
   }
   .content {
-    margin-top: 260rpx;
+    margin-top: 400rpx;
   }
 
   .map-tags {
@@ -488,7 +484,7 @@ onShareAppMessage(() => {
     overflow: hidden;
     border-radius: 15rpx;
     background: linear-gradient(180deg, #E5CDAB 0%, #F0E3D6 100%), #F7F3E8;
-    padding: 20rpx;
+    padding: 30rpx 20rpx;
     font-family: "SongtiSCBlack";
     color: #432A04;
 
@@ -517,9 +513,9 @@ onShareAppMessage(() => {
     }
     .footer {
       position: absolute;
-      right: -30rpx;
+      right: -80rpx;
       bottom: -10rpx;
-      width: 350rpx;
+      width: 180rpx;
       z-index: 2;
       height: auto;
     }

+ 3 - 3
src/pages/inhert/village/details.vue

@@ -123,7 +123,7 @@ import { useLoadQuerys } from '@/common/composeabe/LoadQuerys';
 import { useSwiperImagePreview } from '@/common/composeabe/SwiperImagePreview';
 import { navTo } from '@/components/utils/PageAction';
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
-import { useHomePageMiniCommonListGoMoreAndGoDetail, type IHomePageMiniCommonListGoMoreAndGoDetail } from '@/pages/article/common/CommonContent';
+import { useHomeCommonCategoryBlock, type IHomeCommonCategoryBlock } from '@/pages/article/common/CommonContent';
 import ContentNote from '@/pages/parts/ContentNote.vue';
 import IntroBlock from '@/pages/article/common/IntroBlock.vue';
 import ImagesUrls from '@/common/config/ImagesUrls';
@@ -139,7 +139,7 @@ interface TagDataItem {
   modelId?: number,
   mainBodyColumnId?: number,
 }
-interface TagDataRecommendItem extends TagDataItem, IHomePageMiniCommonListGoMoreAndGoDetail {
+interface TagDataRecommendItem extends TagDataItem, IHomeCommonCategoryBlock {
 }
 
 const center = ref([118.15723, 24.48147]);
@@ -214,7 +214,7 @@ const contentLoader = useSimpleDataLoader(async () => {
   tagsDataRecommend.value = tagsData.value.slice(0, 2).map((t) => {
     return {
       ...t,
-      ...(toRefs(useHomePageMiniCommonListGoMoreAndGoDetail({
+      ...(toRefs(useHomeCommonCategoryBlock({
         title: t.title,
         mainBodyColumnId: t.mainBodyColumnId,
         modelId: t.modelId,

+ 8 - 99
src/pages/introduction/explore.vue

@@ -2,39 +2,7 @@
   <CommonRoot>
     <FlexCol :padding="30" :gap="15" backgroundColor="background.page">
       <!-- 分类 -->
-      <template v-for="category in categoryDatas" :key="category.title">
-        <HomeTitle 
-          :title="category.title"
-          showMore 
-          moreText="更多"
-          @clickMore="category.morePage" 
-        />
-        <SimplePageContentLoader :loader="category.data" >
-          <FlexCol>
-            <template v-if="category.type !== 'article'">
-              <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"
-                @click="category.detailPage(item.id)"
-              />
-            </template>
-            <template v-else>
-              <Box2LineRightShadow
-                v-for="(item, i) in category.data.content.value" 
-                :key="i" 
-                :title="item.title"
-                :desc="item.desc"
-                @click="category.detailPage(item.id)"
-              />
-            </template>
-          </FlexCol>
-        </SimplePageContentLoader>  
-      </template>
+      <CommonCategoryHome :categoryDefine="categoryDefine" />
 
       <!-- 闽南文化概况 -->
       <!-- <HomeTitle title="闽南文化概况(厦门市)" />
@@ -75,35 +43,21 @@
 </template>
 
 <script setup lang="ts">
+import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import { CommonContentApi, GetColumListParams } from '@/api/CommonContent';
+import { useHomeCommonCategoryBlock } from '../article/common/CommonContent';
 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 { CommonContentApi, GetColumListParams, GetContentListParams } from '@/api/CommonContent';
-import { navHomePageMiniCommonDetailGo, navHomePageMiniCommonListGo, useHomePageMiniCommonListGoMoreAndGoDetail } 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';
-import Box2LineRightShadow from '../parts/Box2LineRightShadow.vue';
 import Footer from '@/components/display/Footer.vue';
-import { DateUtils } from '@imengyu/imengyu-utils';
+import CommonCategoryHome, { type CategoryDefine } from '../article/common/CommonCategoryHome.vue';
 
-const categoryDefine = [
+const categoryDefine : CategoryDefine[] = [
   {
     title: '文化概况',
     content: HistoryContent,
@@ -111,7 +65,7 @@ const categoryDefine = [
   },
   {
     title: '文化常识',
-    content: useHomePageMiniCommonListGoMoreAndGoDetail({
+    content: useHomeCommonCategoryBlock({
       title: '文化常识',
       mainBodyColumnId: 320,
       modelId: 18,
@@ -165,52 +119,7 @@ const categoryDefine = [
     noFrom: true,
     morePage: '/pages/home/laws',
   },
-]
-const categoryDatas = categoryDefine.map(item => ({
-  ...item,
-  detailPage: (id: number) => {
-    if (item.content instanceof CommonContentApi) {
-      if (item.detailPage) {
-        navTo(item.detailPage, { id });
-      } else {
-        navHomePageMiniCommonDetailGo({
-          id,
-          mainBodyColumnId: item.content.mainBodyColumnId,
-          modelId: item.content.modelId,
-        })
-      }
-    } else {
-      item.content.goDetail(id);
-    }
-  },
-  morePage: () => {
-    if (item.content instanceof CommonContentApi) {
-      if (item.morePage) {
-        navTo(item.morePage, {});
-      } else {
-        navHomePageMiniCommonListGo({
-          title: item.title,
-          mainBodyColumnId: item.content.mainBodyColumnId,
-          modelId: item.content.modelId,
-          detailsPage: item.detailPage,
-        })
-      }
-    } else {
-      item.content.goList();
-    }
-  },
-  data: item.content instanceof CommonContentApi ? useSimpleDataLoader(async () => {
-    return (await (item.content as CommonContentApi)
-      .getContentList(new GetContentListParams(), 1, 3))
-      .list.map(p => ({
-        id: p.id,
-        title: p.title, 
-        desc: item.noFrom ? (p.desc || DateUtils.formatDate(p.publishAt, 'YYYY-MM-dd')) : `来源:${p.from || '暂无'}\n` + (p.desc || ''), 
-        image: p.thumbnail || p.image,
-        bottomTags: p.keywords as string[],
-      }))
-  }) : item.content.loader,
-}));
+];
 const introdData = useSimpleDataLoader(async () => {
   let i = 0;
   const promises = [];

+ 2 - 2
src/pages/introduction/inhert.vue

@@ -148,7 +148,7 @@ import { ref } from 'vue';
 import { navTo } from '@/components/utils/PageAction';
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
 import { GetContentListParams } from '@/api/CommonContent';
-import { navHomePageMiniCommonListGo, useHomePageMiniCommonListGoMoreAndGoDetail } from '../article/common/CommonContent';
+import { navHomePageMiniCommonListGo, useHomeCommonCategoryBlock } from '../article/common/CommonContent';
 import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import HomeTitle from '../parts/HomeTitle.vue';
@@ -239,7 +239,7 @@ const {
   loader: activityData,
   goList: goActivityList,
   goDetail: goActivityDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '非遗活动',
   mainBodyColumnId: 290,
   modelId: 18,

+ 57 - 13
src/pages/introduction/news.vue

@@ -2,14 +2,34 @@
 <template>
   <CommonRoot>
     <FlexCol :padding="30" innerClass="bg-base">
-      <SearchBar
-        v-model="searchText"
-        leftIcon=""
-        placeholder="搜索新闻"
-        cancelState="hidden"
-        searchState="show"
-        @search="loadNews"
-      />
+      <FlexRow>
+        <Touchable
+          direction="row"
+          width="450"
+          align="center"
+          :activeOpacity="1"
+          @click="($refs.pickerField as any).show()"
+        >
+          <Text>筛选日期:</Text>
+          <PickerField 
+            ref="pickerField"
+            v-model="filterDate"
+            placeholder="选择日期"
+            title="筛选日期"
+            :columns="[filterDates]" 
+          />
+          <Icon name="arrow-down" />
+        </Touchable>
+        <SearchBar
+          v-model="searchText"
+          inputBackgroundColor="transparent"
+          leftIcon=""
+          placeholder="搜索新闻"
+          cancelState="hidden"
+          searchState="show"
+          @search="loadNews"
+        />
+      </FlexRow>
       <Box2LineImageRightShadow 
         v-for="(item, i) in newsLoader.list.value"
         :key="item.id"
@@ -31,7 +51,7 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted, ref } from 'vue';
+import { onMounted, ref, watch } from 'vue';
 import { type GetContentListItem, GetContentListParams } from '@/api/CommonContent';
 import { useSimplePageListLoader } from '@/common/composeabe/SimplePageListLoader';
 import { navHomePageMiniCommonDetailGo } from '../article/common/CommonContent';
@@ -42,16 +62,37 @@ import FlexCol from '@/components/layout/FlexCol.vue';
 import SearchBar from '@/components/form/SearchBar.vue';
 import Box2LineImageRightShadow from '../parts/Box2LineImageRightShadow.vue';
 import ApiCofig from '@/common/config/ApiCofig';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import PickerField from '@/components/form/PickerField.vue';
+import Text from '@/components/basic/Text.vue';
+import Icon from '@/components/basic/Icon.vue';
+import type { PickerItem } from '@/components/form/Picker';
 
 const searchText = ref('');
+const filterDate = ref(['']);
 
 const newsLoader = useSimplePageListLoader(10, async (page, pageSize) => {
-  return await NewsIndexContent.getContentList(new GetContentListParams().setSelfValues({
-    keywords: searchText.value,
-    platform: ApiCofig.platformId,
-  }), page, pageSize);
+  return await NewsIndexContent.getContentList(new GetContentListParams()
+    .setMainBodyColumnId([ 228,298,299 ])
+    .setKeywords(searchText.value)
+    .setSelfValues({
+      publishAt: filterDate.value[0],
+    })
+  , page, pageSize);
 });
 
+const nowYear = new Date().getFullYear();
+const filterDates : PickerItem[] = [
+  { text: '全部', value: 0 },
+  ...(new Array(21).fill(0).map((_, index) => {
+    return { 
+      text: `${nowYear - index}`, 
+      value: nowYear - index 
+    }
+  }))
+];
+
 function loadNews() {
   newsLoader.loadData(undefined, true);
 }
@@ -63,6 +104,9 @@ function goDetails(item: GetContentListItem, id: number) {
   });
 }
 
+watch(filterDate, () => {
+  loadNews();
+});
 onMounted(() => {
   loadNews();
 });

+ 1 - 0
src/pages/introduction/recommend-news.vue

@@ -17,6 +17,7 @@ import { GetContentListParams } from '@/api/CommonContent';
 import SeaContent from '@/api/introduction/SeaContent';
 import NewsIndexContent from '@/api/news/NewsIndexContent';
 import CommonListPage, { type DropDownNames } from '@/pages/article/common/CommonListPage.vue';
+import ResultContent from '@/api/research/ResultContent';
 
 const dropdownNames = ref<DropDownNames[]>([]);
 const startTab = ref(0);

+ 8 - 7
src/pages/introduction/travel.vue

@@ -10,6 +10,7 @@
             v-for="(item, i) in corseData.content.value"
             classNames="width-2-3 mr-2"
             titleColor="title-text"
+            title1
             fixSize
             :key="i"
             :title="item.title"
@@ -126,7 +127,7 @@
 <script setup lang="ts">
 import { navTo } from '@/components/utils/PageAction';
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
-import { useHomePageMiniCommonListGoMoreAndGoDetail } from '../article/common/CommonContent';
+import { useHomeCommonCategoryBlock } from '../article/common/CommonContent';
 import { GetContentListParams } from '@/api/CommonContent';
 import SimplePageContentLoader from '@/common/components/SimplePageContentLoader.vue';
 import FlexCol from '@/components/layout/FlexCol.vue';
@@ -156,7 +157,7 @@ const spotData = useSimpleDataLoader(async () =>
 const {
   loader: songsData,
   goDetail: goSongsDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '闽南歌曲',
   mainBodyColumnId: [315],
   modelId: 16,
@@ -168,7 +169,7 @@ const {
   loader: corseData,
   goList: goCourseList,
   goDetail: goCourseDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '闽南语在线课程',
   mainBodyColumnId: [257/* ,235,237,210 */],
   modelId: 5,
@@ -180,7 +181,7 @@ const {
   loader: routeData,
   goList: goRouteList,
   goDetail: goRouteDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '旅游路线',
   mainBodyColumnId: [274,275,276,277],
   modelId: 17,
@@ -192,7 +193,7 @@ const {
   loader: recommendData,
   goList: goRecommendList,
   goDetail: goRecommendDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '文化景区',
   mainBodyColumnId: 273,
   modelId: 17,
@@ -204,7 +205,7 @@ const {
   loader: creativeData,
   goList: goCreativeList,
   goDetail: goCreativeDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '文化产品',
   mainBodyColumnId: 48,
   modelId: 9,
@@ -216,7 +217,7 @@ const {
   loader: foodData,
   goList: goFoodList,
   goDetail: goFoodDetail,
-} = useHomePageMiniCommonListGoMoreAndGoDetail({
+} = useHomeCommonCategoryBlock({
   title: '闽南美食',
   mainBodyColumnId: 253,
   modelId: 3,

+ 10 - 4
src/pages/parts/Box2LineImageRightShadow.vue

@@ -1,7 +1,7 @@
 <template>
   <view 
     :class="[
-      'd-flex flex-row flex-grow-1 justify-between',
+      'd-flex flex-row flex-grow-1 justify-between align-center',
       'radius-base mb-3 overflow-hidden',
       border ? 'border-all-light-light-primary shadow-s p-2 pt-3 pb-3 ' : '' ,
     ]"
@@ -31,7 +31,7 @@
         <view class="d-flex flex-row">
           <text :class="[
             'color-primary size-base',
-            desc || title1 ? 'text-lines-1' : 'text-lines-2',
+            (desc || title1) && !title2 ? 'text-lines-1' : 'text-lines-2',
             titleBox ? 'border-all-text' : '',
           ]">{{ title }}</text>
         </view> 
@@ -41,7 +41,9 @@
         <RoundTags v-if="tags" :tags="tags" small />
       </view>
     </view>
-    <text class="color-primary-second-text size-ss">{{ right }}</text>
+    <slot name="right">
+      <text class="color-primary-second-text size-ss">{{ right }}</text>
+    </slot>
   </view>
 </template>
 
@@ -82,7 +84,11 @@ defineProps({
   title1: {
     type: Boolean,
     default: false,
-  }
+  },
+  title2: {
+    type: Boolean,
+    default: false,
+  },
 })
 
 defineOptions({

+ 74 - 4
src/pages/research/index.vue

@@ -6,22 +6,31 @@
       { id: 0, text: '创新发展' },
       { id: 1, text: '研究机构' },
     ]"
+    :showListTabIds="[1]"
     :detailsPage="{
-      0: '/pages/doc/details',
+      0: 'byContent',
       1: '/pages/article/details',
     }"
+    :detailsParams="detailsParams"
+    :detailsPageByContentCallback="handleDetailsPageByContentCallback"
     :startTabIndex="startTab"
     :load="loadData" 
-  />
+  >
+    <template #list="{ tabId }">
+      <CommonCategoryHome v-if="tabId === 0" :categoryDefine="categoryDefine" />
+    </template>
+  </CommonListPage>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue';
-import { GetContentListParams } from '@/api/CommonContent';
+import { computed, ref } from 'vue';
+import { GetContentListItem, GetContentListParams } from '@/api/CommonContent';
 import CommonListPage, { type DropDownNames } from '@/pages/article/common/CommonListPage.vue';
 import ResultContent from '@/api/research/ResultContent';
 import InnovationContent from '@/api/research/InnovationContent';
 import TeamsContent from '@/api/research/TeamsContent';
+import CommonCategoryHome, { type CategoryDefine } from '../article/common/CommonCategoryHome.vue';
+import { resolveCommonContentFormData, useHomeCommonCategoryBlock } from '../article/common/CommonContent';
 
 const dropdownNames = ref<DropDownNames[]>([]);
 const startTab = ref(0);
@@ -33,6 +42,7 @@ async function loadData(
   tabSelect: number,
 ) {
   let res;
+  startTab.value = tabSelect;
   switch (tabSelect) {
     case 1: 
       res = (await TeamsContent.getContentList(new GetContentListParams()
@@ -57,4 +67,64 @@ async function loadData(
   })
   return res;
 }
+const detailsParams = computed(() => {
+  switch (startTab.value) {
+    case 1:
+      return {
+        modelId: TeamsContent.modelId,
+        mainBodyColumnId: TeamsContent.mainBodyColumnId,
+      };
+    default:
+    case 0:
+      return {
+        modelId: InnovationContent.modelId,
+        mainBodyColumnId: InnovationContent.mainBodyColumnId,
+      };
+  }
+});
+
+const categoryDefine : CategoryDefine[] = [
+  {
+    title: '非遗作品秀',
+    content: useHomeCommonCategoryBlock({
+      title: '非遗作品秀',
+      mainBodyColumnId: 365,
+      modelId: InnovationContent.modelId,
+      itemType: 'article-common',
+      detailsPage: 'byContent',
+      resolveData: resolveCommonContentFormData,
+    }),
+    type: '',
+  },
+  {
+    title: '非遗新青年',
+    content: useHomeCommonCategoryBlock({
+      title: '非遗新青年',
+      mainBodyColumnId: 364,
+      modelId: InnovationContent.modelId,
+      itemType: 'article-common',
+      detailsPage: 'byContent',
+      resolveData: resolveCommonContentFormData,
+    }),
+    type: '',
+  },
+  {
+    title: '非遗巴士研学',
+    content: useHomeCommonCategoryBlock({
+      title: '非遗巴士研学',
+      mainBodyColumnId: 363,
+      modelId: InnovationContent.modelId,
+      itemType: 'article-common',
+      detailsPage: 'byContent',
+      resolveData: resolveCommonContentFormData,
+    }),
+    type: '',
+  },
+];
+
+function handleDetailsPageByContentCallback(item: GetContentListItem) {
+  if (item.type === GetContentListParams.TYPE_VIDEO || item.video)
+    return '/pages/video/details';
+  return '/pages/article/details';
+}
 </script>