parent
b90d3471ee
commit
04dd2951b2
4 changed files with 708 additions and 2 deletions
@ -0,0 +1,123 @@ |
||||
<template> |
||||
<el-dialog |
||||
title="关于" |
||||
:visible="visible" |
||||
width="450px" |
||||
:show-close="true" |
||||
@close="handleClose" |
||||
> |
||||
<div class="about-content"> |
||||
<div class="logo-section"> |
||||
<img src="@/assets/images/login-background.jpg" class="logo-circle" /> |
||||
<div class="version">信联 RUS_V01.01.16</div> |
||||
</div> |
||||
<div class="copyright"> |
||||
Copyright ©2025 青岛海信智能医疗技术有限公司。<br /> |
||||
保留所有权利 |
||||
</div> |
||||
<div class="button-group"> |
||||
<el-button type="primary" class="green-btn" @click="checkUpdate"> |
||||
版本更新 |
||||
</el-button> |
||||
<el-button type="primary" class="green-btn" @click="confirm"> |
||||
确定 |
||||
</el-button> |
||||
</div> |
||||
<div class="license"> |
||||
此软件基于Qt GNU Lesser General Public License(LGPL)开发。 |
||||
</div> |
||||
<div class="links"> |
||||
<el-link type="primary" href="#">服务协议</el-link> |
||||
<span class="separator">和</span> |
||||
<el-link type="primary" href="#">隐私政策</el-link> |
||||
</div> |
||||
</div> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "AboutDialog", |
||||
props: { |
||||
visible: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
}, |
||||
watch: { |
||||
visible(newVal) { |
||||
if (newVal) { |
||||
// 弹窗打开时可以做一些初始化操作 |
||||
} |
||||
}, |
||||
}, |
||||
methods: { |
||||
checkUpdate() { |
||||
this.$message.info("正在检查更新..."); |
||||
}, |
||||
confirm() { |
||||
this.$emit("update:visible", false); |
||||
}, |
||||
handleClose() { |
||||
this.$emit("update:visible", false); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.about-content { |
||||
text-align: center; |
||||
padding: 30px 0; |
||||
} |
||||
|
||||
.logo-section { |
||||
margin-bottom: 20px; |
||||
|
||||
.logo-circle { |
||||
width: 80px; |
||||
height: 80px; |
||||
// border-radius: 50%; |
||||
background: linear-gradient(135deg, #00c4b6, #00897b); |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
margin: 0 auto 16px; |
||||
color: #fff; |
||||
} |
||||
|
||||
.version { |
||||
font-size: 16px; |
||||
font-weight: bold; |
||||
color: #333; |
||||
} |
||||
} |
||||
|
||||
.copyright { |
||||
color: #666; |
||||
font-size: 14px; |
||||
line-height: 1.8; |
||||
margin-bottom: 24px; |
||||
} |
||||
|
||||
.button-group { |
||||
display: flex; |
||||
justify-content: center; |
||||
gap: 12px; |
||||
margin-bottom: 20px; |
||||
} |
||||
|
||||
.license { |
||||
color: #999; |
||||
font-size: 12px; |
||||
margin-bottom: 12px; |
||||
} |
||||
|
||||
.links { |
||||
color: #999; |
||||
|
||||
.separator { |
||||
margin: 0 4px; |
||||
} |
||||
} |
||||
</style> |
||||
@ -0,0 +1,320 @@ |
||||
<template> |
||||
<el-dialog |
||||
title="电子签名" |
||||
:visible="visible" |
||||
width="49%" |
||||
:show-close="true" |
||||
@close="handleClose" |
||||
@opened="initCanvas" |
||||
> |
||||
<el-form label-width="80px"> |
||||
<el-form-item label="签名区域"> |
||||
<div style="text-align: right"> |
||||
<el-button type="primary" @click="undo"> 撤销 </el-button> |
||||
<el-button type="primary" @click="clear"> 清除 </el-button> |
||||
</div> |
||||
</el-form-item> |
||||
</el-form> |
||||
<div class="canvas-container"> |
||||
<canvas |
||||
ref="signatureCanvas" |
||||
class="signature-canvas" |
||||
@mousedown="startDraw" |
||||
@mousemove="drawing" |
||||
@mouseup="stopDraw" |
||||
@mouseleave="stopDraw" |
||||
@touchstart.prevent="handleTouchStart" |
||||
@touchmove.prevent="handleTouchMove" |
||||
@touchend.prevent="stopDraw" |
||||
></canvas> |
||||
</div> |
||||
<div slot="footer"> |
||||
<el-button type="primary" @click="uploadLocalFile"> |
||||
上传本地文件 |
||||
</el-button> |
||||
<el-button type="primary" @click="submit"> 上传 </el-button> |
||||
<el-button @click="handleClose">取消</el-button> |
||||
</div> |
||||
<input |
||||
ref="fileInput" |
||||
type="file" |
||||
accept="image/*" |
||||
class="file-input" |
||||
@change="handleFileChange" |
||||
/> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "ESignatureDialog", |
||||
props: { |
||||
visible: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
ctx: null, |
||||
isDrawing: false, |
||||
lastX: 0, |
||||
lastY: 0, |
||||
history: [], |
||||
signatureData: null, |
||||
canvasInited: false, |
||||
}; |
||||
}, |
||||
watch: { |
||||
visible(newVal) { |
||||
if (newVal) { |
||||
this.$nextTick(() => { |
||||
this.initCanvas(); |
||||
}); |
||||
} else { |
||||
this.clearCanvas(); |
||||
} |
||||
}, |
||||
}, |
||||
methods: { |
||||
initCanvas() { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
if (!canvas) return; |
||||
|
||||
// 获取 canvas 容器的实际尺寸 |
||||
const container = canvas.parentElement; |
||||
const rect = container.getBoundingClientRect(); |
||||
|
||||
// 设置 canvas 实际像素尺寸 |
||||
canvas.width = rect.width; |
||||
canvas.height = rect.height; |
||||
|
||||
// 获取 2D 上下文 |
||||
this.ctx = canvas.getContext("2d"); |
||||
|
||||
// 设置画笔样式 |
||||
this.ctx.strokeStyle = "#000000"; |
||||
this.ctx.lineWidth = 2; |
||||
this.ctx.lineCap = "round"; |
||||
this.ctx.lineJoin = "round"; |
||||
|
||||
// 清除画布 |
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
|
||||
// 保存初始状态 |
||||
this.history = []; |
||||
this.saveState(); |
||||
|
||||
this.canvasInited = true; |
||||
}, |
||||
|
||||
saveState() { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
if (canvas && this.ctx) { |
||||
this.history.push(canvas.toDataURL()); |
||||
// 限制历史记录数量 |
||||
if (this.history.length > 20) { |
||||
this.history.shift(); |
||||
} |
||||
} |
||||
}, |
||||
|
||||
startDraw(e) { |
||||
if (!this.ctx) return; |
||||
|
||||
this.isDrawing = true; |
||||
const pos = this.getPosition(e); |
||||
this.lastX = pos.x; |
||||
this.lastY = pos.y; |
||||
|
||||
// 开始新路径 |
||||
this.ctx.beginPath(); |
||||
this.ctx.moveTo(this.lastX, this.lastY); |
||||
}, |
||||
|
||||
drawing(e) { |
||||
if (!this.isDrawing || !this.ctx) return; |
||||
|
||||
e.preventDefault(); |
||||
|
||||
const pos = this.getPosition(e); |
||||
|
||||
this.ctx.lineTo(pos.x, pos.y); |
||||
this.ctx.stroke(); |
||||
|
||||
this.lastX = pos.x; |
||||
this.lastY = pos.y; |
||||
}, |
||||
|
||||
stopDraw() { |
||||
if (this.isDrawing) { |
||||
this.isDrawing = false; |
||||
this.saveState(); |
||||
} |
||||
}, |
||||
|
||||
getPosition(e) { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
const rect = canvas.getBoundingClientRect(); |
||||
|
||||
let clientX, clientY; |
||||
|
||||
if (e.touches && e.touches.length > 0) { |
||||
clientX = e.touches[0].clientX; |
||||
clientY = e.touches[0].clientY; |
||||
} else { |
||||
clientX = e.clientX; |
||||
clientY = e.clientY; |
||||
} |
||||
|
||||
return { |
||||
x: clientX - rect.left, |
||||
y: clientY - rect.top, |
||||
}; |
||||
}, |
||||
|
||||
handleTouchStart(e) { |
||||
if (e.touches.length === 1) { |
||||
this.startDraw(e); |
||||
} |
||||
}, |
||||
|
||||
handleTouchMove(e) { |
||||
if (e.touches.length === 1) { |
||||
this.drawing(e); |
||||
} |
||||
}, |
||||
|
||||
undo() { |
||||
if (this.history.length > 1 && this.ctx) { |
||||
this.history.pop(); |
||||
const lastState = this.history[this.history.length - 1]; |
||||
const img = new Image(); |
||||
img.onload = () => { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
this.ctx.drawImage(img, 0, 0); |
||||
}; |
||||
img.src = lastState; |
||||
} |
||||
}, |
||||
|
||||
clear() { |
||||
if (this.ctx) { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
this.saveState(); |
||||
} |
||||
}, |
||||
|
||||
clearCanvas() { |
||||
if (this.ctx) { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
} |
||||
this.history = []; |
||||
this.signatureData = null; |
||||
this.canvasInited = false; |
||||
}, |
||||
|
||||
uploadLocalFile() { |
||||
this.$refs.fileInput.click(); |
||||
}, |
||||
|
||||
handleFileChange(e) { |
||||
const file = e.target.files[0]; |
||||
if (file && this.ctx) { |
||||
const reader = new FileReader(); |
||||
reader.onload = (event) => { |
||||
const img = new Image(); |
||||
img.onload = () => { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
|
||||
// 清除画布 |
||||
this.ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
|
||||
// 计算缩放比例,保持图片比例 |
||||
const scale = Math.min( |
||||
canvas.width / img.width, |
||||
canvas.height / img.height, |
||||
1 |
||||
); |
||||
const x = (canvas.width - img.width * scale) / 2; |
||||
const y = (canvas.height - img.height * scale) / 2; |
||||
|
||||
this.ctx.drawImage( |
||||
img, |
||||
x, |
||||
y, |
||||
img.width * scale, |
||||
img.height * scale |
||||
); |
||||
this.saveState(); |
||||
}; |
||||
img.src = event.target.result; |
||||
}; |
||||
reader.readAsDataURL(file); |
||||
} |
||||
// 清空 input 值,允许重复选择同一文件 |
||||
e.target.value = ""; |
||||
}, |
||||
|
||||
submit() { |
||||
const canvas = this.$refs.signatureCanvas; |
||||
if (!canvas || !this.ctx) return; |
||||
|
||||
// 检查是否有签名内容 |
||||
const imageData = this.ctx.getImageData( |
||||
0, |
||||
0, |
||||
canvas.width, |
||||
canvas.height |
||||
); |
||||
let hasContent = false; |
||||
for (let i = 3; i < imageData.data.length; i += 4) { |
||||
if (imageData.data[i] !== 0) { |
||||
hasContent = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!hasContent) { |
||||
this.$message.warning("请先进行签名"); |
||||
return; |
||||
} |
||||
|
||||
this.signatureData = canvas.toDataURL("image/png"); |
||||
this.$message.success("签名上传成功"); |
||||
this.$emit("submit", this.signatureData); |
||||
this.handleClose(); |
||||
}, |
||||
|
||||
handleClose() { |
||||
this.$emit("update:visible", false); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.canvas-container { |
||||
width: 100%; |
||||
height: 300px; |
||||
border: 2px dashed #00c4b6; |
||||
border-radius: 4px; |
||||
overflow: hidden; |
||||
background-color: #fff; |
||||
|
||||
.signature-canvas { |
||||
width: 100%; |
||||
height: 100%; |
||||
cursor: crosshair; |
||||
display: block; |
||||
touch-action: none; |
||||
} |
||||
} |
||||
|
||||
.file-input { |
||||
display: none; |
||||
} |
||||
</style> |
||||
@ -0,0 +1,227 @@ |
||||
<template> |
||||
<el-dialog |
||||
title="系统设置" |
||||
:visible="visible" |
||||
width="25%" |
||||
:show-close="true" |
||||
@close="handleClose" |
||||
> |
||||
<el-tabs v-model="activeTab"> |
||||
<!-- 基本设置 --> |
||||
<el-tab-pane label="基本设置" name="basic"> |
||||
<el-form :model="basicForm" label-width="80px"> |
||||
<el-form-item label="通知"> |
||||
<el-checkbox v-model="basicForm.notification"> |
||||
有新消息播放提示音 |
||||
</el-checkbox> |
||||
</el-form-item> |
||||
<!-- 只能在软件中使用 --> |
||||
<!-- <el-form-item label="登录"> |
||||
<el-checkbox v-model="basicForm.autoStart"> |
||||
开机时启动 |
||||
</el-checkbox> |
||||
</el-form-item> |
||||
<el-form-item label=""> |
||||
<el-checkbox v-model="basicForm.exitOnClose"> |
||||
关闭主面板时,退出程序 |
||||
</el-checkbox> |
||||
</el-form-item> --> |
||||
<el-form-item label="发送消息"> |
||||
<el-radio-group v-model="basicForm.sendKey"> |
||||
<el-radio label="ctrl">Ctrl+Enter发送</el-radio> |
||||
<el-radio label="enter">Enter发送</el-radio> |
||||
</el-radio-group> |
||||
</el-form-item> |
||||
<!-- 只能在软件中使用:需要访问本地文件系统 --> |
||||
<!-- <el-form-item label="视讯存储"> |
||||
<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> |
||||
</div> |
||||
</div> |
||||
</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> |
||||
</div> |
||||
</div> |
||||
</el-form-item> --> |
||||
</el-form> |
||||
</el-tab-pane> |
||||
<!-- 音频设置 --> |
||||
<el-tab-pane label="音频设置" name="audio"> |
||||
<el-form :model="audioForm" label-width="80px"> |
||||
<el-form-item label="音频算法"> |
||||
<el-checkbox v-model="audioForm.autoGain"> |
||||
音频采集自动增益 |
||||
</el-checkbox> |
||||
<el-checkbox v-model="audioForm.echoCancel"> |
||||
音频回声消除 |
||||
</el-checkbox> |
||||
<el-checkbox v-model="audioForm.noiseReduction"> |
||||
音频降噪 |
||||
</el-checkbox> |
||||
</el-form-item> |
||||
</el-form> |
||||
</el-tab-pane> |
||||
|
||||
<!-- 其他设置 --> |
||||
<el-tab-pane label="其他设置" name="other"> |
||||
<el-form :model="otherForm" label-width="80px"> |
||||
<el-form-item label="电子签名"> |
||||
<div class="signature-control"> |
||||
<el-button type="primary" @click="showSignatureDialog = true"> |
||||
编辑 |
||||
</el-button> |
||||
<span |
||||
:class="['status-text', otherForm.signature ? 'uploaded' : '']" |
||||
> |
||||
{{ otherForm.signature ? "已上传" : "未上传" }} |
||||
</span> |
||||
</div> |
||||
</el-form-item> |
||||
<el-form-item label="会诊评价"> |
||||
<el-checkbox v-model="otherForm.skipEvaluation"> |
||||
结束时不弹出评价 |
||||
</el-checkbox> |
||||
</el-form-item> |
||||
<!-- 只能在软件中使用:需要访问本地文件系统 --> |
||||
<!-- <el-form-item label="缓存"> |
||||
<el-button type="primary" >清除客户端缓存</el-button> |
||||
</el-form-item> --> |
||||
</el-form> |
||||
</el-tab-pane> |
||||
</el-tabs> |
||||
|
||||
<span slot="footer" class="dialog-footer" v-if="activeTab === 'basic'"> |
||||
<el-button type="primary" @click="restoreDefault"> 恢复默认 </el-button> |
||||
</span> |
||||
<!-- 电子签名弹窗 --> |
||||
<ESignatureDialog |
||||
:visible.sync="showSignatureDialog" |
||||
@submit="handleSignatureSubmit" |
||||
/> |
||||
</el-dialog> |
||||
</template> |
||||
|
||||
<script> |
||||
import ESignatureDialog from "./ESignatureDialog"; |
||||
export default { |
||||
name: "SystemSettingDialog", |
||||
components: { |
||||
ESignatureDialog, |
||||
}, |
||||
props: { |
||||
visible: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
}, |
||||
watch: { |
||||
visible(newVal) { |
||||
if (newVal) { |
||||
this.activeTab = "basic"; |
||||
} |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
activeTab: "basic", |
||||
showSignatureDialog: false, |
||||
basicForm: { |
||||
notification: true, |
||||
// autoStart: false, // 只能在软件中使用 |
||||
// exitOnClose: true, // 只能在软件中使用 |
||||
sendKey: "enter", |
||||
// videoPath: "2026-05/HiuUltraTalk_V01.01.16/HiuTalkStore", // 只能在软件中使用 |
||||
// cachePath: "2026-05/HiuUltraTalk_V01.01.16/HiuTalkStore", // 只能在软件中使用 |
||||
}, |
||||
audioForm: { |
||||
autoGain: true, |
||||
echoCancel: true, |
||||
noiseReduction: true, |
||||
}, |
||||
otherForm: { |
||||
signature: null, |
||||
skipEvaluation: false, |
||||
}, |
||||
}; |
||||
}, |
||||
methods: { |
||||
handleClose() { |
||||
this.$emit("update:visible", false); |
||||
}, |
||||
handleSignatureSubmit(signatureData) { |
||||
this.otherForm.signature = signatureData; |
||||
}, |
||||
restoreDefault() { |
||||
this.$confirm("确定要恢复默认设置吗?", "提示", { |
||||
confirmButtonText: "确定", |
||||
cancelButtonText: "取消", |
||||
type: "warning", |
||||
}).then(() => { |
||||
this.basicForm = { |
||||
notification: true, |
||||
// autoStart: false, |
||||
// exitOnClose: true, |
||||
sendKey: "enter", |
||||
// videoPath: "2026-05/HiuUltraTalk_V01.01.16/HiuTalkStore", |
||||
// cachePath: "2026-05/HiuUltraTalk_V01.01.16/HiuTalkStore", |
||||
}; |
||||
this.audioForm = { |
||||
autoGain: true, |
||||
echoCancel: true, |
||||
noiseReduction: true, |
||||
}; |
||||
this.otherForm = { |
||||
skipEvaluation: false, |
||||
}; |
||||
this.$message.success("已恢复默认设置"); |
||||
}); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.path-control { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 12px; |
||||
flex: 1; |
||||
|
||||
.path-input { |
||||
flex: 1; |
||||
width: 200px; |
||||
} |
||||
|
||||
.path-buttons { |
||||
display: flex; |
||||
gap: 8px; |
||||
} |
||||
} |
||||
|
||||
.signature-control { |
||||
display: flex; |
||||
align-items: center; |
||||
gap: 12px; |
||||
|
||||
.status-text { |
||||
color: #999; |
||||
font-size: 14px; |
||||
|
||||
&.uploaded { |
||||
color: #00c4b6; |
||||
} |
||||
} |
||||
} |
||||
.modify-btn { |
||||
border-color: #ccc; |
||||
color: #666; |
||||
} |
||||
</style> |
||||
Loading…
Reference in new issue