关于/系统设置/页面搭建

main
ysn 1 week ago
parent b90d3471ee
commit 04dd2951b2
  1. 123
      src/layout/components/AboutDialog.vue
  2. 320
      src/layout/components/ESignatureDialog.vue
  3. 40
      src/layout/components/Navbar.vue
  4. 227
      src/layout/components/SystemSettingDialog.vue

@ -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>

@ -57,18 +57,31 @@
<router-link to="/user/profile"> <router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item> <el-dropdown-item>个人中心</el-dropdown-item>
</router-link> </router-link>
<el-dropdown-item @click.native="setLayout" v-if="setting"> <!-- <el-dropdown-item @click.native="setLayout" v-if="setting">
<span>布局设置</span> <span>布局设置</span>
</el-dropdown-item> </el-dropdown-item> -->
<el-dropdown-item @click.native="lockScreen"> <el-dropdown-item @click.native="lockScreen">
<span>锁定屏幕</span> <span>锁定屏幕</span>
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click.native="showSystemSetting = true">
系统设置
</el-dropdown-item>
<el-dropdown-item @click.native="showAbout = true">
关于
</el-dropdown-item>
<el-dropdown-item @click.native="switchAccount">
切换账号
</el-dropdown-item>
<el-dropdown-item divided @click.native="logout"> <el-dropdown-item divided @click.native="logout">
<span>退出登录</span> <span>退出登录</span>
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</div> </div>
<!-- 系统设置弹窗 -->
<SystemSettingDialog :visible.sync="showSystemSetting" />
<!-- 关于弹窗 -->
<AboutDialog :visible.sync="showAbout" />
</div> </div>
</template> </template>
@ -85,6 +98,8 @@ import Search from "@/components/HeaderSearch";
import RuoYiGit from "@/components/RuoYi/Git"; import RuoYiGit from "@/components/RuoYi/Git";
import RuoYiDoc from "@/components/RuoYi/Doc"; import RuoYiDoc from "@/components/RuoYi/Doc";
import HeaderNotice from "./HeaderNotice"; import HeaderNotice from "./HeaderNotice";
import SystemSettingDialog from "./SystemSettingDialog";
import AboutDialog from "./AboutDialog";
export default { export default {
components: { components: {
@ -99,6 +114,14 @@ export default {
RuoYiGit, RuoYiGit,
RuoYiDoc, RuoYiDoc,
HeaderNotice, HeaderNotice,
SystemSettingDialog,
AboutDialog,
},
data() {
return {
showSystemSetting: false,
showAbout: false,
};
}, },
computed: { computed: {
...mapGetters(["sidebar", "avatar", "device", "nickName"]), ...mapGetters(["sidebar", "avatar", "device", "nickName"]),
@ -131,6 +154,19 @@ export default {
this.$router.push("/lock"); this.$router.push("/lock");
}); });
}, },
switchAccount() {
this.$confirm("确定要切换账号吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.$store.dispatch("LogOut").then(() => {
location.href = "/index";
});
})
.catch(() => {});
},
logout() { logout() {
this.$confirm("确定注销并退出系统吗?", "提示", { this.$confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",

@ -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…
Cancel
Save