FEAT => 本次更新项目为:

初始化状态链接功能
This commit is contained in:
超级老白兔
2025-08-02 17:10:41 +08:00
parent 56c9c24937
commit ae4cbd46dc
8 changed files with 474 additions and 43 deletions

View File

@@ -42,7 +42,7 @@
export default {
data() {
return {
baseUrl: 'https://kr-op.quwanzhi.com/iframe',
baseUrl: 'https://kr-op.quwanzhi.com/init',
iframeUrl: '', // 动态构建的 URL
receivedMessages: [],
messageId: 0,
@@ -55,7 +55,6 @@
methods: {
// 构建 iframe URL包含参数
buildIframeUrl() {
const params = [];
Object.keys(this.urlParams).forEach(key => {
const value = this.urlParams[key];
@@ -65,7 +64,6 @@
params.push(`${encodedKey}=${encodedValue}`);
}
});
const queryString = params.join('&');
this.iframeUrl = queryString ? `${this.baseUrl}?${queryString}` : this.baseUrl;
},

View File

@@ -33,12 +33,12 @@
</view>
</view>
</view>
</view>
</template>
<script>
import { getTopSafeAreaHeightAsync } from '@utils/common';
export default {
data() {
return {
@@ -46,7 +46,6 @@
iframeUrl: '', // 动态构建的 URL
receivedMessages: [],
messageId: 0,
// URL 参数配置
urlParams: {}
}
},
@@ -56,8 +55,8 @@
methods: {
// 构建 iframe URL包含参数
buildIframeUrl() {
const params = [];
const params = [];
Object.keys(this.urlParams).forEach(key => {
const value = this.urlParams[key];
if (value !== null && value !== undefined) {
@@ -69,49 +68,34 @@
const queryString = params.join('&');
this.iframeUrl = queryString ? `${this.baseUrl}?${queryString}` : this.baseUrl;
// console.log('构建的 iframe URL:', this.iframeUrl);
},
// 发送消息到 iframe通过URL传参
sendMessageToIframe() {
async sendMessageToIframe() {
const paddingTop = await getTopSafeAreaHeightAsync();
this.messageId++;
const message = {
type: 0, // 数据类型0数据交互 1App功能调用
data: {
id: this.messageId,
content: `Hello我是 App 发送的消息 ${this.messageId}`,
timestamp: Date.now()
timestamp: Date.now(),
paddingTop: paddingTop
}
};
// 将消息添加到URL参数中
this.urlParams.message = encodeURIComponent(JSON.stringify(message));
this.buildIframeUrl();
console.log('App 发送消息:', message);
console.log('[App]SendMessage=>\n' + JSON.stringify(message));
},
// 接收 web-view 发送的消息
handleMessage(event) {
const webData = event.detail.data[0];
console.log('App 收到消息:', webData);
this.receivedMessages.push(`[${new Date().toLocaleTimeString()}] ${JSON.stringify(webData)}`);
if (webData.type === 0) {
// 数据交互
console.log('收到数据交互消息:', webData.data);
uni.showToast({
title: `收到数据:${webData.data.content || '无内容'}`,
icon: 'none'
});
} else if (webData.type === 1) {
// App功能调用
console.log('收到App功能调用:', webData.data);
uni.showToast({
title: `收到功能调用:${webData.data.action || '未知功能'}`,
icon: 'none'
});
const [ResDetail] = event.detail.data;
this.receivedMessages.push(`[${new Date().toLocaleTimeString()}] ${JSON.stringify(ResDetail)}`);
if (ResDetail.type === 0) {
console.log('[App]ReceiveMessage=>\n' + JSON.stringify(ResDetail.data));
} else if (ResDetail.type === 1) {
console.log('[App]ReceiveMessage=>\n' + JSON.stringify(ResDetail.data));
}
}
}

View File

@@ -0,0 +1,229 @@
import React, { useState, useEffect } from "react";
import style from "./index.module.scss";
import Layout from "@/components/Layout/Layout";
import NavCommon from "@/components/NavCommon";
import { LoadingOutlined, CheckCircleOutlined } from "@ant-design/icons";
import { Input } from "antd";
// 声明全局的 uni 对象
declare global {
interface Window {
uni: any;
}
}
interface Message {
type: number; // 数据类型0数据交互 1App功能调用
data: any;
}
const IframeDebugPage: React.FC = () => {
const [receivedMessages, setReceivedMessages] = useState<string[]>([]);
const [messageId, setMessageId] = useState(0);
const [inputMessage, setInputMessage] = useState("");
// 解析 URL 参数中的消息
const parseUrlMessage = () => {
const search = window.location.search.substring(1);
let messageParam = null;
if (search) {
const pairs = search.split("&");
for (const pair of pairs) {
const [key, value] = pair.split("=");
if (key === "message" && value) {
messageParam = decodeURIComponent(value);
break;
}
}
}
if (messageParam) {
try {
const message = JSON.parse(decodeURIComponent(messageParam));
console.log("[存客宝]ReceiveMessage=>\n" + JSON.stringify(message));
handleReceivedMessage(message);
// 清除URL中的message参数
const newUrl =
window.location.pathname +
window.location.search
.replace(/[?&]message=[^&]*/, "")
.replace(/^&/, "?");
window.history.replaceState({}, "", newUrl);
} catch (e) {
console.error("解析URL消息失败:", e);
}
}
};
useEffect(() => {
parseUrlMessage();
// 监听 SDK 初始化完成事件
}, []);
// 处理接收到的消息
const handleReceivedMessage = (message: Message) => {
const messageText = `[${new Date().toLocaleTimeString()}] 收到: ${JSON.stringify(message)}`;
setReceivedMessages(prev => [...prev, messageText]);
};
// 向 App 发送消息
const sendMessageToParent = (message: Message) => {
if (window.uni && window.uni.postMessage) {
try {
window.uni.postMessage({
data: message,
});
console.log("[存客宝]SendMessage=>\n" + JSON.stringify(message));
} catch (e) {
console.error(
"[存客宝]SendMessage=>\n" + JSON.stringify(message) + "发送失败:",
e,
);
}
} else {
console.error(
"[存客宝]SendMessage=>\n" + JSON.stringify(message) + "无法发送消息",
);
}
};
// 发送自定义消息到 App
const sendCustomMessage = () => {
if (!inputMessage.trim()) return;
const newMessageId = messageId + 1;
setMessageId(newMessageId);
const message: Message = {
type: 0, // 数据交互
data: {
id: newMessageId,
content: inputMessage,
source: "存客宝消息源",
timestamp: Date.now(),
},
};
sendMessageToParent(message);
setInputMessage("");
};
// 发送测试消息到 App
const sendTestMessage = () => {
const newMessageId = messageId + 1;
setMessageId(newMessageId);
const message: Message = {
type: 0, // 数据交互
data: {
id: newMessageId,
action: "ping",
content: `存客宝测试消息 ${newMessageId}`,
random: Math.random(),
},
};
sendMessageToParent(message);
};
// 发送App功能调用消息
const sendAppFunctionCall = () => {
const message: Message = {
type: 1, // App功能调用
data: {
action: "showToast",
params: {
title: "来自H5的功能调用",
icon: "success",
},
},
};
sendMessageToParent(message);
};
// 清空消息列表
const clearMessages = () => {
setInputMessage("");
setReceivedMessages([]);
};
const [connectStatus, setConnectStatus] = useState(false);
return (
<Layout
header={
<NavCommon
title="iframe调试"
right={
connectStatus ? (
<LoadingOutlined />
) : (
<CheckCircleOutlined color="green" />
)
}
/>
}
>
<div className={style["iframe-debug-page"]}>
<div className={style.content}>
<div className={style["message-panel"]}>
<h4></h4>
<div className={style["message-list"]}>
{receivedMessages.length === 0 ? (
<div className={style["no-messages"]}></div>
) : (
receivedMessages.map((msg, index) => (
<div key={index} className={style["message-item"]}>
<span className={style["message-text"]}>{msg}</span>
</div>
))
)}
</div>
</div>
<div className={style["control-panel"]}>
<h4></h4>
<div className={style["input-group"]}>
<Input
type="text"
value={inputMessage}
onChange={e => setInputMessage(e.target.value)}
placeholder="输入要发送的消息"
/>
<button
onClick={sendCustomMessage}
className={`${style.btn} ${style["btn-primary"]}`}
>
</button>
</div>
<div className={style["button-group"]}>
<button
onClick={sendTestMessage}
className={`${style.btn} ${style["btn-secondary"]}`}
>
</button>
<button
onClick={sendAppFunctionCall}
className={`${style.btn} ${style["btn-warning"]}`}
>
</button>
<button
onClick={clearMessages}
className={`${style.btn} ${style["btn-danger"]}`}
>
</button>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default IframeDebugPage;

View File

@@ -1,4 +1,5 @@
import Home from "@/pages/mobile/home/index";
import Init from "@/pages/iframe/init";
const routes = [
// 基础路由
@@ -7,6 +8,11 @@ const routes = [
element: <Home />,
auth: true, // 需要登录
},
{
path: "/init",
element: <Init />,
auth: false, // 需要登录
},
];
export default routes;

View File

@@ -2,15 +2,87 @@
import { create } from "zustand";
import { persist, PersistOptions } from "zustand/middleware";
// 自定义存储引擎,确保使用 localStorage
const localStorageStorage = {
getItem: (name: string): string | null => {
try {
return localStorage.getItem(name);
} catch (error) {
console.error(`Failed to get item ${name} from localStorage:`, error);
return null;
}
},
setItem: (name: string, value: any): void => {
try {
localStorage.setItem(name, JSON.stringify(value));
} catch (error) {
console.error(`Failed to set item ${name} to localStorage:`, error);
}
},
removeItem: (name: string): void => {
try {
localStorage.removeItem(name);
} catch (error) {
console.error(`Failed to remove item ${name} from localStorage:`, error);
}
},
};
export function createPersistStore<T>(
createState: (set: any, get: any) => T,
name: string,
partialize?: (state: T) => Partial<T>,
options?: Partial<PersistOptions<T, Partial<T>>>,
) {
return create<T>()(
persist(createState, {
name,
partialize,
} as PersistOptions<T>),
storage: localStorageStorage,
version: 1, // 版本控制,用于数据迁移
onRehydrateStorage: () => state => {
console.log(`Store ${name} rehydrated:`, state);
},
onError: error => {
console.error(`Store ${name} persistence error:`, error);
},
...options,
} as PersistOptions<T, Partial<T>>),
);
}
// 工具函数:手动保存状态到 localStorage
export const saveToLocalStorage = <T>(name: string, state: T): void => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem(name, serializedState);
} catch (error) {
console.error(`Failed to save state to localStorage for ${name}:`, error);
}
};
// 工具函数:从 localStorage 加载状态
export const loadFromLocalStorage = <T>(name: string): T | null => {
try {
const serializedState = localStorage.getItem(name);
if (serializedState === null) {
return null;
}
return JSON.parse(serializedState);
} catch (error) {
console.error(`Failed to load state from localStorage for ${name}:`, error);
return null;
}
};
// 工具函数:清除 localStorage 中的状态
export const clearFromLocalStorage = (name: string): void => {
try {
localStorage.removeItem(name);
} catch (error) {
console.error(
`Failed to clear state from localStorage for ${name}:`,
error,
);
}
};

View File

@@ -0,0 +1,130 @@
import { createPersistStore } from "@/store/createPersistStore";
export interface AppState {
// 应用状态
isLoading: boolean;
isOnline: boolean;
lastActiveTime: number;
// 主题设置
theme: "light" | "dark" | "auto";
// 缓存设置
cacheEnabled: boolean;
cacheExpiry: number; // 缓存过期时间(毫秒)
// 调试设置
debugMode: boolean;
logLevel: "error" | "warn" | "info" | "debug";
}
interface AppStoreState {
app: AppState;
setAppState: (app: Partial<AppState>) => void;
setLoading: (loading: boolean) => void;
setOnline: (online: boolean) => void;
setTheme: (theme: AppState["theme"]) => void;
setDebugMode: (debug: boolean) => void;
updateLastActiveTime: () => void;
resetAppState: () => void;
}
// 默认应用状态
const defaultAppState: AppState = {
isLoading: false,
isOnline: navigator.onLine,
lastActiveTime: Date.now(),
theme: "auto",
cacheEnabled: true,
cacheExpiry: 24 * 60 * 60 * 1000, // 24小时
debugMode: false,
logLevel: "info",
};
export const useAppStore = createPersistStore<AppStoreState>(
(set, get) => ({
app: defaultAppState,
setAppState: newAppState =>
set(state => ({
app: { ...state.app, ...newAppState },
})),
setLoading: loading =>
set(state => ({
app: { ...state.app, isLoading: loading },
})),
setOnline: online =>
set(state => ({
app: { ...state.app, isOnline: online },
})),
setTheme: theme =>
set(state => ({
app: { ...state.app, theme },
})),
setDebugMode: debug =>
set(state => ({
app: { ...state.app, debugMode: debug },
})),
updateLastActiveTime: () =>
set(state => ({
app: { ...state.app, lastActiveTime: Date.now() },
})),
resetAppState: () => set({ app: defaultAppState }),
}),
"app-store",
state => ({
app: state.app,
}),
);
// 应用状态工具函数
export const getAppState = (): AppState => {
const { app } = useAppStore.getState();
return app;
};
export const setAppLoading = (loading: boolean): void => {
const { setLoading } = useAppStore.getState();
setLoading(loading);
};
export const setAppTheme = (theme: AppState["theme"]): void => {
const { setTheme } = useAppStore.getState();
setTheme(theme);
};
export const toggleDebugMode = (): void => {
const { app, setDebugMode } = useAppStore.getState();
setDebugMode(!app.debugMode);
};
// 监听网络状态变化
if (typeof window !== "undefined") {
window.addEventListener("online", () => {
const { setOnline } = useAppStore.getState();
setOnline(true);
});
window.addEventListener("offline", () => {
const { setOnline } = useAppStore.getState();
setOnline(false);
});
// 监听用户活动
const updateLastActive = () => {
const { updateLastActiveTime } = useAppStore.getState();
updateLastActiveTime();
};
["mousedown", "mousemove", "keypress", "scroll", "touchstart"].forEach(
event => {
document.addEventListener(event, updateLastActive, true);
},
);
}

View File

@@ -12,6 +12,9 @@ export interface AppSettings {
// 功能设置
autoSave: boolean;
showTutorial: boolean;
//App设置
paddingTop: number;
}
interface SettingsState {
@@ -32,6 +35,7 @@ const defaultSettings: AppSettings = {
crashReportEnabled: true,
autoSave: true,
showTutorial: true,
paddingTop: 44,
};
export const useSettingsStore = createPersistStore<SettingsState>(

View File

@@ -27,21 +27,22 @@ interface UserState {
clearUser: () => void;
login: (token: string, userInfo: User, deviceTotal: number) => void;
logout: () => void;
updateUserInfo: (updates: Partial<User>) => void;
}
export const useUserStore = createPersistStore<UserState>(
set => ({
(set, get) => ({
user: null,
token: null,
isLoggedIn: false,
setUser: user => set({ user, isLoggedIn: true }),
setToken: token => set({ token }),
clearUser: () => set({ user: null, token: null, isLoggedIn: false }),
login: (token, userInfo, deviceTotal) => {
// 只将token存储到localStorage
localStorage.setItem("token", token);
// 用户信息存储在状态管理中
setUser: user => set({ user, isLoggedIn: true }),
setToken: token => set({ token }),
clearUser: () => set({ user: null, token: null, isLoggedIn: false }),
login: (token, userInfo, deviceTotal) => {
const user: User = {
id: userInfo.id,
account: userInfo.account,
@@ -61,11 +62,18 @@ export const useUserStore = createPersistStore<UserState>(
};
set({ user, token, isLoggedIn: true });
},
logout: () => {
// 清除localStorage中的token
localStorage.removeItem("token");
set({ user: null, token: null, isLoggedIn: false });
},
updateUserInfo: updates => {
const currentUser = get().user;
if (currentUser) {
const updatedUser = { ...currentUser, ...updates };
set({ user: updatedUser });
}
},
}),
"user-store",
state => ({