CommonCategoryDetail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <!--详情页可配置主控-->
  3. <FlexCol>
  4. <FlexCol v-if="errorMessage" :padding="30" :gap="30" center height="100%">
  5. <Result status="error" :description="errorMessage" />
  6. <Button type="primary" @click="loadPageConfig">重新加载</Button>
  7. </FlexCol>
  8. <LoadingPage v-else-if="!loadState" />
  9. <DetailTabPage
  10. v-else
  11. ref="pageRef"
  12. v-bind="$attrs"
  13. :noInternalTabs="true"
  14. :load="load"
  15. :extraTabs="tabRenderDefinesArray"
  16. @loaded="onLoaded"
  17. >
  18. <template #extraTabs="{ content, tabCurrentId }">
  19. <template v-if="tabRenderDefines[tabCurrentId].type === 'intro'">
  20. <!-- 简介 -->
  21. <Parse
  22. v-if="content.intro"
  23. :content="(content.intro as string)"
  24. />
  25. <Parse
  26. v-if="content.content"
  27. :content="content.content"
  28. />
  29. <text v-if="!(content.intro || content.content)">暂无简介</text>
  30. <text v-if="content.from" class="size-s color-text-content-second mr-2 ">以上内容摘自 {{ content.from }}</text>
  31. </template>
  32. <template v-else-if="tabRenderDefines[tabCurrentId].type === 'images'">
  33. <!-- 图片 -->
  34. <view v-if="tabRenderDefines[tabCurrentId].prefix" class="d-flex flex-row justify-center align-center mt-2 mb-2">
  35. <text class="size-s font-bold color-text-content">{{ tabRenderDefines[tabCurrentId].prefix }}</text>
  36. </view>
  37. <ImageGrid
  38. :images="content.images"
  39. :rowCount="2"
  40. :preview="true"
  41. imageHeight="200rpx"
  42. />
  43. </template>
  44. <template v-else-if="tabRenderDefines[tabCurrentId].type === 'video'">
  45. <!-- 视频 -->
  46. <video
  47. v-if="content.video"
  48. class="w-100 video"
  49. autoplay
  50. :poster="content.image"
  51. :src="content.video"
  52. controls
  53. />
  54. </template>
  55. <template v-else-if="tabRenderDefines[tabCurrentId].type === 'audio'">
  56. <!-- 视频 -->
  57. <video
  58. v-if="content.audio"
  59. class="w-100 video"
  60. autoplay
  61. :poster="content.image"
  62. :src="content.audio"
  63. controls
  64. />
  65. </template>
  66. <template v-else-if="tabRenderDefines[tabCurrentId].type === 'list'">
  67. <!-- 列表 -->
  68. <CommonCategoryListBlock
  69. v-if="currentCommonCategoryDefine"
  70. :currentCommonCategoryDefine="{
  71. title: '',
  72. name: 'default',
  73. content: tabRenderDefines[tabCurrentId].define
  74. }"
  75. :currentCommonCategoryContentDefine="tabRenderDefines[tabCurrentId].define"
  76. :pageQuerys="pageQuerys"
  77. :parentData="content"
  78. :hasPadding="false"
  79. :hasBg="false"
  80. @error="errorMessage = $event"
  81. />
  82. </template>
  83. <template v-else-if="tabRenderDefines[tabCurrentId].type === 'rich'">
  84. <!-- 富文本 -->
  85. <view class="d-flex flex-col mt-3 mb-2">
  86. <Parse :content="(content[tabRenderDefines[tabCurrentId].key] as string)" />
  87. </view>
  88. </template>
  89. <template v-else-if="tabRenderDefines[tabCurrentId].type === 'nestCategory'">
  90. <!-- 嵌套分类 -->
  91. <CommonCategoryBlocks :categoryDefine="tabRenderDefines[tabCurrentId].categoryDefine" />
  92. </template>
  93. <template v-else-if="tabRenderDefines[tabCurrentId].key === 'vr'">
  94. <!-- VR参观 -->
  95. <view class="d-flex flex-row justify-center p-5">
  96. <Button @click="handleGoToVr(content.vr as string)">
  97. <text class="iconfont icon-go"></text>
  98. 点击参观
  99. </Button>
  100. </view>
  101. </template>
  102. <template v-else>
  103. <CommonCategoryDetailContentBlocks
  104. :define="tabRenderDefines[tabCurrentId]"
  105. :content="content"
  106. />
  107. </template>
  108. </template>
  109. <template #titleEnd="{ content }">
  110. <Tag
  111. v-if="content.levelText"
  112. :text="StringUtils.cutString(content.levelText as string, 4)"
  113. size="small" scheme="light" type="primary"
  114. class="flex-shrink-0"
  115. />
  116. </template>
  117. <template #titleExtra="{ content }">
  118. <view class="d-flex flex-col">
  119. <IntroBlock small :descItems="descItems" />
  120. <CommonCategoryDetailIntroBlocks
  121. :introBlocks="currentCommonCategoryContentDefine?.props.introBlocks"
  122. :content="content"
  123. />
  124. </view>
  125. </template>
  126. </DetailTabPage>
  127. </FlexCol>
  128. </template>
  129. <script setup lang="ts">
  130. import { computed, onMounted, ref, watch } from 'vue';
  131. import LoadingPage from '@/components/display/loading/LoadingPage.vue';
  132. import FlexCol from '@/components/layout/FlexCol.vue';
  133. import Result from '@/components/feedback/Result.vue';
  134. import Button from '@/components/basic/Button.vue';
  135. import { CommonCategoryListTabNestCategoryDataToContent, type IHomeCommonCategoryDefine, type IHomeCommonCategoryListTabNestCategoryItemDefine } from './CommonCategoryDefine';
  136. import { injectCommonCategory } from './CommonCategoryGlobalLoader';
  137. import { doLoadDynamicCategoryDataMergeTypeGetColumns } from './CommonCategoryDynamicData';
  138. import { formatError, StringUtils, waitTimeOut } from '@imengyu/imengyu-utils';
  139. import type { IHomeCommonCategoryDetailDefine, IHomeCommonCategoryDetailTabItemDefine } from './defines/Details';
  140. import type { DetailTabPageProps, DetailTabPageTabsArray } from '../common/DetailTabPage';
  141. import type { CategoryDefine } from './CommonCategoryBlocks';
  142. import DetailTabPage from '../common/DetailTabPage.vue';
  143. import IntroBlock from '../common/IntroBlock.vue';
  144. import CommonCategoryDetailIntroBlocks from './CommonCategoryDetailIntroBlocks.vue';
  145. import Tag from '@/components/display/Tag.vue';
  146. import CommonCategoryBlocks from './CommonCategoryBlocks.vue';
  147. import Parse from '@/components/display/parse/Parse.vue';
  148. import CommonContent from '@/api/CommonContent';
  149. import CommonCategoryDetailContentBlocks from './CommonCategoryDetailContentBlocks.vue';
  150. import ImageGrid from '@/pages/parts/ImageGrid.vue';
  151. import CommonCategoryListBlock from './CommonCategoryListBlock.vue';
  152. import { navTo } from '@/components/utils/PageAction';
  153. export interface CommonCategoryDetailProps extends DetailTabPageProps {
  154. /**
  155. * 简介块描述项
  156. */
  157. introBlockDescs?: {
  158. label: string;
  159. key: string;
  160. map?: Record<string|number, string>;
  161. }[];
  162. /**
  163. * 简介下方块
  164. */
  165. introBlocks?: CommonCategoryDetailIntroBlocksDesc[];
  166. }
  167. export interface CommonCategoryDetailIntroBlocksDesc {
  168. type: string;
  169. props?: Record<string, any>;
  170. }
  171. export type RenderTabDefine = IHomeCommonCategoryDetailTabItemDefine & {
  172. id: number;
  173. categoryDefine?: CategoryDefine[];
  174. };
  175. const pageRef = ref();
  176. const props = defineProps({
  177. pageConfigName: {
  178. type: String,
  179. },
  180. pageQuerys: {
  181. type: Object as () => Record<string, string|number|number[]|undefined>,
  182. default: () => ({}),
  183. },
  184. })
  185. const loadState = ref(false);
  186. const errorMessage = ref('');
  187. const currentCommonCategoryDefine = ref<IHomeCommonCategoryDefine['page'][0]>();
  188. const currentCommonCategoryContentDefine = ref<IHomeCommonCategoryDetailDefine>();
  189. const commonCategory = injectCommonCategory();
  190. const tabDefines = computed(() => currentCommonCategoryContentDefine.value?.props.tabs || []);
  191. const tabRenderDefines = computed(() => {
  192. const result = {} as Record<number, RenderTabDefine>;
  193. try {
  194. tabDefines.value.forEach((item, i) => {
  195. const renderItem : RenderTabDefine = {
  196. ...item,
  197. id: i,
  198. };
  199. function loadNestCategoryData(items: IHomeCommonCategoryListTabNestCategoryItemDefine[]) {
  200. return items
  201. .filter((item) => item.visible !== false)
  202. .map((item) => {
  203. return {
  204. ...item,
  205. showTitle: item.showTitle !== false,
  206. title: item.text,
  207. content: CommonCategoryListTabNestCategoryDataToContent(
  208. item.data, item
  209. ),
  210. type: item.type as CategoryDefine['type'],
  211. }
  212. });
  213. }
  214. switch (item.type) {
  215. case 'nestCategory':
  216. renderItem.categoryDefine = loadNestCategoryData(item.categorys);
  217. break;
  218. }
  219. result[i] = renderItem;
  220. });
  221. } catch (error) {
  222. errorMessage.value = formatError(error);
  223. }
  224. return result;
  225. });
  226. const tabRenderDefinesArray = computed(() => {
  227. return Object.values(tabRenderDefines.value);
  228. });
  229. async function loadPageConfig() {
  230. if (!props.pageConfigName) {
  231. errorMessage.value = '配置有误';
  232. return;
  233. }
  234. currentCommonCategoryDefine.value = commonCategory.value.page
  235. .find((item) => item.name === props.pageConfigName);
  236. if (!currentCommonCategoryDefine.value) {
  237. errorMessage.value = '未找到指定的分类配置:' + props.pageConfigName;
  238. return;
  239. }
  240. if (currentCommonCategoryDefine.value.content.type !== 'Details') {
  241. errorMessage.value = '分类配置:' + props.pageConfigName + ' 不是详情类型';
  242. return;
  243. }
  244. currentCommonCategoryContentDefine.value =
  245. currentCommonCategoryDefine.value.content as IHomeCommonCategoryDetailDefine;
  246. uni.setNavigationBarTitle({
  247. title: currentCommonCategoryDefine.value?.title || '',
  248. })
  249. await waitTimeOut(100);
  250. try {
  251. //特殊处理
  252. let hasNestCategory = false;
  253. for (const [_, tab] of Object.entries(tabDefines.value)) {
  254. if (tab.type === 'nestCategory') {
  255. tab.categorys = await doLoadDynamicCategoryDataMergeTypeGetColumns(tab.categorys)
  256. hasNestCategory = true;
  257. }
  258. }
  259. if (hasNestCategory)
  260. await waitTimeOut(100);
  261. loadState.value = true;
  262. await waitTimeOut(100);
  263. pageRef.value?.load(props.pageQuerys);
  264. } catch (error) {
  265. console.error(error);
  266. loadState.value = false;
  267. errorMessage.value = formatError(error);
  268. }
  269. }
  270. watch(() => props.pageConfigName, loadPageConfig);
  271. onMounted(loadPageConfig);
  272. const content = ref<any>();
  273. const descItems = computed(() => (
  274. currentCommonCategoryContentDefine.value?.props.introBlockDescs || [])
  275. .map((item) => {
  276. let value = content.value?.[item.key] || '';
  277. if (item.map)
  278. value = item.map[value] || '';
  279. return {
  280. ...item,
  281. value,
  282. }
  283. })
  284. );
  285. function onLoaded(d: any) {
  286. content.value = d;
  287. }
  288. async function load(id: number, tabsArray: DetailTabPageTabsArray) {
  289. if (isNaN(id) || id <= 0)
  290. throw new Error("请输入ID。如果正在测试,可在后台复制ID");
  291. const d = await CommonContent.getContentDetail(
  292. id,
  293. undefined,
  294. props.pageQuerys.modelId && Number(props.pageQuerys.modelId) > 0 ? Number(props.pageQuerys.modelId) : undefined
  295. );
  296. for (const tab of tabRenderDefinesArray.value) {
  297. const v = d[tab.key];
  298. let check = true
  299. if (['intro','map'].includes(tab.type))
  300. check = true;
  301. else if (tab.type === 'audio')
  302. check = Boolean(d.audio);
  303. else if (tab.type === 'video')
  304. check = Boolean(d.video);
  305. else if (tab.type === 'images')
  306. check = Boolean(d.images) && (d.images as string[]).length > 0;
  307. else if (!v)
  308. check = false;
  309. else if (Array.isArray(v))
  310. check = (v as any[]).length > 0;
  311. tabsArray.getTabById(tab.id)!.visible = tab.visible !== false && check;
  312. }
  313. return d;
  314. }
  315. function handleGoToVr(vr: string) {
  316. navTo('/pages/article/web/ewebview', { url: vr })
  317. }
  318. defineExpose({
  319. getPageShareData() {
  320. return pageRef.value?.getPageShareData() || {};
  321. }
  322. })
  323. </script>