From 5664a1a616df498cba58b9a8e63a91ac0ba96bab Mon Sep 17 00:00:00 2001
From: zhengyiming <540361168@qq.com>
Date: 星期二, 01 七月 2025 13:44:55 +0800
Subject: [PATCH] feat: add rrweb

---
 .env.development        |    2 
 package.json            |    1 
 src/utils/record.ts     |   91 ++++++++++++++++++++++++++++++
 src/views/Home/Home.vue |   11 +++
 pnpm-lock.yaml          |   47 +++++++++++++++
 5 files changed, 151 insertions(+), 1 deletions(-)

diff --git a/.env.development b/.env.development
index 870a9ef..65872e0 100644
--- a/.env.development
+++ b/.env.development
@@ -2,7 +2,7 @@
 VITE_PORT = 8698
 
 # 寮�鍙戠幆澧冭鍙栭厤缃枃浠惰矾寰�
-VITE_PUBLIC_PATH = /front/
+VITE_PUBLIC_PATH = /
 
 # 寮�鍙戠幆澧冧唬鐞�
 VITE_PROXY_DOMAIN = /api
diff --git a/package.json b/package.json
index 8700bd6..fb05b23 100644
--- a/package.json
+++ b/package.json
@@ -72,6 +72,7 @@
     "pinia": "^2.2.4",
     "qs": "^6.11.0",
     "rgb-hex": "^4.0.0",
+    "rrweb": "2.0.0-alpha.4",
     "semver": "^7.6.3",
     "senin-help": "latest",
     "senin-vue": "latest",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6b8c375..f65b995 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -139,6 +139,9 @@
   rgb-hex:
     specifier: ^4.0.0
     version: 4.0.0
+  rrweb:
+    specifier: 2.0.0-alpha.4
+    version: 2.0.0-alpha.4
   semver:
     specifier: ^7.6.3
     version: 7.6.3
@@ -3627,6 +3630,10 @@
     dev: true
     optional: true
 
+  /@rrweb/types@2.0.0-alpha.18:
+    resolution: {integrity: sha512-iMH3amHthJZ9x3gGmBPmdfim7wLGygC2GciIkw2A6SO8giSn8PHYtRT8OKNH4V+k3SZ6RSnYHcTQxBA7pSWZ3Q==}
+    dev: false
+
   /@sindresorhus/merge-streams@2.3.0:
     resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
     engines: {node: '>=18'}
@@ -3949,6 +3956,10 @@
   /@types/ali-oss@6.16.11:
     resolution: {integrity: sha512-/AyemPZy93ZXGzEokMsoPFgjH37snpzH4X/fwans/n63HLaCleriCG3PyrkHCPkgHEc9vj9Uo6paqsBN3vJ3OA==}
     dev: true
+
+  /@types/css-font-loading-module@0.0.7:
+    resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==}
+    dev: false
 
   /@types/eslint@7.29.0:
     resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==}
@@ -5182,6 +5193,10 @@
     engines: {node: '>=10.0.0'}
     dev: false
 
+  /@xstate/fsm@1.6.5:
+    resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==}
+    dev: false
+
   /@ywwlmm/openapi@0.0.3:
     resolution: {integrity: sha512-zbQMq2LnMX2IJmEJIG12IeggQCEm1sn7E+S7TH6yCLaYOERf8ARA22GJUmjmYaLUD9iKgb8yl2ohO3PSI6aMdw==}
     dependencies:
@@ -5782,6 +5797,11 @@
 
   /balanced-match@2.0.0:
     resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
+
+  /base64-arraybuffer@1.0.2:
+    resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
+    engines: {node: '>= 0.6.0'}
+    dev: false
 
   /base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -8502,6 +8522,10 @@
     resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
     dependencies:
       reusify: 1.0.4
+
+  /fflate@0.4.8:
+    resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+    dev: false
 
   /file-entry-cache@4.0.0:
     resolution: {integrity: sha512-AVSwsnbV8vH/UVbvgEhf3saVQXORNv0ZzSkvkhQIaia5Tia+JhGTaa/ePUSVoPHQyGayQNmYfkzFi3WZV5zcpA==}
@@ -13408,6 +13432,29 @@
     resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
     dev: false
 
+  /rrdom@0.1.7:
+    resolution: {integrity: sha512-ZLd8f14z9pUy2Hk9y636cNv5Y2BMnNEY99wxzW9tD2BLDfe1xFxtLjB4q/xCBYo6HRe0wofzKzjm4JojmpBfFw==}
+    dependencies:
+      rrweb-snapshot: 2.0.0-alpha.4
+    dev: false
+
+  /rrweb-snapshot@2.0.0-alpha.4:
+    resolution: {integrity: sha512-KQ2OtPpXO5jLYqg1OnXS/Hf+EzqnZyP5A+XPqBCjYpj3XIje/Od4gdUwjbFo3cVuWq5Cw5Y1d3/xwgIS7/XpQQ==}
+    dev: false
+
+  /rrweb@2.0.0-alpha.4:
+    resolution: {integrity: sha512-wEHUILbxDPcNwkM3m4qgPgXAiBJyqCbbOHyVoNEVBJzHszWEFYyTbrZqUdeb1EfmTRC2PsumCIkVcomJ/xcOzA==}
+    dependencies:
+      '@rrweb/types': 2.0.0-alpha.18
+      '@types/css-font-loading-module': 0.0.7
+      '@xstate/fsm': 1.6.5
+      base64-arraybuffer: 1.0.2
+      fflate: 0.4.8
+      mitt: 3.0.0
+      rrdom: 0.1.7
+      rrweb-snapshot: 2.0.0-alpha.4
+    dev: false
+
   /rtc-ai-denoiser@1.1.7:
     resolution: {integrity: sha512-53e/4a4lT96K004mqDnLDE+upNSpBLRMfFgYCeIw3Gvuw9F17nxLP5v8MOVLly4/Epomxkx4SXrOFJJMxD2pIw==}
     dev: false
diff --git a/src/utils/record.ts b/src/utils/record.ts
new file mode 100644
index 0000000..1c4c87e
--- /dev/null
+++ b/src/utils/record.ts
@@ -0,0 +1,91 @@
+import * as rrweb from 'rrweb';
+import { guid } from './common';
+import { eventWithTime, listenerHandler } from '@rrweb/types';
+import { storageLocal } from './storage';
+
+export class Recorder {
+  // 鐢熸垚浼氳瘽ID锛堢敤浜庢爣璇嗕竴娆℃姇淇濇祦绋嬶級
+  sessionId = guid();
+  // 瀛樺偍褰曞埗浜嬩欢鐨勬暟缁�
+  events = [];
+
+  stopFn: () => void;
+
+  // 鍒濆鍖栧綍鍒跺櫒
+  init() {
+    this.stopFn = rrweb.record({
+      emit: (event) => {
+        // 鏀堕泦浜嬩欢锛堝彲閫夋嫨瀹炴椂鍙戦�佸埌鍚庣锛�
+        this.events.push(event);
+
+        // 瀹氭湡鍙戦�佷簨浠跺埌鍚庣锛堥伩鍏嶅唴瀛樺崰鐢ㄨ繃澶э級
+        if (this.events.length % 100 === 0) {
+          this.sendEventsToBackend(this.events.splice(0, 100), this.sessionId);
+        }
+      },
+      // 鍙�夐厤缃細蹇界暐鏁忔劅鍏冪礌
+      blockClass: 'rr-block', // 涓烘晱鎰熷厓绱犳坊鍔犳class
+      ignoreClass: 'rr-ignore', // 瀹屽叏蹇界暐鐨勫厓绱�
+    });
+  }
+
+  // 缁撴潫褰曞埗锛堜緥濡傛彁浜ゆ姇淇濊〃鍗曟椂锛�
+  stopRecordingAndSave() {
+    this.stopFn?.();
+    // 鍙戦�佸墿浣欎簨浠跺埌鍚庣
+    if (this.events.length > 0) {
+      this.sendEventsToBackend(this.events, this.sessionId);
+    }
+    // 瀛樺偍浼氳瘽ID锛堝叧鑱斿埌鐢ㄦ埛鎶曚繚璁板綍锛�
+    this.saveSessionIdToUserRecord(this.sessionId);
+  }
+
+  saveSessionIdToUserRecord(sessionId: string) {}
+
+  async sendEventsToBackend(events: eventWithTime[], sessionId: string) {
+    try {
+      const body = JSON.stringify({
+        sessionId,
+        events,
+        timestamp: new Date().toISOString(),
+      });
+      console.log('body: ', body);
+      const storageEvents = storageLocal.getItem(`events`) || {};
+      storageEvents[sessionId] = events;
+      storageLocal.setItem(`events`, storageEvents);
+      // await fetch('/api/recordings', {
+      //   method: 'POST',
+      //   headers: {
+      //     'Content-Type': 'application/json',
+      //   },
+      //   body: ,
+      // });
+    } catch (error) {
+      console.error('Failed to send recording events:', error);
+      // 鍙疄鐜版湰鍦板瓨鍌ㄤ綔涓洪檷绾ф柟妗�
+    }
+  }
+
+  async replaySession(sessionId: string) {
+    // 浠庡悗绔幏鍙栧綍鍒朵簨浠�
+    // const response = await fetch(`/api/recordings/${sessionId}`);
+    // const { events } = await response.json();
+    const storageEvents = storageLocal.getItem(`events`) || {};
+    if (!storageEvents[sessionId]) {
+      return;
+    }
+
+    // 鍒濆鍖栧洖鏀惧櫒
+    const player = new rrweb.Replayer(storageEvents[sessionId], {
+      root: document.getElementById('replay-container'),
+    });
+
+    // 鍙�夛細鎺у埗鍥炴斁閫熷害
+    player.play(1.0); // 1鍊嶉��
+
+    // 鍙�夛細鐩戝惉鍥炴斁浜嬩欢
+    player.on('play', () => console.log('Replay started'));
+    player.on('pause', () => console.log('Replay paused'));
+    player.on('finish', () => console.log('Replay finished'));
+  }
+}
diff --git a/src/views/Home/Home.vue b/src/views/Home/Home.vue
index 0568350..3bc4ade 100644
--- a/src/views/Home/Home.vue
+++ b/src/views/Home/Home.vue
@@ -155,6 +155,7 @@
 import dayjs from 'dayjs';
 import _ from 'lodash';
 import InsureInstructionsDialog from './components/InsureInstructionsDialog.vue';
+// import { Recorder } from '@/utils/record';
 
 defineOptions({
   name: 'Home',
@@ -262,13 +263,23 @@
 };
 
 const state = reactive({ ...BaseState });
+// const recorder = ref(new Recorder());
 
 onMounted(async () => {
   await getList();
   state.loading = false;
   handleOpenInstructions();
+
+  // setTimeout(() => {
+  //   // recorder.value.init();
+  //   recorder.value.replaySession('9cb24e5a-0423-4dcd-abd5-fa7a4117cadc');
+  // }, 3000);
 });
 
+// onUnmounted(() => {
+//   recorder.value.stopRecordingAndSave();
+// });
+
 const {
   getDataSource: getList,
   proTableProps,

--
Gitblit v1.9.1