优化邮件客户端协议授权码展示:平时安全隐藏、密码安全验证后解密回显与一键复制
Some checks are pending
Security Checks / security (push) Waiting to run
Some checks are pending
Security Checks / security (push) Waiting to run
This commit is contained in:
parent
2591adce68
commit
9103da3ff3
@ -3,9 +3,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.authorization-header {
|
.authorization-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.authorization-header .header-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.authorization-header h3 {
|
.authorization-header h3 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
color: var(--pm-text-primary);
|
color: var(--pm-text-primary);
|
||||||
@ -20,6 +29,12 @@
|
|||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.authorization-header .header-actions {
|
||||||
|
flex: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.authorization-section > .el-alert {
|
.authorization-section > .el-alert {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
@ -42,6 +57,7 @@
|
|||||||
|
|
||||||
.protocol-info {
|
.protocol-info {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.protocol-title {
|
.protocol-title {
|
||||||
@ -56,12 +72,57 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.protocol-info p {
|
.protocol-info .protocol-guide {
|
||||||
margin: 8px 0 0;
|
margin: 6px 0 0;
|
||||||
color: var(--pm-text-secondary);
|
color: var(--pm-text-secondary);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.protocol-credential-slot {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: var(--pm-radius-sm);
|
||||||
|
background: var(--pm-bg-primary);
|
||||||
|
border: 1px dashed var(--pm-border-color);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-label {
|
||||||
|
color: var(--pm-text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.masked-code {
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: var(--pm-text-secondary);
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revealed-code {
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: color-mix(in srgb, var(--pm-primary-color) 12%, var(--pm-bg-secondary));
|
||||||
|
color: var(--pm-primary-color);
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal-btn, .copy-btn {
|
||||||
|
padding: 0 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unconfigured-tip {
|
||||||
|
color: var(--pm-text-placeholder, #909399);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.protocol-actions {
|
.protocol-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: none;
|
flex: none;
|
||||||
@ -71,36 +132,25 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.protocol-code-panel {
|
.verify-dialog-tip {
|
||||||
display: flex;
|
margin: 0 0 16px;
|
||||||
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);
|
color: var(--pm-text-secondary);
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.protocol-code-panel code {
|
.verify-dialog-footer {
|
||||||
padding: 8px 12px;
|
display: flex;
|
||||||
border-radius: var(--pm-radius-sm);
|
justify-content: flex-end;
|
||||||
background: var(--pm-bg-secondary);
|
gap: 12px;
|
||||||
color: var(--pm-text-primary);
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
|
.authorization-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
.protocol-item {
|
.protocol-item {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
|
||||||
import { ElMessageBox, ElNotification } from 'element-plus'
|
import { ElMessageBox, ElNotification } from 'element-plus'
|
||||||
|
|
||||||
@ -10,9 +10,19 @@ export function usePage() {
|
|||||||
const generatedProtocolCode = ref('')
|
const generatedProtocolCode = ref('')
|
||||||
const generatedProtocol = ref('')
|
const generatedProtocol = ref('')
|
||||||
|
|
||||||
|
// ??????????{ imap: 'xxxx', pop3: 'yyyy', smtp: 'zzzz' }
|
||||||
|
const revealedCodes = reactive({})
|
||||||
|
// ???????????????
|
||||||
|
const codesRevealed = ref(false)
|
||||||
|
|
||||||
|
// ????????
|
||||||
|
const verifyDialogVisible = ref(false)
|
||||||
|
const verifyPassword = ref('')
|
||||||
|
const verifyLoading = ref(false)
|
||||||
|
|
||||||
const notify = (message, type = 'error') => {
|
const notify = (message, type = 'error') => {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: type === 'success' ? '操作成功' : '操作失败',
|
title: type === 'success' ? '????' : '????',
|
||||||
message,
|
message,
|
||||||
type,
|
type,
|
||||||
})
|
})
|
||||||
@ -29,11 +39,11 @@ export function usePage() {
|
|||||||
try {
|
try {
|
||||||
const response = await http.get('/api/v1/security/credentials/protocols')
|
const response = await http.get('/api/v1/security/credentials/protocols')
|
||||||
if (response.errorNo !== 0) {
|
if (response.errorNo !== 0) {
|
||||||
throw new Error(response.errorMsg || '读取邮件客户端协议设置失败')
|
throw new Error(response.errorMsg || '?????????????')
|
||||||
}
|
}
|
||||||
protocolCredentials.value = Array.isArray(response.data) ? response.data : []
|
protocolCredentials.value = Array.isArray(response.data) ? response.data : []
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notify(error?.message || '读取邮件客户端协议设置失败')
|
notify(error?.message || '?????????????')
|
||||||
} finally {
|
} finally {
|
||||||
protocolLoading.value = false
|
protocolLoading.value = false
|
||||||
}
|
}
|
||||||
@ -50,16 +60,19 @@ export function usePage() {
|
|||||||
enabled,
|
enabled,
|
||||||
})
|
})
|
||||||
if (response.errorNo !== 0) {
|
if (response.errorNo !== 0) {
|
||||||
throw new Error(response.errorMsg || '协议设置失败')
|
throw new Error(response.errorMsg || '??????')
|
||||||
}
|
}
|
||||||
if (!enabled && generatedProtocol.value === item.protocol) {
|
if (!enabled) {
|
||||||
generatedProtocol.value = ''
|
if (generatedProtocol.value === item.protocol) {
|
||||||
generatedProtocolCode.value = ''
|
generatedProtocol.value = ''
|
||||||
|
generatedProtocolCode.value = ''
|
||||||
|
}
|
||||||
|
delete revealedCodes[item.protocol]
|
||||||
}
|
}
|
||||||
notify(`${protocolLabel(item.protocol)} 当前账号客户端访问已${enabled ? '开启' : '关闭'}`, 'success')
|
notify(`${protocolLabel(item.protocol)} ??????????${enabled ? '??' : '??'}`, 'success')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
item.enabled = previous
|
item.enabled = previous
|
||||||
notify(error?.message || '协议设置失败')
|
notify(error?.message || '??????')
|
||||||
} finally {
|
} finally {
|
||||||
protocolLoading.value = false
|
protocolLoading.value = false
|
||||||
}
|
}
|
||||||
@ -67,7 +80,7 @@ export function usePage() {
|
|||||||
|
|
||||||
const generateProtocolCode = async item => {
|
const generateProtocolCode = async item => {
|
||||||
if (!item.enabled) {
|
if (!item.enabled) {
|
||||||
notify(`请先开启 ${protocolLabel(item.protocol)} 协议`)
|
notify(`???? ${protocolLabel(item.protocol)} ??`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,34 +90,39 @@ export function usePage() {
|
|||||||
protocol: item.protocol,
|
protocol: item.protocol,
|
||||||
})
|
})
|
||||||
if (response.errorNo !== 0) {
|
if (response.errorNo !== 0) {
|
||||||
throw new Error(response.errorMsg || '生成授权码失败')
|
throw new Error(response.errorMsg || '???????')
|
||||||
}
|
}
|
||||||
|
const newCode = response.data?.code || ''
|
||||||
generatedProtocol.value = item.protocol
|
generatedProtocol.value = item.protocol
|
||||||
generatedProtocolCode.value = response.data?.code || ''
|
generatedProtocolCode.value = newCode
|
||||||
item.configured = true
|
item.configured = true
|
||||||
notify(`${protocolLabel(item.protocol)} 授权码已生成,请立即保存`, 'success')
|
// ??????????
|
||||||
|
revealedCodes[item.protocol] = newCode
|
||||||
|
codesRevealed.value = true
|
||||||
|
notify(`${protocolLabel(item.protocol)} ???????????`, 'success')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notify(error?.message || '生成授权码失败')
|
notify(error?.message || '???????')
|
||||||
} finally {
|
} finally {
|
||||||
protocolLoading.value = false
|
protocolLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const copyProtocolCode = async () => {
|
const copyProtocolCode = async (code) => {
|
||||||
if (!generatedProtocolCode.value) return
|
const targetCode = typeof code === 'string' && code ? code : generatedProtocolCode.value
|
||||||
|
if (!targetCode) return
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(generatedProtocolCode.value)
|
await navigator.clipboard.writeText(targetCode)
|
||||||
notify('授权码已复制', 'success')
|
notify('??????????', 'success')
|
||||||
} catch {
|
} catch {
|
||||||
notify('复制失败,请手动复制授权码')
|
notify('?????????????')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const revokeProtocolCode = async item => {
|
const revokeProtocolCode = async item => {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm(
|
await ElMessageBox.confirm(
|
||||||
`撤销后 ${protocolLabel(item.protocol)} 授权码将立即失效,是否继续?`,
|
`??? ${protocolLabel(item.protocol)} ??????????????`,
|
||||||
'撤销协议授权码',
|
'???????',
|
||||||
{ type: 'warning' },
|
{ type: 'warning' },
|
||||||
)
|
)
|
||||||
protocolLoading.value = true
|
protocolLoading.value = true
|
||||||
@ -112,23 +130,73 @@ export function usePage() {
|
|||||||
protocol: item.protocol,
|
protocol: item.protocol,
|
||||||
})
|
})
|
||||||
if (response.errorNo !== 0) {
|
if (response.errorNo !== 0) {
|
||||||
throw new Error(response.errorMsg || '撤销授权码失败')
|
throw new Error(response.errorMsg || '???????')
|
||||||
}
|
}
|
||||||
item.configured = false
|
item.configured = false
|
||||||
if (generatedProtocol.value === item.protocol) {
|
if (generatedProtocol.value === item.protocol) {
|
||||||
generatedProtocol.value = ''
|
generatedProtocol.value = ''
|
||||||
generatedProtocolCode.value = ''
|
generatedProtocolCode.value = ''
|
||||||
}
|
}
|
||||||
notify(`${protocolLabel(item.protocol)} 授权码已撤销`, 'success')
|
delete revealedCodes[item.protocol]
|
||||||
|
notify(`${protocolLabel(item.protocol)} ??????`, 'success')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error !== 'cancel' && error !== 'close') {
|
if (error !== 'cancel' && error !== 'close') {
|
||||||
notify(error?.message || '撤销授权码失败')
|
notify(error?.message || '???????')
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
protocolLoading.value = false
|
protocolLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ????????
|
||||||
|
const openVerifyDialog = () => {
|
||||||
|
verifyPassword.value = ''
|
||||||
|
verifyDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ????????
|
||||||
|
const closeVerifyDialog = () => {
|
||||||
|
verifyPassword.value = ''
|
||||||
|
verifyDialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ????????????????
|
||||||
|
const submitVerifyAndReveal = async () => {
|
||||||
|
if (!verifyPassword.value.trim()) {
|
||||||
|
notify('???????????????')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyLoading.value = true
|
||||||
|
try {
|
||||||
|
const response = await http.post('/api/v1/security/credentials/protocols/reveal', {
|
||||||
|
password: verifyPassword.value,
|
||||||
|
})
|
||||||
|
if (response.errorNo !== 0) {
|
||||||
|
throw new Error(response.errorMsg || '????????')
|
||||||
|
}
|
||||||
|
const data = response.data || {}
|
||||||
|
Object.keys(revealedCodes).forEach(k => delete revealedCodes[k])
|
||||||
|
Object.assign(revealedCodes, data)
|
||||||
|
codesRevealed.value = true
|
||||||
|
closeVerifyDialog()
|
||||||
|
notify('???????????????', 'success')
|
||||||
|
} catch (error) {
|
||||||
|
notify(error?.message || '????????')
|
||||||
|
} finally {
|
||||||
|
verifyLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ???????????
|
||||||
|
const hideRevealedCodes = () => {
|
||||||
|
codesRevealed.value = false
|
||||||
|
Object.keys(revealedCodes).forEach(k => delete revealedCodes[k])
|
||||||
|
generatedProtocolCode.value = ''
|
||||||
|
generatedProtocol.value = ''
|
||||||
|
notify('????????', 'success')
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(loadProtocolCredentials)
|
onMounted(loadProtocolCredentials)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -136,10 +204,19 @@ export function usePage() {
|
|||||||
protocolCredentials,
|
protocolCredentials,
|
||||||
generatedProtocolCode,
|
generatedProtocolCode,
|
||||||
generatedProtocol,
|
generatedProtocol,
|
||||||
|
revealedCodes,
|
||||||
|
codesRevealed,
|
||||||
|
verifyDialogVisible,
|
||||||
|
verifyPassword,
|
||||||
|
verifyLoading,
|
||||||
protocolLabel,
|
protocolLabel,
|
||||||
setProtocolEnabled,
|
setProtocolEnabled,
|
||||||
generateProtocolCode,
|
generateProtocolCode,
|
||||||
copyProtocolCode,
|
copyProtocolCode,
|
||||||
revokeProtocolCode,
|
revokeProtocolCode,
|
||||||
|
openVerifyDialog,
|
||||||
|
closeVerifyDialog,
|
||||||
|
submitVerifyAndReveal,
|
||||||
|
hideRevealedCodes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,41 +2,100 @@
|
|||||||
<div class="third-party-authorization" v-loading="protocolLoading">
|
<div class="third-party-authorization" v-loading="protocolLoading">
|
||||||
<section class="authorization-section">
|
<section class="authorization-section">
|
||||||
<div class="authorization-header">
|
<div class="authorization-header">
|
||||||
<h3>邮件客户端协议</h3>
|
<div class="header-main">
|
||||||
<p class="authorization-desc">
|
<h3>???????</h3>
|
||||||
本地服务不依赖 SMTP 中继;开关只控制当前账号。
|
<p class="authorization-desc">
|
||||||
</p>
|
?? IMAP?POP3?SMTP ????????????????????????????
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
v-if="hasConfiguredProtocols && !codesRevealed"
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
@click="openVerifyDialog"
|
||||||
|
>
|
||||||
|
?????????
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else-if="codesRevealed"
|
||||||
|
type="info"
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
@click="hideRevealedCodes"
|
||||||
|
>
|
||||||
|
???????
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-alert
|
<el-alert
|
||||||
title="使用完整邮箱和协议授权码登录;授权码仅显示一次,重新开启可继续使用未撤销的授权码。"
|
title="???????????????????????????????????????????????????????????????"
|
||||||
type="warning"
|
type="info"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
show-icon
|
show-icon
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="protocol-list">
|
<div class="protocol-list">
|
||||||
<div v-for="item in protocolCredentials" :key="item.protocol" class="protocol-item">
|
<div
|
||||||
|
v-for="item in protocolCredentials"
|
||||||
|
:key="item.protocol"
|
||||||
|
class="protocol-item"
|
||||||
|
>
|
||||||
<div class="protocol-info">
|
<div class="protocol-info">
|
||||||
<div class="protocol-title">
|
<div class="protocol-title">
|
||||||
<strong>{{ protocolLabel(item.protocol) }}</strong>
|
<strong>{{ protocolLabel(item.protocol) }}</strong>
|
||||||
<el-tag size="small" :type="item.enabled ? 'success' : 'info'">
|
<el-tag size="small" :type="item.enabled ? 'success' : 'info'">
|
||||||
{{ item.enabled ? '已开启' : '已关闭' }}
|
{{ item.enabled ? '???' : '???' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-tag size="small" :type="item.configured ? 'success' : 'warning'">
|
<el-tag size="small" :type="item.configured ? 'success' : 'warning'">
|
||||||
{{ item.configured ? '已配置授权码' : '未生成授权码' }}
|
{{ item.configured ? '??????' : '??????' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<p>登录名:完整邮箱地址;密码:本协议授权码。</p>
|
<p class="protocol-guide">???????????????????????</p>
|
||||||
|
|
||||||
|
<div class="protocol-credential-slot">
|
||||||
|
<span class="slot-label">??????</span>
|
||||||
|
<template v-if="item.configured">
|
||||||
|
<template v-if="codesRevealed && revealedCodes[item.protocol]">
|
||||||
|
<code class="revealed-code">{{ revealedCodes[item.protocol] }}</code>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
class="copy-btn"
|
||||||
|
@click="copyProtocolCode(revealedCodes[item.protocol])"
|
||||||
|
>
|
||||||
|
??
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span class="masked-code">????????????????</span>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
class="reveal-btn"
|
||||||
|
@click="openVerifyDialog"
|
||||||
|
>
|
||||||
|
??????
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span class="unconfigured-tip">???????????????????</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="protocol-actions">
|
<div class="protocol-actions">
|
||||||
<el-switch
|
<el-switch
|
||||||
:model-value="item.enabled"
|
:model-value="item.enabled"
|
||||||
:loading="protocolLoading"
|
:loading="protocolLoading"
|
||||||
active-text="开启"
|
active-text="??"
|
||||||
inactive-text="关闭"
|
inactive-text="??"
|
||||||
:aria-label="`${protocolLabel(item.protocol)} 当前账号客户端访问`"
|
:aria-label="`${protocolLabel(item.protocol)} ?????????`"
|
||||||
@change="value => setProtocolEnabled(item, value)"
|
@change="value => setProtocolEnabled(item, value)"
|
||||||
/>
|
/>
|
||||||
<el-button
|
<el-button
|
||||||
@ -45,7 +104,7 @@
|
|||||||
:disabled="!item.enabled"
|
:disabled="!item.enabled"
|
||||||
@click="generateProtocolCode(item)"
|
@click="generateProtocolCode(item)"
|
||||||
>
|
>
|
||||||
{{ item.configured ? '重新生成授权码' : '生成授权码' }}
|
{{ item.configured ? '???????' : '?????' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="item.configured"
|
v-if="item.configured"
|
||||||
@ -54,39 +113,83 @@
|
|||||||
size="small"
|
size="small"
|
||||||
@click="revokeProtocolCode(item)"
|
@click="revokeProtocolCode(item)"
|
||||||
>
|
>
|
||||||
撤销授权码
|
?????
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
|
<!-- ???????? -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="verifyDialogVisible"
|
||||||
|
title="??????"
|
||||||
|
width="min(440px, calc(100vw - 32px))"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
class="verify-reveal-dialog"
|
||||||
|
@close="closeVerifyDialog"
|
||||||
|
>
|
||||||
|
<div class="verify-dialog-body">
|
||||||
|
<p class="verify-dialog-tip">
|
||||||
|
?????????????????????????????????
|
||||||
|
</p>
|
||||||
|
<el-form @submit.prevent="submitVerifyAndReveal">
|
||||||
|
<el-form-item label="??????">
|
||||||
|
<el-input
|
||||||
|
v-model="verifyPassword"
|
||||||
|
type="password"
|
||||||
|
show-password
|
||||||
|
placeholder="???????????"
|
||||||
|
autocomplete="current-password"
|
||||||
|
@keyup.enter="submitVerifyAndReveal"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="verify-dialog-footer">
|
||||||
|
<el-button @click="closeVerifyDialog">??</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="verifyLoading"
|
||||||
|
:disabled="!verifyPassword.trim()"
|
||||||
|
@click="submitVerifyAndReveal"
|
||||||
|
>
|
||||||
|
???????
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
import { usePage } from './ThirdPartyAuthorization.js'
|
import { usePage } from './ThirdPartyAuthorization.js'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
protocolLoading,
|
protocolLoading,
|
||||||
protocolCredentials,
|
protocolCredentials,
|
||||||
generatedProtocolCode,
|
revealedCodes,
|
||||||
generatedProtocol,
|
codesRevealed,
|
||||||
|
verifyDialogVisible,
|
||||||
|
verifyPassword,
|
||||||
|
verifyLoading,
|
||||||
protocolLabel,
|
protocolLabel,
|
||||||
setProtocolEnabled,
|
setProtocolEnabled,
|
||||||
generateProtocolCode,
|
generateProtocolCode,
|
||||||
copyProtocolCode,
|
copyProtocolCode,
|
||||||
revokeProtocolCode,
|
revokeProtocolCode,
|
||||||
|
openVerifyDialog,
|
||||||
|
closeVerifyDialog,
|
||||||
|
submitVerifyAndReveal,
|
||||||
|
hideRevealedCodes,
|
||||||
} = usePage()
|
} = usePage()
|
||||||
|
|
||||||
|
const hasConfiguredProtocols = computed(() =>
|
||||||
|
Array.isArray(protocolCredentials.value) &&
|
||||||
|
protocolCredentials.value.some(item => item.configured)
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped src="./ThirdPartyAuthorization.css"></style>
|
<style scoped src="./ThirdPartyAuthorization.css"></style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user