This commit is contained in:
parent
896b335c66
commit
eb7273d415
@ -4,6 +4,8 @@ import {computed, ref, onMounted, onUnmounted} from 'vue'
|
||||
|
||||
import SecuritySettings from '@/views/components/SecuritySettings/SecuritySettings.vue'
|
||||
|
||||
import ThirdPartyAuthorization from '@/views/components/ThirdPartyAuthorization/ThirdPartyAuthorization.vue'
|
||||
|
||||
import ConfigSettings from '@/views/components/ConfigSettings/ConfigSettings.vue'
|
||||
|
||||
import lang from '../../../i18n/i18n';
|
||||
@ -45,6 +47,7 @@ export function usePage() {
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
SecuritySettings,
|
||||
ThirdPartyAuthorization,
|
||||
ConfigSettings,
|
||||
lang,
|
||||
GroupSettings,
|
||||
@ -67,6 +70,7 @@ export {
|
||||
EpMenu,
|
||||
BrandLogo,
|
||||
SecuritySettings,
|
||||
ThirdPartyAuthorization,
|
||||
ConfigSettings,
|
||||
GroupSettings,
|
||||
RuleSettings,
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
<el-tab-pane :label="lang.security">
|
||||
<SecuritySettings/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="第三方授权">
|
||||
<ThirdPartyAuthorization />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="canReadConfig" label="配置管理">
|
||||
<ConfigSettings />
|
||||
</el-tab-pane>
|
||||
@ -43,7 +46,18 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { usePage, EpMenu, BrandLogo, SecuritySettings, ConfigSettings, GroupSettings, RuleSettings, UserManagement, PluginSettings } from './HomeHeader.js'
|
||||
import {
|
||||
usePage,
|
||||
EpMenu,
|
||||
BrandLogo,
|
||||
SecuritySettings,
|
||||
ThirdPartyAuthorization,
|
||||
ConfigSettings,
|
||||
GroupSettings,
|
||||
RuleSettings,
|
||||
UserManagement,
|
||||
PluginSettings,
|
||||
} from './HomeHeader.js'
|
||||
|
||||
const {
|
||||
globalStatus,
|
||||
|
||||
@ -24,19 +24,3 @@
|
||||
.credential-actions { width: 100%; }
|
||||
.credential-actions .el-button { flex: 1; }
|
||||
}
|
||||
|
||||
.protocol-section > .el-alert { margin-bottom: 18px; }
|
||||
.protocol-list { display: grid; gap: 12px; }
|
||||
.protocol-item { display: flex; align-items: center; justify-content: space-between; gap: 18px; padding: 16px; border: 1px solid var(--pm-border-color); border-radius: var(--pm-radius-sm); background: var(--pm-bg-secondary); }
|
||||
.protocol-info { min-width: 0; }
|
||||
.protocol-title { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
|
||||
.protocol-title strong { color: var(--pm-text-primary); font-size: 16px; }
|
||||
.protocol-info p { margin: 8px 0 0; color: var(--pm-text-secondary); font-size: 13px; }
|
||||
.protocol-actions { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; flex: none; }
|
||||
.protocol-code-panel { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin-top: 16px; padding: 16px; border: 1px solid var(--pm-primary-color); border-radius: var(--pm-radius-sm); background: color-mix(in srgb, var(--pm-primary-color) 7%, var(--pm-bg-primary)); }
|
||||
.protocol-code-panel .one-time-tip { width: 100%; color: var(--pm-text-secondary); font-size: 13px; }
|
||||
.protocol-code-panel code { padding: 8px 12px; border-radius: var(--pm-radius-sm); background: var(--pm-bg-secondary); color: var(--pm-text-primary); font-size: 16px; font-weight: 700; letter-spacing: 1px; word-break: break-all; }
|
||||
@media (max-width: 720px) {
|
||||
.protocol-item { align-items: stretch; flex-direction: column; }
|
||||
.protocol-actions { justify-content: flex-start; }
|
||||
}
|
||||
@ -22,10 +22,6 @@ export function usePage() {
|
||||
const generatedCodes = ref([])
|
||||
const externalEmails = ref([])
|
||||
const externalLoading = ref(false)
|
||||
const protocolLoading = ref(false)
|
||||
const protocolCredentials = ref([])
|
||||
const generatedProtocolCode = ref('')
|
||||
const generatedProtocol = ref('')
|
||||
const bindForm = reactive({ email: '', type: 'backup', challengeId: '', bindingId: 0, code: '' })
|
||||
|
||||
const ruleForm = reactive({ current_pwd: '', new_pwd: '', new_pwd2: '' })
|
||||
@ -57,15 +53,13 @@ export function usePage() {
|
||||
securityLoading.value = true
|
||||
try {
|
||||
const requests = [
|
||||
http.get('/api/v1/security/credentials/protocols'),
|
||||
http.get('/api/v1/security/credentials/question'),
|
||||
http.get('/api/v1/security/external-email/list'),
|
||||
]
|
||||
if (!isSuperAdmin.value) {
|
||||
requests.push(http.get('/api/v1/security/credentials/recovery-codes'))
|
||||
}
|
||||
const [protocol, question, external, recovery] = await Promise.all(requests)
|
||||
if (protocol.errorNo === 0) protocolCredentials.value = Array.isArray(protocol.data) ? protocol.data : []
|
||||
const [question, external, recovery] = await Promise.all(requests)
|
||||
if (question.errorNo === 0 && question.data) {
|
||||
securityQuestion.configured = Boolean(question.data.configured)
|
||||
securityQuestion.question = question.data.question || ''
|
||||
@ -80,73 +74,6 @@ export function usePage() {
|
||||
}
|
||||
}
|
||||
|
||||
const protocolLabel = protocol => ({ imap: 'IMAP', pop3: 'POP3', smtp: 'SMTP' }[protocol] || protocol.toUpperCase())
|
||||
|
||||
|
||||
const setProtocolEnabled = async (item, enabled) => {
|
||||
const previous = item.enabled
|
||||
item.enabled = enabled
|
||||
protocolLoading.value = true
|
||||
try {
|
||||
const res = await http.put('/api/v1/security/credentials/protocols/enabled', { protocol: item.protocol, enabled })
|
||||
if (res.errorNo !== 0) throw new Error(res.errorMsg || '协议设置失败')
|
||||
notify(`${protocolLabel(item.protocol)} 协议已${enabled ? '开启' : '关闭'}`, 'success')
|
||||
} catch (error) {
|
||||
item.enabled = previous
|
||||
notify(error?.message || '协议设置失败')
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const generateProtocolCode = async item => {
|
||||
if (!item.enabled) {
|
||||
notify(`请先开启 ${protocolLabel(item.protocol)} 协议`)
|
||||
return
|
||||
}
|
||||
protocolLoading.value = true
|
||||
try {
|
||||
const res = await http.post('/api/v1/security/credentials/protocols/generate', { protocol: item.protocol })
|
||||
if (res.errorNo !== 0) throw new Error(res.errorMsg || '生成授权码失败')
|
||||
generatedProtocol.value = item.protocol
|
||||
generatedProtocolCode.value = res.data?.code || ''
|
||||
item.configured = true
|
||||
notify(`${protocolLabel(item.protocol)} 授权码已生成,请立即保存`, 'success')
|
||||
} catch (error) {
|
||||
notify(error?.message || '生成授权码失败')
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const copyProtocolCode = async () => {
|
||||
if (!generatedProtocolCode.value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(generatedProtocolCode.value)
|
||||
notify('授权码已复制', 'success')
|
||||
} catch {
|
||||
notify('复制失败,请手动复制授权码')
|
||||
}
|
||||
}
|
||||
|
||||
const revokeProtocolCode = async item => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`撤销后 ${protocolLabel(item.protocol)} 授权码将立即失效,是否继续?`, '撤销协议授权码', { type: 'warning' })
|
||||
protocolLoading.value = true
|
||||
const res = await http.post('/api/v1/security/credentials/protocols/revoke', { protocol: item.protocol })
|
||||
if (res.errorNo !== 0) throw new Error(res.errorMsg || '撤销授权码失败')
|
||||
item.configured = false
|
||||
if (generatedProtocol.value === item.protocol) {
|
||||
generatedProtocol.value = ''
|
||||
generatedProtocolCode.value = ''
|
||||
}
|
||||
notify(`${protocolLabel(item.protocol)} 授权码已撤销`, 'success')
|
||||
} catch (error) {
|
||||
if (error !== 'cancel' && error !== 'close') notify(error?.message || '撤销授权码失败')
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
const registerCredential = async () => {
|
||||
if (!supported || registering.value) return
|
||||
registering.value = true
|
||||
@ -308,8 +235,6 @@ export function usePage() {
|
||||
return {
|
||||
lang, isSuperAdmin, supported, registering, credentialsLoading, credentials, securityLoading,
|
||||
securityQuestion, questionForm, recoveryConfigured, generatedCodes, externalEmails, externalLoading,
|
||||
protocolLoading, protocolCredentials, generatedProtocolCode, generatedProtocol, protocolLabel,
|
||||
setProtocolEnabled, generateProtocolCode, copyProtocolCode, revokeProtocolCode,
|
||||
bindForm, ruleForm, rules, submit, registerCredential, renameCredential, revokeCredential,
|
||||
saveSecurityQuestion, generateRecoveryCodes, revokeRecoveryCodes, bindExternalEmail,
|
||||
disableExternalEmail, formatTime, logout,
|
||||
|
||||
@ -45,35 +45,6 @@
|
||||
</section>
|
||||
|
||||
<el-divider class="settings-divider" />
|
||||
<section class="security-section protocol-section">
|
||||
<div class="settings-header">
|
||||
<h3>第三方邮件客户端登录</h3>
|
||||
<p class="settings-desc">开启协议并生成授权码后,才能使用邮件客户端登录。登录名必须填写完整邮箱地址,密码必须填写授权码,不能填写网页登录密码。</p>
|
||||
</div>
|
||||
<el-alert title="授权码只在生成成功后显示一次,请立即保存;撤销授权码或关闭协议后,客户端会立即无法登录。" type="warning" :closable="false" show-icon />
|
||||
<div class="protocol-list" v-loading="protocolLoading">
|
||||
<div v-for="item in protocolCredentials" :key="item.protocol" class="protocol-item">
|
||||
<div class="protocol-info">
|
||||
<div class="protocol-title">
|
||||
<strong>{{ protocolLabel(item.protocol) }}</strong>
|
||||
<el-tag size="small" :type="item.enabled ? 'success' : 'info'">{{ item.enabled ? '已开启' : '已关闭' }}</el-tag>
|
||||
<el-tag size="small" :type="item.configured ? 'success' : 'warning'">{{ item.configured ? '已配置授权码' : '未生成授权码' }}</el-tag>
|
||||
</div>
|
||||
<p>登录名:完整邮箱地址;密码:本协议授权码。</p>
|
||||
</div>
|
||||
<div class="protocol-actions">
|
||||
<el-switch v-model="item.enabled" :loading="protocolLoading" active-text="开启" inactive-text="关闭" @change="value => setProtocolEnabled(item, value)" />
|
||||
<el-button type="primary" size="small" :disabled="!item.enabled" @click="generateProtocolCode(item)">{{ item.configured ? '重新生成授权码' : '生成授权码' }}</el-button>
|
||||
<el-button v-if="item.configured" type="danger" plain size="small" @click="revokeProtocolCode(item)">撤销授权码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="generatedProtocolCode" class="protocol-code-panel">
|
||||
<div class="one-time-tip">{{ protocolLabel(generatedProtocol) }} 授权码仅本次显示,请复制保存:</div>
|
||||
<code>{{ generatedProtocolCode }}</code>
|
||||
<el-button type="primary" plain size="small" @click="copyProtocolCode">复制授权码</el-button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="security-section">
|
||||
<div class="settings-header"><h3>外部安全邮箱</h3><p class="settings-desc">用于登录二次验证和密码恢复,只显示脱敏地址。</p></div>
|
||||
<div class="external-bind-form">
|
||||
@ -108,8 +79,6 @@ import { usePage } from './SecuritySettings.js'
|
||||
const {
|
||||
lang, isSuperAdmin, supported, registering, credentialsLoading, credentials, securityLoading,
|
||||
securityQuestion, questionForm, recoveryConfigured, generatedCodes, externalEmails, externalLoading,
|
||||
protocolLoading, protocolCredentials, generatedProtocolCode, generatedProtocol, protocolLabel,
|
||||
setProtocolEnabled, generateProtocolCode, copyProtocolCode, revokeProtocolCode,
|
||||
bindForm, ruleForm, rules, submit, registerCredential, renameCredential, revokeCredential,
|
||||
saveSecurityQuestion, generateRecoveryCodes, revokeRecoveryCodes, bindExternalEmail,
|
||||
disableExternalEmail, formatTime, logout,
|
||||
|
||||
@ -0,0 +1,112 @@
|
||||
.third-party-authorization {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.authorization-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.authorization-header h3 {
|
||||
margin: 0 0 8px;
|
||||
color: var(--pm-text-primary);
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.authorization-desc {
|
||||
margin: 0;
|
||||
color: var(--pm-text-secondary);
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.authorization-section > .el-alert {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.protocol-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.protocol-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--pm-border-color);
|
||||
border-radius: var(--pm-radius-sm);
|
||||
background: var(--pm-bg-secondary);
|
||||
}
|
||||
|
||||
.protocol-info {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.protocol-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.protocol-title strong {
|
||||
color: var(--pm-text-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.protocol-info p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--pm-text-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.protocol-actions {
|
||||
display: flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.protocol-code-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--pm-primary-color);
|
||||
border-radius: var(--pm-radius-sm);
|
||||
background: color-mix(in srgb, var(--pm-primary-color) 7%, var(--pm-bg-primary));
|
||||
}
|
||||
|
||||
.protocol-code-panel .one-time-tip {
|
||||
width: 100%;
|
||||
color: var(--pm-text-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.protocol-code-panel code {
|
||||
padding: 8px 12px;
|
||||
border-radius: var(--pm-radius-sm);
|
||||
background: var(--pm-bg-secondary);
|
||||
color: var(--pm-text-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.protocol-item {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.protocol-actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
import { ElMessageBox, ElNotification } from 'element-plus'
|
||||
|
||||
import { http } from '@/utils/axios'
|
||||
|
||||
export function usePage() {
|
||||
const protocolLoading = ref(false)
|
||||
const protocolCredentials = ref([])
|
||||
const generatedProtocolCode = ref('')
|
||||
const generatedProtocol = ref('')
|
||||
|
||||
const notify = (message, type = 'error') => {
|
||||
ElNotification({
|
||||
title: type === 'success' ? '操作成功' : '操作失败',
|
||||
message,
|
||||
type,
|
||||
})
|
||||
}
|
||||
|
||||
const protocolLabel = protocol => ({
|
||||
imap: 'IMAP',
|
||||
pop3: 'POP3',
|
||||
smtp: 'SMTP',
|
||||
}[protocol] || protocol.toUpperCase())
|
||||
|
||||
const loadProtocolCredentials = async () => {
|
||||
protocolLoading.value = true
|
||||
try {
|
||||
const response = await http.get('/api/v1/security/credentials/protocols')
|
||||
if (response.errorNo !== 0) {
|
||||
throw new Error(response.errorMsg || '读取第三方授权设置失败')
|
||||
}
|
||||
protocolCredentials.value = Array.isArray(response.data) ? response.data : []
|
||||
} catch (error) {
|
||||
notify(error?.message || '读取第三方授权设置失败')
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const setProtocolEnabled = async (item, enabled) => {
|
||||
const previous = item.enabled
|
||||
item.enabled = enabled
|
||||
protocolLoading.value = true
|
||||
try {
|
||||
const response = await http.put('/api/v1/security/credentials/protocols/enabled', {
|
||||
protocol: item.protocol,
|
||||
enabled,
|
||||
})
|
||||
if (response.errorNo !== 0) {
|
||||
throw new Error(response.errorMsg || '协议设置失败')
|
||||
}
|
||||
notify(`${protocolLabel(item.protocol)} 协议已${enabled ? '开启' : '关闭'}`, 'success')
|
||||
} catch (error) {
|
||||
item.enabled = previous
|
||||
notify(error?.message || '协议设置失败')
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const generateProtocolCode = async item => {
|
||||
if (!item.enabled) {
|
||||
notify(`请先开启 ${protocolLabel(item.protocol)} 协议`)
|
||||
return
|
||||
}
|
||||
|
||||
protocolLoading.value = true
|
||||
try {
|
||||
const response = await http.post('/api/v1/security/credentials/protocols/generate', {
|
||||
protocol: item.protocol,
|
||||
})
|
||||
if (response.errorNo !== 0) {
|
||||
throw new Error(response.errorMsg || '生成授权码失败')
|
||||
}
|
||||
generatedProtocol.value = item.protocol
|
||||
generatedProtocolCode.value = response.data?.code || ''
|
||||
item.configured = true
|
||||
notify(`${protocolLabel(item.protocol)} 授权码已生成,请立即保存`, 'success')
|
||||
} catch (error) {
|
||||
notify(error?.message || '生成授权码失败')
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const copyProtocolCode = async () => {
|
||||
if (!generatedProtocolCode.value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(generatedProtocolCode.value)
|
||||
notify('授权码已复制', 'success')
|
||||
} catch {
|
||||
notify('复制失败,请手动复制授权码')
|
||||
}
|
||||
}
|
||||
|
||||
const revokeProtocolCode = async item => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`撤销后 ${protocolLabel(item.protocol)} 授权码将立即失效,是否继续?`,
|
||||
'撤销协议授权码',
|
||||
{ type: 'warning' },
|
||||
)
|
||||
protocolLoading.value = true
|
||||
const response = await http.post('/api/v1/security/credentials/protocols/revoke', {
|
||||
protocol: item.protocol,
|
||||
})
|
||||
if (response.errorNo !== 0) {
|
||||
throw new Error(response.errorMsg || '撤销授权码失败')
|
||||
}
|
||||
item.configured = false
|
||||
if (generatedProtocol.value === item.protocol) {
|
||||
generatedProtocol.value = ''
|
||||
generatedProtocolCode.value = ''
|
||||
}
|
||||
notify(`${protocolLabel(item.protocol)} 授权码已撤销`, 'success')
|
||||
} catch (error) {
|
||||
if (error !== 'cancel' && error !== 'close') {
|
||||
notify(error?.message || '撤销授权码失败')
|
||||
}
|
||||
} finally {
|
||||
protocolLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadProtocolCredentials)
|
||||
|
||||
return {
|
||||
protocolLoading,
|
||||
protocolCredentials,
|
||||
generatedProtocolCode,
|
||||
generatedProtocol,
|
||||
protocolLabel,
|
||||
setProtocolEnabled,
|
||||
generateProtocolCode,
|
||||
copyProtocolCode,
|
||||
revokeProtocolCode,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="third-party-authorization" v-loading="protocolLoading">
|
||||
<section class="authorization-section">
|
||||
<div class="authorization-header">
|
||||
<h3>第三方邮件客户端登录</h3>
|
||||
<p class="authorization-desc">
|
||||
开启协议并生成授权码后,才能使用邮件客户端登录。登录名必须填写完整邮箱地址,密码必须填写授权码,不能填写网页登录密码。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
title="授权码只在生成成功后显示一次,请立即保存;撤销授权码或关闭协议后,客户端会立即无法登录。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
|
||||
<div class="protocol-list">
|
||||
<div v-for="item in protocolCredentials" :key="item.protocol" class="protocol-item">
|
||||
<div class="protocol-info">
|
||||
<div class="protocol-title">
|
||||
<strong>{{ protocolLabel(item.protocol) }}</strong>
|
||||
<el-tag size="small" :type="item.enabled ? 'success' : 'info'">
|
||||
{{ item.enabled ? '已开启' : '已关闭' }}
|
||||
</el-tag>
|
||||
<el-tag size="small" :type="item.configured ? 'success' : 'warning'">
|
||||
{{ item.configured ? '已配置授权码' : '未生成授权码' }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<p>登录名:完整邮箱地址;密码:本协议授权码。</p>
|
||||
</div>
|
||||
|
||||
<div class="protocol-actions">
|
||||
<el-switch
|
||||
v-model="item.enabled"
|
||||
:loading="protocolLoading"
|
||||
active-text="开启"
|
||||
inactive-text="关闭"
|
||||
@change="value => setProtocolEnabled(item, value)"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!item.enabled"
|
||||
@click="generateProtocolCode(item)"
|
||||
>
|
||||
{{ item.configured ? '重新生成授权码' : '生成授权码' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="item.configured"
|
||||
type="danger"
|
||||
plain
|
||||
size="small"
|
||||
@click="revokeProtocolCode(item)"
|
||||
>
|
||||
撤销授权码
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="generatedProtocolCode" class="protocol-code-panel">
|
||||
<div class="one-time-tip">
|
||||
{{ protocolLabel(generatedProtocol) }} 授权码仅本次显示,请复制保存:
|
||||
</div>
|
||||
<code>{{ generatedProtocolCode }}</code>
|
||||
<el-button type="primary" plain size="small" @click="copyProtocolCode">
|
||||
复制授权码
|
||||
</el-button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { usePage } from './ThirdPartyAuthorization.js'
|
||||
|
||||
const {
|
||||
protocolLoading,
|
||||
protocolCredentials,
|
||||
generatedProtocolCode,
|
||||
generatedProtocol,
|
||||
protocolLabel,
|
||||
setProtocolEnabled,
|
||||
generateProtocolCode,
|
||||
copyProtocolCode,
|
||||
revokeProtocolCode,
|
||||
} = usePage()
|
||||
</script>
|
||||
|
||||
<style scoped src="./ThirdPartyAuthorization.css"></style>
|
||||
Loading…
Reference in New Issue
Block a user