Compare commits
No commits in common. "366056dcac42a3d2975f093069cbe6955f0edfd6" and "8af8ed143d6e2eed12c73e8c92568a301a081237" have entirely different histories.
366056dcac
...
8af8ed143d
@ -139,35 +139,11 @@ async function submit() {
|
||||
function onClose() { updateVisible(false); if (result.value) emit("refresh") }
|
||||
|
||||
async function copySecret(text: string) {
|
||||
// 优先用现代 Clipboard API (HTTPS / localhost 才允许)
|
||||
// frp 走 http://direv.cloud:8080 是非安全上下文, clipboard API 不可用, 需降级
|
||||
try {
|
||||
if (navigator.clipboard?.writeText) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
ElMessage.success('已复制')
|
||||
return
|
||||
}
|
||||
throw new Error('no clipboard api')
|
||||
await navigator.clipboard.writeText(text)
|
||||
ElMessage.success('已复制')
|
||||
} catch {
|
||||
// 降级方案: document.execCommand('copy') - HTTP 下也能用
|
||||
try {
|
||||
const ta = document.createElement('textarea')
|
||||
ta.value = text
|
||||
ta.style.position = 'fixed'
|
||||
ta.style.opacity = '0'
|
||||
ta.style.pointerEvents = 'none'
|
||||
document.body.appendChild(ta)
|
||||
ta.select()
|
||||
const ok = document.execCommand('copy')
|
||||
document.body.removeChild(ta)
|
||||
if (ok) {
|
||||
ElMessage.success('已复制')
|
||||
} else {
|
||||
ElMessage.warning('浏览器阻止自动复制,请手动双击输入框选中后 Cmd/Ctrl+C')
|
||||
}
|
||||
} catch {
|
||||
ElMessage.warning('浏览器阻止自动复制,请手动双击输入框选中后 Cmd/Ctrl+C')
|
||||
}
|
||||
ElMessage.error('复制失败,请手动选择文本')
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,25 +330,23 @@ function notifyContact() {
|
||||
请立即复制 connectCode 和 appSecret!关弹窗后无法再次查看
|
||||
</el-alert>
|
||||
<p>已为 {{ result.length }} 个客户生成 APIKey:</p>
|
||||
<el-table :data="result" border class="rr-apply-table rr-result-table" size="small">
|
||||
<el-table-column prop="customerName" label="公司名称" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="appId" label="AppID" min-width="180" show-overflow-tooltip>
|
||||
<el-table :data="result" border class="rr-apply-table">
|
||||
<el-table-column prop="customerName" label="公司名称" width="160" />
|
||||
<el-table-column prop="appId" label="AppID" width="280">
|
||||
<template #default="{ row }">
|
||||
<code class="rr-cell-code">{{ row.appId }}</code>
|
||||
<code>{{ row.appId }}</code>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="connectCode" label="ConnectCode" min-width="160" show-overflow-tooltip>
|
||||
<el-table-column prop="connectCode" label="ConnectCode">
|
||||
<template #default="{ row }">
|
||||
<code class="rr-cell-code rr-code-accent">{{ row.connectCode }}</code>
|
||||
<code class="rr-code-accent">{{ row.connectCode }}</code>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="expireAt" label="过期时间" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="AppSecret" min-width="200">
|
||||
<el-table-column prop="expireAt" label="过期时间" width="180" />
|
||||
<el-table-column label="AppSecret" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<div class="rr-secret-cell">
|
||||
<el-input :model-value="row.appSecret" readonly type="password" size="small" show-password />
|
||||
<el-button size="small" type="primary" @click="copySecret(row.appSecret)">复制</el-button>
|
||||
</div>
|
||||
<el-input :model-value="row.appSecret" readonly type="password" size="small" show-password style="width: 220px; margin-right: 8px;" />
|
||||
<el-button size="small" type="primary" @click="copySecret(row.appSecret)">复制</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@ -522,25 +496,4 @@ function notifyContact() {
|
||||
:deep(.rr-apply-dialog .rr-apply-table th.el-table-column--selection .cell) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Step3 结果表格: AppSecret 单元格紧凑布局 */
|
||||
.rr-secret-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 2px 0;
|
||||
}
|
||||
.rr-secret-cell .el-input {
|
||||
width: 100%;
|
||||
}
|
||||
/* code 文本省略, 不撑列宽 */
|
||||
.rr-cell-code {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -6,9 +6,10 @@
|
||||
* - 操作列(根据状态显示):
|
||||
* * 详情(任意状态,弹窗)
|
||||
* * 重置密钥(status=0 或 1)
|
||||
* * 重发二维码(status=0 或 1, W4.4 后端补 resend-qr)
|
||||
* * 撤销(status=0 或 1)
|
||||
* * 删除(status=2)
|
||||
* - 4 个弹窗:详情/撤销确认/删除确认/重置密钥
|
||||
* - 5 个弹窗:详情/撤销确认/删除确认/重置密钥/重发连接码
|
||||
* - 申请 APIKey 走 ApplyApiKeyDialog 2 步向导
|
||||
*
|
||||
* 后端端点:
|
||||
@ -39,6 +40,8 @@ interface OpenApp {
|
||||
totalCallCount: number | string
|
||||
lastUsedAt?: string | null
|
||||
createDate: string
|
||||
connectCode?: string
|
||||
connectCodeExpire?: string | number
|
||||
activatedAt?: string | null
|
||||
lastUsedIp?: string | null
|
||||
ipWhitelist?: string | null
|
||||
@ -88,6 +91,11 @@ const resetSecretVisible = ref(false)
|
||||
const resetSecretTarget = ref<OpenApp | null>(null)
|
||||
const newSecret = ref('')
|
||||
|
||||
const resendQrVisible = ref(false)
|
||||
const resendQrTarget = ref<OpenApp | null>(null)
|
||||
const newConnectCode = ref('')
|
||||
const newConnectCodeExpire = ref('')
|
||||
|
||||
function fmtDate(ts?: string | number | null) {
|
||||
if (!ts) return '--'
|
||||
const n = typeof ts === 'string' ? Date.parse(ts) : ts
|
||||
@ -119,6 +127,8 @@ async function load() {
|
||||
totalCallCount: it.totalCallCount ?? 0,
|
||||
lastUsedAt: it.lastUsedAt,
|
||||
createDate: it.createDate,
|
||||
connectCode: it.connectCode,
|
||||
connectCodeExpire: it.connectCodeExpire,
|
||||
activatedAt: it.activatedAt,
|
||||
lastUsedIp: it.lastUsedIp,
|
||||
ipWhitelist: it.ipWhitelist,
|
||||
@ -248,6 +258,29 @@ async function resetSecretConfirm() {
|
||||
}
|
||||
}
|
||||
|
||||
function resendQrHandle(row: OpenApp) {
|
||||
resendQrTarget.value = row
|
||||
newConnectCode.value = ''
|
||||
newConnectCodeExpire.value = ''
|
||||
resendQrVisible.value = true
|
||||
}
|
||||
|
||||
async function resendQrConfirm() {
|
||||
if (!resendQrTarget.value) return
|
||||
try {
|
||||
const data = await httpPost<{ connectCode: string; connectCodeExpire: string | number }>(
|
||||
`/sys/openapi/app/resend-qr/${resendQrTarget.value.id}`
|
||||
)
|
||||
newConnectCode.value = data?.connectCode || ''
|
||||
newConnectCodeExpire.value = data?.connectCodeExpire
|
||||
? String(data.connectCodeExpire)
|
||||
: ''
|
||||
ElMessage.success('连接码已重发,旧码已作废')
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.msg || e?.message || '重发失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function copyText(text: string, label = '已复制') {
|
||||
// 优先用现代 Clipboard API (HTTPS / localhost 才允许)
|
||||
try {
|
||||
@ -345,6 +378,7 @@ onMounted(() => { load(); loadCustomers() })
|
||||
:row="row"
|
||||
:actions="[
|
||||
{ label: '详情', type: 'primary', onClick: detailHandle },
|
||||
{ label: '重发二维码', type: 'primary', when: (r: any) => r.status === 0 || r.status === 1, onClick: resendQrHandle },
|
||||
{ label: '重置密钥', type: 'warning', when: (r: any) => r.status === 0 || r.status === 1, onClick: resetSecretHandle },
|
||||
{ label: '撤销', type: 'danger', divided: true, when: (r: any) => r.status === 0 || r.status === 1, onClick: revokeHandle },
|
||||
{ label: '删除', type: 'danger', divided: true, when: (r: any) => r.status === 2, onClick: deleteHandle }
|
||||
@ -368,6 +402,10 @@ onMounted(() => { load(); loadCustomers() })
|
||||
<el-descriptions-item label="状态">
|
||||
<span :class="['rr-badge', `rr-badge-${currentRow.status}`]">{{ STATUS_LABEL[currentRow.status] || currentRow.status }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="ConnectCode">
|
||||
<code class="rr-code-accent">{{ currentRow.connectCode || '—' }}</code>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="ConnectCode 过期">{{ fmtDate(currentRow.connectCodeExpire) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="权限范围">{{ currentRow.scopes }}</el-descriptions-item>
|
||||
<el-descriptions-item label="IP 白名单">{{ currentRow.ipWhitelist || '—' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="激活时间">{{ currentRow.activatedAt || '—' }}</el-descriptions-item>
|
||||
@ -445,6 +483,54 @@ onMounted(() => { load(); loadCustomers() })
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 重发连接码(二维码)弹窗 -->
|
||||
<el-dialog
|
||||
v-model="resendQrVisible"
|
||||
title="重发连接码"
|
||||
width="560"
|
||||
:style="{ borderRadius: '12px', overflow: 'hidden' }"
|
||||
align-center
|
||||
destroy-on-close
|
||||
>
|
||||
<el-alert v-if="!newConnectCode" type="warning" :closable="false" style="margin-bottom: 16px">
|
||||
<p>重发后会生成新的 connectCode,旧码立即作废</p>
|
||||
<p>新码有效期 2 小时,请尽快发给客户扫码激活</p>
|
||||
</el-alert>
|
||||
<el-alert v-if="newConnectCode" type="error" :closable="false" style="margin-bottom: 16px">
|
||||
<p>新连接码仅显示一次!请立即复制并通过安全渠道发给客户</p>
|
||||
<p>关闭弹窗后无法再次查看(只能再次重发)</p>
|
||||
</el-alert>
|
||||
<p v-if="resendQrTarget && !newConnectCode">
|
||||
app: <code class="rr-code-accent">{{ resendQrTarget.appId }}</code>
|
||||
({{ resendQrTarget.appName }})
|
||||
</p>
|
||||
<div v-if="newConnectCode" style="margin-top: 12px;">
|
||||
<p style="margin-bottom: 6px; font-size: 13px; color: var(--el-text-color-regular);">
|
||||
新的 ConnectCode: <span style="color: var(--el-text-color-secondary); font-size: 12px;">(明文,可直接选中复制)</span>
|
||||
</p>
|
||||
<el-input
|
||||
v-model="newConnectCode"
|
||||
readonly
|
||||
type="text"
|
||||
size="default"
|
||||
style="font-family: ui-monospace, monospace;"
|
||||
@focus="onSecretInputFocus"
|
||||
>
|
||||
<template #append>
|
||||
<el-button type="primary" @click="copyText(newConnectCode, '连接码已复制')">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<p v-if="newConnectCodeExpire" style="margin-top: 8px; font-size: 12px; color: var(--el-text-color-secondary);">
|
||||
过期时间: {{ fmtDate(newConnectCodeExpire) }}
|
||||
</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button v-if="!newConnectCode" @click="resendQrVisible = false">取消</el-button>
|
||||
<el-button v-if="!newConnectCode" type="primary" @click="resendQrConfirm">确认重发</el-button>
|
||||
<el-button v-if="newConnectCode" type="primary" @click="resendQrVisible = false">我已复制,关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 申请 APIKey 向导(2 步) -->
|
||||
<ApplyApiKeyDialog v-model="applyDialogVisible" @refresh="onApplySubmitted" />
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user