FEAT => 本次更新项目为:
初始化状态链接功能
This commit is contained in:
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
229
nkebao/src/pages/iframe/init.tsx
Normal file
229
nkebao/src/pages/iframe/init.tsx
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
130
nkebao/src/store/module/app.ts
Normal file
130
nkebao/src/store/module/app.ts
Normal 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);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -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>(
|
||||
|
||||
@@ -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 => ({
|
||||
|
||||
Reference in New Issue
Block a user