648 lines
31 KiB
Vue
648 lines
31 KiB
Vue
<template>
|
||
<section class="settings-page">
|
||
<div class="settings-shell panel">
|
||
<aside class="settings-nav" aria-label="系统设置分类">
|
||
<div class="settings-nav-head">
|
||
<span class="nav-kicker">Settings</span>
|
||
<h2>系统设置</h2>
|
||
<p>已完成 {{ completedSectionCount }} / {{ sections.length }} 项配置,敏感字段不会保存在浏览器草稿中。</p>
|
||
</div>
|
||
|
||
<nav class="settings-nav-list">
|
||
<button
|
||
v-for="section in sections"
|
||
:key="section.id"
|
||
class="settings-nav-item"
|
||
:class="{
|
||
active: activeSection === section.id
|
||
}"
|
||
type="button"
|
||
@click="activateSection(section.id)"
|
||
>
|
||
<span class="nav-item-copy">
|
||
<strong>{{ section.label }}</strong>
|
||
<small>{{ section.desc }}</small>
|
||
</span>
|
||
</button>
|
||
</nav>
|
||
</aside>
|
||
|
||
<div class="settings-body">
|
||
<header class="settings-toolbar">
|
||
<div class="settings-toolbar-copy">
|
||
<span class="settings-breadcrumb">首页 / 系统设置 / {{ activeSectionConfig.label }}</span>
|
||
<h3>{{ activeSectionConfig.title }}</h3>
|
||
<p>{{ activeSectionConfig.longDesc }}</p>
|
||
</div>
|
||
|
||
<div class="settings-toolbar-actions">
|
||
<button class="save-button" type="button" @click="saveActiveSection">
|
||
<i class="mdi mdi-content-save-outline"></i>
|
||
<span>{{ activeSectionConfig.actionLabel }}</span>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="settings-content">
|
||
<template v-if="activeSection === 'profile'">
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>系统基本信息</h4>
|
||
<p>统一维护企业名称、系统显示名称和版权信息,保存后会同步更新当前系统品牌名称。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid profile-grid">
|
||
<label class="field logo-field">
|
||
<span><em>*</em> 系统图标</span>
|
||
<div class="logo-tile" aria-hidden="true">
|
||
<i class="mdi mdi-domain"></i>
|
||
</div>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 企业名称</span>
|
||
<input v-model="pageState.companyForm.companyName" type="text" placeholder="请输入企业法定名称" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>企业编码</span>
|
||
<input v-model="pageState.companyForm.companyCode" type="text" placeholder="例如 XF-001" />
|
||
</label>
|
||
|
||
<label class="field field-wide">
|
||
<span><em>*</em> 系统显示名称</span>
|
||
<input v-model="pageState.companyForm.displayName" type="text" placeholder="请输入系统对内展示名称" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>备案号</span>
|
||
<input v-model="pageState.companyForm.recordNumber" type="text" placeholder="请输入备案号" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> 版权信息</span>
|
||
<input
|
||
v-model="pageState.companyForm.copyright"
|
||
type="text"
|
||
placeholder="例如 Copyright © 2024-2026 X-Financial. All Rights Reserved."
|
||
/>
|
||
</label>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<template v-else-if="activeSection === 'admin'">
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>管理员账号</h4>
|
||
<p>维护最高权限管理员的登录账户、密码和安全通知邮箱。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field">
|
||
<span><em>*</em> 管理员账号</span>
|
||
<input v-model="pageState.adminForm.adminAccount" type="text" placeholder="请输入管理员账号" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 管理员邮箱</span>
|
||
<input v-model="pageState.adminForm.adminEmail" type="email" placeholder="请输入管理员邮箱" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>新密码</span>
|
||
<input
|
||
v-model="pageState.adminForm.newPassword"
|
||
type="password"
|
||
autocomplete="new-password"
|
||
placeholder="至少 5 位"
|
||
/>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>确认密码</span>
|
||
<input
|
||
v-model="pageState.adminForm.confirmPassword"
|
||
type="password"
|
||
autocomplete="new-password"
|
||
placeholder="再次输入管理员密码"
|
||
/>
|
||
</label>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>登录安全策略</h4>
|
||
<p>控制会话超时、登录提醒和管理员高风险操作的基础安全策略。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid compact-grid">
|
||
<label class="field">
|
||
<span><em>*</em> 会话超时(分钟)</span>
|
||
<input v-model.number="pageState.adminForm.sessionTimeout" type="number" min="5" max="240" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>安全通知邮箱</span>
|
||
<input v-model="pageState.adminForm.noticeEmail" type="email" placeholder="用于接收安全提醒" />
|
||
</label>
|
||
</div>
|
||
|
||
<div class="switch-group">
|
||
<button class="switch-row" type="button" @click="toggleBoolean('adminForm', 'mfaEnabled')">
|
||
<span class="switch-copy">
|
||
<strong>开启双因素验证</strong>
|
||
<small>要求管理员使用附加验证步骤登录后台。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.adminForm.mfaEnabled }"><i></i></span>
|
||
</button>
|
||
|
||
<button class="switch-row" type="button" @click="toggleBoolean('adminForm', 'strongPassword')">
|
||
<span class="switch-copy">
|
||
<strong>启用强密码策略</strong>
|
||
<small>管理员密码修改时需要满足强度要求。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.adminForm.strongPassword }"><i></i></span>
|
||
</button>
|
||
|
||
<button class="switch-row" type="button" @click="toggleBoolean('adminForm', 'loginAlertEnabled')">
|
||
<span class="switch-copy">
|
||
<strong>异常登录提醒</strong>
|
||
<small>检测到高风险登录时,向安全通知邮箱发送告警。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.adminForm.loginAlertEnabled }"><i></i></span>
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<template v-else-if="activeSection === 'llm'">
|
||
<div class="model-grid">
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>主模型配置</h4>
|
||
<p>用于 AI 助手和主业务链路的默认模型接入。</p>
|
||
</div>
|
||
<div class="card-head-actions">
|
||
<button class="test-button" type="button" :disabled="isModelTesting('main')" @click="testModelConnection('main')">
|
||
<i :class="isModelTesting('main') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||
<span>{{ isModelTesting('main') ? '测试中...' : '测试模型' }}</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<div class="field field-full">
|
||
<small class="secret-bound-state">
|
||
<i class="mdi mdi-source-branch"></i>
|
||
<span>保存后会同步写入 Hermes 配置;外部 Hermes agent 也可通过后端共享接口读取这里的主模型配置。</span>
|
||
</small>
|
||
</div>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 供应商</span>
|
||
<select v-model="pageState.llmForm.mainProvider" @change="applyProviderPreset('main')">
|
||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 模型名称</span>
|
||
<input v-model="pageState.llmForm.mainModel" type="text" placeholder="请输入主模型名称" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> 接口地址</span>
|
||
<input v-model="pageState.llmForm.mainEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span>API Key</span>
|
||
<input
|
||
v-model="pageState.llmForm.mainApiKey"
|
||
type="password"
|
||
autocomplete="off"
|
||
@focus="clearModelSecretMask('main')"
|
||
:placeholder="pageState.llmForm.mainApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||
/>
|
||
<small v-if="pageState.llmForm.mainApiKeyConfigured" class="secret-bound-state">
|
||
<i class="mdi mdi-database-lock"></i>
|
||
<span>已从数据库加密加载,测试会使用已保存密钥。</span>
|
||
</small>
|
||
</label>
|
||
</div>
|
||
<div v-if="getModelTestState('main').message" class="test-feedback" :class="`is-${getModelTestState('main').status}`">
|
||
<i :class="getModelTestState('main').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('main').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||
<span>{{ getModelTestState('main').message }}</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>备份模型配置</h4>
|
||
<p>主模型不可用时用于兜底切换的备用模型接入。</p>
|
||
</div>
|
||
<div class="card-head-actions">
|
||
<button class="test-button" type="button" :disabled="isModelTesting('backup')" @click="testModelConnection('backup')">
|
||
<i :class="isModelTesting('backup') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||
<span>{{ isModelTesting('backup') ? '测试中...' : '测试模型' }}</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field">
|
||
<span><em>*</em> 供应商</span>
|
||
<select v-model="pageState.llmForm.backupProvider" @change="applyProviderPreset('backup')">
|
||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 模型名称</span>
|
||
<input v-model="pageState.llmForm.backupModel" type="text" placeholder="请输入备份模型名称" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> 接口地址</span>
|
||
<input v-model="pageState.llmForm.backupEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span>API Key</span>
|
||
<input
|
||
v-model="pageState.llmForm.backupApiKey"
|
||
type="password"
|
||
autocomplete="off"
|
||
@focus="clearModelSecretMask('backup')"
|
||
:placeholder="pageState.llmForm.backupApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||
/>
|
||
<small v-if="pageState.llmForm.backupApiKeyConfigured" class="secret-bound-state">
|
||
<i class="mdi mdi-database-lock"></i>
|
||
<span>已从数据库加密加载,测试会使用已保存密钥。</span>
|
||
</small>
|
||
</label>
|
||
</div>
|
||
<div v-if="getModelTestState('backup').message" class="test-feedback" :class="`is-${getModelTestState('backup').status}`">
|
||
<i :class="getModelTestState('backup').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('backup').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||
<span>{{ getModelTestState('backup').message }}</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>VLM 模型设置</h4>
|
||
<p>用于票据、图像等多模态识别场景的视觉语言模型配置。</p>
|
||
</div>
|
||
<div class="card-head-actions">
|
||
<button class="test-button" type="button" :disabled="isModelTesting('vlm')" @click="testModelConnection('vlm')">
|
||
<i :class="isModelTesting('vlm') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||
<span>{{ isModelTesting('vlm') ? '测试中...' : '测试模型' }}</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field">
|
||
<span><em>*</em> 供应商</span>
|
||
<select v-model="pageState.llmForm.vlmProvider" @change="applyProviderPreset('vlm')">
|
||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 模型名称</span>
|
||
<input v-model="pageState.llmForm.vlmModel" type="text" placeholder="请输入 VLM 模型名称" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> 接口地址</span>
|
||
<input v-model="pageState.llmForm.vlmEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span>API Key</span>
|
||
<input
|
||
v-model="pageState.llmForm.vlmApiKey"
|
||
type="password"
|
||
autocomplete="off"
|
||
@focus="clearModelSecretMask('vlm')"
|
||
:placeholder="pageState.llmForm.vlmApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||
/>
|
||
<small v-if="pageState.llmForm.vlmApiKeyConfigured" class="secret-bound-state">
|
||
<i class="mdi mdi-database-lock"></i>
|
||
<span>已从数据库加密加载,测试会使用已保存密钥。</span>
|
||
</small>
|
||
</label>
|
||
</div>
|
||
<div v-if="getModelTestState('vlm').message" class="test-feedback" :class="`is-${getModelTestState('vlm').status}`">
|
||
<i :class="getModelTestState('vlm').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('vlm').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||
<span>{{ getModelTestState('vlm').message }}</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>Embedding 模型配置</h4>
|
||
<p>用于向量检索、知识库召回和语义匹配的嵌入模型设置。</p>
|
||
</div>
|
||
<div class="card-head-actions">
|
||
<button class="test-button" type="button" :disabled="isModelTesting('embedding')" @click="testModelConnection('embedding')">
|
||
<i :class="isModelTesting('embedding') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||
<span>{{ isModelTesting('embedding') ? '测试中...' : '测试模型' }}</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field">
|
||
<span><em>*</em> 供应商</span>
|
||
<select v-model="pageState.llmForm.embeddingProvider" @change="applyProviderPreset('embedding')">
|
||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 模型名称</span>
|
||
<input v-model="pageState.llmForm.embeddingModel" type="text" placeholder="请输入 Embedding 模型名称" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> 接口地址</span>
|
||
<input v-model="pageState.llmForm.embeddingEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span>API Key</span>
|
||
<input
|
||
v-model="pageState.llmForm.embeddingApiKey"
|
||
type="password"
|
||
autocomplete="off"
|
||
@focus="clearModelSecretMask('embedding')"
|
||
:placeholder="pageState.llmForm.embeddingApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||
/>
|
||
<small v-if="pageState.llmForm.embeddingApiKeyConfigured" class="secret-bound-state">
|
||
<i class="mdi mdi-database-lock"></i>
|
||
<span>已从数据库加密加载,测试会使用已保存密钥。</span>
|
||
</small>
|
||
</label>
|
||
</div>
|
||
<div
|
||
v-if="getModelTestState('embedding').message"
|
||
class="test-feedback"
|
||
:class="`is-${getModelTestState('embedding').status}`"
|
||
>
|
||
<i :class="getModelTestState('embedding').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('embedding').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||
<span>{{ getModelTestState('embedding').message }}</span>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<template v-else-if="activeSection === 'rendering'">
|
||
<section class="settings-card rendering-settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>ONLYOFFICE 服务配置</h4>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="switch-group">
|
||
<button class="switch-row" type="button" @click="toggleBoolean('renderForm', 'enabled')">
|
||
<span class="switch-copy">
|
||
<strong>启用 ONLYOFFICE 文件渲染</strong>
|
||
<small>启用后,知识库中的 Office 文件将优先走 ONLYOFFICE 在线预览。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.renderForm.enabled }"><i></i></span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field field-full">
|
||
<span><em>*</em> ONLYOFFICE 服务地址</span>
|
||
<input
|
||
v-model="pageState.renderForm.publicUrl"
|
||
type="text"
|
||
placeholder="例如 http://10.10.10.122:8082"
|
||
/>
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> JWT 密钥</span>
|
||
<input
|
||
v-model="pageState.renderForm.jwtSecret"
|
||
type="password"
|
||
autocomplete="off"
|
||
@focus="clearRenderSecretMask"
|
||
:placeholder="pageState.renderForm.jwtSecretConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||
/>
|
||
<small v-if="pageState.renderForm.jwtSecretConfigured" class="secret-bound-state">
|
||
<i class="mdi mdi-database-lock"></i>
|
||
<span>已从数据库加密加载,预览签名会使用已保存密钥。</span>
|
||
</small>
|
||
</label>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<template v-else-if="activeSection === 'logs'">
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>日志级别与留存</h4>
|
||
<p>定义系统记录粒度、归档周期和告警接收人,方便后续审计与排障。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="chip-row">
|
||
<button
|
||
v-for="level in logLevels"
|
||
:key="level"
|
||
class="level-chip"
|
||
:class="{ active: pageState.logForm.level === level }"
|
||
type="button"
|
||
@click="pageState.logForm.level = level"
|
||
>
|
||
{{ level }}
|
||
</button>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field">
|
||
<span><em>*</em> 留存天数</span>
|
||
<input v-model.number="pageState.logForm.retentionDays" type="number" min="7" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>归档周期</span>
|
||
<select v-model="pageState.logForm.archiveCycle">
|
||
<option value="daily">按天归档</option>
|
||
<option value="weekly">按周归档</option>
|
||
<option value="monthly">按月归档</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span><em>*</em> 日志路径</span>
|
||
<input v-model="pageState.logForm.logPath" type="text" placeholder="例如 server/logs/app.log" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span>告警邮箱</span>
|
||
<input v-model="pageState.logForm.alertEmail" type="email" placeholder="用于接收日志异常提醒" />
|
||
</label>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>审计策略</h4>
|
||
<p>决定是否记录关键操作、登录行为以及是否对敏感字段进行脱敏处理。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="switch-group">
|
||
<button class="switch-row" type="button" @click="toggleBoolean('logForm', 'operationAudit')">
|
||
<span class="switch-copy">
|
||
<strong>记录关键操作日志</strong>
|
||
<small>保存配置修改、审批动作和账户管理等重要事件。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.logForm.operationAudit }"><i></i></span>
|
||
</button>
|
||
|
||
<button class="switch-row" type="button" @click="toggleBoolean('logForm', 'loginAudit')">
|
||
<span class="switch-copy">
|
||
<strong>记录登录审计</strong>
|
||
<small>追踪登录来源、登录结果和异常登录行为。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.logForm.loginAudit }"><i></i></span>
|
||
</button>
|
||
|
||
<button class="switch-row" type="button" @click="toggleBoolean('logForm', 'maskSensitive')">
|
||
<span class="switch-copy">
|
||
<strong>敏感字段脱敏</strong>
|
||
<small>日志写入时自动隐藏密码、密钥与认证令牌。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.logForm.maskSensitive }"><i></i></span>
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<template v-else>
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>SMTP 基础配置</h4>
|
||
<p>维护系统发信地址、认证账号和加密方式,用于审批提醒与系统通知。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid">
|
||
<label class="field">
|
||
<span><em>*</em> SMTP Host</span>
|
||
<input v-model="pageState.mailForm.smtpHost" type="text" placeholder="请输入 SMTP Host" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 端口</span>
|
||
<input v-model.number="pageState.mailForm.port" type="number" min="1" max="65535" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>加密方式</span>
|
||
<select v-model="pageState.mailForm.encryption">
|
||
<option value="SSL/TLS">SSL/TLS</option>
|
||
<option value="STARTTLS">STARTTLS</option>
|
||
<option value="None">无</option>
|
||
</select>
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>发件人名称</span>
|
||
<input v-model="pageState.mailForm.senderName" type="text" placeholder="请输入发件人名称" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 发件人邮箱</span>
|
||
<input v-model="pageState.mailForm.senderAddress" type="email" placeholder="请输入发件人邮箱" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span><em>*</em> 登录账号</span>
|
||
<input v-model="pageState.mailForm.username" type="text" placeholder="请输入 SMTP 登录账号" />
|
||
</label>
|
||
|
||
<label class="field field-full">
|
||
<span>SMTP 密码</span>
|
||
<input
|
||
v-model="pageState.mailForm.password"
|
||
type="password"
|
||
autocomplete="off"
|
||
:placeholder="pageState.mailForm.passwordConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||
/>
|
||
</label>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="settings-card">
|
||
<div class="card-head">
|
||
<div>
|
||
<h4>通知策略</h4>
|
||
<p>控制是否启用邮件通知、日报摘要以及默认接收邮箱。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="switch-group">
|
||
<button class="switch-row" type="button" @click="toggleBoolean('mailForm', 'alertEnabled')">
|
||
<span class="switch-copy">
|
||
<strong>启用系统通知</strong>
|
||
<small>审批、异常告警和系统事件可通过邮件触达用户。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.mailForm.alertEnabled }"><i></i></span>
|
||
</button>
|
||
|
||
<button class="switch-row" type="button" @click="toggleBoolean('mailForm', 'digestEnabled')">
|
||
<span class="switch-copy">
|
||
<strong>启用日报摘要</strong>
|
||
<small>按固定时间发送系统运行与待办摘要。</small>
|
||
</span>
|
||
<span class="switch" :class="{ active: pageState.mailForm.digestEnabled }"><i></i></span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="form-grid compact-grid">
|
||
<label class="field">
|
||
<span>摘要发送时间</span>
|
||
<input v-model="pageState.mailForm.digestTime" type="time" />
|
||
</label>
|
||
|
||
<label class="field">
|
||
<span>默认接收邮箱</span>
|
||
<input v-model="pageState.mailForm.defaultReceiver" type="email" placeholder="请输入默认接收邮箱" />
|
||
</label>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<script src="./scripts/SettingsView.js"></script>
|
||
|
||
<style scoped src="../assets/styles/views/settings-view.css"></style>
|