소스 검색

📦 按要求修改细节问题

快乐的梦鱼 2 달 전
부모
커밋
591fa41431

+ 1 - 1
package-lock.json

@@ -24,7 +24,7 @@
         "@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
         "@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
         "@imengyu/imengyu-utils": "^0.0.19",
-        "@imengyu/js-request-transform": "^0.3.3",
+        "@imengyu/js-request-transform": "^0.3.7",
         "async-validator": "^4.2.5",
         "pinia": "^3.0.1",
         "tslib": "^2.8.1",

+ 1 - 1
package.json

@@ -51,7 +51,7 @@
     "@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
     "@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
     "@imengyu/imengyu-utils": "^0.0.19",
-    "@imengyu/js-request-transform": "^0.3.3",
+    "@imengyu/js-request-transform": "^0.3.7",
     "async-validator": "^4.2.5",
     "pinia": "^3.0.1",
     "tslib": "^2.8.1",

+ 1 - 0
src/App.vue

@@ -29,5 +29,6 @@ configTheme((theme) => {
 @use "@/common/scss/fonts.scss" as *;
 @use "@/common/scss/common.scss" as *;
 @use "@/common/scss/global/base.scss" as *;
+@use "@/components/index.scss" as *;
 </style>
 

+ 5 - 0
src/api/CommonContent.ts

@@ -215,6 +215,11 @@ export class GetContentDetailItem extends DataModel<GetContentDetailItem> {
   constructor() {
     super(GetContentDetailItem, "内容详情");
     this.setNameMapperCase('Camel', 'Snake');
+    this._beforeSolveServer = (data) => {
+      if (!data.id && data.content_id)
+        data.id = Number(data.content_id);
+      return data;
+    }
     this._convertTable = {
       id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
       title: { clientSide: 'string', serverSide: 'string', clientSideRequired: true },

+ 5 - 5
src/api/RequestModules.ts

@@ -36,15 +36,15 @@ function matchNotReportMessage(str: string) {
 
 //请求拦截器
 function requestInceptor(url: string, req: RequestOptions) {
-  //获取store中的token,追加到头;
+  //获取app中的token,追加到头;
+  const app = getApp();
   if (StringUtils.isNullOrEmpty((req.header as KeyValue).token as string)) {
-    const t = getApp()?.globalData?.token ?? '';
+    const t = app?.globalData?.token ?? '';
     req.header['token'] = t
     req.header['__token__'] = t;
   }
-  const main_body_user_id = getApp()?.globalData?.userId ?? '';
-  const append_main_body_user_id = 
-    !(url.includes('content/content'));
+  const main_body_user_id = app?.globalData?.userId ?? '';
+  const append_main_body_user_id = !(url.includes('content/content'));
 
   if (req.method == 'GET') {
     //追加GET参数

+ 35 - 0
src/api/user/UserApi.ts

@@ -0,0 +1,35 @@
+import { DataModel, transformArrayDataModel } from '@imengyu/js-request-transform';
+import { AppServerRequestModule } from '../RequestModules';
+import { GetContentDetailItem } from '../CommonContent';
+
+export class UserApi extends AppServerRequestModule<DataModel> {
+
+  constructor() {
+    super();
+  }
+
+  async like(id: number) {
+    return (this.post('/content/main_body_user/like', { content_id: id }, '点赞'));
+  }
+  async unlike(id: number) {
+    return (this.post('/content/main_body_user/unLike', { content_ids: id }, '取消点赞'));
+  }
+  async collect(id: number) {
+    return (this.post('/content/main_body_user/collect', { content_id: id }, '收藏'));
+  }
+  async uncollect(id: number) {
+    return (this.post('/content/main_body_user/unCollect', { content_ids: id }, '取消收藏'));
+  }
+  async getUserCollect(page: number, pageSize: number) {
+    return (this.post('/content/main_body_user/getUserCollect', { page, pageSize }, '获取用户收藏')) 
+      .then(res => {
+        return { 
+          list: transformArrayDataModel(GetContentDetailItem, res.data2?.data ?? [], `获取用户收藏列表`, true),
+          total: res.data2?.total as number ?? 0,
+        }
+      })
+      .catch(e => { throw e });
+  }
+}
+
+export default new UserApi();

+ 3 - 5
src/common/composeabe/TabControl.ts

@@ -11,7 +11,9 @@ export function useTabControl(options: {
 }) {
 
   const tabCurrentIndex = ref(0)
-  const tabCurrentId = ref(0)
+  const tabCurrentId = computed(() => {
+    return tabsArray.value.filter(t => t.visible !== false)[tabCurrentIndex.value].id
+  });
   const tabsArray = ref<TabControlItem[]>(options.tabs ?? []);
 
   watch(tabCurrentIndex, (v) => {
@@ -29,9 +31,5 @@ export function useTabControl(options: {
     tabCurrentIndex,
     tabs,
     tabsArray,
-    onTabClick(e: any) {
-      tabCurrentIndex.value = e.index
-      tabCurrentId.value = e.id
-    }
   }
 }

+ 23 - 1
src/components/index.scss

@@ -20,7 +20,29 @@
 }
 
 wx-action-sheet-item {
-  padding: 0!important;
+  padding: 0 !important;
+}
+.remove-button-style {
+  margin: 0;
+  padding: 0;
+  border: none;
+  outline: none;
+  border-radius: 0;
+  background: none;
+  line-height: normal;
+  font-weight: normal;
+  font-size: inherit;
+  color: inherit;
+  text-align: inherit;
+  -webkit-tap-highlight-color: transparent;
+
+  &::after {
+    border: none;
+  }
+  &.button-hover {
+    background: none;
+    color: inherit;
+  }
 }
 
 :root {

+ 1 - 1
src/components/layout/FlexView.vue

@@ -34,7 +34,7 @@ export interface FlexProps {
   /**
    * 盒子定位
    */ 
-  position?: "absolute" | "relative",
+  position?: "absolute" | "relative" | 'fixed' | 'sticky',
   /**
    * 弹性盒子方向
    */

+ 7 - 0
src/pages.json

@@ -240,6 +240,13 @@
       }
     },
     {
+      "path": "pages/user/collect/index",
+      "style": {
+        "navigationBarTitleText": "我的收藏",
+        "enablePullDownRefresh": false
+      }
+    },
+    {
       "path": "pages/user/contribute/list",
       "style": {
         "navigationBarTitleText": "我的投稿",

+ 15 - 3
src/pages/article/common/CommonListPage.vue

@@ -271,6 +271,10 @@ function handleChangeDropDownValue(index: number, value: number) {
 }
 function handleTabClick(e: any) {
   nextTick(() => {
+    if (props.tabs?.[tabCurrentIndex.value]?.jump) {
+      props.tabs[tabCurrentIndex.value].jump?.();
+      return;
+    }
     listLoader.loadData(undefined, true);
   })
 }
@@ -282,14 +286,22 @@ function goDetails(item: any, id: number) {
     emit('goCustomDetails', item, id)
     return;
   }
-  if (typeof props.detailsPage == 'object') {
+  if (typeof props.detailsPage === 'object' && typeof props.detailsPage[0] === 'string') {
     navTo(props.detailsPage[tabCurrentIndex.value], { 
-      ...props.detailsPage.params, 
+      ...props.detailsParams, 
       id 
     })
     return; 
   }
-  navTo(props.detailsPage, { 
+  if (typeof props.detailsPage == 'object' && typeof props.detailsPage[0] === 'object') {
+    const item = props.detailsPage[tabCurrentIndex.value];
+    navTo(item.page, { 
+      ...item.params, 
+      id 
+    })
+    return; 
+  }
+  navTo(props.detailsPage as string, { 
     ...props.detailsParams,
     id
   })

+ 16 - 4
src/pages/article/common/DetailTabPage.vue

@@ -35,7 +35,6 @@
               :autoItemWidth="false"
               :defaultIndicatorWidth="130"
               class="top-tab"
-              @click="onTabClick"
             />
           </view>
 
@@ -52,7 +51,7 @@
                 :content="loader.content.value.content"
                 :tagStyle="commonParserStyle"
               />
-              <text v-if="!loader.content.value.intro && !loader.content.value.content">暂无简介</text>
+              <text v-if="emptyContent">暂无简介</text>
             </template>
             <!-- 图片 -->
             <template v-else-if="tabCurrentId == 1">
@@ -91,6 +90,7 @@
           </view>
           <ContentNote />
         </view>
+        <LikeFooter :content="loader.content.value" />
       </template>
     </SimplePageContentLoader>
   </view>
@@ -105,9 +105,10 @@ import ImageGrid from "@/pages/parts/ImageGrid.vue";
 import ImageSwiper from "@/pages/parts/ImageSwiper.vue";
 import ContentNote from "@/pages/parts/ContentNote.vue";
 import commonParserStyle from "@/common/style/commonParserStyle";
-import type { PropType, Ref } from "vue";
+import { computed, type PropType, type Ref } from "vue";
 import Parse from "@/components/display/parse/Parse.vue";
 import Tabs from "@/components/nav/Tabs.vue";
+import LikeFooter from "@/pages/parts/LikeFooter.vue";
 
 const props = defineProps({
   load: {
@@ -128,6 +129,10 @@ const emit = defineEmits([
   "tabChange"
 ])
 
+const emptyContent = computed(() => {
+  return !(loader.content.value?.intro as string || '').trim() && !(loader.content.value?.content || '').trim();
+})
+
 const loader = useSimplePageContentLoader<
   GetContentDetailItem, 
   { id: number }
@@ -141,6 +146,14 @@ const loader = useSimplePageContentLoader<
 
   if (d.title)
     uni.setNavigationBarTitle({ title: d.title });
+  setTimeout(() => {
+    if (emptyContent.value) {
+      if (d.video)
+        tabCurrentIndex.value = 1;
+      else if (d.video)
+        tabCurrentIndex.value = 2;
+    }
+  }, 200);
   return d;
 });
 
@@ -149,7 +162,6 @@ const {
   tabCurrentIndex,
   tabsArray,
   tabs,
-  onTabClick
 } = useTabControl({
   tabs: [
     {

+ 4 - 50
src/pages/article/details.vue

@@ -47,7 +47,7 @@
           
           <!-- 推荐 -->
           <view v-if="recommendListLoader.content.value?.length" class="d-flex flex-col p-3">
-            <text class="size-base text-bold mb-3">推荐文章</text>
+            <text class="size-base text-bold mb-3">相关推荐</text>
             <Box2LineImageRightShadow
               class="w-100"
               titleColor="title-text"
@@ -63,17 +63,7 @@
           </view>
 
           <ContentNote />
-        </view>
-        <view class="bottom-actions">
-          <view class="action">
-            <text class="iconfont icon-read"></text>
-            {{ loader.content.value.views }}
-          </view>
-          <view class="action">
-            <text class="iconfont icon-like" v-if="!loader.content.value.isLike"></text>
-            <text class="iconfont icon-liked" v-else></text>
-            {{ loader.content.value.likes }}
-          </view>
+          <LikeFooter :content="loader.content.value" />
         </view>
       </template>
     </SimplePageContentLoader>
@@ -98,6 +88,7 @@ import { navTo } from "@/components/utils/PageAction";
 import CommonContent, { GetContentListParams } from "@/api/CommonContent";
 import Box2LineImageRightShadow from "../parts/Box2LineImageRightShadow.vue";
 import AppCofig from "@/common/config/AppCofig";
+import LikeFooter from "../parts/LikeFooter.vue";
 
 const loader = useSimplePageContentLoader<
   GetContentDetailItem, 
@@ -116,7 +107,7 @@ const { onPreviewImage } = useSwiperImagePreview(() => loader.content.value?.ima
 const emptyContent = computed(() => (loader.content.value?.content || '').trim() === '')
 
 const recommendListLoader = useSimpleDataLoader(async () => {
-  if (!querys.value.modelId || !querys.value.mainBodyColumnId)
+  if (!querys.value.modelId)
     return []
   return (await CommonContent.getContentList(new GetContentListParams()
     .setModelId(querys.value.modelId)
@@ -154,40 +145,3 @@ onShareAppMessage(() => {
   return getPageShareData();
 })
 </script>
-
-<style lang="scss" scoped>
-.bottom-actions {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  display: flex;
-  align-items: center;
-  justify-content: flex-end;
-  width: 100%;
-  height: 75rpx;
-  background: #FFFFFF;
-
-  .action {
-    margin-left: 48rpx;
-    font-weight: 800;
-    font-size: 24rpx;
-    color: #191919;
-    display: flex;
-    align-items: center;
-
-    &:last-child {
-      margin-right: 70rpx;
-    }
-
-    .iconfont {
-      font-size: 30rpx;
-      margin-right: 14rpx;
-    }
-
-    .iconfont.icon-liked {
-      color: #FF8719;
-    }
-  }
-}
-</style>

+ 3 - 0
src/pages/article/list.vue

@@ -3,6 +3,9 @@
     title="闽南文化资讯"
     :load="loadData"
     itemType="article-common"
+    :detailsParams="{
+      modelId: NewsIndexContent.modelId,
+    }"
   />
 </template>
 

+ 3 - 3
src/pages/discover.vue

@@ -193,11 +193,11 @@ const categories = [
     }) 
   },
   { 
-    name: '美术技艺', 
+    name: '民间技艺', 
     icon: 'https://mncdn.wenlvti.net/app_static/minnan/images/discover/CategoryIcon8.png', 
     onClick: () => navTo('/pages/article/common/list', {
-      title: '美术技艺',
-      mainBodyColumnId: 314,
+      title: '民间技艺',
+      mainBodyColumnId: 242,
       modelId: 3,
       itemType: 'article-common',
       detailsPage: '/pages/article/details',

+ 7 - 18
src/pages/home.vue

@@ -2,7 +2,7 @@
   <view class="home-container page-home d-flex flex-col bg-base">
     <image 
       class="w-100 position-absolute"
-      src="https://mn.wenlvti.net/app_static/minnan/images/home/BackgroundBanner5.jpg"
+      src="https://mncdn.wenlvti.net/app_static/minnan/images/home/BackgroundBanner5.jpg"
       mode="widthFix"
     />
     <view class="content d-flex flex-col wing-l">
@@ -280,20 +280,6 @@ function handleGoAudioList() {
   navTo('/pages/inhert/language/list') 
 }
 
-const activityLoader = useSimpleDataLoader(async () => {
-  //TODO: 活动接口
-  return [
-    {
-      title: '茶艺传承工坊',
-      image: ImageTest,
-      count: 8,
-      content: '这是一个活动的内容',
-      time: '2025年06月16日 12:00',
-      location: '湖里创新园',
-      link: '/pages/article/details',
-    }
-  ]
-})
 const recommendLoader = useSimpleDataLoader(async () => {
   const list = [];
   list.push(...(await ProjectsContent.getContentList(new GetContentListParams(), 1, 6)).list.map((p) => {
@@ -303,11 +289,11 @@ const recommendLoader = useSimpleDataLoader(async () => {
   list.push(...(await CommonContent.getContentList(new GetContentListParams()
     .setModelId(1)
   , 1, 6)).list.map((p) => {
-    p.itemType = 'artifact';
+    p.itemType = p.type == GetContentListParams.TYPE_VIDEO ? 'video' : 'artifact';
     return p;
   }));
   list.push(...(await ProductsContent.getContentList(new GetContentListParams(), 1, 6)).list.map((p) => {
-    p.itemType = 'intangible';
+    p.itemType = p.type == GetContentListParams.TYPE_VIDEO ? 'video' : 'intangible';
     return p;
   }));
   return list;
@@ -417,8 +403,11 @@ function handleGoDetails(item: any) {
     case 'intangible': 
       navTo('/pages/inhert/intangible/details', { id: item.id });
       break;
+    case 'video': 
+      navTo('/pages/video/details', { id: item.id, modelId: item.modelId, mainBodyColumnId: item.mainBodyColumnId });
+      break;
     default:
-      navTo('/pages/article/details', { id: item.id });
+      navTo('/pages/article/details', { id: item.id, modelId: item.modelId, mainBodyColumnId: item.mainBodyColumnId });
       break;
   }
 }

+ 2 - 4
src/pages/inhert/map/index.vue

@@ -3,9 +3,8 @@
     
     <Tabs
       :tabs="tabs" 
-      :currentIndex="tabCurrentIndex"
+      v-model:currentIndex="tabCurrentIndex"
       class="top-tab"
-      @click="onTabClick"
     />
     <view class="d-flex flex-col p-2">
       <SearchBar
@@ -51,9 +50,8 @@ import Tabs from '@/components/nav/Tabs.vue';
 import SearchBar from '@/components/form/SearchBar.vue';
 
 const { 
-  tabCurrentIndex ,
+  tabCurrentIndex,
   tabs,
-  onTabClick
 } = useTabControl({
   tabs: [
     {

+ 23 - 9
src/pages/introduction/custom/list.vue

@@ -3,19 +3,33 @@
     title="闽南民俗"
     itemType="image-large-2"
     :detailsPage="[
-      '/pages/article/details',
-      '/pages/inhert/intangible/details',
+      {
+        page: '/pages/article/details',
+        params: {
+          modelId: 4,
+          mainBodyColumnId: 245,
+        }
+      },
+      {
+        page: '/pages/article/details',
+        params: {
+          modelId: 4,
+          mainBodyColumnId: 248,
+        }
+      },
+      {
+        page: '/pages/inhert/intangible/details',
+        params: {},
+      }
     ]"
     showTotal
     :dropDownNames="dropdownNames"
     :load="loadData" 
     :tabs="[
-      { id: 0, text: '民俗资讯' },
-      { id: 1, text: '非遗民俗' },
+      { id: 245, text: '婚丧嫁娶' },
+      { id: 248, text: '民俗节庆' },
+      { id: 100, text: '非遗民俗' },
     ]"
-    :detailsParams="{
-      modelId: 4,
-    }"
   />
   <!--  -->
 </template>
@@ -37,7 +51,7 @@ async function loadData(
 ) {
   let res;
   switch (tabSelect) {
-    case 1:
+    case 100:
       res = await ProjectsContent.getContentList(new GetContentListParams()
         .setKeywords('民俗 ' + searchText)
       , page, pageSize);
@@ -55,7 +69,7 @@ async function loadData(
       res = await CommonContent.getContentList(new GetContentListParams()
         .setKeywords(searchText)
         .setModelId(4)
-        .setMainBodyColumnId([ 245,248 ])
+        .setMainBodyColumnId(tabSelect)
       , page, pageSize);
       break;
   }

+ 16 - 3
src/pages/introduction/food/list.vue

@@ -3,8 +3,17 @@
     title="闽南美食"
     itemType="image-large-2"
     :detailsPage="[
-      '/pages/article/details',
-      '/pages/inhert/intangible/details',
+      {
+        page: '/pages/article/details',
+        params: {
+          modelId: 3,
+          mainBodyColumnId: 253,
+        }
+      },
+      {
+        page: '/pages/inhert/intangible/details',
+        params: {},
+      }
     ]"
     showTotal
     :dropDownNames="dropdownNames"
@@ -12,7 +21,6 @@
     :tabs="[
       { id: 0, text: '饮食文化' },
       { id: 1, text: '非遗美食' },
-      { id: 2, text: '美食资讯' },
     ]"
   />
 <!--   
@@ -27,6 +35,11 @@ import ProjectsContent from '@/api/inheritor/ProjectsContent';
 
 const dropdownNames = ref<DropDownNames[]>([]);
 
+const detailsParams = ref({
+  modelId: 3,
+  mainBodyColumnId: 253,
+})
+
 async function loadData(
   page: number, 
   pageSize: number,

+ 101 - 0
src/pages/parts/LikeFooter.vue

@@ -0,0 +1,101 @@
+<template>
+  <FlexCol position="fixed" :bottom="0" :left="0" :right="0" backgroundColor="#f5ebe0" :padding="[20,20,0,20]">
+    <FlexRow justify="space-between">
+      <FlexRow align="center">
+      
+      </FlexRow>
+      <FlexRow align="center">
+        <Touchable direction="row" :gap="10" :padding="[0,10]" @click="doLike">
+          <Icon icon="good" :color="content.isLike ? 'primary' : 'text.content'" />
+          <Text :text="likes" :color="content.isLike ? 'primary' : 'text.content'" />
+        </Touchable>
+        <Touchable direction="row" :gap="10" :padding="[0,10]" @click="doCollect">
+          <Icon icon="favorite" :color="content.isCollect ? 'warning' : 'text.content'" />
+          <Text :text="collects" :color="content.isCollect ? 'warning' : 'text.content'" />
+        </Touchable>
+        <button class="remove-button-style" direction="row" open-type="share">
+          <FlexRow :gap="10" align="center" :padding="[0,10]" >
+            <Icon icon="share" color="text.content" />
+            <Text text="分享" />
+          </FlexRow>
+        </button>
+      </FlexRow>
+    </FlexRow>
+    <XBarSpace />
+  </FlexCol>
+</template>
+
+<script setup lang="ts">
+import type { GetContentDetailItem } from '@/api/CommonContent';
+import { computed, type PropType } from 'vue';
+import { toast } from "@/components/utils/DialogAction";
+import UserApi from "@/api/user/UserApi";
+import Icon from "@/components/basic/Icon.vue";
+import FlexRow from "@/components/layout/FlexRow.vue";
+import XBarSpace from "@/components/layout/space/XBarSpace.vue";
+import FlexCol from "@/components/layout/FlexCol.vue";
+import Touchable from "@/components/feedback/Touchable.vue";
+import Text from '@/components/basic/Text.vue';
+
+const props = defineProps({
+  content: {
+    type: Object as PropType<GetContentDetailItem>,
+    default: () => ({
+      isLike: false,
+      likes: 0,
+    }),
+  }
+})
+
+const collects = computed(() => formatNumber(props.content.collects));
+const likes = computed(() => formatNumber(props.content.likes));
+
+async function doLike() {
+  const content = props.content;
+  if (!content)
+    return;
+  try {
+    if (content.isLike) {
+      await UserApi.unlike(content.id);
+      toast('取消点赞');
+      content.isLike = false;
+      content.likes--;
+    } else {
+      await UserApi.like(content.id);
+      content.isLike = true;
+      content.likes++;
+      toast('感谢点赞');
+    }
+  } catch (error) {
+    toast('操作失败' + error);
+  }
+}
+async function doCollect() {
+  const content = props.content;
+  if (!content)
+    return;
+  console.log(content);
+  
+  try {
+    if (content.isCollect) {
+      await UserApi.uncollect(content.id);
+      toast('取消收藏');
+      content.isCollect = false;
+      content.collects--;
+    } else {
+      await UserApi.collect(content.id);
+      content.isCollect = true;
+      content.collects++;
+      toast('收藏成功');
+    }
+  } catch (error) {
+    toast('操作失败' + error);
+  }
+}
+function formatNumber(num: number) {
+  num = Math.max(num, 0);
+  if (num >= 10000) 
+    return (num / 10000).toFixed(2) + '万';
+  return num.toString();
+}
+</script>

+ 92 - 0
src/pages/user/collect/index.vue

@@ -0,0 +1,92 @@
+<template>
+  <CommonListPage 
+    title="我的收藏"
+    showTotal
+    :dropDownNames="dropdownNames"
+    detailsPage="custom"
+    :load="loadData" 
+    @goCustomDetails="goCustomDetails"
+  />
+</template>
+
+<script setup lang="ts">
+import { GetContentListParams } from '@/api/CommonContent';
+import InheritorContent from '@/api/inheritor/InheritorContent';
+import ProductsContent from '@/api/inheritor/ProductsContent';
+import ProjectsContent from '@/api/inheritor/ProjectsContent';
+import UnmoveableContent from '@/api/inheritor/UnmoveableContent';
+import UserApi from '@/api/user/UserApi';
+import { navTo } from '@/components/utils/PageAction';
+import CommonListPage, { type CommonListItem, 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 UserApi.getUserCollect(page, pageSize);
+  res.list.forEach((p) => {
+    switch(p.modelId) {
+      default:
+        p.itemType = p.type === GetContentListParams.TYPE_VIDEO ? 'video' : 'article';
+        break;
+      case UnmoveableContent.modelId:
+        p.itemType = 'artifact';
+        break;
+      case ProductsContent.modelId:
+      case ProjectsContent.modelId:
+        p.itemType = 'intangible';
+        break;
+      case InheritorContent.modelId:
+        p.itemType = 'inheritor';
+        break;
+    }
+    p.bottomTags = [
+      p.levelText, 
+      p.batchText,
+      p.ichTypeText,
+    ];
+  })
+  return {
+    list: res.list as unknown as CommonListItem[],
+    total: res.total,
+  };
+}
+
+function goCustomDetails(item: any) {
+  const params = {
+    id: item.id,
+    modelId: item.modelId,
+    mainBodyColumnId: item.mainBodyColumnId,
+  }
+  switch (item.itemType) {
+    default:
+    case 'article':
+      navTo('/pages/article/details', params)
+      break;
+    case 'video':
+      navTo('/pages/video/details', params)
+      break;
+    case 'artifact':
+      navTo('/pages/inhert/artifact/details', params)
+      break;
+    case 'intangible':
+      navTo('/pages/inhert/intangible/details', params)
+      break;
+    case 'inheritor':
+      navTo('/pages/inhert/inheritor/details', params)
+      break;
+
+  }
+
+}
+
+onMounted(async () => {
+  
+})
+</script>

+ 22 - 80
src/pages/user/index.vue

@@ -27,60 +27,28 @@
         </view>
         <text class="iconfont icon-arrow-right"></text>
       </view>
-      <view class="d-flex bg-base flex-col shadow-l radius-l">
+
+      <CellGroup round>
+        <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/07f750b4cf4959654c40171fdae91c3a.png" title="去投稿" showArrow touchable @click="goContribute" />
+        <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/66d4665b1da5075e60148312469b2630.png" title="我的投稿" showArrow touchable @click="goContributeList" />
+        <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/042236758da5aaed21c1010e5b9440ce.png" title="我的收藏" showArrow touchable @click="navTo('collect/index')" />
+        <Cell icon="https://mncdn.wenlvti.net/uploads/20250313/d2e9010323d098aa51e268fc32f14d3d.png" title="在线客服" showArrow touchable @click="showService" />
+        <Cell v-if="userInfo" icon="https://mncdn.wenlvti.net/uploads/20250313/cbc47d0b9cad7891e6154359952858c6.png" title="退出登录" showArrow touchable @click="doLogout" />
+
         <view class="list">
-          <!-- <view class="entry">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/042236758da5aaed21c1010e5b9440ce.png" mode="aspectFill"></image>
-            <text class="label">我的好友</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
-          <view class="entry">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/9fb29e8bdb66490034145c90f892773a.png" mode="aspectFill"></image>
-            <text class="label">邀请好友</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
-          <view class="entry">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/1366973c061bf98594036e42c0344593.png" mode="aspectFill"></image>
-            <text class="label">积分日志</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
-          <view class="entry">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/042236758da5aaed21c1010e5b9440ce.png" mode="aspectFill"></image>
-            <text class="label">我的收藏</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view> -->
-          <view class="entry" @click="goContribute">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/07f750b4cf4959654c40171fdae91c3a.png" mode="aspectFill"></image>
-            <text class="label">投稿</text>
-            <view class="btn">去投稿</view><text class="iconfont icon-arrow-right"></text>
-          </view>
-          <view class="entry" @click="goContributeList">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/66d4665b1da5075e60148312469b2630.png" mode="aspectFill"></image>
-            <text class="label">我的投稿</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
-         <!--  <view class="entry">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/acd97ca7b3f7736942495c7aec1dd65b.png" mode="aspectFill"></image>
-            <text class="label">加入我们</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
-          <view class="entry">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/d2e9010323d098aa51e268fc32f14d3d.png" mode="aspectFill"></image>
-            <text class="label">我的预约</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view> -->
-          <view class="entry" @click="showService">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/d2e9010323d098aa51e268fc32f14d3d.png" mode="aspectFill"></image>
-            <text class="label">在线客服</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
-          <view v-if="userInfo" class="entry" @click="doLogout">
-            <image src="https://mncdn.wenlvti.net/uploads/20250313/cbc47d0b9cad7891e6154359952858c6.png" mode="aspectFill"></image>
-            <text class="label">退出登录</text>
-            <text class="iconfont icon-arrow-right"></text>
-          </view>
+          <!-- 
+          https://mncdn.wenlvti.net/uploads/20250313/042236758da5aaed21c1010e5b9440ce.png
+          我的好友
+          https://mncdn.wenlvti.net/uploads/20250313/9fb29e8bdb66490034145c90f892773a.png
+          邀请好友
+          https://mncdn.wenlvti.net/uploads/20250313/1366973c061bf98594036e42c0344593.png
+          积分日志
+          https://mncdn.wenlvti.net/uploads/20250313/acd97ca7b3f7736942495c7aec1dd65b.png
+          加入我们
+          https://mncdn.wenlvti.net/uploads/20250313/d2e9010323d098aa51e268fc32f14d3d.png
+          我的预约 -->
         </view>
-      </view>
+      </CellGroup>
     </view>
     <tabbar :current="4"></tabbar>
   </view>
@@ -93,6 +61,8 @@ import { navTo } from '@/components/utils/PageAction';
 import { useAuthStore } from '@/store/auth';
 import { computed } from 'vue';
 import { useReqireLogin } from '@/common/composeabe/RequireLogin';
+import CellGroup from '@/components/basic/CellGroup.vue';
+import Cell from '@/components/basic/Cell.vue';
 
 const UserHead = 'https://mncdn.wenlvti.net/app_static/minnan/images/home/UserHead.png';
 
@@ -133,34 +103,6 @@ function showService() {
     width: 100rpx;
   }
 }
-.list{
-  margin-bottom: 34rpx;
-
-  .entry{
-    border-bottom: 1rpx solid #dddddd;
-    font-size: 28rpx;
-    color: #333333;
-    display: flex;
-    align-items: center;
-    padding: 30rpx 0;
-    margin: 0 30rpx;
-    image{
-      width: 32rpx;
-      height: 32rpx;
-    }
-    text.label{
-      flex:1;
-      margin-left: 12rpx;
-    }
-    &:last-child{
-      border-bottom: none;
-    }
-    text.iconfont{
-      color:#AAAAAA;
-      font-size: 20rpx;
-    }
-  }
-}
 .user-info{
   display: flex;
   align-items: center;

+ 19 - 45
src/pages/video/details.vue

@@ -56,17 +56,8 @@
           </view>
         </view>
         <ContentNote />
+        <LikeFooter :content="loader.content.value" />
 
-        <view class="bottom-actions">
-          <view class="action">
-            <text class="iconfont icon-like"></text>
-            <text>{{ loader.content.value.likes }}</text>
-          </view>
-          <view class="action">
-            <text class="iconfont icon-share"></text>
-            <text>分享</text>
-          </view>
-        </view>
       </template>
     </SimplePageContentLoader>
   </view>
@@ -88,6 +79,8 @@ import Box2LineImageRightShadow from "../parts/Box2LineImageRightShadow.vue";
 import AppCofig from "@/common/config/AppCofig";
 import { navTo } from "@/components/utils/PageAction";
 import { computed } from "vue";
+import LikeFooter from "../parts/LikeFooter.vue";
+import { onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
 
 const loader = useSimplePageContentLoader<
   GetContentDetailItem, 
@@ -101,7 +94,7 @@ const loader = useSimplePageContentLoader<
 });
 
 const recommendListLoader = useSimpleDataLoader(async () => {
-  if (!querys.value.modelId || !querys.value.mainBodyColumnId)
+  if (!querys.value.modelId)
     return []
   return (await CommonContent.getContentList(new GetContentListParams()
     .setModelId(querys.value.modelId)
@@ -119,6 +112,21 @@ function goDetails(id: number) {
   });
 }
 
+function getPageShareData() {
+  if (!loader.content.value)
+    return { title: '文章详情', imageUrl: '' }
+  return {
+    title: loader.content.value.title,
+    imageUrl: loader.content.value.images[0],
+  }
+}
+onShareTimeline(() => {
+  return getPageShareData(); 
+})
+onShareAppMessage(() => {
+  return getPageShareData();
+})
+
 const { querys } = useLoadQuerys({ 
   id: 0,
   mainBodyColumnId: 0,
@@ -136,39 +144,5 @@ const { querys } = useLoadQuerys({
   video {
     width: 750rpx;
   }
-  .bottom-actions {
-    position: fixed;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    display: flex;
-    align-items: center;
-    justify-content: flex-end;
-    width: 100%;
-    height: 75rpx;
-    background: #FFFFFF;
-
-    .action {
-      margin-left: 48rpx;
-      font-weight: 800;
-      font-size: 24rpx;
-      color: #191919;
-      display: flex;
-      align-items: center;
-
-      &:last-child {
-        margin-right: 70rpx;
-      }
-
-      .iconfont {
-        font-size: 30rpx;
-        margin-right: 14rpx;
-      }
-
-      .iconfont.icon-liked {
-        color: #FF8719;
-      }
-    }
-  }
 }
 </style>

+ 8 - 0
src/store/auth.ts

@@ -21,6 +21,14 @@ export const useAuthStore = defineStore('auth', {
         this.userId = authInfo.userId;
         this.expireAt = authInfo.expireAt;
         this.userInfo = authInfo.userInfo;
+
+        // 全局变量存储
+        const app = getApp();
+        if (!app.globalData) 
+          app.globalData = {};
+        app.globalData.token = this.token;
+        app.globalData.userId = this.userId;
+        app.globalData.userInfo = this.userInfo;
       } catch (error) {
         this.token = '';
         this.userId = 0;