浏览代码

📦 按要求修改细节问题,首页数据统计,列表详情页字段修改

快乐的梦鱼 1 月之前
父节点
当前提交
d73bcdd6a3

文件差异内容过多而无法显示
+ 180 - 3584
package-lock.json


+ 8 - 6
src/api/CommonContent.ts

@@ -208,10 +208,6 @@ export class GetContentDetailItem extends DataModel<GetContentDetailItem> {
       flag: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
       tags: { clientSide: 'splitCommaArray', serverSide: 'commaArrayMerge' },
       type: { clientSide: 'number', serverSide: 'number' },
-      associationMeList: [
-        { clientSide: 'map', serverSide: 'original'},
-        { clientSide: 'array', serverSide: 'original' },
-      ],
     }
     this._convertKeyType = (key, direction) => {
       if (key.endsWith('Time'))
@@ -219,6 +215,12 @@ export class GetContentDetailItem extends DataModel<GetContentDetailItem> {
           clientSide: 'date',
           serverSide: 'string',
         };
+      if (key.endsWith('List')) {
+        return [
+          { clientSide: 'map', serverSide: 'original'},
+          { clientSide: 'array', clientSideChildDataModel: GetContentDetailItem, serverSide: 'original' },
+        ]
+      }
       return undefined;
     };
     this._afterSolveServer = () => {
@@ -408,10 +410,10 @@ export class CommonContentApi extends AppServerRequestModule<DataModel> {
    * @param querys 额外参数
    * @returns 
    */
-  getContentDetail<T extends DataModel = GetContentDetailItem>(id: number, modelClassCreator: NewDataModel = GetContentDetailItem, querys?: QueryParams) {
+  getContentDetail<T extends DataModel = GetContentDetailItem>(id: number, modelClassCreator: NewDataModel = GetContentDetailItem, modelId?: number, querys?: QueryParams) {
     return this.get('/content/content/getContentDetail', `${this.debugName} (${id}) 内容详情`, {
       main_body_id: this.mainBodyId,
-      model_id: this.modelId,
+      model_id: modelId ?? this.modelId,
       id,
       ...querys,
     }, modelClassCreator)

+ 27 - 0
src/api/introduction/IndexContent.ts

@@ -1,10 +1,37 @@
+import { DataModel } from '@imengyu/js-request-transform';
 import { CommonContentApi } from '../CommonContent';
 
+export class IndexStats extends DataModel<IndexStats> {
+  constructor() {
+    super(IndexStats, "内容详情");
+    this.setNameMapperCase('Camel', 'Snake');
+    this._convertTable = {
+      crData: { clientSide: 'forceArray' },
+      minnanCr: { clientSide: 'forceArray' },
+      historyData: { clientSide: 'forceArray' },
+      inheritorData: { clientSide: 'forceArray' },
+      ichData: { clientSide: 'forceArray' },
+      ichCenter: { clientSide: 'forceArray' },
+    }
+  }
+
+  crData: any;
+  minnanCr: any;
+  historyData: any;
+  inheritorData: any;
+  ichData: any;
+  ichCenter: any;
+}
+
 export class IndexContentApi extends CommonContentApi {
 
   constructor() {
     super(undefined, 3, "闽南文化概况", 288);
   }
+  async getStats() {
+    return (await this.get('/volunteer/statistics/webData', '闽南文化首页数据统计', {
+    }, IndexStats)).data as IndexStats
+  }
 }
 
 export default new IndexContentApi();

+ 11 - 4
src/common/composeabe/SimplePageListLoader.ts

@@ -5,11 +5,12 @@ import type { ILoaderCommon, LoaderLoadType } from "./LoaderCommon";
 export interface ISimplePageListLoader<T, P> extends ILoaderCommon<P> {
   list: Ref<T[]>;
   page: Ref<number>;
+  total: Ref<number>;
 }
 
 export function useSimplePageListLoader<T, P = any>(
   pageSize: number, 
-  loader: (page: number, pageSize: number, params?: P) => Promise<T[]>,
+  loader: (page: number, pageSize: number, params?: P) => Promise<{ list: T[], total: number }>,
   showGlobalLoading = false,
 )  : ISimplePageListLoader<T, P>
 {
@@ -17,6 +18,7 @@ export function useSimplePageListLoader<T, P = any>(
   const loadStatus = ref<LoaderLoadType>('loading');
   const loadError = ref('');
   const page = ref(0);
+  const total = ref(0);
   const list = ref<T[]>([]) as Ref<T[]>;
 
   let lastParams: P | undefined;
@@ -38,10 +40,14 @@ export function useSimplePageListLoader<T, P = any>(
       uni.showLoading({ title: '加载中...' });
 
     try {
-      const res = (await loader(page.value, pageSize, lastParams)) as T[];
-      list.value = list.value.concat(res);
+      const res = (await loader(page.value, pageSize, lastParams));
+      list.value = list.value.concat(res.list as T[]);
+      total.value = res.total;
 
-      loadStatus.value = res.length > 0 ? 'finished' : 'nomore';
+      if (!(res.list instanceof Array))
+        throw new Error('list is not array!');
+
+      loadStatus.value = res.list.length > 0 ? 'finished' : 'nomore';
       loadError.value = '';
       loading = false;
     } catch(e) {
@@ -69,6 +75,7 @@ export function useSimplePageListLoader<T, P = any>(
 
   return {
     list,
+    total,
     page,
     loadStatus,
     loadError,

+ 9 - 0
src/common/scss/global/base.scss

@@ -82,4 +82,13 @@
 	&-visible {
 		overflow: visible;
 	}
+}
+
+.visible {
+	&-hidden {
+		visibility: hidden;
+	}
+	&-visible {
+		visibility: visible;
+	}
 }

+ 31 - 0
src/common/utils/ConvertRgeistry.ts

@@ -88,4 +88,35 @@ export function registryConvert() {
       };
     }
   })
+  
+  DataConverter.registerConverter({
+    key: 'ForceArray',
+    targetType: 'forceArray',
+    converter: (source, key, type) => {
+      if (source instanceof Array) 
+        return {
+          success: true,
+          result: source,
+        }
+      if (typeof source === 'object' && source !== null) {
+        const arr = []
+        for (const key in source) {
+          arr.push((source as Record<string, any>)[key])
+        }
+        return {
+          success: true,
+          result: arr,
+        }
+      }
+      if (typeof source === 'string')
+        return {
+          success: true,
+          result: source.split(','), 
+        }
+      return {
+        success: false,
+        convertFailMessage: `[${key}] 不是数组类型`,
+      };
+    }
+  })
 }

+ 7 - 2
src/pages.json

@@ -61,8 +61,7 @@
     {
       "path": "pages/inhert/artifact/list",
       "style": {
-        "navigationBarTitleText": "文物列表",
-        "navigationStyle": "custom"
+        "navigationBarTitleText": "文物古迹"
       }
     },
     {
@@ -96,6 +95,12 @@
       }
     },
     {
+      "path": "pages/inhert/old/list",
+      "style": {
+        "navigationBarTitleText": "老字号"
+      }
+    },
+    {
       "path": "pages/inhert/map/index",
       "style": {
         "navigationBarTitleText": "文化地图"

+ 71 - 21
src/pages/article/common/CommonListPage.vue

@@ -38,8 +38,11 @@
     </view>
     <!-- 下拉框 -->
     <view 
-      v-if="dropDownNames && dropDownNames.length > 0" 
-      class="d-flex flex-row justify-around mt-2"
+      v-if="dropDownNames.length > 0" 
+      class="d-flex flex-row justify-between align-center mt-2"
+      :class="[
+        dropDownNames.length >= 3 ? 'justify-around' : ('justify-between')
+      ]"
     >
       <template v-for="(drop, k) in dropDownNames" :key="k" >
         <SimpleDropDownPicker 
@@ -49,47 +52,70 @@
           @update:modelValue="(v) => handleChangeDropDownValue(k, v)"
         />
       </template>
+      <view 
+        v-if="showTotal && dropDownNames.length < 3" 
+        class="d-flex flex-row align-center mt-3 size-s color-primary text-bold"
+      >
+        <text>总共有 {{ listLoader.total }} 个</text>
+      </view>
+    </view>
+    <view 
+      v-if="showTotal && (dropDownNames.length >= 3 || dropDownNames.length == 0)" 
+      class="d-flex flex-row justify-center align-center mt-3 size-s color-primary text-bold"
+    >
+      <text>总共有 {{ listLoader.total }} 个</text>
     </view>
+    
     <!-- 列表 -->
-    <view class="d-flex flex-row flex-wrap justify-between mt-3">
+    <view class="position-relative d-flex flex-row flex-wrap justify-between align-stretch mt-3">
       <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)"
           :title="item.title"
           :desc="item.desc"
-          @click="goDetails(item.id)"
+          :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"
           :classNames="getItemClass(i)"
           :image="getImage(item)"
           :title="item.title"
           :desc="item.desc"
+          :tags="item.bottomTags"
+          :badge="item.badge"
           :wideImage="true"
-          @click="goDetails(item.id)"
+          @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"
-          :tags="item.keywords"
+          :tags="item.bottomTags || item.keywords"
           :desc="item.desc"
-          @click="goDetails(item.id)"
+          :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>
     <SimplePageListLoader :loader="listLoader" />
   </view>
@@ -129,7 +155,7 @@ const props = defineProps({
    */
   title: {
     type: String,
-    default: '通用列表页',
+    default: '',
   },
   /**
    * 分组标签
@@ -150,11 +176,18 @@ const props = defineProps({
     default: true,
   },
   /**
+   * 显示总数
+   */
+  showTotal: {
+    type: Boolean,
+    default: false,
+  },
+  /**
    * 下拉框选项控制
    */
   dropDownNames: {
     type: Object as PropType<DropDownNames[]>,
-    default: null,
+    default: () => ([]),
   },
   /**
    * 列表项类型
@@ -184,7 +217,7 @@ const props = defineProps({
       searchText: string,
       dropDownValues: number[],
       tabSelect: number,
-    ) => Promise<CommonListItem[]>>,
+    ) => Promise<{ list: CommonListItem[], total: number }>>,
     required: true,
   },
   /**
@@ -205,16 +238,24 @@ const props = defineProps({
     type: Boolean,
     default: true,
   },
+  startTab: {
+    type: Number,
+    default: undefined, 
+  }
 })
 
+const emit = defineEmits([ 'goCustomDetails' ])
+
 const dropDownValues = ref<any>([]);
 const searchValue = ref('');
-const listLoader = useSimplePageListLoader(props.pageSize, async (page, pageSize) => await props.load(
-  page, pageSize, 
-  searchValue.value,
-  dropDownValues.value,
-  tab.value,
-));
+const listLoader = useSimplePageListLoader(props.pageSize, async (page, pageSize) => {
+  return await props.load(
+    page, pageSize, 
+    searchValue.value,
+    dropDownValues.value,
+    tab.value,
+  )
+});
 const tab = ref(0)
 
 function handleChangeDropDownValue(index: number, value: number) {
@@ -224,7 +265,11 @@ function handleChangeDropDownValue(index: number, value: number) {
 function doSearch() {
   listLoader.loadData(undefined, true);
 }
-function goDetails(id: number) {
+function goDetails(item: any, id: number) {
+  if (props.detailsPage == 'custom') {
+    emit('goCustomDetails', item, id)
+    return;
+  }
   navTo(props.detailsPage, { 
     ...props.detailsParams,
     id
@@ -245,10 +290,15 @@ watch(tab, () => {
 });
 
 onMounted(() => {
-  uni.setNavigationBarTitle({
-    title: props.title,
-  })
-  listLoader.loadData(undefined, true);
+  setTimeout(() => {  
+    if (props.startTab)
+      tab.value = props.startTab;
+    if (props.title)
+      uni.setNavigationBarTitle({ title: props.title, })
+    for (const element of props.dropDownNames)
+      dropDownValues.value.push(element.defaultSelectedValue);
+    listLoader.loadData(undefined, true);
+  }, 200);
 });
 </script>
 

+ 23 - 1
src/pages/article/common/DetailTabPage.vue

@@ -6,7 +6,26 @@
         <view class="d-flex flex-col">
 
           <!-- 轮播大图 -->
-          <ImageSwiper v-if="showHead" :images="loader.content.value.images" />
+          <ImageSwiper 
+            v-if="showHead && loader.content.value.images.length > 1" 
+            :images="loader.content.value.images"
+          />
+          <video 
+            v-else-if="showHead && loader.content.value.video" 
+            :src="loader.content.value.video"
+            autoplay
+            :poster="loader.content.value.image"
+            controls
+            class="w-100 height-400"
+          />
+          <!--非纯图片有内容才显示大图-->
+          <ImageSwiper 
+            v-else-if="showHead && (
+              loader.content.value.content
+              || loader.content.value.intro
+            )" 
+            :images="loader.content.value.images"
+          />
 
           <!-- 标题区域 -->
           <view class="d-flex flex-col mt-3 p-3">
@@ -139,6 +158,9 @@ const loader = useSimplePageContentLoader<
   tabsArray.value[1].visible = Boolean(d.images && d.images.length > 1);
   tabsArray.value[2].visible = Boolean(d.video);
   tabsArray.value[3].visible = Boolean(d.audio);
+
+  if (d.title)
+    uni.setNavigationBarTitle({ title: d.title });
   return d;
 });
 

+ 2 - 0
src/pages/article/common/IntroBlock.vue

@@ -83,11 +83,13 @@ const emit = defineEmits([
       color: #666666;
       font-weight: 400;
       font-size: 28rpx;
+      flex-shrink: 0;
     }
     .value {
       font-size: 28rpx;
       color: #312520;
       font-weight: 400;
+      text-align: right;
     }
   }
   .sub-title{

+ 1 - 1
src/pages/article/common/list.vue

@@ -40,6 +40,6 @@ async function loadData(
     region: querys.value.region || undefined,
   }), page, pageSize);
 
-  return res.list;
+  return res;
 }
 </script>

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

@@ -57,14 +57,14 @@ const listLoader = useSimplePageListLoader<{
     modelId: params.modelId, 
     mainBodyColumnId: params.mainBodyColumnId ,
   }), page, pageSize);
-  return res.list.map((item) => {
+  return { list: res.list.map((item) => {
     return {
       id: item.id,
       image: item.thumbnail || item.image,
       title: item.title,
       date: DataDateUtils.formatDate(item.publishAt, 'YYYY-MM-dd'),
     }
-  })
+  }), total: res.total }
 });
 
 function goDetail(id: number) {

+ 4 - 3
src/pages/discover.vue

@@ -24,7 +24,7 @@
       </scroll-view>
 
       <!-- 闽南语猜猜猜 -->
-      <view class="home-title">
+      <!-- <view class="home-title">
         <text>闽南语猜猜猜</text>
       </view>
       <Box2LinePlayRightArrow 
@@ -33,7 +33,7 @@
         @click="navTo('/pages/article/web/ewebview', {
           url: 'https://mn.wenlvti.net/assets/addons/yunexamine/h5/#/pages/home/dashboard'
         })"
-      />
+      /> -->
 
       <!-- 文化挑战 -->
       <HomeTitle title="文化挑战" />
@@ -47,7 +47,7 @@
         <image class="width-60 height-60 radius-base" src="https://mn.wenlvti.net/app_static/minnan/images/discover/IconCup.png" mode="aspectFill" />
       </Box2LineRightSlot> -->
       <Box2LineRightSlot
-        title="闽南知识问答"
+        title="非遗百科问答"
         desc="可获积分:500"
       >
         <view class="width-1-5">
@@ -109,6 +109,7 @@
             :image="item.thumbnail || item.image"
             :likes="item.likes"
             :comment="item.comments"
+            fixSize
             @click="goCultureDetail(item.id)"
           />
         </view>

+ 125 - 13
src/pages/home.vue

@@ -59,6 +59,28 @@
         </view>
       </view>
 
+      <!-- 数据统计 -->
+      <SimplePageContentLoader :loader="statsLoader">
+        <view v-if="statsLoader.content.value" class="d-flex flex-col mt-3 pt-3 b-3 b-0order-all-light-light-primary">
+          
+          <view class="d-flex flex-row">
+            <StatsText 
+              class="border-right-forth"
+              :title="statsLoader.content.value[0].title" 
+              :data="statsLoader.content.value[0].datas" 
+            />
+            <StatsText 
+              :title="statsLoader.content.value[1].title" 
+              :data="statsLoader.content.value[1].datas" 
+            />
+          </view>
+          <StatsText
+            class="border-top-forth pt-2 mt-3"
+            :data="statsLoader.content.value[2].datas" 
+          />
+        </view>
+      </SimplePageContentLoader>
+
       <!-- 文化地图 -->
       <HomeTitle title="文化地图" />
       <view class="position-relative radius-l overflow-hidden">
@@ -161,7 +183,6 @@
 </template>
 
 <script setup lang="ts">
-import Tabbar from '@/common/components/tabs/tabbar.vue';
 const MainBoxIcon1 = 'https://mn.wenlvti.net/app_static/minnan/images/home/MainBoxIcon1.png';
 const MainBoxIcon2 = 'https://mn.wenlvti.net/app_static/minnan/images/home/MainBoxIcon2.png';
 const MainBoxIcon3 = 'https://mn.wenlvti.net/app_static/minnan/images/home/MainBoxIcon3.png';
@@ -171,21 +192,25 @@ const MainBoxIcon6 = 'https://mn.wenlvti.net/app_static/minnan/images/home/MainB
 const MainBoxIcon7 = 'https://mn.wenlvti.net/app_static/minnan/images/home/MainBoxIcon7.png';
 const MainBoxIcon8 = 'https://mn.wenlvti.net/app_static/minnan/images/home/MainBoxIcon8.png';
 const ImageTest = 'https://mn.wenlvti.net/app_static/minnan/images/home/ImageTest.jpg';
-import HomeTitle from '@/pages/parts/HomeTitle.vue'; 
-import Box1AudioPlay from '@/pages/parts/Box1AudioPlay.vue';
-import SimplePageContentLoader from "@/common/components/SimplePageContentLoader.vue";
+
+import { ref, watch } from 'vue';
 import { navTo } from '@/common/utils/PageAction';
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
+import { useSimpleListAudioPlayer } from '@/common/composeabe/SimpleAudioPlayer';
 import CommonContent, { GetContentListParams } from '@/api/CommonContent';
-import { ref, watch } from 'vue';
 import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
 import SeminarContent from '@/api/inheritor/SeminarContent';
 import ProjectsContent from '@/api/inheritor/ProjectsContent';
-import { useSimpleListAudioPlayer } from '@/common/composeabe/SimpleAudioPlayer';
 import ProductsContent from '@/api/inheritor/ProductsContent';
 import AppCofig from '@/common/config/AppCofig';
 import VillageApi from '@/api/inhert/VillageApi';
 import ScenicSpotContent from '@/api/fusion/ScenicSpotContent';
+import IndexContent from '@/api/introduction/IndexContent';
+import StatsText from './parts/StatsText.vue';
+import HomeTitle from '@/pages/parts/HomeTitle.vue'; 
+import Tabbar from '@/common/components/tabs/tabbar.vue';
+import Box1AudioPlay from '@/pages/parts/Box1AudioPlay.vue';
+import SimplePageContentLoader from "@/common/components/SimplePageContentLoader.vue";
 
 const subTabs1 = [
   { 
@@ -237,13 +262,7 @@ const subTabs2 = [
   { 
     name: '老字号', 
     icon: MainBoxIcon7 , 
-    onClick: () => navTo('/pages/article/common/list', {
-      title: '老字号',
-      mainBodyColumnId: 312,
-      modelId: 17,
-      itemType: 'article-common',
-      detailsPage: '/pages/article/details',
-    }) 
+    onClick: () => navTo('/pages/inhert/old/list') 
   },
   { 
     name: '传统村落', 
@@ -358,6 +377,99 @@ const recommendLoader = useSimpleDataLoader(async () => {
   }));
   return list;
 });
+const statsLoader = useSimpleDataLoader(async () => {
+  const data = (await IndexContent.getStats());
+  console.log(data);
+
+  const semiCount = (await SeminarContent.getContentList(new GetContentListParams(), 1, 6)).total;
+  const unmoveableCount = (await UnmoveableContent.getContentList(new GetContentListParams(), 1, 6)).total;
+  const villageCount = (await VillageApi.getVallageList()).length;
+  
+  return [
+    {
+      title: '非遗项目',
+      type: '1',
+      datas: data.ichData.map((item: any) => {
+        return {
+          title: item.level_text,
+          value: item.total,
+          onClick: () => navTo('/pages/inhert/intangible/list', { tab: 0, level: item.level_text }),
+        }
+      })
+    },
+    {
+      title: '非遗传承人',
+      type: '2',
+      datas: data.inheritorData.map((item: any) => {
+        return {
+          title: item.title,
+          value: item.total,
+          onClick: () => navTo('/pages/inhert/inheritor/list', { level: item.title }),
+        }
+      })
+    },
+    {
+      datas: [
+        {
+          title: '非遗传习所',
+          value: semiCount,
+          onClick: () => navTo('/pages/inhert/map/index', { tab: 1 }),
+        },
+        {
+          title: '传统村落',
+          value: villageCount,
+          onClick: () => navTo('/pages/inhert/village/list'),
+        },
+        {
+          title: '文物古迹',
+          value: unmoveableCount,
+          onClick: () => navTo('/pages/inhert/artifact/list'),
+        },
+      ],
+    },
+    {
+      title: '不可移动文物',
+      type: '3',
+      datas: data.crData.map((item: any) => {
+        return {
+          title: item.title,
+          value: item.total
+        }
+      })
+    },
+    {
+      title: '闽南文化重要相关文物古迹',
+      type: '2',
+      datas: data.minnanCr.map((item: any) => {
+        return {
+          title: item.title,
+          value: item.total
+        }
+      })
+    },
+    {
+      title: '重要相关历史风貌区',
+      type: '1',
+      datas: data.historyData.map((item: any) => {
+        return {
+          title: item.title,
+          value: item.total
+        }
+      })
+    },
+    {
+      title: '传习中心',
+      type: '3',
+      datas: data.ichCenter.map((item: any) => {
+        return {
+          title: item.title,
+          value: item.total
+        }
+      })
+    },
+  ]
+
+});
 
 function handleGoDetails(item: any) {
   switch (item.itemType) {

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

@@ -75,14 +75,14 @@ const listLoader = useSimplePageListLoader<{
     modelId: 17, 
     mainBodyColumnId: '255,256,283,284',
   }), page, pageSize);
-  return res.list.map((item) => {
+  return { list: res.list.map((item) => {
     return {
       id: item.id,
       image: item.thumbnail || item.image,
       title: item.title,
       date: DataDateUtils.formatDate(item.publishAt, 'YYYY-MM-dd'),
     }
-  })
+  }), total: res.total }
 });
 
 function goDetail(id: number) {

+ 57 - 13
src/pages/inhert.vue

@@ -16,34 +16,34 @@
               v-for="(item, i) in intangibleData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :desc="item.desc"
               :image="item.image"
-              :bottomLocate="item.bottomLocate"
-              :bottomScore="item.bottomScore"
+              :tags="item.bottomTags"
               @click="navTo('inhert/intangible/details', { id: item.id })"
             />
           </view>
         </scroll-view>
       </SimplePageContentLoader>
       
-      <!-- 文物 -->
-      <HomeTitle title="文物古迹" showMore inWing @clickMore="navTo('inhert/artifact/list')" />
-      <SimplePageContentLoader :loader="artifactData">
+      <!-- 非遗传承人 -->
+      <HomeTitle title="非遗传承人" showMore inWing @clickMore="navTo('inhert/inheritor/list')" />
+      <SimplePageContentLoader :loader="inheritorData">
         <scroll-view scroll-x>
           <view class="padding-wing-l pb-3 pt-3 d-flex flex-row overflow-visible align-stretch">
             <Box2LineLargeImageUserShadow
-              v-for="(item, i) in artifactData.content.value"
+              v-for="(item, i) in inheritorData.content.value"
               classNames="width-2-5 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :image="item.image"
-              :likes="item.likes"
-              :comment="item.comment"
+              :tags="item.bottomTags"
               title1
-              @click="navTo('inhert/artifact/details', { id: item.id })"
+              @click="navTo('inhert/inheritor/details', { id: item.id })"
             />
           </view>
         </scroll-view>
@@ -58,6 +58,7 @@
               v-for="(item, i) in productsData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :desc="item.desc"
@@ -70,6 +71,28 @@
         </scroll-view>
       </SimplePageContentLoader>
 
+      <!-- 文物 -->
+      <HomeTitle title="文物古迹" showMore inWing @clickMore="navTo('inhert/artifact/list')" />
+      <SimplePageContentLoader :loader="artifactData">
+        <scroll-view scroll-x>
+          <view class="padding-wing-l pb-3 pt-3 d-flex flex-row overflow-visible align-stretch">
+            <Box2LineLargeImageUserShadow
+              v-for="(item, i) in artifactData.content.value"
+              classNames="width-2-5 mr-2"
+              titleColor="title-text"
+              fixSize
+              :key="i"
+              :title="item.title"
+              :image="item.image"
+              :likes="item.likes"
+              :comment="item.comment"
+              title1
+              @click="navTo('inhert/artifact/details', { id: item.id })"
+            />
+          </view>
+        </scroll-view>
+      </SimplePageContentLoader>
+
       <!-- 闽南语在线课程 -->
       <HomeTitle title="闽南语在线课程" showMore inWing @clickMore="goCourseList" />
       <SimplePageContentLoader :loader="corseData" >
@@ -79,6 +102,7 @@
               v-for="(item, i) in corseData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :desc="item.desc"
@@ -118,13 +142,14 @@
       </view>
 
       <!-- 老字号 -->
-      <HomeTitle title="老字号" showMore inWing @clickMore="goOldList" />
+      <HomeTitle title="老字号" showMore inWing @clickMore="navTo('/pages/inhert/old/list')" />
       <SimplePageContentLoader :loader="oldData">
         <scroll-view scroll-x>
           <view class="padding-wing-l pb-3 pt-3 d-flex flex-row overflow-visible align-stretch">
             <Box2LineLargeImageUserShadow
               v-for="(item, i) in oldData.content.value"
               classNames="width-2-3 mr-2"
+              fixSize
               titleColor="title-text"
               :key="i"
               :title="item.title"
@@ -132,7 +157,7 @@
               :image="item.thumbnail"
               :bottomLocate="(item.regionText as string)"
               :bottomScore="''"
-              @click="goOldDetail(item.id)"
+              @click="navTo('/pages/inhert/intangible/details', { id: item.id })"
             />
           </view>
         </scroll-view>
@@ -147,6 +172,7 @@
               v-for="(item, i) in memoryData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :desc="item.desc"
@@ -191,6 +217,7 @@ import ProjectsContent from '@/api/inheritor/ProjectsContent';
 import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
 import { useHomePageMiniCommonListGoMoreAndGoDetail } from './article/common/CommonContent';
 import ProductsContent from '@/api/inheritor/ProductsContent';
+import InheritorContent from '@/api/inheritor/InheritorContent';
 
 const artifactData = useSimpleDataLoader(async () => 
   (await UnmoveableContent.getContentList(new GetContentListParams(), 1, 4)).list.map(p => ({
@@ -208,8 +235,25 @@ const intangibleData = useSimpleDataLoader(async () =>
     title: p.title, 
     desc: p.desc, 
     image: p.thumbnail || p.image,
-    bottomLocate: p.area as string,
-    bottomScore: '5.0',
+    bottomTags: [
+      p.levelText, 
+      p.ichTypeText, 
+      p.batchText,
+      p.regionText,
+    ] as string[],
+  }))
+);
+const inheritorData = useSimpleDataLoader(async () => 
+  (await InheritorContent.getContentList(new GetContentListParams(), 1, 4)).list.map(p => ({
+    id: p.id,
+    title: p.title, 
+    desc: '', 
+    image: p.thumbnail || p.image,
+    bottomTags: [
+      p.levelText, 
+      p.nation,
+      p.ichName
+    ] as string[],
   }))
 );
 

+ 2 - 3
src/pages/inhert/artifact/list.vue

@@ -1,6 +1,5 @@
 <template>
   <view class="d-flex flex-column bg-base">
-    <u-navbar title="文物" autoBack placeholder bgColor="transparent" />
     <u-tabs 
       :list="tabs" 
       lineWidth="30"
@@ -124,13 +123,13 @@ const listLoader = useSimplePageListLoader<{
     region: selectedRegion.value == 0 ? undefined: selectedRegion.value,
     keywords: searchValue.value,
   }), page, pageSize);
-  return res.list.map((item) => {
+  return { list: res.list.map((item) => {
     return {
       id: item.id,
       image: item.thumbnail || item.image || AppCofig.defaultImage,
       name: item.title,
     }
-  })
+  }), total: res.total }
 });
 
 watch(selectedLevel, () => {

+ 36 - 19
src/pages/inhert/inheritor/details.vue

@@ -60,33 +60,38 @@
         small
         :descItems="[
           {
-            label: '传承项目',
-            value: content.associationMeList[0]?.title || '暂无',
-          }, 
-          {
-            label: '级别',
-            value: content.levelLext || '暂无',
-          }, 
-          {
-            label: '生日',
-            value: content.dateBirth,
-          }, 
+            label: '民族',
+            value: content.nation,
+          },
           {
             label: '性别',
             value: content.gender == '1'? '男' : '女', 
           },
           {
-            label: '出生',
-            value: content.birthplace,
-          },
+            label: '出生日期',
+            value: content.dateBirth,
+          }, 
           {
-            label: '民族',
-            value: content.nation,
+            label: '出生地区',
+            value: content.birthplace,
           },
           {
             label: '单位',
             value: content.unit,
-          }
+          },
+          
+          {
+            label: '传承项目',
+            value: content.associationMeList[0]?.title || '暂无',
+          }, 
+          {
+            label: '传承人级别',
+            value: content.levelLext || '暂无',
+          }, 
+          {
+            label: '公布批次',
+            value: content.batchText,
+          },
         ]"
       />
     </template>
@@ -110,8 +115,20 @@ async function load(id: number, tabsArray: Ref<TabControlItem[]>) {
   return d;
 }
 async function loadSubList(page: number, pageSize: number, content: any, subList: string) {
-  return (content[subList] as any[] || [])
-    .slice((page - 1) * pageSize, page * pageSize)
+  const list = (content[subList] as any[] || [])
+    .slice((page - 1) * pageSize, page * pageSize);
+
+  list.forEach((p) => {
+    p.bottomTags = [
+      p.levelText,
+      p.ichTypeText,
+      p.batchText
+    ];
+  });
+  return {
+    list,
+    total: list.length,
+  }
 }
 </script>
 

+ 23 - 10
src/pages/inhert/inheritor/list.vue

@@ -3,7 +3,8 @@
     title="非遗传承人"
     itemType="article-character"
     detailsPage="/pages/inhert/inheritor/details"
-    :dropdownNames="dropdownNames"
+    showTotal
+    :dropDownNames="dropdownNames"
     :load="loadData" 
   />
 </template>
@@ -12,6 +13,7 @@
 import CommonContent, { GetContentListParams } from '@/api/CommonContent';
 import InheritorContent from '@/api/inheritor/InheritorContent';
 import CommonListPage, { type DropDownNames } from '@/pages/article/common/CommonListPage.vue';
+import { onLoad } from '@dcloudio/uni-app';
 import { onMounted, ref } from 'vue';
 
 const dropdownNames = ref<DropDownNames[]>([]);
@@ -22,38 +24,49 @@ async function loadData(
   searchText: string,
   dropDownValues: number[]
 ) {
-  return (await InheritorContent.getContentList(new GetContentListParams().setSelfValues({
+  const res = (await InheritorContent.getContentList(new GetContentListParams().setSelfValues({
     ichType: dropDownValues[0] == 0 ? undefined: dropDownValues[0],
-    level: dropDownValues[1] == 0 ? undefined: dropDownValues[1],
+    ichLevel: dropDownValues[1] == 0 ? undefined: dropDownValues[1],
     region: dropDownValues[2] == 0 ? undefined: dropDownValues[2],
-  }), page, pageSize)).list;
+  }), page, pageSize));
+  res.list.forEach((p) => {
+    p.desc = p.ichName as string;
+    p.bottomTags = [
+      p.levelText, 
+      p.nation,
+      p.ichName,
+    ];
+  })
+  return res;
 }
 
-onMounted(async () => {
+onLoad(async (querys) => {
   dropdownNames.value.push({ 
     options: [{
       id: 0, 
-      name: '全部'
+      name: '全部类别'
     }].concat((await CommonContent.getCategoryList(4)).map((item) => ({
       id: item.id,
       name: item.title,
     }))),
     defaultSelectedValue: 0,
   });
+  const levels = await CommonContent.getCategoryList(2);
   dropdownNames.value.push({ 
     options: [{
       id: 0, 
-      name: '全部'
-    }].concat((await CommonContent.getCategoryList(2)).map((item) => ({
+      name: '全部级别'
+    }].concat(levels.map((item) => ({
       id: item.id,
       name: item.title,
     }))),
-    defaultSelectedValue: 0,
+    activeTab: 0,
+    defaultSelectedValue: querys?.level ? (levels.find(p => p.title == querys.level)?.id ?? 0) : 0,
   });
   dropdownNames.value.push({ 
     options: [{
       id: 0, 
-      name: '全部'
+      name: '全部区域'
     }].concat((await CommonContent.getCategoryList(1)).map((item) => ({
       id: item.id,
       name: item.title,

+ 43 - 8
src/pages/inhert/intangible/details.vue

@@ -73,23 +73,23 @@
           small
           :descItems="[
             {
-              label: '级别',
+              label: '项目级别',
               value: content.levelText ,
             },
             {
-              label: '类别',
+              label: '项目类别',
               value: content.ichTypeText,
             },
             {
-              label: '批次',
-              value: content.batch ,
+              label: '批次时间',
+              value: content.batchText,
             },
             {
               label: '所属区域',
               value: content.regionText ,
             },
             {
-              label: '单位',
+              label: '保护单位',
               value: content.unit 
             },
           ]"
@@ -108,19 +108,54 @@ import InheritorContent from "@/api/inheritor/InheritorContent";
 import ProductsContent from "@/api/inheritor/ProductsContent";
 import SeminarContent from "@/api/inheritor/SeminarContent";
 import IntroBlock from "@/pages/article/common/IntroBlock.vue";
+import { useLoadQuerys } from "@/common/composeabe/LoadQuerys";
 
 async function load(id: number, tabsArray: Ref<TabControlItem[]>) {
-  const d = await ProjectsContent.getContentDetail(id);
+  const d = await ProjectsContent.getContentDetail(
+    id, 
+    undefined, 
+    querys.value.modelId > 0 ? querys.value.modelId : undefined
+  );
   tabsArray.value[4].visible = Boolean(d.ichSitesList && (d.ichSitesList as any[]).length > 0);
   tabsArray.value[5].visible = Boolean(d.inheritorsList && (d.inheritorsList as any[]).length > 0);
   tabsArray.value[6].visible = Boolean(d.associationMeList && (d.associationMeList as any[]).length > 0);
   return d;
 }
 async function loadSubList(page: number, pageSize: number, content: any, subList: string) {
-  return (content[subList] as any[] || [])
-    .slice((page - 1) * pageSize, page * pageSize)
+  const list = (content[subList] as any[] || [])
+    .slice((page - 1) * pageSize, page * pageSize);
+
+  if (subList == 'associationMeList') {
+    list.forEach((p) => {
+      p.bottomTags = [
+        p.levelText,
+        p.ichTypeText,
+        p.batchText,
+      ];
+    })
+  } else if (subList == 'ichSitesList') {
+    list.forEach((p) => {
+      p.bottomTags = [
+        content.levelText,
+        content.ichTypeText,
+      ];
+    })
+  } else if (subList == 'inheritorsList') {
+    list.forEach((p) => {
+      p.bottomTags = [
+        content.levelText,
+        p.nation,
+        content.ichTypeText,
+      ];
+    }) 
+  }
+  return {
+    list,
+    total: list.length,
+  }
 }
 
+const { querys } = useLoadQuerys({ modelId: 0 })
 </script>
 
 <style lang="scss">

+ 23 - 6
src/pages/inhert/intangible/list.vue

@@ -2,6 +2,7 @@
   <CommonListPage 
     title="非遗"
     itemType="image-large-2"
+    showTotal
     detailsPage="/pages/inhert/intangible/details"
     :dropDownNames="dropdownNames"
     :tabs="[
@@ -9,6 +10,7 @@
       { id: 1, name: '非遗作品' },
       { id: 2, name: '非遗传承人', jump: () => navTo('/pages/inhert/inheritor/list') },
     ]"
+    :startTab="startTab"
     :load="loadData" 
   />
 </template>
@@ -19,10 +21,11 @@ import ProductsContent from '@/api/inheritor/ProductsContent';
 import ProjectsContent from '@/api/inheritor/ProjectsContent';
 import { navTo } from '@/common/utils/PageAction';
 import CommonListPage, { type DropDownNames } from '@/pages/article/common/CommonListPage.vue';
+import { onLoad } from '@dcloudio/uni-app';
 import { onMounted, ref } from 'vue';
 
 const dropdownNames = ref<DropDownNames[]>([]);
-
+const startTab = ref(0);
 async function loadData(
   page: number, 
   pageSize: number,
@@ -36,15 +39,28 @@ async function loadData(
     default:
     case 1: api = ProductsContent; break;
   }
-  return (await api.getContentList(new GetContentListParams().setSelfValues({
+  const res = (await api.getContentList(new GetContentListParams().setSelfValues({
     ichType: tabSelect !== 0 || dropDownValues[0] == 0 ? undefined: dropDownValues[0],
     level: tabSelect !== 0 || dropDownValues[1] == 0 ? undefined: dropDownValues[1],
     region: tabSelect !== 0 || dropDownValues[2] == 0 ? undefined: dropDownValues[2],
     keyword: searchText,
-  }), page, pageSize)).list;
+  }), page, pageSize));
+
+  res.list.forEach((item) => {
+    item.bottomTags = [
+      item.levelText, 
+      item.ichTypeText, 
+      item.batchText,
+      item.regionText,
+    ]
+  })
+  return res;
 }
 
-onMounted(async () => {
+onLoad(async (querys) => {
+  if (querys?.tab) {
+    startTab.value = Number(querys.tab); 
+  }
   dropdownNames.value.push({ 
     options: [{
       id: 0, 
@@ -56,16 +72,17 @@ onMounted(async () => {
     activeTab: 0,
     defaultSelectedValue: 0,
   });
+  const levels = await CommonContent.getCategoryList(2);
   dropdownNames.value.push({ 
     options: [{
       id: 0, 
       name: '全部级别'
-    }].concat((await CommonContent.getCategoryList(2)).map((item) => ({
+    }].concat(levels.map((item) => ({
       id: item.id,
       name: item.title,
     }))),
     activeTab: 0,
-    defaultSelectedValue: 0,
+    defaultSelectedValue: querys?.level ? (levels.find(p => p.title == querys.level)?.id ?? 0) : 0,
   });
   dropdownNames.value.push({ 
     options: [{

+ 18 - 16
src/pages/inhert/map/index.vue

@@ -123,44 +123,46 @@ const selectedLevel = ref(0);
 const selectedRegion = ref(0);
 const searchValue = ref('');
 const listLoader = useSimplePageListLoader(50, async (page, pageSize) => {
-  let list;
+  let res;
   switch (tabCurrentIndex.value) {
     default:
     case 0:
-      list = (await ProjectsContent.getContentList(new GetContentListParams().setSelfValues({
+      res = (await ProjectsContent.getContentList(new GetContentListParams().setSelfValues({
         level: selectedLevel.value == 0 ? undefined: selectedLevel.value,
         region: selectedRegion.value == 0 ? undefined: selectedRegion.value,
         keywords: searchValue.value,
-      }), page, pageSize)).list;
+      }), page, pageSize));
       break;
     case 1:
-      list = (await SeminarContent.getContentList(new GetContentListParams().setSelfValues({
+      res = (await SeminarContent.getContentList(new GetContentListParams().setSelfValues({
         level: selectedLevel.value == 0 ? undefined: selectedLevel.value,
         region: selectedRegion.value == 0 ? undefined: selectedRegion.value,
         keywords: searchValue.value,
-      }), page, pageSize)).list;
+      }), page, pageSize));
       break;
     case 2:
-      list = (await UnmoveableContent.getContentList(new GetContentListParams().setSelfValues({
+      res = (await UnmoveableContent.getContentList(new GetContentListParams().setSelfValues({
         crType: selectedTag.value == 0 ? undefined: selectedTag.value,
         level: selectedLevel.value == 0 ? undefined: selectedLevel.value,
         region: selectedRegion.value == 0 ? undefined: selectedRegion.value,
         keywords: searchValue.value,
-      }), page, pageSize)).list;
+      }), page, pageSize));
       break;
-    case 3:
-      list = (await VillageApi.getVallageList()).filter(p => 
+    case 3: {
+      const list = (await VillageApi.getVallageList()).filter(p => 
         (!searchValue.value || p.villageName.indexOf(searchValue.value) > -1)
-      );
+      )
+      res = { list, total: list.length };
       break;
+    }
     case 4:
-      list = (await ScenicSpotContent.getContentList(new GetContentListParams().setSelfValues({
+      res = (await ScenicSpotContent.getContentList(new GetContentListParams().setSelfValues({
         keywords: searchValue.value,
-      }), page, pageSize)).list
+      }), page, pageSize))
       break;
   }
   
-  const res = list.map((p) => {
+  const makers = res.list.map((p) => {
     if (!p.longitude || !p.latitude) {
       p.longitude = AppCofig.defaultLonLat[0] + Math.random() * 0.1 - 0.05;
       p.latitude = AppCofig.defaultLonLat[1] + Math.random() * 0.1 - 0.05;
@@ -186,7 +188,7 @@ const listLoader = useSimplePageListLoader(50, async (page, pageSize) => {
     }
   })
   mapCtx.includePoints({
-    points: res.map(p => ({
+    points: makers.map(p => ({
       latitude: p.latitude,
       longitude: p.longitude,
     })),
@@ -194,9 +196,9 @@ const listLoader = useSimplePageListLoader(50, async (page, pageSize) => {
   });
   mapCtx.addMarkers({
     clear: true,
-    markers: res, 
+    markers: makers, 
   })
-  return res;
+  return { list: makers, total: res.total };
 }, true);
 
 watch(selectedLevel, () => {

+ 49 - 0
src/pages/inhert/old/list.vue

@@ -0,0 +1,49 @@
+<template>
+  <CommonListPage 
+    title="老字号"
+    itemType="article-common"
+    detailsPage="/pages/inhert/intangible/details"
+    showTotal
+    :dropDownNames="dropdownNames"
+    :detailsParams="{
+      mainBodyColumnId: 312,
+      modelId: 17,
+    }"
+    :load="loadData" 
+  />
+</template>
+
+<script setup lang="ts">
+import CommonContent, { GetContentListParams } from '@/api/CommonContent';
+import CommonListPage, { type DropDownNames } from '@/pages/article/common/CommonListPage.vue';
+import { onMounted, ref } from 'vue';
+
+const dropdownNames = ref<DropDownNames[]>([]);
+
+async function loadData(
+  page: number, 
+  pageSize: number,
+  searchText: string,
+  dropDownValues: number[]
+) {
+  const res = (await CommonContent.getContentList(new GetContentListParams()
+    .setModelId(17)
+    .setMainBodyColumnId(312)
+    .setSelfValues({
+    })
+  , page, pageSize));
+  res.list.forEach((p) => {
+    p.desc = p.ichName as string;
+    p.bottomTags = [
+      p.levelText, 
+      p.batchText,
+      p.ichTypeText,
+    ];
+  })
+  return res;
+}
+
+onMounted(async () => {
+  
+})
+</script>

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

@@ -26,6 +26,31 @@
 
         <view class="d-flex flex-col">
           <HomeTitle :title="data.villageName" />
+          <IntroBlock 
+            small
+            :descItems="[
+              {
+                label: '保护级别',
+                value: data.historyLevelRext ,
+              },
+              {
+                label: '非遗级别',
+                value: data.ichLevelText ,
+              },
+              {
+                label: '年份时间',
+                value: data.ageText,
+              },
+              {
+                label: '所属区域',
+                value: data.regionText ,
+              },
+              {
+                label: '保护编号',
+                value: data.code 
+              },
+            ]"
+          />
           <view class="mt-3 color-text-content">
             <u-parse :content="data.overview" :tagStyle="commonParserStyle"></u-parse>
             <text v-if="!data.overview" >无内容,请添加内容! {{ data.overview }}</text>
@@ -44,8 +69,8 @@
           </view>
         </view>
 
-        <view class="d-flex flex-col mt-3">
-         <HomeTitle title="位置" />
+        <view class="d-flex flex-col mt-3 mb-2">
+         <HomeTitle title="地理位置" />
           <map id="map"
             class="w-100 height-350 mt-3"
             :latitude="center[1]"
@@ -107,6 +132,7 @@ import { navTo } from '@/common/utils/PageAction';
 import { useSimpleDataLoader } from '@/common/composeabe/SimpleDataLoader';
 import { useHomePageMiniCommonListGoMoreAndGoDetail, type IHomePageMiniCommonListGoMoreAndGoDetail } from '@/pages/article/common/CommonContent';
 import ContentNote from '@/pages/parts/ContentNote.vue';
+import IntroBlock from '@/pages/article/common/IntroBlock.vue';
 
 const EmptyImage = 'https://mn.wenlvti.net/app_static/minnan/EmptyImage.png';
 
@@ -128,7 +154,7 @@ const { querys } = useLoadQuerys({
   id: 0,
 }, () => contentLoader.loadData());
 
-const data = ref({
+const data = ref<Record<string, any>>({
   images: [],
   overview: '',
   longitude: 0,

+ 45 - 51
src/pages/inhert/village/list.vue

@@ -1,66 +1,60 @@
 <template>
-  <view class="d-flex flex-column bg-base" style="min-height: 100vh;">
-    <view class="d-flex flex-col p-2">
-      <uni-search-bar 
-        v-model="searchValue"
-        radius="100" 
-        bgColor="#fff" 
-        placeholder="搜索特色村社" 
-        clearButton="auto" 
-        cancelButton="none"
-        @confirm="doSearch"
-      />
-    </view>
-    <view class="d-flex flex-row flex-wrap justify-between p-2">
-      <view
-        v-for="item in listLoader.list.value"
-        :key="item.id"
-        class="width-1-2"
-      >
-        <Box2LineLargeImageUserShadow 
-          classNames="ml-2 mb-3"
-          titleColor="title-text"
-          :image="item.image"
-          :title="item.name"
-          @click="goDetails(item)"
-        />
-      </view>
-    </view>
-    <SimplePageListLoader :loader="listLoader" />
-  </view>
+  <CommonListPage 
+    title="传统村落"
+    itemType="image-large-2"
+    detailsPage="custom"
+    showTotal
+    :dropDownNames="dropdownNames"
+    :load="loadData" 
+    @goCustomDetails="goDetails"
+  />
 </template>
 
 <script setup lang="ts">
-import { onMounted, ref, watch } from 'vue';
-import { useSimplePageListLoader } from '@/common/composeabe/SimplePageListLoader';
+import CommonListPage, { type DropDownNames } from '@/pages/article/common/CommonListPage.vue';
+import { onMounted, ref } from 'vue';
+import VillageApi, { VillageListItem } from '@/api/inhert/VillageApi';
 import { navTo } from '@/common/utils/PageAction';
-import SimplePageListLoader from '@/common/components/SimplePageListLoader.vue';
-import Box2LineLargeImageUserShadow from '@/pages/parts/Box2LineLargeImageUserShadow.vue';
-import VillageApi from '@/api/inhert/VillageApi';
 
-const searchValue = ref('');
-const listLoader = useSimplePageListLoader<{
-  id: number,
-  image: string,
-  name: string
-}>(8, async (page, pageSize) => {
-  const res = await VillageApi.getVallageList();
-  return res.map((item) => {
-    return {
-      ...item,
-      name: item.villageName,
-    }
+const dropdownNames = ref<DropDownNames[]>([]);
+
+async function loadData(
+  page: number, 
+  pageSize: number,
+  searchText: string,
+  dropDownValues: number[]
+) {
+  const list = dropDownValues[0] == 1 ? await VillageApi.getVallageList() : [];
+  list.forEach((p) => {
+    p.desc = p.ichName as string;
+    p.badge = p.district;
+    p.bottomTags = [
+      p.levelText, 
+      p.batchText,
+      p.ichTypeText,
+    ];
   })
-});
-function doSearch() {
-  listLoader.loadData(undefined, true);
+  return { list: list, total: list.length }
 }
+
 function goDetails(item: any) {
   uni.setStorageSync('VillageTemp', JSON.stringify(item));
   navTo('details', { id: item.id })
 }
 
-onMounted(() => {
-  doSearch();
+onMounted(async () => {
+  dropdownNames.value.push({ 
+    options: [
+      {
+        id: 0, 
+        name: '传统村落'
+      },
+      {
+        id: 1, 
+        name: '特色村舍'
+      },
+    ],
+    defaultSelectedValue: 1,
+  });
 })
 </script>

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

@@ -1,6 +1,12 @@
 <template>
   <view 
-    class="d-flex flex-row justify-between shadow-s radius-base mb-3 p-2 pt-3 pb-3 overflow-hidden"
+    class="d-flex flex-row flex-grow-1 justify-between shadow-s 
+    radius-base mb-3 p-2 pt-3 pb-3 overflow-hidden
+    border-all-light-light-primary"
+    :style="{ 
+      height: 'calc(100% - 20rpx)',
+      width: 'calc(100% - 10rpx)',
+    }"
     @click="$emit('click')"
   >
     <view class="d-flex flex-row w-100">
@@ -16,11 +22,11 @@
       />
       <view class="d-flex flex-col ml-3 flex-one width-500">
         <text :class="[
-          'color-primary',
+          'color-primary size-base',
           desc || title1 ? 'text-lines-1' : 'text-lines-2',
         ]">{{ title }}</text>
-        <text class="color-second text-lines-2 mt-2">{{ desc }}</text>
-        <RoundTags v-if="tags" :tags="tags" />
+        <text class="size-s color-second text-lines-2 mt-2">{{ desc }}</text>
+        <RoundTags v-if="tags" :tags="tags" small />
       </view>
     </view>
     <text class="color-primary-second-text size-ss">{{ right }}</text>

+ 29 - 1
src/pages/parts/Box2LineLargeImageUserShadow.vue

@@ -1,6 +1,17 @@
 <template>
   <view 
-    :class="'position-relative grid4-item d-flex flex-col shadow-l radius-l bg-base p-2 mb-2 overflow-hidden ' + classNames"
+    :class="[
+      'position-relative grid4-item',
+      'd-flex flex-col shadow-l radius-l bg-base p-2 mb-2 overflow-hidden',
+      'border-all-light-light-primary',
+      classNames,
+      fixSize ? 'flex-shrink-0' : ' flex-grow-1',
+    ]"
+    
+    :style="{ 
+      height: 'calc(100% - 20rpx)',
+      width: fixSize ? undefined : 'calc(100% - 10rpx)',
+    }"
     @click="$emit('click')"
   >
     <image 
@@ -27,6 +38,7 @@
     >
       {{ title }}
     </text>
+    <text v-if="badge" class="position-absolute color-primary-text size-s bg-light-primary radius-base p-1 radius-s text-lines-1 r-0 t-0 mr-3 mt-3">{{ badge }}</text>
     <text v-if="desc" class="color-second text-lines-2 mt-2">{{ desc }}</text>
     <view v-if="likes !== undefined && comment !== undefined" class="d-flex flex-row mt-2">
       <image class="width-40 mr-2" :src="IconHeart" mode="widthFix" />
@@ -34,6 +46,7 @@
       <image class="width-40 mr-2" :src="IconChat" mode="widthFix" />
       <text class="size-s">{{ comment }}</text>
     </view>
+    <RoundTags v-if="tags" :tags="tags" small />
     <view v-if="bottomTime" class="d-flex flex-row mt-2">
       <image class="width-40 mr-2" :src="IconTime" mode="widthFix" />
       <text class="size-s mr-3">{{ bottomTime }}</text>
@@ -52,6 +65,9 @@
 </template>
 
 <script setup lang="ts">
+import type { PropType } from 'vue';
+import RoundTags from './RoundTags.vue';
+
 const IconHeart = 'https://mn.wenlvti.net/app_static/minnan/images/discover/IconHeart.png';
 const IconChat = 'https://mn.wenlvti.net/app_static/minnan/images/discover/IconChat.png';
 const IconLocation = 'https://mn.wenlvti.net/app_static/minnan/images/inhert/IconLocation.png';
@@ -74,6 +90,18 @@ defineProps({
     type: Boolean,
     default: false,
   },
+  fixSize: {
+    type: Boolean,
+    default: false,
+  },
+  badge: {
+    type: String,
+    default: '',
+  },
+  tags: {
+    type: Array as PropType<string[]>,
+    default: [],
+  },
   videoMark: {
     type: Boolean,
     default: false,

+ 2 - 2
src/pages/parts/ContentNote.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="d-flex flex-row justify-content-center align-items-center p-3">
-    <img class="mr-2 width-40 height-40 flex-shrink-0" src="https://mn.wenlvti.net/app_static/minnan/images/icon_info.svg" />
-    <span class="color-text-content-second size-s">
+    <img class="visible-hidden mr-2 width-40 height-40 flex-shrink-0" src="https://mn.wenlvti.net/app_static/minnan/images/icon_info.svg" />
+    <span class="visible-hidden color-text-content-second size-s">
       此平台为公益平台,部分信息来源于网络,如涉侵权,请联系我们删除
       <br>联系邮箱:153168270@qq.com
     </span>

+ 2 - 4
src/pages/parts/ImageSwiper.vue

@@ -9,11 +9,10 @@
   >
     <swiper-item v-for="(item, key) in images" :key="key">
       <view class="item">
-        <ImageWrapper
+        <image
           :src="item"
+          class="w-100 radius-base"
           mode="aspectFill"
-          width="750rpx"
-          radius="20rpx"
           @click="onPreviewImage(key)"
         />
       </view>
@@ -22,7 +21,6 @@
 </template>
 
 <script setup lang="ts">
-import ImageWrapper from '@/common/components/ImageWrapper.vue';
 import { useSwiperImagePreview } from '@/common/composeabe/SwiperImagePreview';
 import type { PropType } from 'vue';
 

+ 12 - 2
src/pages/parts/RoundTags.vue

@@ -1,9 +1,15 @@
 <template>
-  <view class="d-flex flex-row flex-wrap mt-2">
+  <view 
+    class="d-flex flex-row flex-wrap mt-2"
+  >
     <view 
       v-for="(tag, k) in tags"
       :key="k" 
-      class="bg-place radius-ll p-25 pt-1 mr-2"
+      class="bg-place mr-2 mb-2"
+      :class="[
+        tag ? '' : 'd-none',
+        small ? 'radius-l p-2 pt-0 pb-1' : 'radius-ll p-25 pt-1',
+      ]"
     >
       <text class="color-text-content-second size-ss">{{ tag }}</text>
     </view>
@@ -22,6 +28,10 @@ const props = defineProps({
     type: Array as PropType<string[]>,
     default: null,
   },
+  small: {
+    type: Boolean,
+    default: false, 
+  }
 })
 
 const tagss = computed(() => {

+ 97 - 0
src/pages/parts/StatsText.vue

@@ -0,0 +1,97 @@
+<template>
+  <view 
+    :class="[
+      'main-stats-text',
+      title ? '' : 'no-title'
+    ]"
+  >
+    <text class="title">{{ title }}</text>
+    <view class="stats">
+      <view
+        v-for="(item, i) in data"
+        :key="i"
+        class="item"
+        @click="item.onClick"
+      >
+        <text class="number">{{ item.value }}</text>
+        <text class="sub-title">{{ item.title }}</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup lang="ts">
+import type { PropType } from 'vue';
+
+defineProps({	
+  title: {
+    type: String,
+    default: ''
+  },
+  data : {
+    type: Object as PropType<{
+      title: string,
+      value: string,
+      onClick?: () => void
+    }[]>,
+    default: () => ([])
+  }	
+})
+</script>
+
+<style lang="scss">
+@use "sass:map";
+@use "@/common/scss/define/colors.scss" as *;
+
+$color-primary: map.get($colors, "primary");
+$color-text: map.get($colors, "text");
+
+.main-stats-text {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+
+  .title {
+    font-size: 30rpx;
+    margin: 10rpx 0;
+    color: $color-primary;
+    text-align: center;
+  }
+  .stats {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    flex-wrap: wrap;
+    color: $color-text;
+
+    .item {
+      display: flex;
+      flex-direction: column;
+      text-align: center;
+      margin: 10rpx 40rpx;
+
+      .sub-title {
+        font-size: 24rpx;
+      }
+      .number {
+        font-size: 50rpx;
+        font-weight: bold;
+      }
+    }
+  }
+
+  &.no-title {
+    .stats .item {
+      width: calc(30% - 60rpx);
+      flex-direction: column-reverse;
+
+      .sub-title {
+        font-size: 30rpx;
+        color: $color-primary;
+        margin-bottom: 10rpx;
+      }
+    }
+  }
+}
+</style>

+ 3 - 0
src/pages/travel.vue

@@ -41,6 +41,7 @@
               v-for="(item, i) in routeData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :desc="item.desc"
@@ -60,6 +61,7 @@
               v-for="(item, i) in recommendData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               title1
@@ -81,6 +83,7 @@
               v-for="(item, i) in creativeData.content.value"
               classNames="width-2-3 mr-2"
               titleColor="title-text"
+              fixSize
               :key="i"
               :title="item.title"
               :image="item.thumbnail || item.image"

+ 5 - 1
src/pages/user/contribute/list.vue

@@ -42,7 +42,11 @@ const searchText = ref('');
 const listLoader = useSimplePageListLoader<GetContentListItem>(
   8, 
   async (page, pageSize, params) => {
-  return await ContributeApi.getContributeList(page, pageSize);
+  const list = await ContributeApi.getContributeList(page, pageSize);
+  return {
+    list,
+    total: list.length,
+  }
 });
 
 function goDetail(id: number) {

+ 1 - 1
src/pages/video/list.vue

@@ -60,7 +60,7 @@ const listLoader = useSimplePageListLoader<
     modelId: params.modelId, 
     mainBodyColumnId: params.mainBodyColumnId ,
   }), page, pageSize);
-  return res.list;
+  return res;
 });
 
 function goDetail(id: number) {