main
SWX\10484 2 days ago
parent aa9ed9e3c9
commit 78e93a3e31
  1. 104
      src/layout/components/SystemSettingDialog.vue
  2. 118
      src/views/videoCommunication/realTimeConsultation.vue

@ -37,19 +37,21 @@
<div class="path-control">
<el-input :value="basicForm.videoPath" disabled class="path-input" />
<div class="path-buttons">
<el-button type="primary" >打开文件夹</el-button>
<el-button class="modify-btn">修改</el-button>
<el-button class="modify-btn" @click="modifyVideoPath">修改</el-button>
</div>
</div>
<!-- <span v-if="videoAuthorized" class="auth-tag authorized">已授权</span> -->
<!-- <span v-else class="auth-tag unauthorized">未授权截图将下载到浏览器默认目录</span> -->
</el-form-item>
<el-form-item label="缓存存储">
<div class="path-control">
<el-input :value="basicForm.cachePath" disabled class="path-input" />
<div class="path-buttons">
<el-button type="primary" >打开文件夹</el-button>
<el-button class="modify-btn">修改</el-button>
<el-button class="modify-btn" @click="modifyCachePath">修改</el-button>
</div>
</div>
<!-- <span v-if="cacheAuthorized" class="auth-tag authorized">已授权</span> -->
<!-- <span v-else class="auth-tag unauthorized">未授权</span> -->
</el-form-item>
</el-form>
</el-tab-pane>
@ -122,6 +124,7 @@ export default {
visible(newVal) {
if (newVal) {
this.activeTab = "basic";
this.refreshAuthStatus();
}
},
//
@ -166,6 +169,8 @@ export default {
return {
visible: false,
activeTab: "basic",
videoAuthorized: false,
cacheAuthorized: false,
basicForm: {
notification: true,
sendKey: "enter",
@ -268,6 +273,84 @@ export default {
//
});
},
// ==================== IndexedDB realTimeConsultation HiUTalkStoreDB ====================
_openIDB() {
return new Promise((resolve, reject) => {
const req = indexedDB.open('HiUTalkStoreDB', 1);
req.onupgradeneeded = () => { req.result.createObjectStore('handles'); };
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
},
async _getStoredDirHandle(key) {
const db = await this._openIDB();
return new Promise((resolve, reject) => {
const tx = db.transaction('handles', 'readonly');
const req = tx.objectStore('handles').get(key);
req.onsuccess = () => { db.close(); resolve(req.result); };
req.onerror = () => { db.close(); reject(req.error); };
});
},
async _storeDirHandle(key, handle) {
const db = await this._openIDB();
return new Promise((resolve, reject) => {
const tx = db.transaction('handles', 'readwrite');
const req = tx.objectStore('handles').put(handle, key);
req.onsuccess = () => { db.close(); resolve(); };
req.onerror = () => { db.close(); reject(req.error); };
});
},
//
async refreshAuthStatus() {
this.videoAuthorized = await this._checkAuthorized('screenshot_dir');
this.cacheAuthorized = await this._checkAuthorized('cache_dir');
},
async _checkAuthorized(key) {
const handle = await this._getStoredDirHandle(key);
if (!handle) return false;
const perm = await handle.queryPermission({ mode: 'readwrite' });
return perm === 'granted';
},
//
async modifyVideoPath() {
await this._selectAndStorePath('screenshot_dir', 'videoPath', '视讯存储');
},
//
async modifyCachePath() {
await this._selectAndStorePath('cache_dir', 'cachePath', '缓存存储');
},
// HiUTalkStore
async _selectAndStorePath(handleKey, pathKey, label) {
if (!window.showDirectoryPicker) {
this.$message.error('当前浏览器不支持,请使用 Chrome 或 Edge');
return;
}
try {
let dirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
// / HiUTalkStore
if (dirHandle.name !== 'HiUTalkStore') {
dirHandle = await dirHandle.getDirectoryHandle('HiUTalkStore', { create: true });
}
await this._storeDirHandle(handleKey, dirHandle);
if (pathKey === 'videoPath') this.videoAuthorized = true;
if (pathKey === 'cachePath') this.cacheAuthorized = true;
// D:
const drive = this._extractDrive(this.basicForm[pathKey]);
this.basicForm[pathKey] = `${drive}\\...\\${dirHandle.name}`;
this.$message.success(`${label}路径已更新`);
} catch (err) {
if (err.name !== 'AbortError') {
console.error(`${label}路径修改失败`, err);
this.$message.error('修改失败');
}
}
},
// "D:\RUS\HiUTalkStore" "D:"
_extractDrive(path) {
if (!path) return 'D:';
const match = path.match(/^([A-Za-z]:)/);
return match ? match[1] : 'D:';
},
restoreDefault() {
this.$confirm("确定要恢复默认设置吗?", "提示", {
confirmButtonText: "确定",
@ -276,6 +359,8 @@ export default {
}).then(() => {
const version = this.$store.getters.loginInfo?.upgrade_data?.version || "V01.01.16";
const defaultPath = `D:\\RUS_${version}\\HiUTalkStore`;
this.videoAuthorized = false;
this.cacheAuthorized = false;
this.basicForm = {
notification: true,
sendKey: "enter",
@ -335,4 +420,15 @@ export default {
border-color: #ccc;
color: #666;
}
.auth-tag {
display: inline-block;
margin-top: 4px;
font-size: 12px;
&.authorized {
color: #67c23a;
}
&.unauthorized {
color: #e6a23c;
}
}
</style>

@ -458,7 +458,7 @@ export default {
window.hirtcwebsdk.init({
serviceID: '56da5fd8921f4f7093a42e2a',
serviceKey: '2c17c6393771ee3048ae34d6b965sdew',
Services: { BasicRoomServiceToken: "https://192.168.69.174:3001/v1/auth/token" },
Services: { BasicRoomServiceToken: "https://wjw-ultrasoundappl/v1/auth/token" },
cameraLayers: [
{
width: 320,
@ -902,18 +902,12 @@ export default {
mimeType: 'video/webm;codecs=vp8,opus',
});
this.recordedChunks = [];
const fileName = `${this.meetingTitle}录制_${new Date().toLocaleString().replace(/[/:\\s]/g, '_')}.webm`;
this.mediaRecorder.ondataavailable = e => e.data.size && this.recordedChunks.push(e.data);
this.mediaRecorder.onstop = () => {
this.mediaRecorder.onstop = async () => {
const blob = new Blob(this.recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${this.meetingTitle}录制_${new Date().toLocaleString()}.webm`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
this.$message.success('录制完成');
//
await this._saveBlobToVideoPath(blob, fileName, '录制');
};
this.mediaRecorder.start();
this.isRecording = true;
@ -921,6 +915,7 @@ export default {
this.startRecordingTimer();
this.$message.warning('录制中');
} catch (e) {
console.error('录制失败', e);
this.$message.error('录制失败');
}
},
@ -957,8 +952,44 @@ export default {
this.saveMeetingSnapshot();
},
//
// ==================== IndexedDB ====================
_openIDB() {
return new Promise((resolve, reject) => {
const req = indexedDB.open('HiUTalkStoreDB', 1);
req.onupgradeneeded = () => { req.result.createObjectStore('handles'); };
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
},
async _getStoredDirHandle() {
const db = await this._openIDB();
return new Promise((resolve, reject) => {
const tx = db.transaction('handles', 'readonly');
const req = tx.objectStore('handles').get('screenshot_dir');
req.onsuccess = () => { db.close(); resolve(req.result); };
req.onerror = () => { db.close(); reject(req.error); };
});
},
//
async _getAuthorizedHandle() {
const dirHandle = await this._getStoredDirHandle();
if (!dirHandle) return null;
let perm = await dirHandle.queryPermission({ mode: 'readwrite' });
// 'prompt'saveMeetingSnapshot
if (perm !== 'granted') {
try {
perm = await dirHandle.requestPermission({ mode: 'readwrite' });
} catch (e) {
console.error('重新请求目录权限失败', e);
}
}
return perm === 'granted' ? dirHandle : null;
},
//
async saveMeetingSnapshot() {
// 1. canvas blob
let blob, fileName;
try {
const html2canvas = (await import('html2canvas')).default;
const container = document.querySelector('.meeting-container');
@ -972,14 +1003,61 @@ export default {
logging: false,
allowTaint: false,
});
const link = document.createElement('a');
link.download = `会诊截图_${new Date().toLocaleString()}.png`;
link.href = canvas.toDataURL('image/png');
link.click();
this.$message.success('截图已保存');
} catch (err) {
console.error('截图失败', err);
this.$message.error('截图失败');
fileName = `会诊截图_${new Date().toLocaleString().replace(/[/:\\s]/g, '_')}.png`;
blob = await new Promise((resolve, reject) => {
canvas.toBlob(b => { if (b) resolve(b); else reject(new Error('toBlob 失败')); }, 'image/png');
});
} catch (e) {
console.error('截图生成失败', e);
this.$message.error('截图生成失败');
return;
}
// 2.
await this._saveBlobToVideoPath(blob, fileName, '截图');
},
// blob
async _saveBlobToVideoPath(blob, fileName, label) {
const videoPath = this._getSystemVideoPath();
// 1.
const dirHandle = await this._getAuthorizedHandle();
if (dirHandle) {
try {
const fileHandle = await dirHandle.getFileHandle(fileName, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(blob);
await writable.close();
this.$message.success(`${label}已保存到 ${videoPath || dirHandle.name}`);
return;
} catch (err) {
console.error(`${label}写入失败,降级为下载`, err);
}
}
// 2.
try {
const { saveAs } = await import('file-saver');
saveAs(blob, fileName);
this.$message.success(videoPath
? `${label}已下载(请在系统设置中授权"${videoPath}"以自动保存)`
: `${label}已保存`);
} catch (e) {
console.error(`${label}保存失败`, e);
this.$message.error(`${label}保存失败`);
}
},
//
_getSystemVideoPath() {
try {
const raw = localStorage.getItem('systemSettings');
if (!raw) return '';
const settings = JSON.parse(raw);
return settings.basicForm?.videoPath || '';
} catch (e) {
return '';
}
},

Loading…
Cancel
Save