Compare commits

...

8 Commits

Author SHA1 Message Date
Apcallover
bd72faa9a6 Merge branch 'main' of http://124.243.245.42:3000/sdz/live-stream-app 2025-12-31 18:26:14 +08:00
Apcallover
a2808e47ac flat:zanc 2025-12-31 18:25:11 +08:00
Apcallover
3cd7514066 flat: 暂存 2025-12-31 17:01:36 +08:00
Apcallover
6641e07b8a flat: 暂存 2025-12-30 15:40:46 +08:00
Apcallover
634610ca7e flat: 暂存 2025-12-30 10:49:47 +08:00
lion风
9f18b32c7e 岗位信息调用润色接口 2025-12-27 14:37:08 +08:00
lion风
de65b813e1 界面优化:去掉json格式,去掉转换按钮,去掉转换后区域 2025-12-25 15:53:16 +08:00
lion风
9792c88e53 基本完成添加岗位&岗位详情&逐句播放&插入播放 2025-12-25 15:34:00 +08:00
9 changed files with 4068 additions and 5742 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 59 KiB

BIN
build/icon3.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

8307
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
"build:win:x64": "npm run build && electron-builder --win --x64",
"build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux"
},
@@ -49,9 +50,9 @@
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.20.1",
"prettier": "^3.2.4",
"typescript": "^5.3.3",
"typescript": "^5.9.3",
"vite": "^5.0.12",
"vue": "^3.4.15",
"vue-tsc": "^1.8.27"
"vue-tsc": "^3.2.1"
}
}

View File

@@ -40,6 +40,7 @@ export function setupLiveHandlers() {
hasLiveWindow: !!liveState.liveWindow,
isVideoInserted: liveState.isVideoInserted,
cameraActive: liveState.cameraActive,
sessionId: liveState.sessionId,
},
};
});
@@ -104,6 +105,8 @@ export function setupLiveHandlers() {
}
liveState.sessionId = sessionResult.sessionId;
// socket
// 5. 设置直播状态
liveState.isLiveOn = true;
@@ -200,7 +203,6 @@ export function setupLiveHandlers() {
type: "echo",
interrupt: true,
};
await sendMessage(params);
return { success: true };
} catch (error: any) {
@@ -238,23 +240,34 @@ export function setupLiveHandlers() {
allowRunningInsecureContent: true, // 允许运行本地内容
},
});
// liveState.liveWindow.webContents.openDevTools();
liveState.liveWindow.webContents.openDevTools();
liveState.liveWindow.on("closed", () => {
liveState.liveWindow = null;
liveState.isLiveOn = false;
});
console.log(liveUrl);
// if (ELECTRON_RENDERER_URL) {
// await liveState.liveWindow.loadURL(liveUrl);
// } else {
// await liveState.liveWindow.loadFile(indexHtml, { hash: "/live" });
// }
if (ELECTRON_RENDERER_URL) {
let liveUrl = `${ELECTRON_RENDERER_URL}/#/live`;
if (liveState.userId) {
liveUrl += `?userId=${liveState.userId}`;
}
await liveState.liveWindow.loadURL(liveUrl);
} else {
await liveState.liveWindow.loadFile(indexHtml, { hash: "/live" });
await liveState.liveWindow.loadFile(indexHtml, {
hash: "live",
search: `userId=${liveState.userId}`
});
}
}
// 等待页面加载完成
async function waitForIframeVideoPlaying(
webContents: Electron.WebContents,
timeoutMs = 20000,
timeoutMs = 120000, // 2 分钟超时
) {
console.log("开始检测 iframe 内视频的实际播放状态...");

View File

@@ -0,0 +1,38 @@
// src/types/socket.ts
/**
* Socket 配置项接口
*/
export interface SocketOptions {
/** 心跳间隔ms默认 30000 */
heartbeatInterval?: number;
/** 初始重连间隔ms默认 3000 */
reconnectInterval?: number;
/** 最大重连次数,默认 10 */
maxReconnectCount?: number;
/** 消息接收回调 */
onMessage?: (data: string | ArrayBuffer | Blob) => void;
/** 错误回调 */
onError?: (error: Event) => void;
/** 关闭回调 */
onClose?: (event: CloseEvent) => void;
/** 连接成功回调 */
onOpen?: () => void;
}
/**
* 消息记录类型
*/
export interface MessageItem {
time: string;
type: 'send' | 'receive' | 'system';
content: string | object;
}
/**
* 心跳包类型(可根据后端格式调整)
*/
export interface HeartbeatData {
type: 'ping' | 'pong';
timestamp?: number;
}

View File

@@ -0,0 +1,175 @@
// src/utils/socket.ts
import type { SocketOptions, HeartbeatData } from "@renderer/types/socket";
/**
* WebSocket 长连接工具类TS 版)
* @class SocketClient
* @param {string} url - WS/WSS 连接地址
* @param {SocketOptions} options - 配置项
*/
class SocketClient {
private url: string; // 连接地址(私有属性)
private ws: WebSocket | null; // WebSocket 实例
private isConnected: boolean; // 连接状态
private heartbeatTimer: NodeJS.Timeout | null; // 心跳定时器
private reconnectTimer: NodeJS.Timeout | null; // 重连定时器
private reconnectCount: number; // 已重连次数
private config: Required<SocketOptions>; // 完整配置(包含默认值)
constructor(url: string, options: SocketOptions = {}) {
this.url = url;
this.ws = null;
this.isConnected = false;
this.heartbeatTimer = null;
this.reconnectTimer = null;
this.reconnectCount = 0;
// 合并默认配置与用户配置(确保所有配置项有值)
this.config = {
heartbeatInterval: options.heartbeatInterval || 30000,
reconnectInterval: options.reconnectInterval || 3000,
maxReconnectCount: options.maxReconnectCount || 10,
onMessage: options.onMessage || (() => {}),
onError: options.onError || (() => {}),
onClose: options.onClose || (() => {}),
onOpen: options.onOpen || (() => {})
};
// 初始化连接
this.init();
}
/**
* 初始化 WebSocket 连接
*/
private init(): void {
// 关闭已有连接
this.close();
try {
this.ws = new WebSocket(this.url);
// 连接成功
this.ws.onopen = (): void => {
this.isConnected = true;
this.reconnectCount = 0;
this.config.onOpen();
this.startHeartbeat();
};
// 接收消息
this.ws.onmessage = (event: MessageEvent): void => {
this.config.onMessage(event.data);
};
// 错误回调
this.ws.onerror = (error: Event): void => {
this.isConnected = false;
this.config.onError(error);
};
// 关闭回调
this.ws.onclose = (event: CloseEvent): void => {
this.isConnected = false;
this.config.onClose(event);
this.stopHeartbeat();
// 自动重连(未超过最大次数)
if (this.reconnectCount < this.config.maxReconnectCount) {
this.reconnect();
}
};
} catch (error) {
this.config.onError(error as Event);
this.reconnect();
}
}
/**
* 启动心跳保活
*/
private startHeartbeat(): void {
this.stopHeartbeat();
this.heartbeatTimer = setInterval(() => {
if (this.isConnected && this.ws) {
// 发送心跳包(强类型)
const heartbeatData: HeartbeatData = {
type: 'ping',
timestamp: Date.now()
};
this.send(heartbeatData);
}
}, this.config.heartbeatInterval);
}
/**
* 停止心跳
*/
private stopHeartbeat(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
/**
* 发送消息
* @param {string | object} data - 要发送的消息(字符串/对象)
* @throws {Error} 连接未建立时抛出错误
*/
public send(data: string | object): void {
if (!this.isConnected || !this.ws) {
throw new Error('WebSocket 未连接,无法发送消息');
}
// 类型判断:对象转为 JSON 字符串
const sendData: string = typeof data === 'object'
? JSON.stringify(data)
: data;
this.ws.send(sendData);
}
/**
* 断线重连(阶梯式间隔)
*/
private reconnect(): void {
this.reconnectCount++;
const currentInterval = this.config.reconnectInterval * this.reconnectCount;
this.reconnectTimer = setTimeout(() => {
console.log(`WebSocket 第 ${this.reconnectCount} 次重连...`);
this.init();
}, currentInterval);
}
/**
* 主动关闭连接
* @param {number} code - 关闭码(默认 1000正常关闭
* @param {string} reason - 关闭原因
*/
public close(code: number = 1000, reason: string = '主动关闭连接'): void {
if (this.ws) {
this.ws.close(code, reason);
this.ws = null;
}
this.isConnected = false;
this.stopHeartbeat();
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
}
/**
* 获取当前连接状态
* @returns {boolean} 连接状态
*/
public getConnectedState(): boolean {
return this.isConnected;
}
}
export default SocketClient;

File diff suppressed because it is too large Load Diff

View File

@@ -190,15 +190,23 @@ onBeforeUnmount(() => {
})
function startLive() {
const paramsUserId = route.query.userId;
console.log(paramsUserId)
let paramsUserId = route.query.userId;
if (!paramsUserId) {
const urlParams = new URLSearchParams(window.location.search);
paramsUserId = urlParams.get('userId');
}
if (!paramsUserId && window.location.hash.includes('?')) {
const hashQuery = window.location.hash.split('?')[1];
const searchParams = new URLSearchParams(hashQuery);
paramsUserId = searchParams.get('userId');
}
if (paramsUserId) {
userId.value = paramsUserId
console.log(userId.value)
// 直播数字人 正式地址
userId.value = paramsUserId;
liveUrl.value = `https://dmdemo.hx.cn/dashboard.html?userId=${userId.value}`;
// 测试地址
// liveUrl.value = "https://www.baidu.com/"
}
}