|
@@ -3,8 +3,8 @@
|
|
|
* 浮动面板展示层:组合 Vant Overlay + FloatingPanel,关闭动画与 CSS 变量。
|
|
* 浮动面板展示层:组合 Vant Overlay + FloatingPanel,关闭动画与 CSS 变量。
|
|
|
* 锚点合并 / autoHeight 逻辑见 `floatPanelAnchors.ts`、`useFloatPanelAnchorsAutoHeight.ts`。
|
|
* 锚点合并 / autoHeight 逻辑见 `floatPanelAnchors.ts`、`useFloatPanelAnchorsAutoHeight.ts`。
|
|
|
*/
|
|
*/
|
|
|
-import { nextTick, ref } from 'vue';
|
|
|
|
|
-import { tryOnMounted, useElementSize } from '@vueuse/core';
|
|
|
|
|
|
|
+import { computed, nextTick, onUpdated, ref, watch, watchPostEffect } from 'vue';
|
|
|
|
|
+import { useElementSize } from '@vueuse/core';
|
|
|
import { computeAnchorsAndSnapHeight } from './floatPanelAnchors';
|
|
import { computeAnchorsAndSnapHeight } from './floatPanelAnchors';
|
|
|
import type { FloatPanelInstance, FloatPanelProps } from './types';
|
|
import type { FloatPanelInstance, FloatPanelProps } from './types';
|
|
|
import { useFloatPanelAnchorsAutoHeight } from './useFloatPanelAnchorsAutoHeight';
|
|
import { useFloatPanelAnchorsAutoHeight } from './useFloatPanelAnchorsAutoHeight';
|
|
@@ -95,9 +95,33 @@ const { height: panelHeaderHeight } = useElementSize(panelHeaderRef, { height: 3
|
|
|
|
|
|
|
|
const panelContentRootRef = useTemplateRef('panel-content-root');
|
|
const panelContentRootRef = useTemplateRef('panel-content-root');
|
|
|
const { height: measuredContentHeight } = useElementSize(panelContentRootRef, { height: 0 }, { box: 'border-box' });
|
|
const { height: measuredContentHeight } = useElementSize(panelContentRootRef, { height: 0 }, { box: 'border-box' });
|
|
|
-
|
|
|
|
|
const panelContentHeight = ref(0);
|
|
const panelContentHeight = ref(0);
|
|
|
|
|
|
|
|
|
|
+/** autoHeight 下内容区被限制高度后,useElementSize 只有可视高;锚点需按 scrollHeight 取真实内容高 */
|
|
|
|
|
+const intrinsicAutoContentHeight = ref(0);
|
|
|
|
|
+function syncIntrinsicAutoContentHeight() {
|
|
|
|
|
+ if (!autoHeight) return;
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ const el = panelContentRootRef.value;
|
|
|
|
|
+ const sh = el?.scrollHeight ?? 0;
|
|
|
|
|
+ if (intrinsicAutoContentHeight.value !== sh) intrinsicAutoContentHeight.value = sh;
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+watchPostEffect(() => {
|
|
|
|
|
+ if (!autoHeight) return;
|
|
|
|
|
+ height.value;
|
|
|
|
|
+ panelContentHeight.value;
|
|
|
|
|
+ measuredContentHeight.value;
|
|
|
|
|
+ syncIntrinsicAutoContentHeight();
|
|
|
|
|
+});
|
|
|
|
|
+onUpdated(() => {
|
|
|
|
|
+ if (autoHeight) syncIntrinsicAutoContentHeight();
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const measuredContentHeightForAnchors = computed(() =>
|
|
|
|
|
+ autoHeight ? intrinsicAutoContentHeight.value : measuredContentHeight.value,
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
const style = computed(() => {
|
|
const style = computed(() => {
|
|
|
return {
|
|
return {
|
|
|
'--van-floating-panel-header-height': `${panelHeaderHeight.value}px`,
|
|
'--van-floating-panel-header-height': `${panelHeaderHeight.value}px`,
|
|
@@ -170,7 +194,7 @@ const instance: FloatPanelInstance = {
|
|
|
|
|
|
|
|
const { resetAutoHeightSyncOnReopen } = useFloatPanelAnchorsAutoHeight({
|
|
const { resetAutoHeightSyncOnReopen } = useFloatPanelAnchorsAutoHeight({
|
|
|
height,
|
|
height,
|
|
|
- measuredContentHeight,
|
|
|
|
|
|
|
+ measuredContentHeight: measuredContentHeightForAnchors,
|
|
|
panelHeaderHeight,
|
|
panelHeaderHeight,
|
|
|
closable: () => closable,
|
|
closable: () => closable,
|
|
|
full: () => full,
|
|
full: () => full,
|
|
@@ -184,10 +208,23 @@ const onClose = () => applyCloseAnimation();
|
|
|
const onClickOverlay = () => {
|
|
const onClickOverlay = () => {
|
|
|
if (closeOnClickOverlay) onClose();
|
|
if (closeOnClickOverlay) onClose();
|
|
|
};
|
|
};
|
|
|
-const onUpdateHeight = ({ height: h }) => {
|
|
|
|
|
- panelContentHeight.value = h - panelHeaderHeight.value;
|
|
|
|
|
|
|
+
|
|
|
|
|
+/** 与 Vant `height-change` 一致:内容区高度 = 面板总高 − 头高。逻辑层改 `v-model:height` 时未必触发该事件,需自行同步 CSS 变量 */
|
|
|
|
|
+function syncPanelContentHeightFromTotal(total: number) {
|
|
|
|
|
+ panelContentHeight.value =
|
|
|
|
|
+ total <= 0 ? 0 : Math.max(0, total - panelHeaderHeight.value);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onUpdateHeight = ({ height: h }: { height: number }) => {
|
|
|
|
|
+ syncPanelContentHeightFromTotal(h);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+watch(
|
|
|
|
|
+ [height, panelHeaderHeight],
|
|
|
|
|
+ () => syncPanelContentHeightFromTotal(height.value),
|
|
|
|
|
+ { flush: 'post', immediate: true },
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
watch(height, (h, prev) => {
|
|
watch(height, (h, prev) => {
|
|
|
if (h !== 0) {
|
|
if (h !== 0) {
|
|
|
clearCloseTimer();
|
|
clearCloseTimer();
|
|
@@ -199,7 +236,6 @@ watch(height, (h, prev) => {
|
|
|
resetAutoHeightSyncOnReopen(prev, h);
|
|
resetAutoHeightSyncOnReopen(prev, h);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-tryOnMounted(() => onUpdateHeight({ height: height.value }));
|
|
|
|
|
defineExpose(instance);
|
|
defineExpose(instance);
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -270,8 +306,8 @@ defineExpose(instance);
|
|
|
}
|
|
}
|
|
|
.van-floating-panel__header_icon {
|
|
.van-floating-panel__header_icon {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
- top: $gap;
|
|
|
|
|
- right: $gap;
|
|
|
|
|
|
|
+ top: calc($gap / 2);
|
|
|
|
|
+ right:calc($gap / 2);
|
|
|
color: var(--van-floating-panel-bar-color);
|
|
color: var(--van-floating-panel-bar-color);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -279,23 +315,12 @@ defineExpose(instance);
|
|
|
height: var(--van-floating-panel-content-height);
|
|
height: var(--van-floating-panel-content-height);
|
|
|
overflow: hidden !important;
|
|
overflow: hidden !important;
|
|
|
flex: none;
|
|
flex: none;
|
|
|
|
|
+ min-height: 0;
|
|
|
> * {
|
|
> * {
|
|
|
height: 100%;
|
|
height: 100%;
|
|
|
|
|
+ min-height: 0;
|
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- &.float-panel--auto-height {
|
|
|
|
|
- :deep(.van-floating-panel__content) {
|
|
|
|
|
- height: auto !important;
|
|
|
|
|
- overflow: visible !important;
|
|
|
|
|
- flex: 1 0 auto;
|
|
|
|
|
- > .float-panel-content-root {
|
|
|
|
|
- height: auto !important;
|
|
|
|
|
- min-height: 0;
|
|
|
|
|
- overflow: visible !important;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|