feat: 首页交互的
This commit is contained in:
114
scripts/test-discovery-panel-scroll.mjs
Normal file
114
scripts/test-discovery-panel-scroll.mjs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { readFile } from "node:fs/promises";
|
||||||
|
|
||||||
|
const helperUrl = new URL("../src/utils/discoveryPanelScroll.js", import.meta.url);
|
||||||
|
const chatMainListUrl = new URL("../src/pages/ChatMain/ChatMainList/index.vue", import.meta.url);
|
||||||
|
const helperSource = await readFile(helperUrl, "utf8");
|
||||||
|
const chatMainListSource = await readFile(chatMainListUrl, "utf8");
|
||||||
|
const helperModule = await import(
|
||||||
|
`data:text/javascript;charset=utf-8,${encodeURIComponent(helperSource)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
canExpandDiscoveryPanelFromScroll,
|
||||||
|
getDiscoveryPanelCollapseState,
|
||||||
|
} = helperModule;
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
getDiscoveryPanelCollapseState({
|
||||||
|
currentScrollTop: 24,
|
||||||
|
lastScrollTop: 0,
|
||||||
|
isCollapsed: false,
|
||||||
|
threshold: 16,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
"scrolling up into discovery content should collapse the welcome panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
getDiscoveryPanelCollapseState({
|
||||||
|
currentScrollTop: 18,
|
||||||
|
lastScrollTop: 15,
|
||||||
|
isCollapsed: false,
|
||||||
|
threshold: 16,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
"slow upward scrolling past the threshold should collapse the welcome panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
getDiscoveryPanelCollapseState({
|
||||||
|
currentScrollTop: 40,
|
||||||
|
lastScrollTop: 64,
|
||||||
|
isCollapsed: true,
|
||||||
|
threshold: 16,
|
||||||
|
allowExpand: true,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
"scrolling down should reveal the welcome panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
getDiscoveryPanelCollapseState({
|
||||||
|
currentScrollTop: 40,
|
||||||
|
lastScrollTop: 64,
|
||||||
|
isCollapsed: true,
|
||||||
|
threshold: 16,
|
||||||
|
allowExpand: false,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
"layout-driven reverse scroll after release should not reveal the welcome panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
getDiscoveryPanelCollapseState({
|
||||||
|
currentScrollTop: 63,
|
||||||
|
lastScrollTop: 64,
|
||||||
|
isCollapsed: true,
|
||||||
|
threshold: 16,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
"small scroll jitter should keep the current panel state"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.match(
|
||||||
|
chatMainListSource,
|
||||||
|
/@touchmove\.capture="handleDiscoveryPanelTouchMove"/,
|
||||||
|
"discovery shell should capture touchmove before nested scroll-view consumes the gesture"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
canExpandDiscoveryPanelFromScroll({
|
||||||
|
isTouching: false,
|
||||||
|
touchDirection: "",
|
||||||
|
lastDownGestureAt: 1000,
|
||||||
|
now: 1400,
|
||||||
|
gestureRetainMs: 800,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
"momentum scroll after a down gesture should still be allowed to reveal the welcome panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
canExpandDiscoveryPanelFromScroll({
|
||||||
|
isTouching: false,
|
||||||
|
touchDirection: "",
|
||||||
|
lastDownGestureAt: 1000,
|
||||||
|
now: 1901,
|
||||||
|
gestureRetainMs: 800,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
"old reverse scroll should not be treated as an active down gesture"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
canExpandDiscoveryPanelFromScroll({
|
||||||
|
isTouching: true,
|
||||||
|
touchDirection: "up",
|
||||||
|
lastDownGestureAt: 1000,
|
||||||
|
now: 1200,
|
||||||
|
gestureRetainMs: 800,
|
||||||
|
}),
|
||||||
|
false,
|
||||||
|
"an active upward gesture should block reveal even if a down gesture happened earlier"
|
||||||
|
);
|
||||||
@@ -21,7 +21,9 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-show="tabIndex === 0" class="discovery-shell flex-full" @touchstart.capture="handleDiscoveryPanelTouchStart"
|
<view v-show="tabIndex === 0" class="discovery-shell flex-full" @touchstart.capture="handleDiscoveryPanelTouchStart"
|
||||||
@touchstart="handleDiscoveryPanelTouchStart" @touchmove="handleDiscoveryPanelTouchMove">
|
@touchstart="handleDiscoveryPanelTouchStart" @touchmove.capture="handleDiscoveryPanelTouchMove"
|
||||||
|
@touchmove="handleDiscoveryPanelTouchMove"
|
||||||
|
@touchend="handleDiscoveryPanelTouchEnd" @touchcancel="handleDiscoveryPanelTouchEnd">
|
||||||
<view class="home-welcome-panel">
|
<view class="home-welcome-panel">
|
||||||
<HomeWelcome class="flex-shrink-0" :mainPageDataModel="mainPageDataModel" />
|
<HomeWelcome class="flex-shrink-0" :mainPageDataModel="mainPageDataModel" />
|
||||||
</view>
|
</view>
|
||||||
@@ -196,6 +198,10 @@ import {
|
|||||||
recentConversation,
|
recentConversation,
|
||||||
} from "@/request/api/ConversationApi";
|
} from "@/request/api/ConversationApi";
|
||||||
import WebSocketManager from "@/utils/WebSocketManager";
|
import WebSocketManager from "@/utils/WebSocketManager";
|
||||||
|
import {
|
||||||
|
canExpandDiscoveryPanelFromScroll,
|
||||||
|
getDiscoveryPanelCollapseState,
|
||||||
|
} from "@/utils/discoveryPanelScroll";
|
||||||
import { IdUtils } from "@/utils";
|
import { IdUtils } from "@/utils";
|
||||||
import {
|
import {
|
||||||
appendLongTextChunk,
|
appendLongTextChunk,
|
||||||
@@ -228,8 +234,14 @@ const scrollTop = ref(99999);
|
|||||||
const discoveryInnerScrollTop = ref(0);
|
const discoveryInnerScrollTop = ref(0);
|
||||||
const discoveryPanelTouchStartY = ref(0);
|
const discoveryPanelTouchStartY = ref(0);
|
||||||
const isDiscoveryPanelCollapsed = ref(false);
|
const isDiscoveryPanelCollapsed = ref(false);
|
||||||
|
let lastDiscoveryInnerScrollTop = 0;
|
||||||
|
let isDiscoveryPanelTouching = false;
|
||||||
|
let discoveryPanelTouchDirection = "";
|
||||||
|
let lastDiscoveryDownGestureAt = 0;
|
||||||
const DISCOVERY_PANEL_EXPAND_TOP = 4;
|
const DISCOVERY_PANEL_EXPAND_TOP = 4;
|
||||||
const DISCOVERY_PANEL_TOUCH_DELTA = 16;
|
const DISCOVERY_PANEL_TOUCH_DELTA = 16;
|
||||||
|
const DISCOVERY_PANEL_DIRECTION_DELTA = 2;
|
||||||
|
const DISCOVERY_PANEL_GESTURE_RETAIN_MS = 800;
|
||||||
|
|
||||||
/// 会话列表
|
/// 会话列表
|
||||||
const chatMsgList = ref([]);
|
const chatMsgList = ref([]);
|
||||||
@@ -389,6 +401,10 @@ const handleScrollAreaTouchMove = () => {
|
|||||||
const resetDiscoveryScrollState = () => {
|
const resetDiscoveryScrollState = () => {
|
||||||
discoveryInnerScrollTop.value = 0;
|
discoveryInnerScrollTop.value = 0;
|
||||||
discoveryPanelTouchStartY.value = 0;
|
discoveryPanelTouchStartY.value = 0;
|
||||||
|
lastDiscoveryInnerScrollTop = 0;
|
||||||
|
isDiscoveryPanelTouching = false;
|
||||||
|
discoveryPanelTouchDirection = "";
|
||||||
|
lastDiscoveryDownGestureAt = 0;
|
||||||
isDiscoveryPanelCollapsed.value = false;
|
isDiscoveryPanelCollapsed.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -400,6 +416,8 @@ const getTouchPointY = (e) => {
|
|||||||
|
|
||||||
const handleDiscoveryPanelTouchStart = (e) => {
|
const handleDiscoveryPanelTouchStart = (e) => {
|
||||||
handleScrollAreaTouchStart();
|
handleScrollAreaTouchStart();
|
||||||
|
isDiscoveryPanelTouching = true;
|
||||||
|
discoveryPanelTouchDirection = "";
|
||||||
discoveryPanelTouchStartY.value = getTouchPointY(e);
|
discoveryPanelTouchStartY.value = getTouchPointY(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -410,6 +428,14 @@ const handleDiscoveryPanelTouchMove = (e) => {
|
|||||||
if (!currentY || !discoveryPanelTouchStartY.value) return;
|
if (!currentY || !discoveryPanelTouchStartY.value) return;
|
||||||
|
|
||||||
const touchDelta = currentY - discoveryPanelTouchStartY.value;
|
const touchDelta = currentY - discoveryPanelTouchStartY.value;
|
||||||
|
if (touchDelta < -DISCOVERY_PANEL_DIRECTION_DELTA) {
|
||||||
|
discoveryPanelTouchDirection = "up";
|
||||||
|
lastDiscoveryDownGestureAt = 0;
|
||||||
|
} else if (touchDelta > DISCOVERY_PANEL_DIRECTION_DELTA) {
|
||||||
|
discoveryPanelTouchDirection = "down";
|
||||||
|
lastDiscoveryDownGestureAt = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
if (touchDelta < -DISCOVERY_PANEL_TOUCH_DELTA) {
|
if (touchDelta < -DISCOVERY_PANEL_TOUCH_DELTA) {
|
||||||
isDiscoveryPanelCollapsed.value = true;
|
isDiscoveryPanelCollapsed.value = true;
|
||||||
} else if (
|
} else if (
|
||||||
@@ -420,11 +446,27 @@ const handleDiscoveryPanelTouchMove = (e) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDiscoveryPanelTouchEnd = () => {
|
||||||
|
isDiscoveryPanelTouching = false;
|
||||||
|
discoveryPanelTouchDirection = "";
|
||||||
|
};
|
||||||
|
|
||||||
const handleDiscoveryContentScroll = ({ scrollTop }) => {
|
const handleDiscoveryContentScroll = ({ scrollTop }) => {
|
||||||
discoveryInnerScrollTop.value = Number(scrollTop || 0);
|
const currentScrollTop = Number(scrollTop || 0);
|
||||||
if (discoveryInnerScrollTop.value > DISCOVERY_PANEL_TOUCH_DELTA) {
|
isDiscoveryPanelCollapsed.value = getDiscoveryPanelCollapseState({
|
||||||
isDiscoveryPanelCollapsed.value = true;
|
currentScrollTop,
|
||||||
}
|
lastScrollTop: lastDiscoveryInnerScrollTop,
|
||||||
|
isCollapsed: isDiscoveryPanelCollapsed.value,
|
||||||
|
threshold: DISCOVERY_PANEL_TOUCH_DELTA,
|
||||||
|
allowExpand: canExpandDiscoveryPanelFromScroll({
|
||||||
|
isTouching: isDiscoveryPanelTouching,
|
||||||
|
touchDirection: discoveryPanelTouchDirection,
|
||||||
|
lastDownGestureAt: lastDiscoveryDownGestureAt,
|
||||||
|
gestureRetainMs: DISCOVERY_PANEL_GESTURE_RETAIN_MS,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
discoveryInnerScrollTop.value = currentScrollTop;
|
||||||
|
lastDiscoveryInnerScrollTop = currentScrollTop;
|
||||||
};
|
};
|
||||||
|
|
||||||
const welcomeHeight = ref(0);
|
const welcomeHeight = ref(0);
|
||||||
|
|||||||
42
src/utils/discoveryPanelScroll.js
Normal file
42
src/utils/discoveryPanelScroll.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
export const getDiscoveryPanelCollapseState = ({
|
||||||
|
currentScrollTop = 0,
|
||||||
|
lastScrollTop = 0,
|
||||||
|
isCollapsed = false,
|
||||||
|
threshold = 16,
|
||||||
|
directionThreshold = 2,
|
||||||
|
allowExpand = true,
|
||||||
|
}) => {
|
||||||
|
const current = Math.max(Number(currentScrollTop) || 0, 0);
|
||||||
|
const last = Math.max(Number(lastScrollTop) || 0, 0);
|
||||||
|
const delta = current - last;
|
||||||
|
|
||||||
|
if (delta > directionThreshold && current > threshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowExpand && delta < -directionThreshold) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isCollapsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const canExpandDiscoveryPanelFromScroll = ({
|
||||||
|
isTouching = false,
|
||||||
|
touchDirection = "",
|
||||||
|
lastDownGestureAt = 0,
|
||||||
|
now = Date.now(),
|
||||||
|
gestureRetainMs = 800,
|
||||||
|
}) => {
|
||||||
|
if (isTouching) {
|
||||||
|
return touchDirection === "down";
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastDownAt = Math.max(Number(lastDownGestureAt) || 0, 0);
|
||||||
|
if (!lastDownAt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = Math.max(Number(now) || 0, 0);
|
||||||
|
return currentTime - lastDownAt <= gestureRetainMs;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user