| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- <template>
- <!--详情页可配置主控-->
- <FlexCol>
- <FlexCol v-if="errorMessage" :padding="30" :gap="30" center height="100%">
- <Result status="error" :description="errorMessage" />
- <Button type="primary" @click="loadPageConfig">重新加载</Button>
- </FlexCol>
- <LoadingPage v-else-if="!loadState" />
- <DetailTabPage
- v-else
- ref="pageRef"
- v-bind="$attrs"
- :noInternalTabs="true"
- :load="load"
- :extraTabs="tabRenderDefinesArray"
- @loaded="onLoaded"
- >
- <template #extraTabs="{ content, tabCurrentId }">
- <template v-if="tabRenderDefines[tabCurrentId].type === 'intro'">
- <!-- 简介 -->
- <Parse
- v-if="content.intro"
- :content="(content.intro as string)"
- />
- <Parse
- v-if="content.content"
- :content="content.content"
- />
- <text v-if="!(content.intro || content.content)">暂无简介</text>
- <text v-if="content.from" class="size-s color-text-content-second mr-2 ">以上内容摘自 {{ content.from }}</text>
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].type === 'images'">
- <!-- 图片 -->
- <view v-if="tabRenderDefines[tabCurrentId].prefix" class="d-flex flex-row justify-center align-center mt-2 mb-2">
- <text class="size-s font-bold color-text-content">{{ tabRenderDefines[tabCurrentId].prefix }}</text>
- </view>
- <ImageGrid
- :images="content.images"
- :rowCount="2"
- :preview="true"
- imageHeight="200rpx"
- />
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].type === 'video'">
- <!-- 视频 -->
- <video
- v-if="content.video"
- class="w-100 video"
- autoplay
- :poster="content.image"
- :src="content.video"
- controls
- />
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].type === 'audio'">
- <!-- 视频 -->
- <video
- v-if="content.audio"
- class="w-100 video"
- autoplay
- :poster="content.image"
- :src="content.audio"
- controls
- />
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].type === 'list'">
- <!-- 列表 -->
- <CommonCategoryListBlock
- v-if="currentCommonCategoryDefine"
- :currentCommonCategoryDefine="{
- title: '',
- name: 'default',
- content: tabRenderDefines[tabCurrentId].define
- }"
- :currentCommonCategoryContentDefine="tabRenderDefines[tabCurrentId].define"
- :pageQuerys="pageQuerys"
- :parentData="content"
- :hasPadding="false"
- :hasBg="false"
- @error="errorMessage = $event"
- />
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].type === 'rich'">
- <!-- 富文本 -->
- <view class="d-flex flex-col mt-3 mb-2">
- <Parse :content="(content[tabRenderDefines[tabCurrentId].key] as string)" />
- </view>
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].type === 'nestCategory'">
- <!-- 嵌套分类 -->
- <CommonCategoryBlocks :categoryDefine="tabRenderDefines[tabCurrentId].categoryDefine" />
- </template>
- <template v-else-if="tabRenderDefines[tabCurrentId].key === 'vr'">
- <!-- VR参观 -->
- <view class="d-flex flex-row justify-center p-5">
- <Button @click="handleGoToVr(content.vr as string)">
- <text class="iconfont icon-go"></text>
- 点击参观
- </Button>
- </view>
- </template>
- <template v-else>
- <CommonCategoryDetailContentBlocks
- :define="tabRenderDefines[tabCurrentId]"
- :content="content"
- />
- </template>
- </template>
- <template #titleEnd="{ content }">
- <Tag
- v-if="content.levelText"
- :text="StringUtils.cutString(content.levelText as string, 4)"
- size="small" scheme="light" type="primary"
- class="flex-shrink-0"
- />
- </template>
- <template #titleExtra="{ content }">
- <view class="d-flex flex-col">
- <IntroBlock small :descItems="descItems" />
- <CommonCategoryDetailIntroBlocks
- :introBlocks="currentCommonCategoryContentDefine?.props.introBlocks"
- :content="content"
- />
- </view>
- </template>
- </DetailTabPage>
- </FlexCol>
- </template>
- <script setup lang="ts">
- import { computed, onMounted, ref, watch } from 'vue';
- import LoadingPage from '@/components/display/loading/LoadingPage.vue';
- import FlexCol from '@/components/layout/FlexCol.vue';
- import Result from '@/components/feedback/Result.vue';
- import Button from '@/components/basic/Button.vue';
- import { CommonCategoryListTabNestCategoryDataToContent, type IHomeCommonCategoryDefine, type IHomeCommonCategoryListTabNestCategoryItemDefine } from './CommonCategoryDefine';
- import { injectCommonCategory } from './CommonCategoryGlobalLoader';
- import { doLoadDynamicCategoryDataMergeTypeGetColumns } from './CommonCategoryDynamicData';
- import { formatError, StringUtils, waitTimeOut } from '@imengyu/imengyu-utils';
- import type { IHomeCommonCategoryDetailDefine, IHomeCommonCategoryDetailTabItemDefine } from './defines/Details';
- import type { DetailTabPageProps, DetailTabPageTabsArray } from '../common/DetailTabPage';
- import type { CategoryDefine } from './CommonCategoryBlocks';
- import DetailTabPage from '../common/DetailTabPage.vue';
- import IntroBlock from '../common/IntroBlock.vue';
- import CommonCategoryDetailIntroBlocks from './CommonCategoryDetailIntroBlocks.vue';
- import Tag from '@/components/display/Tag.vue';
- import CommonCategoryBlocks from './CommonCategoryBlocks.vue';
- import Parse from '@/components/display/parse/Parse.vue';
- import CommonContent from '@/api/CommonContent';
- import CommonCategoryDetailContentBlocks from './CommonCategoryDetailContentBlocks.vue';
- import ImageGrid from '@/pages/parts/ImageGrid.vue';
- import CommonCategoryListBlock from './CommonCategoryListBlock.vue';
- import { navTo } from '@/components/utils/PageAction';
- export interface CommonCategoryDetailProps extends DetailTabPageProps {
- /**
- * 简介块描述项
- */
- introBlockDescs?: {
- label: string;
- key: string;
- map?: Record<string|number, string>;
- }[];
- /**
- * 简介下方块
- */
- introBlocks?: CommonCategoryDetailIntroBlocksDesc[];
- }
- export interface CommonCategoryDetailIntroBlocksDesc {
- type: string;
- props?: Record<string, any>;
- }
- export type RenderTabDefine = IHomeCommonCategoryDetailTabItemDefine & {
- id: number;
- categoryDefine?: CategoryDefine[];
- };
- const pageRef = ref();
- const props = defineProps({
- pageConfigName: {
- type: String,
- },
- pageQuerys: {
- type: Object as () => Record<string, string|number|number[]|undefined>,
- default: () => ({}),
- },
- })
- const loadState = ref(false);
- const errorMessage = ref('');
- const currentCommonCategoryDefine = ref<IHomeCommonCategoryDefine['page'][0]>();
- const currentCommonCategoryContentDefine = ref<IHomeCommonCategoryDetailDefine>();
- const commonCategory = injectCommonCategory();
- const tabDefines = computed(() => currentCommonCategoryContentDefine.value?.props.tabs || []);
- const tabRenderDefines = computed(() => {
- const result = {} as Record<number, RenderTabDefine>;
- try {
- tabDefines.value.forEach((item, i) => {
- const renderItem : RenderTabDefine = {
- ...item,
- id: i,
- };
- function loadNestCategoryData(items: IHomeCommonCategoryListTabNestCategoryItemDefine[]) {
- return items
- .filter((item) => item.visible !== false)
- .map((item) => {
- return {
- ...item,
- showTitle: item.showTitle !== false,
- title: item.text,
- content: CommonCategoryListTabNestCategoryDataToContent(
- item.data, item
- ),
- type: item.type as CategoryDefine['type'],
- }
- });
- }
- switch (item.type) {
- case 'nestCategory':
- renderItem.categoryDefine = loadNestCategoryData(item.categorys);
- break;
- }
- result[i] = renderItem;
- });
- } catch (error) {
- errorMessage.value = formatError(error);
- }
- return result;
- });
- const tabRenderDefinesArray = computed(() => {
- return Object.values(tabRenderDefines.value);
- });
- async function loadPageConfig() {
- if (!props.pageConfigName) {
- errorMessage.value = '配置有误';
- return;
- }
- currentCommonCategoryDefine.value = commonCategory.value.page
- .find((item) => item.name === props.pageConfigName);
- if (!currentCommonCategoryDefine.value) {
- errorMessage.value = '未找到指定的分类配置:' + props.pageConfigName;
- return;
- }
- if (currentCommonCategoryDefine.value.content.type !== 'Details') {
- errorMessage.value = '分类配置:' + props.pageConfigName + ' 不是详情类型';
- return;
- }
- currentCommonCategoryContentDefine.value =
- currentCommonCategoryDefine.value.content as IHomeCommonCategoryDetailDefine;
- uni.setNavigationBarTitle({
- title: currentCommonCategoryDefine.value?.title || '',
- })
- await waitTimeOut(100);
-
- try {
- //特殊处理
- let hasNestCategory = false;
- for (const [_, tab] of Object.entries(tabDefines.value)) {
- if (tab.type === 'nestCategory') {
- tab.categorys = await doLoadDynamicCategoryDataMergeTypeGetColumns(tab.categorys)
- hasNestCategory = true;
- }
- }
- if (hasNestCategory)
- await waitTimeOut(100);
- loadState.value = true;
- await waitTimeOut(100);
- pageRef.value?.load(props.pageQuerys);
- } catch (error) {
- console.error(error);
- loadState.value = false;
- errorMessage.value = formatError(error);
- }
- }
- watch(() => props.pageConfigName, loadPageConfig);
- onMounted(loadPageConfig);
- const content = ref<any>();
- const descItems = computed(() => (
- currentCommonCategoryContentDefine.value?.props.introBlockDescs || [])
- .map((item) => {
- let value = content.value?.[item.key] || '';
- if (item.map)
- value = item.map[value] || '';
- return {
- ...item,
- value,
- }
- })
- );
- function onLoaded(d: any) {
- content.value = d;
- }
- async function load(id: number, tabsArray: DetailTabPageTabsArray) {
- if (isNaN(id) || id <= 0)
- throw new Error("请输入ID。如果正在测试,可在后台复制ID");
- const d = await CommonContent.getContentDetail(
- id,
- undefined,
- props.pageQuerys.modelId && Number(props.pageQuerys.modelId) > 0 ? Number(props.pageQuerys.modelId) : undefined
- );
- for (const tab of tabRenderDefinesArray.value) {
- const v = d[tab.key];
- let check = true
- if (['intro','map'].includes(tab.type))
- check = true;
- else if (tab.type === 'audio')
- check = Boolean(d.audio);
- else if (tab.type === 'video')
- check = Boolean(d.video);
- else if (tab.type === 'images')
- check = Boolean(d.images) && (d.images as string[]).length > 0;
- else if (!v)
- check = false;
- else if (Array.isArray(v))
- check = (v as any[]).length > 0;
- tabsArray.getTabById(tab.id)!.visible = tab.visible !== false && check;
- }
- return d;
- }
- function handleGoToVr(vr: string) {
- navTo('/pages/article/web/ewebview', { url: vr })
- }
- defineExpose({
- getPageShareData() {
- return pageRef.value?.getPageShareData() || {};
- }
- })
- </script>
|