细节优化
This commit is contained in:
@@ -82,10 +82,55 @@ async function loadDocs() {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 链接有效期
|
||||
const expiryOptions = [
|
||||
{ label: '10 分钟', value: 10 },
|
||||
{ label: '30 分钟', value: 30 },
|
||||
{ label: '1 小时', value: 60 },
|
||||
{ label: '3 小时', value: 180 },
|
||||
{ label: '24 小时', value: 1440 },
|
||||
{ label: '长期有效', value: 0 },
|
||||
]
|
||||
const linkExpiry = ref<{ expires_at: string | null; is_expired: boolean }>({
|
||||
expires_at: null,
|
||||
is_expired: false,
|
||||
})
|
||||
const expiryText = computed(() => {
|
||||
if (linkExpiry.value.is_expired) return '⛔ 已失效'
|
||||
if (!linkExpiry.value.expires_at) return '♾️ 长期有效'
|
||||
return `⏱️ 有效期至 ${linkExpiry.value.expires_at}`
|
||||
})
|
||||
|
||||
async function loadLink() {
|
||||
try {
|
||||
const { data } = await apiClient.get(`/knowledge-bases/${kbId}/link`)
|
||||
aiUrl.value = `${window.location.origin}/k/${data.token}`
|
||||
linkExpiry.value = {
|
||||
expires_at: data.expires_at,
|
||||
is_expired: data.is_expired,
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
// 设置链接有效期(0 = 长期有效,需二次确认)
|
||||
async function handleExpiryChange(val: number) {
|
||||
if (val === 0) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'长期有效的链接一旦泄露,任何持有该链接的人都可永久访问此知识库,存在泄露风险。确定设为长期有效?',
|
||||
'⚠️ 泄露风险提示',
|
||||
{ type: 'warning', confirmButtonText: '仍要设为长期有效', cancelButtonText: '取消' },
|
||||
)
|
||||
} catch {
|
||||
return // 用户取消
|
||||
}
|
||||
}
|
||||
try {
|
||||
await apiClient.post(`/knowledge-bases/${kbId}/set-expiry`, {
|
||||
expires_in_minutes: val === 0 ? null : val,
|
||||
})
|
||||
ElMessage.success('链接有效期已更新。')
|
||||
loadLink()
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
@@ -121,6 +166,12 @@ async function handleUpload(options: any) {
|
||||
uploading.value = false
|
||||
}
|
||||
|
||||
// 打开添加文本对话框(预填左侧选中的目录,对话框内的选择优先)
|
||||
function openTextDialog() {
|
||||
textForm.value.category_id = selectedCategoryId.value
|
||||
showTextDialog.value = true
|
||||
}
|
||||
|
||||
// 创建文本内容
|
||||
async function handleCreateText() {
|
||||
if (!textForm.value.title.trim() || !textForm.value.content.trim()) {
|
||||
@@ -134,7 +185,7 @@ async function handleCreateText() {
|
||||
title: textForm.value.title,
|
||||
content: textForm.value.content,
|
||||
content_format: textForm.value.content_format,
|
||||
category_id: selectedCategoryId.value || textForm.value.category_id,
|
||||
category_id: textForm.value.category_id,
|
||||
})
|
||||
ElMessage.success('文本内容已创建!')
|
||||
showTextDialog.value = false
|
||||
@@ -255,6 +306,16 @@ function getCategoryName(catId: string) {
|
||||
const cat = allCategoriesFlat.value.find((c: any) => c.id === catId)
|
||||
return cat ? cat.name : '未分类'
|
||||
}
|
||||
|
||||
// AI 链接打码:显示域名,token 部分用 • 代替
|
||||
const maskedAiUrl = computed(() => {
|
||||
if (!aiUrl.value) return '尚未生成'
|
||||
const idx = aiUrl.value.indexOf('/k/')
|
||||
if (idx === -1) return '••••••••'
|
||||
const prefix = aiUrl.value.slice(0, idx + 3)
|
||||
const tokenLen = aiUrl.value.length - idx - 3
|
||||
return prefix + '•'.repeat(tokenLen)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -315,22 +376,40 @@ function getCategoryName(catId: string) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 链接 -->
|
||||
<!-- AI 链接(打码显示,只能复制获取) -->
|
||||
<el-card style="margin-bottom: 16px" shadow="hover">
|
||||
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap">
|
||||
<span style="font-weight: bold; font-size: 14px">AI 链接:</span>
|
||||
<el-input :model-value="aiUrl" readonly style="flex: 1; min-width: 200px" size="large">
|
||||
<template #append>
|
||||
<el-button @click="handleCopyLink">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<span style="flex: 1; min-width: 200px; font-size: 14px; color: #666; font-family: monospace; user-select: none">
|
||||
{{ maskedAiUrl }}
|
||||
</span>
|
||||
<el-select
|
||||
:model-value="null"
|
||||
placeholder="⏱️ 设置有效期"
|
||||
style="width: 150px"
|
||||
@change="(val: number) => handleExpiryChange(val)"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in expiryOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="handleCopyLink" size="large">📋 复制 AI 链接</el-button>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; margin-top: 8px; font-size: 12px">
|
||||
<span :style="{ color: linkExpiry.is_expired ? '#f56c6c' : '#67c23a', fontWeight: linkExpiry.is_expired ? 'bold' : 'normal' }">
|
||||
{{ expiryText }}
|
||||
</span>
|
||||
<span style="color: #999">🔒 链接即访问凭证,已隐藏显示;仅可通过复制按钮获取。请勿公开传播。</span>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 手机端目录按钮 + 操作按钮 -->
|
||||
<div class="mobile-actions">
|
||||
<el-button @click="showMobileSidebar = true" style="flex: 1">📁 目录</el-button>
|
||||
<el-button @click="showTextDialog = true" style="flex: 1">✏️ 添加文本</el-button>
|
||||
<el-button @click="openTextDialog" style="flex: 1">✏️ 添加文本</el-button>
|
||||
<el-upload :show-file-list="false" :http-request="handleUpload" accept=".docx,.pdf" style="flex: 1">
|
||||
<el-button type="primary" :loading="uploading" style="width: 100%">📤 上传</el-button>
|
||||
</el-upload>
|
||||
@@ -346,7 +425,7 @@ function getCategoryName(catId: string) {
|
||||
</el-tag>
|
||||
</span>
|
||||
<div class="desktop-actions">
|
||||
<el-button @click="showTextDialog = true">✏️ 添加文本</el-button>
|
||||
<el-button @click="openTextDialog">✏️ 添加文本</el-button>
|
||||
<el-upload :show-file-list="false" :http-request="handleUpload" accept=".docx,.pdf">
|
||||
<el-button type="primary" :loading="uploading">📤 上传文档</el-button>
|
||||
</el-upload>
|
||||
|
||||
Reference in New Issue
Block a user