| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- <script setup lang="ts">
- import NavHomeSelect from '@/assets/images/nav-home.select.png?url';
- import NavScheme from '@/assets/images/nav-scheme.png?url';
- import SchemeMedia from '@/modules/report/SchemeMedia.vue';
- import { getReportSchemeMethod } from '@/request/api/report.api';
- import { useRouteParams } from '@vueuse/router';
- import { useWatcher } from 'alova/client';
- import { useRouter } from 'vue-router';
- import MiniProgram from '@/components/MiniProgram.vue';
- import type { SchemeGoodsProps } from '@/request/model';
- import { createReusableTemplate } from '@vueuse/core';
- import { Toast } from '@/platform';
- import { useVisitor } from '@/stores';
- const miniProgramRef = useTemplateRef<InstanceType<typeof MiniProgram>>('mini-program');
- const route = useRoute();
- const Visitor = useVisitor();
- const id = useRouteParams<string>('id', Visitor.reportId);
- const scrollable = ref(true);
- const closeable = computed(() => !data.value.payLock);
- const { data, loading } = useWatcher(() => getReportSchemeMethod(id.value, !route.meta.toggle), [id], {
- initialData: {
- children: [],
- },
- immediate: true,
- }).onSuccess(({ data }) => {
- if ( data?.miniProgramURL && data.payLock ) {
- scrollable.value = false;
- nextTick(() => miniProgramRef.value?.open());
- }
- });
- const router = useRouter();
- const toggleable = computed(() => route.meta.toggle ?? true);
- function toggle() {
- const path = router.currentRoute.value.fullPath.replace('/scheme', '');
- router.replace({ path });
- }
- const { define: PreviewLinkSlot, reuse: ReusePreviewLink } = createReusableTemplate<{ src: string | void; complete?: Function; error?: Function }>();
- const panelConfig = reactive({
- fullHeight: 0,
- height: 0,
- anchors: [0],
- goods: void 0 as unknown as SchemeGoodsProps,
- onError() {},
- onComplete(event?: Event) {},
- });
- const container = useTemplateRef('container');
- const getHeightAndScrollTop = (value?: HTMLElement | string) => {
- const el = typeof value === 'string' ? container.value?.querySelector<HTMLDivElement>(`#${value}`) : value;
- if (!el) return true;
- el.scrollIntoView({ behavior: 'instant', block: 'start' });
- const rect = el.getBoundingClientRect();
- const maxHeight = window.innerHeight - rect.top;
- const height = maxHeight - rect.height;
- panelConfig.anchors = [0, height, maxHeight];
- panelConfig.height = height;
- panelConfig.fullHeight = maxHeight;
- };
- async function openGoodsPanel(goods: SchemeGoodsProps, event?: Event | string) {
- if (panelConfig.goods === goods) {
- panelConfig.height = panelConfig.anchors[panelConfig.anchors.length - 1];
- return;
- }
- if (goods.type === 'link') {
- const toast = Toast.loading(500);
- panelConfig.goods = goods;
- panelConfig.onComplete = () => {
- toast.close();
- panelConfig.height = panelConfig.anchors[panelConfig.anchors.length - 1];
- };
- panelConfig.onError = () => {
- Toast.error(`链接加载错误`)
- panelConfig.height = 0;
- };
- } else {
- Toast.warning(`暂不支持该操作 (${goods.type})`);
- return;
- }
- if (getHeightAndScrollTop(typeof event === 'string' ? event : (event?.target as HTMLElement))) {
- const height = window.innerHeight * 0.8;
- panelConfig.anchors = [0, height];
- panelConfig.height = height;
- panelConfig.fullHeight = window.innerHeight;
- }
- }
- </script>
- <template>
- <div>
- <div class="page-header flex py-4 px-4">
- <div class="grow shrink-0 h-full min-w-16"></div>
- <div class="grow-[3] shrink mx-2 flex flex-col justify-center overflow-hidden">
- <div class="font-bold text-3xl text-nowrap text-center tracking-wide overflow-ellipsis overflow-hidden">
- 调理方案
- </div>
- </div>
- <div class="grow shrink-0 h-full min-w-16">
- <router-link :to="{ path: '/screen' }" replace>
- <img class="size-8 object-scale-down" :src="NavHomeSelect" alt="返回首页" />
- </router-link>
- </div>
- </div>
- <div class="page-content flex flex-col overflow-hidden">
- <!--{{ data }}-->
- <van-skeleton class="flex-auto" title :row="3" :loading>
- <div class="flex-auto px-6" :class="[scrollable ? 'overflow-y-auto' : 'overflow-hidden']" ref="container">
- <div class="card my-6 text-lg" :id="item.id" v-for="item in data.children" :key="item.id">
- <div class="card__title mb-3 text-primary text-2xl font-bold">{{ item.title }}</div>
- <div class="card__content">
- <div class="my-4" :id="'T_' + card.id" v-for="card in item.children" :key="card.id">
- <div class="relative" :class="{ 'has-link': card.goods }">
- <div class="text-xl text-center text-primary" v-if="card.title">{{ card.title }}</div>
- <van-button
- class="!absolute top-0 right-0" v-if="card.goods"
- type="primary" icon="cart-o" size="small" plain
- @click="openGoodsPanel(card.goods, 'T_' + card.id)"
- >{{ card.goods.label }}</van-button>
- </div>
- <SchemeMedia :media="card.media"></SchemeMedia>
- <div v-if="card.description">{{ card.description }}</div>
- <div v-for="(item, index) in card.descriptions">
- <span class="text-primary">【{{ item.title }}】</span>
- <span v-html="item.description"></span>
- </div>
- </div>
- </div>
- </div>
- </div>
- </van-skeleton>
- <div class="flex-none flex justify-between py-2 nav-wrapper" style="background-color: #12312c">
- <div v-if="toggleable" class="m-auto min-w-16 text-center hover:text-primary" @click="toggle()">
- <img class="nav-img" :src="NavScheme" alt="健康报告" />
- <div class="mt-2">健康报告</div>
- </div>
- <mini-program ref="mini-program" :url="data.miniProgramURL" :closeable="!data.payLock"></mini-program>
- </div>
- </div>
- <PreviewLinkSlot v-slot="{ src, complete, error }">
- <iframe v-if="src" :src="src" @load="complete!" @error="error!"></iframe>
- </PreviewLinkSlot>
- <van-floating-panel
- ref="panel-wrapper-ref"
- :class="{ full: panelConfig.height === panelConfig.fullHeight }"
- :content-draggable="false"
- :lock-scroll="true"
- :anchors="panelConfig.anchors"
- v-model:height="panelConfig.height"
- >
- <template #header>
- <div class="van-floating-panel__header !justify-between">
- <div></div>
- <div class="van-floating-panel__header-bar"></div>
- <van-icon class="pr-2" name="cross" @click="panelConfig.height = 0" />
- </div>
- </template>
- <ReusePreviewLink v-if="panelConfig.goods?.type === 'link'" :src="panelConfig.goods?.value" :complete="panelConfig.onComplete"></ReusePreviewLink>
- </van-floating-panel>
- </div>
- </template>
- <style scoped lang="scss">
- .card {
- padding: 24px;
- border-radius: 24px;
- box-shadow: inset 0 0 80px 0 #34a76b60;
- }
- .van-button.decorate {
- font-size: 20px;
- height: 62px;
- width: 240px;
- background-size: 80%;
- letter-spacing: 2px;
- }
- .text-grey {
- color: #e3e3e3;
- }
- .nav-wrapper {
- .nav-img {
- margin: auto;
- width: 36px;
- height: 36px;
- object-fit: scale-down;
- }
- }
- .full {
- --van-floating-panel-border-radius: 0;
- }
- .has-link {
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 40px;
- > div + .van-button {
- top: 4px !important;
- }
- }
- iframe {
- width: 100%;
- height: 100%;
- border: none;
- }
- </style>
|