Files
border-red-post-nalati/frontend/src/views/IndexManagementView.vue
T

87 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { buildIndex, getActiveIndex, getIndexVersions, publishIndex, type ActiveIndex, type IndexVersion } from '../services/indexes'
import { buildVectorIndex, getActiveVectors, getVectorVersions, publishVectorIndex, type ActiveVectors, type VectorVersion } from '../services/vectors'
const textActive = ref<ActiveIndex>()
const vectorActive = ref<ActiveVectors>()
const textVersions = ref<IndexVersion[]>([])
const vectorVersions = ref<VectorVersion[]>([])
const loading = ref(true)
const buildingText = ref(false)
const buildingVector = ref<string>()
const publishing = ref<string>()
const error = ref('')
const notice = ref('')
const config = ref({ chunk_size: 800, overlap: 120 })
async function refresh() {
loading.value = true
error.value = ''
try {
const [text, texts, vectors, vectorList] = await Promise.all([getActiveIndex(), getIndexVersions(), getActiveVectors(), getVectorVersions()])
textActive.value = text
textVersions.value = texts.items
vectorActive.value = vectors
vectorVersions.value = vectorList.items
} catch (cause) {
error.value = cause instanceof Error ? cause.message : '无法读取索引状态'
} finally {
loading.value = false
}
}
async function buildText() {
error.value = ''; notice.value = ''
if (config.value.chunk_size <= 0 || config.value.overlap < 0 || config.value.overlap >= config.value.chunk_size) {
error.value = '切分长度必须大于 0,且重叠长度必须小于切分长度。'
return
}
buildingText.value = true
try {
const version = await buildIndex(config.value)
notice.value = `文本索引 ${version.id} 已构建为 ready,需确认后再发布。`
await refresh()
} catch (cause) { error.value = cause instanceof Error ? cause.message : '构建文本索引失败' } finally { buildingText.value = false }
}
async function publishText(version: IndexVersion) {
publishing.value = `text:${version.id}`; error.value = ''; notice.value = ''
try { await publishIndex(version.id, textActive.value?.generation ?? 0); notice.value = '文本索引已发布。'; await refresh() }
catch (cause) { error.value = cause instanceof Error ? cause.message : '发布文本索引失败' } finally { publishing.value = undefined }
}
async function buildVector(version: IndexVersion) {
buildingVector.value = version.id; error.value = ''; notice.value = ''
try { const created = await buildVectorIndex(version.id); notice.value = `向量索引 ${created.id} 已构建为 ready。`; await refresh() }
catch (cause) { error.value = cause instanceof Error ? cause.message : '构建向量索引失败' } finally { buildingVector.value = undefined }
}
async function publishVector(version: VectorVersion) {
publishing.value = `vector:${version.id}`; error.value = ''; notice.value = ''
try { await publishVectorIndex(version.id, vectorActive.value?.generation ?? 0); notice.value = '向量索引已发布。'; await refresh() }
catch (cause) { error.value = cause instanceof Error ? cause.message : '发布向量索引失败' } finally { publishing.value = undefined }
}
onMounted(refresh)
</script>
<template>
<main>
<RouterLink to="/">返回工作台</RouterLink>
<h1>索引管理</h1>
<p>操作直接调用管理员受保护接口;构建与发布会以服务端当前审核语料和代数校验为准。</p>
<p v-if="error" role="alert">{{ error }}</p><p v-if="notice" role="status">{{ notice }}</p>
<p v-if="loading">正在读取索引状态…</p>
<template v-else>
<section><h2>文本索引</h2><p>当前版本:{{ textActive?.active_version?.id || '未发布' }};代数:{{ textActive?.generation ?? 0 }}</p>
<form @submit.prevent="buildText"><label>切分长度<input v-model.number="config.chunk_size" type="number" min="1" required /></label><label>重叠长度<input v-model.number="config.overlap" type="number" min="0" required /></label><button :disabled="buildingText">{{ buildingText ? '构建中…' : '构建文本索引' }}</button></form>
<table><thead><tr><th>版本</th><th>状态</th><th>条目/片段</th><th>创建时间</th><th>操作</th></tr></thead><tbody><tr v-for="version in textVersions" :key="version.id"><td>{{ version.id }}</td><td>{{ version.status }}</td><td>{{ version.item_count }} / {{ version.chunk_count }}</td><td>{{ new Date(version.created_at).toLocaleString() }}</td><td><button v-if="version.status==='ready'" :disabled="publishing===`text:${version.id}`" @click="publishText(version)">发布</button><button v-if="version.status==='published'" :disabled="buildingVector===version.id" @click="buildVector(version)">{{ buildingVector===version.id?'构建中…':'构建向量索引' }}</button></td></tr><tr v-if="!textVersions.length"><td colspan="5">暂无文本索引。</td></tr></tbody></table>
</section>
<section><h2>向量索引</h2><p>当前版本:{{ vectorActive?.active_version?.id || '未发布' }};代数:{{ vectorActive?.generation ?? 0 }}</p>
<table><thead><tr><th>版本</th><th>文本版本</th><th>状态</th><th>片段数</th><th>操作</th></tr></thead><tbody><tr v-for="version in vectorVersions" :key="version.id"><td>{{ version.id }}</td><td>{{ version.text_index_version_id }}</td><td>{{ version.status }}</td><td>{{ version.chunk_count }}</td><td><button v-if="version.status==='ready'" :disabled="publishing===`vector:${version.id}`" @click="publishVector(version)">发布</button></td></tr><tr v-if="!vectorVersions.length"><td colspan="5">暂无向量索引;需先发布文本索引并启用本地 Embedding。</td></tr></tbody></table>
</section>
</template>
</main>
</template>