|
@@ -8,6 +8,11 @@ import { useWatcher } from 'alova/client';
|
|
|
import { useRouter } from 'vue-router';
|
|
import { useRouter } from 'vue-router';
|
|
|
|
|
|
|
|
import MiniProgram from '@/components/MiniProgram.vue';
|
|
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 miniProgramRef = useTemplateRef<InstanceType<typeof MiniProgram>>('mini-program');
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
const route = useRoute();
|
|
@@ -31,6 +36,57 @@ function toggle() {
|
|
|
const path = router.currentRoute.value.fullPath.replace('/scheme', '');
|
|
const path = router.currentRoute.value.fullPath.replace('/scheme', '');
|
|
|
router.replace({ path });
|
|
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>
|
|
</script>
|
|
|
<template>
|
|
<template>
|
|
|
<div>
|
|
<div>
|
|
@@ -50,12 +106,19 @@ function toggle() {
|
|
|
<div class="page-content flex flex-col overflow-hidden">
|
|
<div class="page-content flex flex-col overflow-hidden">
|
|
|
<!--{{ data }}-->
|
|
<!--{{ data }}-->
|
|
|
<van-skeleton class="flex-auto" title :row="3" :loading>
|
|
<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__title mb-3 text-primary text-2xl font-bold">{{ item.title }}</div>
|
|
|
<div class="card__content">
|
|
<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>
|
|
<SchemeMedia :media="card.media"></SchemeMedia>
|
|
|
<div v-if="card.description">{{ card.description }}</div>
|
|
<div v-if="card.description">{{ card.description }}</div>
|
|
|
<div v-for="(item, index) in card.descriptions">
|
|
<div v-for="(item, index) in card.descriptions">
|
|
@@ -75,6 +138,27 @@ function toggle() {
|
|
|
<mini-program ref="mini-program" :url="data.miniProgramURL" :closeable="!data.payLock"></mini-program>
|
|
<mini-program ref="mini-program" :url="data.miniProgramURL" :closeable="!data.payLock"></mini-program>
|
|
|
</div>
|
|
</div>
|
|
|
</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>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
<style scoped lang="scss">
|
|
<style scoped lang="scss">
|
|
@@ -104,4 +188,25 @@ function toggle() {
|
|
|
object-fit: scale-down;
|
|
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>
|
|
</style>
|