Procházet zdrojové kódy

📦 村社首页魅力乡源进入采集列表优化

快乐的梦鱼 před 1 měsícem
rodič
revize
e7d5a9d403

+ 4 - 0
src/api/inhert/VillageApi.ts

@@ -175,6 +175,9 @@ export class VillageCatalogListItem extends DataModel<VillageCatalogListItem> {
     this.setNameMapperCase('Camel', 'Snake');
     this._convertTable = {
       id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
+      childlist: [
+        { clientSide: 'array', clientSideChildDataModel: VillageCatalogListItem, serverSide: 'undefined' },
+      ],
     }
     this._nameMapperServer = {
     };
@@ -207,6 +210,7 @@ export class VillageCatalogListItem extends DataModel<VillageCatalogListItem> {
   pid = 0;
   collectModuleId = 0;
   collectModuleName = '';
+  childlist = [] as VillageCatalogListItem[];
   villageName = '';
   spacer = '';
   haschild = false;

+ 7 - 0
src/pages.json

@@ -292,6 +292,13 @@
           }
         },
         {
+          "path": "forms/list-ordinary",
+          "style": {
+            "navigationBarTitleText": "普通信息列表",
+            "enablePullDownRefresh": true
+          }
+        },
+        {
           "path": "forms/submits",
           "style": {
             "navigationBarTitleText": "我的投稿",

+ 2 - 54
src/pages/chat/components/ChatMessage.vue

@@ -62,7 +62,7 @@
           </div>
           <!-- 图片内容 -->
           <div v-if="message.type === 'image_url' && message.image_url" class="image-content">
-            <image :src="message.image_url" />
+            <image class="img" :src="message.image_url" />
           </div>
           <!-- 音频内容 -->
           <div v-if="message.type === 'input_audio' && message.input_audio?.data" class="audio-content">
@@ -395,7 +395,7 @@ function onLongpress() {
 
       //不同类型的内容样式
       .image-content {
-        img {
+        .img {
           max-width: 300px;
           height: auto;
           object-fit: cover;
@@ -462,11 +462,6 @@ function onLongpress() {
     overflow: hidden;
     max-width: 100%;
 
-    span {
-      overflow: hidden;
-      text-overflow: ellipsis;
-    }
-
     &.expanded {
       overflow: hidden;
 
@@ -489,31 +484,6 @@ function onLongpress() {
     margin-top: 10px;
     gap: 2px;
   }
-  .message-actions {
-    position: absolute;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    bottom: -26px;
-    right: 6px;
-    left: 6px;
-    visibility: hidden;
-    z-index: 10;
-    gap: 12px;
-
-    > div {
-      display: flex;
-      align-items: center;
-      gap: 6px;
-    }
-
-    .time {
-      font-size: 0.8rem;
-      color: var(--color-text-light);
-      margin: 0 6px;
-      margin-top: 3px;
-    }
-  }
   .select-container {
     padding: 14px 0;
   }
@@ -528,10 +498,6 @@ function onLongpress() {
       border: 1px solid var(--color-border);
       border-top-right-radius: 0;
     }
-    .message-actions {
-      left: unset;
-      right: 46px;
-    }
   }
   &.type-system,
   &.type-tool,
@@ -542,17 +508,6 @@ function onLongpress() {
       border: 1px solid var(--color-border-light);
       border-top-left-radius: 0;
     }
-    .message-actions {
-      left: 46px;
-      justify-content: flex-start;
-    }
-  }
-  &.type-assistant {
-    .message-actions {
-      left: 46px;
-      right: 66px;
-      justify-content: space-between;
-    }
   }
   &.type-tool {
     .message {
@@ -563,13 +518,6 @@ function onLongpress() {
   }
 
   &.mobile {
-    .message-actions {
-      visibility: visible;
-      justify-content: space-between;
-      left: 0px;
-      right: 0px;
-      bottom: -36px;
-    }
     .message {
       padding: 5px 10px;
     }

+ 52 - 58
src/pages/dig/components/CollectModuleList.vue

@@ -25,16 +25,16 @@
 import { onMounted, ref, watch } from 'vue';
 import { useCollectStore } from '@/store/collect';
 import { useTaskEntryForm } from '../forms/composeable/TaskEntryForm';
-import { TaskMenuDef, type TaskMenuDefGoForm, type TaskMenuDefItem } from '../forms/tasks';
+import { useAuthStore } from '@/store/auth';
+import { type TaskMenuDefGoForm, type TaskMenuDefItem } from '../forms/tasks';
 import { alert } from '@/components/utils/DialogAction';
 import { getVillageInfoForm } from '../forms/forms';
 import { navTo } from '@/components/utils/PageAction';
-import { TaskRootDef } from '../forms/tasks';
 import FlexCol from '@/components/layout/FlexCol.vue';
 import Image from '@/components/basic/Image.vue';
 import TaskList from '../components/TaskList.vue';
 import VillageApi from '@/api/inhert/VillageApi';
-import { useAuthStore } from '@/store/auth';
+import { waitTimeOut } from '@imengyu/imengyu-utils';
 
 const { goForm, goPreviewForm } = useTaskEntryForm();
 const authStore = useAuthStore();
@@ -51,65 +51,59 @@ const props = withDefaults(defineProps<{
   isView: false,
 });
 
+
 async function loadList() {
-  if (props.taskName) {
-    currentTaskDefItems.value = TaskMenuDef[props.taskName].list.concat();
-    currentTaskBanner.value = TaskMenuDef[props.taskName].banner;
-  } else {
-    currentTaskDefItems.value = TaskRootDef
-  }
-  if (props.taskPid >= 0) {
-    const res = (await VillageApi.getCatalogList(
-      props.villageId, 
-      authStore.isAdmin ? undefined : props.villageVolunteerId,
-      props.taskPid
-    ));
-    if (res.length === 0)
-      return;
-    currentTaskDefItems.value = res
-      .map(item => {
-        try {
-          const collectModuleInternalName = getCollectModuleInternalNameById(item.collectModuleId);
-          const formDefine = collectModuleInternalName ? getVillageInfoForm(collectModuleInternalName, -1) : undefined;
-          return {
-            ...item,
-            extra: authStore.isAdmin && item.total >= 0 ? `已采编 ${item.total}` : '',
-            enable: canCollectCatalog(item.id),
-            catalogItem: item,
-            goForm: collectModuleInternalName ? [ 
-              collectModuleInternalName, 
-              item.typeId ?? -1, 
-              formDefine?.[2].typeName, 
-              collectModuleInternalName === 'overview' ? 'common' : undefined,
-              item.title,
-              item.id
-            ] as TaskMenuDefGoForm : undefined,
-            onClick: () => {
-              if (item.haschild) {
-                navTo('/pages/dig/forms/task', {
-                  ...props,
-                  taskName: '',
-                  taskTitle: item.title,
-                  taskPid: item.id,
-                  isView: props.isView,
-                })
-              } else {
-                alert({
-                  title: item.title,
-                  content: '您暂无权限采集该板块',
-                })
-              }
+  await waitTimeOut(400);
+
+  const res = (await VillageApi.getCatalogList(
+    props.villageId, 
+    authStore.isAdmin ? undefined : props.villageVolunteerId,
+    props.taskPid
+  ));
+  if (res.length === 0)
+    return;
+  currentTaskDefItems.value = res
+    .map(item => {
+      try {
+        const collectModuleInternalName = getCollectModuleInternalNameById(item.collectModuleId);
+        const formDefine = collectModuleInternalName ? getVillageInfoForm(collectModuleInternalName, -1) : undefined;
+        return {
+          ...item,
+          extra: authStore.isAdmin && item.total >= 0 ? `已采编 ${item.total}` : '',
+          enable: canCollectCatalog(item.id) || collectModuleInternalName,
+          catalogItem: item,
+          goForm: !item.haschild && collectModuleInternalName ? [ 
+            collectModuleInternalName, 
+            item.typeId ?? -1, 
+            formDefine?.[2].typeName, 
+            collectModuleInternalName === 'overview' ? 'common' : undefined,
+            item.title,
+            item.id
+          ] as TaskMenuDefGoForm : undefined,
+          onClick: () => {
+            if (item.haschild) {
+              navTo('/pages/dig/forms/task', {
+                ...props,
+                taskTitle: item.title,
+                taskPid: item.id,
+                isView: props.isView,
+              })
+            } else {
+              alert({
+                title: item.title,
+                content: '您暂无权限采集该板块',
+              })
             }
           }
-        } catch (e) {
-          return {
-            ...item,
-            desc: '' + (e instanceof Error ? e.message : e),
-            enable: false,
-          }
         }
-      });
-  }
+      } catch (e) {
+        return {
+          ...item,
+          desc: '' + (e instanceof Error ? e.message : e),
+          enable: false,
+        }
+      }
+    });
 }
 
 watch(() => props.taskPid, loadList);

+ 7 - 15
src/pages/dig/forms/common.vue

@@ -101,23 +101,15 @@ const { querys } = useLoadQuerys({
       readonly: querys.isView,
       disabled: querys.isView,
     };
-    if (querys.id >= 0) {
-      let findId = querys.id;
+    let findId = querys.id;
+    if (findId <= 0) {
       if (querys.subType === 'overview') {
-        const list = await VillageInfoApi.getList(
-          collectStore.getCollectModuleId(querys.subType), 
-          querys.subType, 
-          undefined, 
-          undefined, 
-          querys.villageId, 
-          querys.villageVolunteerId,
-          undefined,
-          1,
-          1
-        );
-        if (list?.length > 0)
-          findId = list[0].id;
+        const info = await VillageInfoApi.getInfoByVillageId(querys.villageId);
+        console.log(info);
+        findId = (info.myOverviewId || info.overviewId) as number;
       }
+    }
+    if (findId > 0) {
       formData = await VillageInfoApi.getInfo(
         collectStore.getCollectModuleId(querys.subType),
         querys.subType, 

+ 3 - 3
src/pages/dig/forms/composeable/TaskEntryForm.ts

@@ -9,7 +9,7 @@ export function useTaskEntryForm() {
   
   function goForm(subType: string, subId: number, subKey = 'type', type = 'list', subTitle = '', catalogId : number|undefined = undefined) {
     navTo('/pages/dig/forms/' + type, {
-      id: type === 'common' ? 1 : undefined,
+      id: type === 'common' ? -1 : undefined,
       villageId: querys.value.villageId,  
       villageVolunteerId: querys.value.villageVolunteerId,  
       catalogId,
@@ -21,7 +21,7 @@ export function useTaskEntryForm() {
   }
   function goPreviewForm(subType: string, subId: number, subKey = 'type', type = 'list', subTitle = '', catalogId : number|undefined = undefined) {
     navTo('/pages/dig/forms/' + type, {
-      id: type === 'common' ? 1 : undefined,
+      id: type === 'common' ? -1 : undefined,
       villageId: querys.value.villageId,  
       villageVolunteerId: querys.value.villageVolunteerId,  
       catalogId,
@@ -41,7 +41,7 @@ export function useTaskEntryForm() {
 
 export function goFormStatic(villageId: number, villageVolunteerId: number, subType: string, subId: number, subKey = 'type', type = 'list', subTitle = '', catalogId : number|undefined = undefined) {
   navTo('/pages/dig/forms/' + type, {
-    id: type === 'common' ? 1 : undefined,
+    id: type === 'common' ? -1 : undefined,
     villageId,  
     villageVolunteerId,  
     catalogId,

+ 331 - 0
src/pages/dig/forms/list-ordinary.vue

@@ -0,0 +1,331 @@
+<template>
+  <FlexCol padding="space.md" gap="space.md">
+    <Result v-if="error" 
+      :title="error"
+    />
+    <template v-else>
+      <FlexRow justify="space-between" align="center" gap="space.sm">
+        <SearchBar
+          v-model="searchText"
+          placeholder="搜一搜" 
+          :innerStyle="{ width: '280rpx' }"
+          @search="search"
+        />
+        <picker @change="handleCatalogChange" :value="currentCatalogIndex" :range="currentCatalogList" range-key="name">
+          <FlexRow align="center" border="primary" radius="radius.md" padding="padding.sm">
+            <Text :text="(currentCatalog?.title || '未选择栏目')" :lines="1" :maxWidth="180" />
+            <Icon icon="arrow-down" :size="40" />
+          </FlexRow>
+        </picker>
+        <Button type="primary" @click="newData">+ 编写</Button>
+      </FlexRow>
+      <FlexCol 
+        v-if="!isJoined" 
+        center 
+        gap="gap.lg" 
+        border="primary" 
+        backgroundColor="background.tertiary"
+        radius="radius.md" 
+        padding="padding.md"
+      > 
+        <Image 
+          src="https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png" 
+          mode="widthFix" 
+          :width="200" 
+          :height="200" 
+        />
+        <Text>您还不是当前村社的志愿者</Text>
+        <Text>欢迎注册,加入志愿者队伍,点亮村落</Text>
+        <Button type="primary" @click="goJoin">去点亮村落</Button>
+      </FlexCol>
+      <Height :height="20" />
+      <SimplePageListLoader :loader="listLoader" :noEmpty="true">
+        <FlexCol :gap="20">
+          <Touchable 
+            v-for="item in listLoader.list.value"
+            :key="item.id" 
+            :gap="20"
+            :padding="[15,20]"
+            radius="radius.md"
+            align="center"
+            backgroundColor="white"
+            direction="row"
+            touchable
+            @click="goDetail(item)"
+          >
+            <Image 
+              :src="item.image"
+              :showFailed="false"
+              :width="100"
+              :height="100"
+              radius="radius.sm"
+              mode="aspectFill"
+              round
+            />
+            <FlexCol>
+              <H4 :size="36">{{ item.title }}</H4>
+              <Text :size="23">{{ item.desc }}</Text>
+            </FlexCol>
+          </Touchable>
+        </FlexCol>
+        <template #empty>
+          <Empty image="search" description="这里还没有数据,快来编写完善吧!">
+            <Height :height="40" />
+            <Button type="primary" :text="`+ 新增${subTitle}数据`" @click="newData" />
+          </Empty>
+        </template>
+      </SimplePageListLoader>
+    </template>
+    <XBarSpace />
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import { computed, ref } from 'vue';
+import { onPullDownRefresh } from '@dcloudio/uni-app';
+import { DataDateUtils } from '@imengyu/js-request-transform';
+import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePageListLoader';
+import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
+import { useCollectStore } from '@/store/collect';
+import { useAuthStore } from '@/store/auth';
+import { type TaskMenuDefGoForm, type TaskMenuDefItem } from './tasks';
+import { useUserTools } from '@/common/composeabe/UserTools';
+import { navTo } from '@/components/utils/PageAction';
+import { confirm, alert } from '@/components/utils/DialogAction';
+import SimplePageListLoader from '@/components/loader/SimplePageListLoader.vue';
+import VillageInfoApi from '@/api/inhert/VillageInfoApi';
+import Image from '@/components/basic/Image.vue';
+import Empty from '@/components/feedback/Empty.vue';
+import Button from '@/components/basic/Button.vue';
+import SearchBar from '@/components/form/SearchBar.vue';
+import FlexCol from '@/components/layout/FlexCol.vue';
+import FlexRow from '@/components/layout/FlexRow.vue';
+import Text from '@/components/basic/Text.vue';
+import Height from '@/components/layout/space/Height.vue';
+import H4 from '@/components/typography/H4.vue';
+import Touchable from '@/components/feedback/Touchable.vue';
+import XBarSpace from '@/components/layout/space/XBarSpace.vue';
+import Result from '@/components/feedback/Result.vue';
+import { getVillageInfoForm } from './forms';
+import VillageApi, { type VillageCatalogListItem } from '@/api/inhert/VillageApi';
+import Icon from '@/components/basic/Icon.vue';
+
+const subTitle = ref('');
+const searchText = ref('');
+const collectStore = useCollectStore();
+const { getCollectModuleInternalNameById } = collectStore;
+const { getIsVolunteer, getIsJoinedVillage, volunteerInfo } = useUserTools();
+const isJoined = ref(false);
+const authStore = useAuthStore();
+const error = ref('');
+
+const listLoader = useSimplePageListLoader<{
+  id: number,
+  image: string,
+  title: string,
+  desc: string,
+  villageVolunteerId: number,
+}, {
+  villageId: number,  
+  villageVolunteerId: number,
+  subType: string,
+  subId: number,
+  subKey: string,
+}>(8, async (page, pageSize, params) => {
+  if (!params )
+    throw new Error("未传入参数,当前页面需要参数");
+  if (!params.subType)
+    throw new Error("params.subType");
+  if (!params.villageId)
+    throw new Error("params.villageId");
+  let res = await VillageInfoApi.getList(
+    collectStore.getCollectModuleId(params.subType),
+    params.subType,
+    params.subKey ? params.subId : undefined,
+    params.subKey,
+    params.villageId,
+    authStore.isAdmin && params.villageVolunteerId ? undefined : params.villageVolunteerId,
+    currentCatalog.value?.id || 0,
+    page,
+    pageSize,
+  )
+  if (searchText.value)
+    res = res.filter((p) => p.title.includes(searchText.value));
+  const list = res.map((item) => {
+    return {
+      id: item.id,
+      image: item.image,
+      title: item.title,
+      villageVolunteerId: item.villageVolunteerId,
+      desc: DataDateUtils.formatDate(item.updatedAt, 'YYYY-MM-dd') + (
+        authStore.isAdmin ? (' 投稿人:' + item.villageVolunteerName) : ''
+      )
+    }
+  })
+  return {
+    list: list,
+    total: list.length,
+  };
+}, false);
+
+function newData() {
+  if (!isJoined.value) {
+    confirm({ 
+      title: '提示', 
+      content: "您还不是当前村社的志愿者,请先加入志愿者队伍", 
+      confirmText: '去点亮',
+    }).then((res) => {
+      if (res) {
+        goJoin();
+      }
+    });
+    return;
+  }
+  navTo('common', { 
+    id: -1,
+    villageId: querys.value.villageId,  
+    villageVolunteerId: querys.value.villageVolunteerId,  
+    catalogId: currentCatalog.value?.id || 0,
+    subType: currentLoadData.value.subType,  
+    subId: currentLoadData.value.subId,  
+  });
+}
+function goDetail(item: { id: number, villageVolunteerId: number }) {
+  if (!authStore.isAdmin 
+    && (!isJoined.value || item.villageVolunteerId !== volunteerInfo.value?.id)
+  ) {
+    //非当前村社志愿者或者不是自己提交的文章,只能查看详情
+    navTo('/pages/home/discover/details', {
+      villageId: querys.value.villageId,
+      id: item.id,
+    });
+  } else {
+    navTo('common', { 
+      id: item.id,
+      villageId: querys.value.villageId,
+      villageVolunteerId: querys.value.villageVolunteerId,
+      catalogId: currentCatalog.value?.id || 0,
+      subType: currentLoadData.value.subType,
+      subKey: currentLoadData.value.subKey,
+      subId: currentLoadData.value.subId,
+      subTitle: currentLoadData.value.subTitle,
+    });
+  }
+}
+function search() {
+  listLoader.reload();
+}
+function goJoin() {
+  navTo('/pages/home/light/submit', {
+    villageId: querys.value.villageId,
+  });
+}
+
+const catalogs = ref<VillageCatalogListItem[]>([]);
+const currentCatalogIndex = ref(0);
+const currentCatalogList = computed(() => {
+  return catalogs.value.map((p) => ({
+    id: p.id,
+    name: p.title,
+  }));
+});
+const currentCatalog = ref<VillageCatalogListItem | null>(null);
+const currentLoadData = ref({
+  subType: '',
+  subId: 0,
+  subKey: '',
+  subTitle: '',
+  catalogId: 0,
+  villageId: 0,
+  villageVolunteerId: 0,
+});
+
+function handleCatalogChange(e: any) {
+  currentCatalogIndex.value = e.detail.value;
+  currentCatalog.value = catalogs.value[currentCatalogIndex.value];
+  loadListCatalog(currentCatalog.value as VillageCatalogListItem);
+}
+
+function loadListCatalog(catalog: VillageCatalogListItem) {
+  if (currentCatalog.value?.id === catalog.id) 
+    return;
+  currentCatalog.value = catalog;
+  
+  const collectModuleInternalName = getCollectModuleInternalNameById(querys.value.collectModuleId);
+  if (!collectModuleInternalName) {
+    error.value = '任务不存在';
+    return;
+  }
+
+  const formDefine = collectModuleInternalName ? getVillageInfoForm(collectModuleInternalName, -1) : undefined;
+  const goForm = [ 
+    collectModuleInternalName, 
+    catalog.typeId ?? -1, 
+    formDefine?.[2].typeName, 
+    collectModuleInternalName === 'overview' ? 'common' : undefined,
+    catalog.title,
+    catalog.id
+  ] as TaskMenuDefGoForm;
+
+  currentLoadData.value = {
+    ...querys.value,
+    subType: goForm[0],
+    subId: goForm[1],
+    subKey: goForm[2] || 'type',
+    subTitle: goForm[4] || querys.value.title,
+    catalogId: goForm[5] || 0,
+    villageId: querys.value.villageId,
+    villageVolunteerId: volunteerInfo.value?.id || 0,
+  }
+  listLoader.load(false, currentLoadData.value)
+}
+
+const { querys } = useLoadQuerys({ 
+  collectModuleId: 0,
+  villageId: 0,  
+  villageVolunteerId: 0,
+  title: '',
+}, async (querys) => {
+  isJoined.value = await getIsJoinedVillage(querys.villageId);
+
+  //普通用户进入预览模式
+  await getIsVolunteer();
+
+  const catalogList = [] as VillageCatalogListItem[]; 
+  function pushCatalogWithCurrentCatalog(catalog: VillageCatalogListItem) {
+    if (catalog.collectModuleId === querys.collectModuleId) {
+      catalogs.value.push(catalog);
+    }
+    catalog.childlist.forEach((c) => {
+      pushCatalogWithCurrentCatalog(c);
+    });
+  }
+  (await VillageApi.getCatalogList(
+    querys.villageId, 
+    authStore.isAdmin ? undefined : querys.villageVolunteerId,
+  )).forEach((catalog) => {
+    pushCatalogWithCurrentCatalog(catalog);
+  });
+
+  if (catalogs.value.length === 0) {
+    error.value = '未找到指定的采集目录';
+    return;
+  }
+  loadListCatalog(catalogs.value[0] as VillageCatalogListItem);
+
+  if (querys.title) {
+    uni.setNavigationBarTitle({  title: querys.title, })
+  }
+});
+
+onPullDownRefresh(() => {
+  listLoader.reload();
+});
+
+defineExpose({
+  onPageBack(name: string, param: any) {
+    if (param && param.needRefresh)
+      listLoader.reload();
+  }
+})
+</script>

+ 54 - 63
src/pages/dig/forms/list.vue

@@ -13,55 +13,36 @@
         />
         <Button v-if="!querys.isView" type="primary" @click="newData">+ 编写</Button>
       </FlexRow>
-      <FlexCol 
-        v-if="!isJoined" 
-        center 
-        gap="gap.lg" 
-        border="primary" 
-        backgroundColor="background.tertiary"
-        radius="radius.md" 
-        padding="padding.md"
-      > 
-        <Image 
-          src="https://xy.wenlvti.net/app_static/images/village/PlaceholderVolunteer.png" 
-          mode="widthFix" 
-          :width="200" 
-          :height="200" 
-        />
-        <Text>您还不是当前村社的志愿者</Text>
-        <Text>欢迎注册,加入志愿者队伍,点亮村落</Text>
-        <Button type="primary" @click="goJoin">去点亮村落</Button>
-      </FlexCol>
       <Height :height="20" />
-      <FlexCol :gap="20">
-        <Touchable 
-          v-for="item in listLoader.list.value"
-          :key="item.id" 
-          :gap="20"
-          :padding="[15,20]"
-          radius="radius.md"
-          align="center"
-          backgroundColor="white"
-          direction="row"
-          touchable
-          @click="goDetail(item)"
-        >
-          <Image 
-            :src="item.image"
-            :showFailed="false"
-            :width="100"
-            :height="100"
-            radius="radius.sm"
-            mode="aspectFill"
-            round
-          />
-          <FlexCol>
-            <H4 :size="36">{{ item.title }}</H4>
-            <Text :size="23">{{ item.desc }}</Text>
-          </FlexCol>
-        </Touchable>
-      </FlexCol>
       <SimplePageListLoader :loader="listLoader" :noEmpty="true">
+        <FlexCol :gap="20">
+          <Touchable 
+            v-for="item in listLoader.list.value"
+            :key="item.id" 
+            :gap="20"
+            :padding="[15,20]"
+            radius="radius.md"
+            align="center"
+            backgroundColor="white"
+            direction="row"
+            touchable
+            @click="goDetail(item)"
+          >
+            <Image 
+              :src="item.image"
+              :showFailed="false"
+              :width="100"
+              :height="100"
+              radius="radius.sm"
+              mode="aspectFill"
+              round
+            />
+            <FlexCol>
+              <H4 :size="36">{{ item.title }}</H4>
+              <Text :size="23">{{ item.desc }}</Text>
+            </FlexCol>
+          </Touchable>
+        </FlexCol>
         <template #empty>
           <Empty v-if="querys.isView" image="search" description="暂无数据,等待志愿者编写中..." />
           <Empty v-else image="search" description="这里还没有数据,快来编写完善吧!">
@@ -83,7 +64,7 @@ import { useSimplePageListLoader } from '@/components/composeabe/loader/SimplePa
 import { useLoadQuerys } from '@/components/composeabe/LoadQuerys';
 import { useCollectStore } from '@/store/collect';
 import { useAuthStore } from '@/store/auth';
-import { TaskMenuDef, type TaskMenuDefGoForm, type TaskMenuDefItem } from './tasks';
+import { type TaskMenuDefGoForm } from './tasks';
 import { useUserTools } from '@/common/composeabe/UserTools';
 import { navTo } from '@/components/utils/PageAction';
 import { confirm, alert } from '@/components/utils/DialogAction';
@@ -101,10 +82,13 @@ import H4 from '@/components/typography/H4.vue';
 import Touchable from '@/components/feedback/Touchable.vue';
 import XBarSpace from '@/components/layout/space/XBarSpace.vue';
 import Result from '@/components/feedback/Result.vue';
+import { getVillageInfoForm } from './forms';
+import VillageApi from '@/api/inhert/VillageApi';
 
 const subTitle = ref('');
 const searchText = ref('');
 const collectStore = useCollectStore();
+const { getCollectModuleInternalNameById } = collectStore;
 const { getIsVolunteer, getIsJoinedVillage, volunteerInfo } = useUserTools();
 const isJoined = ref(false);
 const authStore = useAuthStore();
@@ -218,8 +202,7 @@ function goJoin() {
 }
 
 const { querys } = useLoadQuerys({ 
-  taskName: '',
-  taskFindCatalogName: '',
+  collectModuleId: 0,
   villageId: 0,  
   villageVolunteerId: 0,
   catalogId: 0,
@@ -231,26 +214,34 @@ const { querys } = useLoadQuerys({
 }, async (querys) => {
   isJoined.value = await getIsJoinedVillage(querys.villageId);
 
-  if (querys.taskName) {
+  if (querys.collectModuleId) {
     //普通用户进入预览模式
     await getIsVolunteer();
-    const menuDef = TaskMenuDef[querys.taskName];
-    if (!menuDef) {
+
+    const catalogList = (await VillageApi.getCatalogList(
+      querys.villageId, 
+      authStore.isAdmin ? undefined : querys.villageVolunteerId,
+    ));
+    const collectModuleInternalName = getCollectModuleInternalNameById(querys.collectModuleId);
+    if (!collectModuleInternalName) {
       error.value = '任务不存在';
       return;
     }
-    let catalog : TaskMenuDefItem | undefined ;
-    if (querys.taskFindCatalogName) {
-      querys.subTitle = querys.taskFindCatalogName;
-      catalog = menuDef.list.find((p) => p.title === querys.taskFindCatalogName);
-    } else {
-      catalog = menuDef.list[0];
-    }
-    if (!catalog || !catalog.goForm) {
-      error.value = '查询目录不存在';
+    console.log('catalogList', catalogList, 'find', querys.collectModuleId);
+    const catalog = catalogList.find((p) => p.collectModuleId === querys.collectModuleId || p.childlist.find((c) => c.collectModuleId === querys.collectModuleId));
+    if (!catalog) {
+      error.value = '未找到采集目录';
       return;
     }
-    const goForm = catalog.goForm as TaskMenuDefGoForm;
+    const formDefine = collectModuleInternalName ? getVillageInfoForm(collectModuleInternalName, -1) : undefined;
+    const goForm = [ 
+      collectModuleInternalName, 
+      catalog.typeId ?? -1, 
+      formDefine?.[2].typeName, 
+      collectModuleInternalName === 'overview' ? 'common' : undefined,
+      catalog.title,
+      catalog.id
+    ] as TaskMenuDefGoForm;
     querys.subType = goForm[0];
     querys.subId = goForm[1];
     querys.subKey = goForm[2] || 'type';

+ 0 - 371
src/pages/dig/forms/tasks.ts

@@ -22,375 +22,4 @@ export type TaskMenuDefItem = {
     name: string;
   }|TaskMenuDefGoForm;
   onClick?: () => void;
-}
-
-export const TaskRootDef : TaskMenuDefItem[] = [
-  {
-    title: '村社概况',
-    desc: '探索村社的历史渊源与发生轨迹',
-    icon: 'icon-task-summary',
-    enable: 'overview',
-    name: 'overview',
-    goForm: {
-      title: '村社概况',
-      name: 'overview',
-    }
-  },
-  {
-    title: '历史文化',
-    desc: '传承百年文化遗产和精神财富',
-    icon: 'icon-task-history',
-    enable: true,
-    name: 'history',
-    goForm: {
-      title: '历史文化',
-      name: 'history',
-    }
-  },
-  {
-    title: '非物质文化遗产项目',
-    desc: '维护文化多样性',
-    icon: 'icon-task-custom-1',
-    enable: 'ich',
-    name: 'ich',
-    goForm: [ 'ich', 0, undefined, undefined, '非物质文化遗产项目', undefined ],
-  },
-  {
-    title: '环境格局',
-    desc: '感受自然人文环境之美',
-    icon: 'icon-task-environment',
-    enable: 'environment',
-    name: 'environment',
-    goForm: {
-      title: '环境格局',
-      name: 'environment',
-    }
-  },
-  {
-    title: '传统建筑',
-    desc: '领略古建筑的独特魅力',
-    icon: 'icon-task-building',
-    enable: true,
-    name: 'building',
-    goForm: {
-      title: '传统建筑',
-      name: 'building',
-    }
-  },
-  {
-    title: '民俗文化',
-    desc: '体验民间传统习俗与节庆',
-    icon: 'icon-task-custom',
-    enable: 'folk_culture',
-    name: 'folk_culture',
-    goForm: {
-      title: '民俗文化',
-      name: 'custom',
-    }
-  },
-  {
-    title: '地道美食',
-    desc: '正宗、传统地方特色美食',
-    icon: 'icon-task-food',
-    enable: 'food_product',
-    name: 'food',
-    goForm: {
-      title: '地道美食',
-      name: 'food',
-    }
-  },
-  {
-    title: '物产资源',
-    desc: '特定地域的植物、矿物或工艺品',
-    icon: 'icon-task-mine',
-    enable: 'food_product',
-    name: 'product',
-    goForm: {
-      title: '物产资源',
-      name: 'product',
-    }
-  },
-  {
-    title: '旅游路线',
-    desc: '体验独特的文化魅力',
-    icon: 'icon-task-trip',
-    enable: true,
-    name: 'trip',
-    goForm: {
-      title: '旅游路线',
-      name: 'trip',
-    }
-  }
-]
-
-export const TaskMenuDef : Record<string, TaskMenuDefGroup> = {
-  'building': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_building.jpg',
-    list: [
-      {
-        title: '建筑分布',
-        desc: '村社内传统建筑分布情况',
-        icon: 'icon-task-building-1',
-        enable: 'distribution',
-        goForm: [ 'distribution', 0, undefined, undefined, '建筑分布', undefined ],
-      },
-      {
-        title: '文物建筑',
-        desc: '历史、艺术、科学价值',
-        icon: 'icon-task-building-2',
-        enable: 'building',
-        goForm: [ 'building', 1, 'nature', undefined, '文物建筑', undefined ],
-      },
-      {
-        title: '历史建筑',
-        desc: '重大历史事件记录',
-        icon: 'icon-task-building-3',
-        enable: 'building',
-        goForm: [ 'building', 2, 'nature', undefined, '历史建筑', undefined ],
-      },
-      {
-        title: '重要传统建筑',
-        desc: '重要传统建筑的信息',
-        icon: 'icon-task-building-4',
-        enable: 'building',
-        goForm: [ 'building', 3, 'nature', undefined, '重要传统建筑', undefined ],
-      },
-    ],
-  },
-  'custom': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_custom.jpg',
-    list: [
-      {
-        title: '节庆活动',
-        desc: '欢庆与传承并重的文化盛宴',
-        icon: 'icon-task-custom-2',
-        enable: 'folk_culture',
-        goForm: [ 'folk_culture', 1, 'folkCultureType', undefined, '节庆活动', undefined ],
-      },
-      {
-        title: '祭祀崇礼',
-        desc: '对先贤与自然的崇高致敬',
-        icon: 'icon-task-custom-3',
-        enable: 'folk_culture',
-        goForm: [ 'folk_culture', 2, 'folkCultureType', undefined, '祭祀崇礼', undefined ],
-      },
-      {
-        title: '婚丧嫁娶',
-        desc: '生命礼赞与文化传承的双重奏鸣',
-        icon: 'icon-task-custom-4',
-        enable: 'folk_culture',
-        goForm: [ 'folk_culture', 3, 'folkCultureType', undefined, '婚丧嫁娶', undefined ],
-      },
-      {
-        title: '地方方言',
-        desc: '历史沉淀的语言瑰宝',
-        icon: 'icon-task-custom-5',
-        enable: 'folk_culture',
-        goForm: [ 'folk_culture', 4, 'folkCultureType', undefined, '地方方言', undefined ],
-      },
-      {
-        title: '特色文化',
-        desc: '民族精神的鲜明烙印',
-        icon: 'icon-task-custom-6',
-        enable: 'folk_culture',
-        goForm: [ 'folk_culture', 5, 'folkCultureType', undefined, '特色文化', undefined ],
-      },
-    ],
-  },
-  'environment': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_environment.jpg',
-    list: [
-      {
-        title: '自然环境',
-        desc: '村社建立与发展历程',
-        icon: 'icon-task-environment-1',
-        enable: 'environment',
-        goForm: [ 'environment', 0, undefined, undefined, '自然环境', undefined ],
-      },
-      {
-        title: '文物古迹',
-        desc: '重要历史文献资料',
-        icon: 'icon-task-environment-5',
-        enable: 'relic',
-        goForm: [ 'relic', 0, undefined, undefined, '文物古迹', undefined ],
-      },
-      {
-        title: '历史环境要素',
-        desc: '村民口述历史记录',
-        icon: 'icon-task-environment-6',
-        enable: 'element',
-        goForm: [ 'element', 0, undefined, undefined, '历史环境要素', undefined ],
-      },
-    ],
-  },
-  'food': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_food.jpg',
-    list: [
-      {
-        title: '农副产品',
-        desc: '乡村繁荣的多元支柱',
-        icon: 'icon-task-food-1',
-        enable: 'food_product',
-        goForm: [ 'food_product', 1, 'productType', undefined, '农副产品', undefined ],
-      },
-      {
-        title: '特色美食',
-        desc: '给味蕾探索带来无限惊喜',
-        icon: 'icon-task-food-2',
-        enable: 'food_product',
-        goForm: [ 'food_product', 3, 'productType', undefined, '特色美食', undefined ],
-      },
-    ],
-  },
-  'history': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_history.jpg',
-    list: [
-      {
-        title: '建村历史',
-        desc: '村社建立与发展历程',
-        icon: 'icon-task-history-1',
-        enable: 'cultural',
-        goForm: [ 'cultural', 1, 'culturalType', undefined, '建村历史', undefined ],
-      },
-      {
-        title: '历史人物',
-        desc: '重要历史人物事迹',
-        icon: 'icon-task-history-2',
-        enable: 'cultural',
-        goForm: [ 'figure', 0, undefined, undefined, '历史人物', undefined ],
-      },
-      {
-        title: '历史事件',
-        desc: '重大历史事件记录',
-        icon: 'icon-task-history-3',
-        enable: 'cultural',
-        goForm: [ 'cultural', 2, 'culturalType', undefined, '历史事件', undefined ],
-      },
-      {
-        title: '掌故轶事',
-        desc: '民间传说与历史故事',
-        icon: 'icon-task-history-4',
-        enable: 'story',
-        goForm: [ 'story', 0, undefined, undefined, '掌故轶事', undefined ],
-      },
-      {
-        title: '历史文献',
-        desc: '重要历史文献资料',
-        icon: 'icon-task-history-5',
-        enable: 'cultural',
-        goForm: [ 'cultural', 3, 'culturalType', undefined, '历史文献', undefined ],
-      },
-      {
-        title: '口述历史',
-        desc: '村民口述历史记录',
-        icon: 'icon-task-history-6',
-        enable: 'cultural',
-        goForm: [ 'cultural', 4, 'culturalType', undefined, '口述历史', undefined ],
-      },
-      {
-        title: '口述人管理',
-        desc: '口述人管理登记',
-        icon: 'icon-task-history-1',
-        enable: 'cultural',
-        goForm: [ 'speaker', 1, undefined, undefined, '口述人管理', undefined ],
-      },
-    ],
-  },
-  'product': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_mine.jpg',
-    list: [
-      {
-        title: '商业集市',
-        desc: '文化交流的开放窗口',
-        icon: 'icon-task-mine-1',
-        enable: 'food_product',
-        goForm: [ 'food_product', 4, 'productType', undefined, '商业集市', undefined ],
-      },
-      {
-        title: '服装服饰',
-        desc: '艺术与功能交织的时尚篇章',
-        icon: 'icon-task-mine-2',
-        enable: 'food_product',
-        goForm: [ 'food_product', 5, 'productType', undefined, '服装服饰', undefined ],
-      },
-      {
-        title: '运输工具',
-        desc: '历史的运输工具',
-        icon: 'icon-task-mine-2',
-        enable: 'food_product',
-        goForm: [ 'food_product', 6, 'productType', undefined, '运输工具', undefined ],
-      },
-    ],
-  },
-  'overview': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_summary.jpg',
-    list: [
-      {
-        title: '行政区划',
-        desc: '村社行政区域划分及变迁',
-        icon: 'icon-task-summary-1',
-        enable: 'overview',
-        goForm: [ 'overview', 1, undefined, 'common', '行政区划', undefined ],
-      },
-      {
-        title: '村社综述',
-        desc: '村社整体概况介绍',
-        icon: 'icon-task-summary-5',
-        enable: 'overview',
-        goForm: [ 'overview', 5, undefined, 'common', '村社综述', undefined ],
-      },
-      {
-        title: '地理信息',
-        desc: '地理位置和自然环境特征',
-        icon: 'icon-task-summary-2',
-        enable: 'overview',
-        goForm: [ 'overview', 2, undefined, 'common', '地理信息', undefined ],
-      },
-      {
-        title: '建设与保护',
-        desc: '村社发展与文化遗产保护',
-        icon: 'icon-task-summary-3',
-        enable: 'overview',
-        goForm: [ 'overview', 3, undefined, 'common', '建设与保护', undefined ],
-      },
-      {
-        title: '人口与经济',
-        desc: '人口与经济情况',
-        icon: 'icon-task-summary-4',
-        enable: 'overview',
-        goForm: [ 'overview', 4, undefined, 'common', '人口与经济', undefined ],
-      },
-    ],
-  },
-  'trip': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_trip.jpg',
-    list: [
-      {
-        title: '旅游导览',
-        desc: '',
-        icon: 'icon-task-trip-3',
-        enable: 'travel_guide',
-        goForm: [ 'travel_guide', 0, undefined, undefined, '旅游导览', undefined ],
-      },
-      {
-        title: '旅游路线',
-        desc: '',
-        icon: 'icon-task-trip-1',
-        enable: 'route',
-        goForm: [ 'route', 0, undefined, undefined, '旅游路线', undefined ],
-      },
-    ],
-  },
-  'ich': {
-    banner: 'https://mn.wenlvti.net/app_static/xiangan/banner_dig_trip.jpg',
-    list: [
-      {
-        title: '非遗展示',
-        desc: '',
-        icon: 'icon-task-trip-3',
-        enable: 'ich',
-        goForm: [ 'ich', 0, undefined, undefined, '非物质文化遗产项目', undefined ],
-      },
-    ],
-  },
 }

+ 1 - 1
src/pages/home/components/LightMap.vue

@@ -120,7 +120,7 @@ const mapLoader = useSimpleDataLoader<MapMarker[]>(async () => {
   if (!selectedRegion.value)
     return [];
   await waitTimeOut(200);
-  const res = (await LightVillageApi.getVillageList(undefined, selectedRegion.value, undefined, 1, 100)).map((p, i) => {
+  const res = (await LightVillageApi.getVillageList(undefined, selectedRegion.value, undefined, 1, 10000)).map((p, i) => {
     villageData.set(p.id, p);
     const maker : MapMarker = {
       id: p.id ?? i,

+ 13 - 14
src/pages/home/village/introd/card.vue

@@ -160,14 +160,14 @@
       GridItemPaddingVertical: 8,
     }">
       <Grid :borderGrid="false" :mainAxisCount="4">
-        <GridItem title="村社概况" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeIntrod.png" touchable @click="handleGoCollect('overview')" />
-        <GridItem title="自然风光" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeEnvirounment.png" touchable @click="handleGoCollect('environment', '自然环境')" />
-        <GridItem title="历史沿革" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeHistory.png" touchable @click="handleGoCollect('history', '建村历史')" />
-        <GridItem title="特色产业" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeIndustry.png" touchable @click="handleGoCollect('product')"    />
-        <GridItem title="文艺活动" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeActivity.png" touchable @click="handleGoCollect('custom')" />
-        <GridItem title="非遗展示" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeShow.png" touchable @click="handleGoCollect('ich')" />
-        <GridItem title="民俗风采" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeFolkloreVibe.png" touchable @click="handleGoCollect('custom')" />
-        <GridItem title="乡贤故事" icon="https://xy.wenlvti.net/app_static/images/village/IconGoods.png" touchable @click="handleGoCollect('history', '历史人物')" />
+        <GridItem title="村社概况" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeIntrod.png" touchable @click="handleGoCollect(11, '村社概况')" />
+        <GridItem title="自然风光" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeEnvirounment.png" touchable @click="handleGoCollect(13, '自然风光')" />
+        <GridItem title="历史沿革" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeHistory.png" touchable @click="handleGoCollect(2, '历史沿革')" />
+        <GridItem title="特色产业" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeIndustry.png" touchable @click="handleGoCollect(9, '特色产业'  )"    />
+        <GridItem title="文艺活动" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeActivity.png" touchable @click="handleGoCollect(12, '文艺活动')" />
+        <GridItem title="非遗展示" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeShow.png" touchable @click="handleGoCollect(10, '非遗展示')" />
+        <GridItem title="民俗风采" icon="https://xy.wenlvti.net/app_static/images/village/IconLargeFolkloreVibe.png" touchable @click="handleGoCollect(8, '民俗风采')" />
+        <GridItem title="历史人物" icon="https://xy.wenlvti.net/app_static/images/village/IconGoods.png" touchable @click="handleGoCollect(7, '历史人物')" />
       </Grid>
     </ProvideVar>
 
@@ -331,18 +331,17 @@ function handleGoJoin() {
 function handleGoNew() {
   navTo('/pages/home/light/help/new');
 }
-function handleGoCollect(taskName?: string, taskFindCatalogName?: string) {
-  if (!taskName) {
+function handleGoCollect(collectModuleId?: number, title?: string) {
+  if (!collectModuleId) {
     navTo('/pages/dig/details', {
       villageId: villageStore.currentVillage?.id ?? undefined,
     });
     return;
   }
-  
-  navTo('/pages/dig/forms/list', {
+  navTo('/pages/dig/forms/list-ordinary', {
     villageId: villageStore.currentVillage?.id ?? undefined,
-    taskName: taskName,
-    taskFindCatalogName: taskFindCatalogName,
+    collectModuleId: collectModuleId,
+    title: title,
   });
 }
 function handleGoPublish() {