录制和网络路径

This commit is contained in:
24kycj
2025-09-03 22:09:07 +08:00
parent b39e3356d3
commit 39d754d162
18 changed files with 3159 additions and 101 deletions
@@ -0,0 +1,198 @@
<template>
<el-dialog
:title="$t('networkAuth.title')"
:visible.sync="visible"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
:append-to-body="true"
:z-index="9999"
>
<div class="network-auth-content">
<p class="auth-description">
{{ $t('networkAuth.description') }}
</p>
<div class="network-paths">
<div
v-for="(path, index) in networkPaths"
:key="index"
class="network-path-item"
>
<div class="path-info">
<span class="path-label">{{ $t('networkAuth.pathLabel') }}</span>
<span class="path-value">{{ path.path }}</span>
</div>
<div class="auth-fields">
<el-input
v-model="path.userName"
:placeholder="$t('networkAuth.userName')"
size="small"
style="width: 150px; margin-right: 10px;"
/>
<el-input
v-model="path.password"
type="password"
:placeholder="$t('networkAuth.password')"
size="small"
style="width: 150px;"
show-password
/>
</div>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel">{{ $t('networkAuth.cancel') }}</el-button>
<el-button type="primary" @click="handleConfirm" :loading="loading">
{{ $t('networkAuth.confirm') }}
</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'NetworkAuthDialog',
props: {
visible: {
type: Boolean,
default: false
},
networkPaths: {
type: Array,
default: () => []
}
},
data() {
return {
loading: false
}
},
methods: {
handleCancel() {
this.$emit('cancel')
},
async handleConfirm() {
// 验证所有路径都填写了用户名和密码
for (let path of this.networkPaths) {
if (!path.userName || !path.password) {
this.$message.error(this.$t('networkAuth.authRequired'))
return
}
}
this.loading = true
try {
// 保存到本地存储
this.saveNetworkCredentials()
// 构建 net_info 数据
const netInfo = this.networkPaths.map(path => ({
host_name: this.extractHostName(path.path),
user_name: path.userName,
password: path.password
}))
this.$emit('confirm', netInfo)
} catch (error) {
console.error('保存网络认证信息失败:', error)
this.$message.error('保存认证信息失败')
} finally {
this.loading = false
}
},
extractHostName(path) {
// 从网络路径中提取主机名
// 支持格式:\\hostname\path, //hostname/path, \\192.168.1.1\path
if (path.startsWith('\\\\')) {
// Windows 网络路径 \\hostname\path
const parts = path.substring(2).split('\\')
return parts[0]
} else if (path.startsWith('//')) {
// Unix 网络路径 //hostname/path
const parts = path.substring(2).split('/')
return parts[0]
} else if (path.includes(':')) {
// 可能包含 IP 地址
const match = path.match(/^([^\\\/:]+)/)
return match ? match[1] : 'unknown'
}
return 'unknown'
},
saveNetworkCredentials() {
// 保存到 localStorage
const credentials = {}
this.networkPaths.forEach(path => {
const hostName = this.extractHostName(path.path)
credentials[hostName] = {
userName: path.userName,
password: path.password,
lastUsed: new Date().toISOString()
}
})
localStorage.setItem('networkCredentials', JSON.stringify(credentials))
// 同时保存到 Vuex store(若可用)
if (this.$store && this.$store.commit) {
this.$store.commit('SET_NETWORK_CREDENTIALS', credentials)
}
}
}
}
</script>
<style lang="less" scoped>
.network-auth-content {
.auth-description {
margin-bottom: 20px;
color: #606266;
font-size: 14px;
}
.network-paths {
.network-path-item {
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 15px;
margin-bottom: 15px;
background-color: #fafafa;
.path-info {
margin-bottom: 10px;
.path-label {
font-weight: bold;
color: #303133;
margin-right: 10px;
}
.path-value {
color: #409eff;
font-family: monospace;
background-color: #f0f9ff;
padding: 2px 6px;
border-radius: 3px;
}
}
.auth-fields {
display: flex;
align-items: center;
}
}
}
}
.dialog-footer {
text-align: right;
}
</style>
+1 -1
View File
@@ -251,7 +251,7 @@ export default {
StopOnFailure: "false"
SystemSn: "830001"
TaskDir: "C:\\PrintTasks"
Version: "V3.0"
Version: "V3.1"
iniparser.parse("F:\\controll\\config.ini", function (err, data) {
console.log(err);
+1 -1
View File
@@ -1 +1 @@
{"files":["C:\\Users\\jerry\\Desktop\\cardsoon\\卡树产品保修条款.docx"]}
{"files":["\\\\NAS\\study\\research_reports\\餐饮服务\\“保健水晶鸡、鸭皮蛋”商业计划书.docx"]}
+91
View File
@@ -61,6 +61,32 @@ import archiverdialog from "./archiverdialog";
import { calcSize, getFileName, isFolder } from "./calc";
import { copy } from "./copy";
import { zip } from "./archiver";
// 网络路径检测工具函数
const isNetworkPath = (path) => {
// 检测是否为网络路径
// Windows 网络路径: \\hostname\path 或 \\IP\path
// Unix 网络路径: //hostname/path 或 //IP/path
return path.startsWith('\\\\') || path.startsWith('//') ||
(path.includes(':') && !path.includes('\\') && !path.includes('/'));
};
const extractHostName = (path) => {
if (path.startsWith('\\\\')) {
// Windows 网络路径 \\hostname\path
const parts = path.substring(2).split('\\')
return parts[0]
} else if (path.startsWith('//')) {
// Unix 网络路径 //hostname/path
const parts = path.substring(2).split('/')
return parts[0]
} else if (path.includes(':')) {
// 可能包含 IP 地址
const match = path.match(/^([^\\\/:]+)/)
return match ? match[1] : 'unknown'
}
return 'unknown'
};
export default {
name: "Files",
props: {
@@ -96,6 +122,7 @@ export default {
isCopy: false,
isSucess: true,
copyPath: "",
networkPaths: [], // 存储检测到的网络路径
};
},
mounted() {
@@ -132,6 +159,12 @@ export default {
// 是文件夹
this.sizeChange(f.size);
}
// 检测是否为网络路径
if (isNetworkPath(f.path)) {
this.addNetworkPath(f.path);
}
return true;
} else {
//有了,不用加入
@@ -145,6 +178,7 @@ export default {
properties: ["multiSelections"],
})
.then(async (res) => {
console.log(res)
for (const item of res.filePaths) {
await fs.stat(item, function (err, res) {
if (err) {
@@ -167,6 +201,7 @@ export default {
properties: ["openDirectory", "multiSelections"],
})
.then((res) => {
console.log(res)
for (const item of res.filePaths) {
const result = _this.insertList({
name: getFileName(item),
@@ -309,6 +344,62 @@ export default {
getLists() {
return this.filesList;
},
// 添加网络路径到列表(按主机去重,仅认证根目录,如 \\\\NAS)
addNetworkPath(path) {
const hostName = extractHostName(path);
const existingIndex = this.networkPaths.findIndex(p => p.hostName === hostName);
if (existingIndex === -1) {
// 尝试从本地存储获取已保存的认证信息
let userName = '';
let password = '';
try {
const stored = localStorage.getItem('networkCredentials');
if (stored) {
const credentials = JSON.parse(stored);
if (credentials[hostName]) {
userName = credentials[hostName].userName;
password = credentials[hostName].password;
}
}
} catch (error) {
console.log('无法获取已保存的认证信息');
}
// 仅保存根路径用于展示与认证
const rootPath = `\\\\${hostName}`;
this.networkPaths.push({
path: rootPath,
hostName: hostName,
userName: userName,
password: password
});
}
},
// 获取网络路径列表(按主机去重,返回根路径)
getNetworkPaths() {
const uniqueByHost = {};
for (const item of this.networkPaths) {
if (!uniqueByHost[item.hostName]) {
uniqueByHost[item.hostName] = {
path: `\\\\${item.hostName}`,
hostName: item.hostName,
userName: item.userName || '',
password: item.password || ''
};
}
}
return Object.values(uniqueByHost);
},
// 检查是否有网络路径
hasNetworkPaths() {
return this.networkPaths.length > 0;
},
haveFolderIsCalc() {},
},
computed: {},
+93 -89
View File
@@ -210,6 +210,7 @@ export default {
},
onSubmituser() {
if (this.ruleForm.name == '1') {
this.setStep()
this.$router.push({
path: '/main'
})
@@ -234,95 +235,7 @@ export default {
console.log(res.data)
localStorage.setItem('loginrole', res.data.resultinfo.userRole)
localStorage.setItem('loginname', res.data.resultinfo.userName)
const guideStep = [
{
show: false,
placement: 'right',
step: this.$t('guide.step1'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step2'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step3'),
},
{
show: false,
placement: 'left',
step: this.$t('guide.step4'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step5'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step6'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step7'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step8'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step9'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step10'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step11'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step12'),
},
{
show: false,
placement: 'left',
step: this.$t('guide.step13'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step14'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step15'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step16'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step17'),
},
]
localStorage.setItem('guideStep', JSON.stringify(guideStep))
localStorage.setItem('currentStep', 0)
this.setStep()
this.$router.push({
path: '/main'
})
@@ -346,6 +259,97 @@ export default {
}
})
},
setStep() {
const guideStep = [
{
show: false,
placement: 'right',
step: this.$t('guide.step1'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step2'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step3'),
},
{
show: false,
placement: 'left',
step: this.$t('guide.step4'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step5'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step6'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step7'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step8'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step9'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step10'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step11'),
},
{
show: false,
placement: 'right',
step: this.$t('guide.step12'),
},
{
show: false,
placement: 'left',
step: this.$t('guide.step13'),
},
{
show: false,
placement: 'bottom',
step: this.$t('guide.step14'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step15'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step16'),
},
{
show: false,
placement: 'top',
step: this.$t('guide.step17'),
},
]
localStorage.setItem('guideStep', JSON.stringify(guideStep))
localStorage.setItem('currentStep', 0)
},
// 监听回车键执行事件
keyDown(e) {
// 回车则执行登录方法 enter键的ASCII是13
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,252 @@
<template>
<div class="screen-recorder">
<el-tooltip
:content="isRecording ? $t('recorder.stopRecording') : $t('recorder.startRecording')"
placement="top">
<el-button
:type="isRecording ? 'danger' : 'primary'"
:icon="isRecording ? 'el-icon-video-pause' : 'el-icon-video-camera'"
circle
@click="toggleRecording"
:loading="loading"
class="record-btn"
>
</el-button>
</el-tooltip>
<!-- 录制状态指示器 -->
<div v-if="isRecording" class="recording-indicator">
<span class="recording-dot"></span>
<span class="recording-text">{{ recordingTime }}</span>
</div>
</div>
</template>
<script>
const { ipcRenderer } = require('electron');
export default {
name: 'ScreenRecorder',
data() {
return {
isRecording: false,
loading: false,
mediaRecorder: null,
recordedChunks: [],
startTime: null,
recordingTime: '00:00',
timer: null,
stream: null
};
},
methods: {
async toggleRecording() {
if (this.isRecording) {
await this.stopRecording();
} else {
await this.startRecording();
}
},
async startRecording() {
this.loading = true;
try {
// 调用主进程开始录制
const result = await ipcRenderer.invoke('start-recording');
if (!result.success) {
this.$message.error(result.message);
this.loading = false;
return;
}
// 获取屏幕流
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: result.sourceId,
minWidth: 1280,
maxWidth: 1920,
minHeight: 720,
maxHeight: 1080
}
}
});
this.stream = stream;
this.recordedChunks = [];
// 创建MediaRecorder
this.mediaRecorder = new MediaRecorder(stream, {
mimeType: 'video/webm'
});
this.mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
this.recordedChunks.push(event.data);
}
};
this.mediaRecorder.onstop = async () => {
await this.saveRecording();
};
// 开始录制
this.mediaRecorder.start();
this.isRecording = true;
this.startTime = Date.now();
this.startTimer();
this.$message.success(this.$t('recorder.recordingStarted') || '开始录制');
this.$emit('recording-started');
} catch (error) {
console.error('启动录制失败:', error);
this.$message.error(this.$t('recorder.startFailed') || '启动录制失败');
} finally {
this.loading = false;
}
},
async stopRecording() {
this.loading = true;
try {
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
this.mediaRecorder.stop();
}
// 停止所有轨道
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
this.stream = null;
}
this.isRecording = false;
this.stopTimer();
this.$emit('recording-stopped');
} catch (error) {
console.error('停止录制失败:', error);
this.$message.error(this.$t('recorder.stopFailed') || '停止录制失败');
} finally {
this.loading = false;
}
},
async saveRecording() {
if (this.recordedChunks.length === 0) {
return;
}
try {
// 将录制的数据块合并为Blob
const blob = new Blob(this.recordedChunks, {
type: 'video/webm'
});
// 转换为base64
const reader = new FileReader();
reader.onloadend = async () => {
const base64data = reader.result;
// 发送到主进程保存
const result = await ipcRenderer.invoke('stop-recording', base64data);
if (result.success) {
this.$message.success(this.$t('recorder.savedSuccess') || `录制已保存: ${result.fileName}`);
this.$emit('recording-saved', result);
} else {
this.$message.error(result.message);
}
};
reader.readAsDataURL(blob);
} catch (error) {
console.error('保存录制失败:', error);
this.$message.error(this.$t('recorder.saveFailed') || '保存录制失败');
}
},
startTimer() {
this.timer = setInterval(() => {
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
const seconds = (elapsed % 60).toString().padStart(2, '0');
this.recordingTime = `${minutes}:${seconds}`;
}, 1000);
},
stopTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
this.recordingTime = '00:00';
}
},
// 外部调用的方法,用于在提交作业时自动停止录制
async stopIfRecording() {
if (this.isRecording) {
await this.stopRecording();
}
}
},
beforeDestroy() {
// 组件销毁时确保停止录制
if (this.isRecording) {
this.stopRecording();
}
}
};
</script>
<style lang="less" scoped>
.screen-recorder {
display: inline-flex;
align-items: center;
gap: 10px;
.record-btn {
&:hover {
transform: scale(1.1);
}
}
.recording-indicator {
display: flex;
align-items: center;
gap: 5px;
padding: 5px 10px;
background: rgba(245, 108, 108, 0.1);
border-radius: 15px;
.recording-dot {
width: 8px;
height: 8px;
background: #f56c6c;
border-radius: 50%;
animation: blink 1s infinite;
}
.recording-text {
font-size: 14px;
color: #f56c6c;
font-weight: 500;
}
}
}
@keyframes blink {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
</style>
+120 -3
View File
@@ -251,6 +251,22 @@
{{ $t('work.senior') }}
</el-button>
</el-popover>
<!-- 屏幕录制按钮 -->
<screen-recorder
ref="screenRecorder"
@recording-started="onRecordingStarted"
@recording-stopped="onRecordingStopped"
@recording-saved="onRecordingSaved"
style="margin-left: 10px; display: inline-block;"
/>
<!-- 网络路径认证对话框 -->
<network-auth-dialog
:visible="networkAuthVisible"
:network-paths="networkAuthPaths"
@confirm="onNetworkAuthConfirm"
@cancel="onNetworkAuthCancel"
/>
</div>
</div>
<el-row
@@ -1145,6 +1161,8 @@ function getNowFormatDate() {
return currentdate
}
import Files from './files/file.vue'
import ScreenRecorder from './recorder/ScreenRecorder.vue'
import NetworkAuthDialog from './NetworkAuthDialog.vue'
let fs = require('fs')
let path = require('path')
const { app, dialog } = require('@electron/remote')
@@ -1182,10 +1200,17 @@ export default {
}
},
components: {
Files
Files,
ScreenRecorder,
NetworkAuthDialog
},
data() {
return {
// 网络路径认证相关
networkAuthVisible: false,
networkAuthPaths: [],
networkCredentials: {},
// 模板列表
templates: [],
currentTemplate: '',
@@ -1392,10 +1417,77 @@ export default {
this.guideStep[this.currentStep].show = true
}, 200)
}
// 加载网络认证信息
this.loadNetworkCredentials()
this.getTemplates()
},
updated() {},
methods: {
// 录制相关方法
onRecordingStarted() {
console.log('录制已开始')
},
onRecordingStopped() {
console.log('录制已停止')
},
onRecordingSaved(result) {
console.log('录制已保存:', result)
this.$message.success(`录制已保存: ${result.fileName}`)
},
async stopRecordingIfActive() {
// 如果正在录制,则停止录制
if (this.$refs.screenRecorder) {
await this.$refs.screenRecorder.stopIfRecording()
}
},
// 网络路径认证相关方法
loadNetworkCredentials() {
try {
const stored = localStorage.getItem('networkCredentials')
if (stored) {
this.networkCredentials = JSON.parse(stored)
}
} catch (error) {
console.error('加载网络认证信息失败:', error)
}
},
// 检查是否有网络路径需要认证
checkNetworkPaths() {
if (this.$refs.files && this.$refs.files.hasNetworkPaths()) {
const networkPaths = this.$refs.files.getNetworkPaths()
if (networkPaths.length > 0) {
this.networkAuthPaths = networkPaths
this.networkAuthVisible = true
return true
}
}
return false
},
// 网络认证确认回调
onNetworkAuthConfirm(netInfo) {
this.networkCredentials = netInfo
this.networkAuthVisible = false
// 继续提交流程
this.continueSubmit()
},
// 网络认证取消回调
onNetworkAuthCancel() {
this.networkAuthVisible = false
this.$message.info(this.$t('networkAuth.authCanceled'))
},
// 继续提交流程
continueSubmit() {
// 这里继续原来的提交逻辑
this.performSubmit()
},
addFolder() {
this.$refs.files.addFolder()
},
@@ -2025,7 +2117,21 @@ export default {
//}, 0);
//console.log(1);
},
submit() {
async submit() {
// 停止录制(如果正在录制)
await this.stopRecordingIfActive()
// 检查是否有网络路径需要认证
if (this.checkNetworkPaths()) {
return // 等待用户完成网络认证
}
// 继续提交流程
this.performSubmit()
},
// 实际的提交逻辑
async performSubmit() {
//提交最终的任务请求
let data = ''
let pathName
@@ -2201,6 +2307,12 @@ export default {
if (this.high_setting_form.formatFile != 0) {
data += '&formatFile=' + this.high_setting_form.formatFile //拷贝前类型
}
// 添加网络路径认证信息
if (this.networkCredentials && this.networkCredentials.length > 0) {
data += '&net_info=' + JSON.stringify(this.networkCredentials)
}
// if (this.file_form == 0 || this.file_form == 1 || this.file_form == 4 || this.file_form == 2 || this.file_form == 3 || this.file_form == 7) {
// } else {
@@ -2286,6 +2398,11 @@ export default {
// 使用FormData上传文件
const formData = new FormData()
formData.append('file', blob, path.basename(jsonFilePath))
// 添加网络路径认证信息
if (this.networkCredentials && this.networkCredentials.length > 0) {
formData.append('net_info', JSON.stringify(this.networkCredentials))
}
that.submitLoading = true
// 发送HTTP请求上传文件
that
@@ -2517,7 +2634,7 @@ export default {
computed: {
file_percent() {
let t = (this.size / (((this.size_form * 1000) / 1.024 / 1.024 / 1.024) * 1024 * 1024)) * 100
return t > 100 ? 100.1 : t
return t ? t > 100 ? 100.1 : t : 0
},
print_op() {
return [
+1 -1
View File
@@ -1637,7 +1637,7 @@ export default {
(this.size /
(((this.size_form * 1000) / 1.024 / 1.024 / 1.024) * 1024 * 1024)) *
100;
return t > 100 ? 100.1 : t;
return t ? t > 100 ? 100.1 : t : 0
},
print_op() {
return [
+23 -1
View File
@@ -1,4 +1,26 @@
export default {
recorder: {
startRecording: "开始录制",
stopRecording: "停止录制",
recordingStarted: "开始录制屏幕",
recordingStopped: "录制已停止",
savedSuccess: "录制已保存",
startFailed: "启动录制失败",
stopFailed: "停止录制失败",
saveFailed: "保存录制失败"
},
networkAuth: {
title: "网络路径认证",
description: "检测到以下网络路径,请输入对应的用户名和密码:",
pathLabel: "网络路径:",
userName: "用户名",
password: "密码",
confirm: "确认",
cancel: "取消",
authRequired: "请填写所有网络路径的用户名和密码",
authSuccess: "网络认证信息已保存",
authCanceled: "已取消网络路径认证"
},
finger: {
intTips: "指纹模块正在初始化",
errorInttips: "指纹模块初始化失败",
@@ -18,7 +40,7 @@ export default {
login: "登录",
fingerLogin: "使用指纹登录",
title1: "CARDSOON USB存储卡",
title2: "自动拷贝打印系统 V3.0",
title2: "自动拷贝打印系统 V3.1",
successLogin: "登录成功!",
errorFingerlogin: "登录失败,请重试指纹!",
errorLogin: "登录失败,请检查账号密码!",
+22
View File
@@ -1,4 +1,26 @@
export default {
recorder: {
startRecording: "Start Recording",
stopRecording: "Stop Recording",
recordingStarted: "Screen recording started",
recordingStopped: "Recording stopped",
savedSuccess: "Recording saved",
startFailed: "Failed to start recording",
stopFailed: "Failed to stop recording",
saveFailed: "Failed to save recording"
},
networkAuth: {
title: "Network Path Authentication",
description: "The following network paths were detected. Please enter the corresponding username and password:",
pathLabel: "Network Path:",
userName: "Username",
password: "Password",
confirm: "Confirm",
cancel: "Cancel",
authRequired: "Please fill in username and password for all network paths",
authSuccess: "Network authentication information saved",
authCanceled: "Network path authentication canceled"
},
finger: {
intTips: "The fingerprint module is initializing",
errorInttips: "Fingerprint module initialization fail",
+1 -1
View File
@@ -17,7 +17,7 @@ export default {
login:"登入",
fingerLogin:"使用指紋登入",
title1:"CARDSOON USB存儲卡",
title2:"自動拷貝列印系統V3.0",
title2:"自動拷貝列印系統V3.1",
successLogin:"登入成功! ",
errorFingerlogin:"登入失敗,請重試指紋! ",
errorLogin:"登入失敗,請檢查帳號密碼! ",
+70
View File
@@ -0,0 +1,70 @@
const state = {
networkCredentials: {}
}
const mutations = {
SET_NETWORK_CREDENTIALS(state, credentials) {
state.networkCredentials = { ...state.networkCredentials, ...credentials }
},
CLEAR_NETWORK_CREDENTIALS(state) {
state.networkCredentials = {}
},
UPDATE_NETWORK_CREDENTIAL(state, { hostName, credentials }) {
state.networkCredentials = {
...state.networkCredentials,
[hostName]: credentials
}
}
}
const actions = {
// 从 localStorage 加载网络认证信息
loadNetworkCredentials({ commit }) {
try {
const stored = localStorage.getItem('networkCredentials')
if (stored) {
const credentials = JSON.parse(stored)
commit('SET_NETWORK_CREDENTIALS', credentials)
}
} catch (error) {
console.error('加载网络认证信息失败:', error)
}
},
// 保存网络认证信息到 localStorage
saveNetworkCredentials({ state }) {
try {
localStorage.setItem('networkCredentials', JSON.stringify(state.networkCredentials))
} catch (error) {
console.error('保存网络认证信息失败:', error)
}
},
// 获取指定主机的认证信息
getCredentialsForHost({ state }, hostName) {
return state.networkCredentials[hostName] || null
},
// 更新指定主机的认证信息
updateCredentialsForHost({ commit }, { hostName, credentials }) {
commit('UPDATE_NETWORK_CREDENTIAL', { hostName, credentials })
}
}
const getters = {
// 获取所有网络认证信息
allNetworkCredentials: state => state.networkCredentials,
// 检查是否有网络认证信息
hasNetworkCredentials: state => Object.keys(state.networkCredentials).length > 0
}
export default {
namespaced: true,
state,
mutations,
actions,
getters
}