157 lines
4.1 KiB
Vue
157 lines
4.1 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
import { useLoginStore } from '@/stores/login'
|
|
|
|
import EmbedSettingsCard from './components/EmbedSettingsCard.vue'
|
|
import MenuResourceSettings from './components/MenuResourceSettings.vue'
|
|
import SignatureSettingsCard from './components/SignatureSettingsCard.vue'
|
|
import SmsSettingsCard from './components/SmsSettingsCard.vue'
|
|
import TabletDevicesCard from './components/TabletDevicesCard.vue'
|
|
import {
|
|
defaultSmsChannel,
|
|
mockSignatureSettings,
|
|
mockTabletDevices,
|
|
smsChannelOptions,
|
|
} from './mock'
|
|
import type { SettingToggleKey, SignatureSettings, SmsChannel, TabletDevice } from './types'
|
|
|
|
const loading = ref(true)
|
|
const activeTab = ref('general')
|
|
const loginStore = useLoginStore()
|
|
const canManageMenus = computed(() => loginStore.hasPermission('system:config:menu'))
|
|
watch(canManageMenus, (canManage) => {
|
|
if (!canManage && activeTab.value === 'menus') activeTab.value = 'general'
|
|
})
|
|
const signatureSettings = reactive<SignatureSettings>({ ...mockSignatureSettings })
|
|
const smsChannel = ref<SmsChannel>(defaultSmsChannel)
|
|
const tabletDevices = ref<TabletDevice[]>([])
|
|
|
|
async function loadSettings() {
|
|
loading.value = true
|
|
await Promise.resolve()
|
|
tabletDevices.value = mockTabletDevices.map((device) => ({ ...device }))
|
|
loading.value = false
|
|
}
|
|
|
|
function updateLinkExpireHours(value: number) {
|
|
signatureSettings.linkExpireHours = value
|
|
}
|
|
|
|
function toggleSetting(key: SettingToggleKey) {
|
|
signatureSettings[key] = !signatureSettings[key]
|
|
|
|
if (key === 'caCertificate' && signatureSettings.caCertificate) {
|
|
ElMessage.info('已开启 CA 扩展位(当前版本未启用)')
|
|
return
|
|
}
|
|
|
|
ElMessage.info('设置已更新')
|
|
}
|
|
|
|
function updateSmsChannel(value: SmsChannel) {
|
|
smsChannel.value = value
|
|
}
|
|
|
|
function sendTestSms() {
|
|
ElMessage.success('测试短信已发送至 138****5678')
|
|
}
|
|
|
|
function reconnectDevice(device: TabletDevice) {
|
|
if (device.status === 'connected') {
|
|
ElMessage.success('已重新连接')
|
|
return
|
|
}
|
|
|
|
ElMessage.info('正在重连…')
|
|
}
|
|
|
|
function copyEmbedUrl() {
|
|
ElMessage.success('原型演示:已复制嵌入 URL')
|
|
}
|
|
|
|
function rotateKey() {
|
|
ElMessage.success('原型演示:密钥轮换成功')
|
|
}
|
|
|
|
onMounted(loadSettings)
|
|
</script>
|
|
|
|
<template>
|
|
<section class="settings-page">
|
|
<el-tabs v-model="activeTab" class="settings-tabs">
|
|
<el-tab-pane label="系统参数" name="general">
|
|
<div v-if="loading" class="loading-grid" aria-label="正在加载系统设置">
|
|
<span v-for="index in 4" :key="index" />
|
|
</div>
|
|
|
|
<div v-else class="settings-grid">
|
|
<SignatureSettingsCard
|
|
:settings="signatureSettings"
|
|
@update:link-expire-hours="updateLinkExpireHours"
|
|
@toggle="toggleSetting"
|
|
/>
|
|
<SmsSettingsCard
|
|
:channel="smsChannel"
|
|
:channel-options="smsChannelOptions"
|
|
@update:channel="updateSmsChannel"
|
|
@test="sendTestSms"
|
|
/>
|
|
<TabletDevicesCard :devices="tabletDevices" @reconnect="reconnectDevice" />
|
|
<EmbedSettingsCard @copy="copyEmbedUrl" @rotate-key="rotateKey" />
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane v-if="canManageMenus" label="菜单设置" name="menus">
|
|
<MenuResourceSettings />
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
</section>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.settings-page {
|
|
width: 100%;
|
|
max-width: 1440px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.settings-tabs {
|
|
background: transparent;
|
|
}
|
|
|
|
.settings-grid,
|
|
.loading-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.loading-grid span {
|
|
display: block;
|
|
height: 240px;
|
|
background: linear-gradient(90deg, #edf3f5 25%, #f7fafb 37%, #edf3f5 63%);
|
|
background-size: 400% 100%;
|
|
border-radius: var(--r);
|
|
box-shadow: var(--sh);
|
|
animation: skeleton-shimmer 1.4s ease infinite;
|
|
}
|
|
|
|
@keyframes skeleton-shimmer {
|
|
0% {
|
|
background-position: 100% 0;
|
|
}
|
|
|
|
100% {
|
|
background-position: -100% 0;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
.settings-grid,
|
|
.loading-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|