|
|
@@ -7,21 +7,86 @@ 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';
|
|
|
+
|
|
|
+const miniProgramRef = useTemplateRef<InstanceType<typeof MiniProgram>>('mini-program');
|
|
|
+
|
|
|
+const route = useRoute();
|
|
|
const id = useRouteParams<string>('id');
|
|
|
-const { data, loading } = useWatcher(() => getReportSchemeMethod(id.value), [ id ], {
|
|
|
+const { data, loading } = useWatcher(() => getReportSchemeMethod(id.value, !route.meta.toggle), [id], {
|
|
|
initialData: {
|
|
|
children: [],
|
|
|
},
|
|
|
immediate: true,
|
|
|
+}).onSuccess(({ data }) => {
|
|
|
+ if ( data?.miniProgramURL && data.payLock ) {
|
|
|
+ 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>
|
|
|
@@ -41,15 +106,22 @@ function toggle() {
|
|
|
<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 overflow-y-auto">
|
|
|
- <div class="card my-6 text-lg" v-for="item in data.children" :key="item.id">
|
|
|
+ <div class="flex-auto px-6 overflow-y-auto" 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" v-for="card in item.children" :key="card.id">
|
|
|
- <div class="text-xl text-center text-primary">{{ card.title }}</div>
|
|
|
+ <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 ">
|
|
|
+ <div v-for="(item, index) in card.descriptions">
|
|
|
<span class="text-primary">【{{ item.title }}】</span>
|
|
|
<span v-html="item.description"></span>
|
|
|
</div>
|
|
|
@@ -58,13 +130,35 @@ function toggle() {
|
|
|
</div>
|
|
|
</div>
|
|
|
</van-skeleton>
|
|
|
- <div class="flex-none flex justify-between py-2 nav-wrapper" style="background-color: #12312c;">
|
|
|
- <div class="m-auto min-w-16 text-center hover:text-primary" @click="toggle()">
|
|
|
- <img :src="NavScheme" alt="健康报告">
|
|
|
+ <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">
|
|
|
@@ -85,4 +179,34 @@ function toggle() {
|
|
|
.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>
|