From 1c9b351507a32acc1a80bb31b7a7496381528080 Mon Sep 17 00:00:00 2001 From: jhy <184825448@qq.com> Date: Thu, 15 Oct 2026 20:23:19 +0800 Subject: [PATCH] feat: validate knowledge and source forms --- frontend/src/App.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7ecc2b1..e463d41 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -11,12 +11,13 @@ const nav=[{id:'chat',label:'智能问答',icon:MessageSquareText},{id:'knowledg const filtered=computed(()=>items.value.filter(x=>`${x.title} ${x.category} ${x.content}`.toLowerCase().includes(search.value.toLowerCase()))) async function refresh(){try{items.value=await getKnowledge();error.value=''}catch{error.value='无法读取后端知识库,请确认 API 和 PostgreSQL 已启动。'}} async function refreshSources(){try{sources.value=(await getSources(sourceSearch.value)).items;error.value=''}catch{error.value='无法读取来源列表,请确认 API 和 PostgreSQL 已启动。'}} -async function saveSource(){error.value='';notice.value='';try{await createSource({...sourceForm.value,...(sourceForm.value.source_url?{source_url:sourceForm.value.source_url}:{})});sourceForm.value={name:'',source_url:'',notes:''};showSourceForm.value=false;notice.value='来源已登记,可继续创建关联文档和知识条目。';await refreshSources()}catch(e){error.value=e instanceof Error?e.message:'保存失败'}} +async function saveSource(){error.value='';notice.value='';if(!sourceForm.value.name.trim()){error.value='请填写来源名称。';return}if(!validUrl(sourceForm.value.source_url)){error.value='来源网址必须是有效的 http(s) 地址。';return}try{await createSource({...sourceForm.value,name:sourceForm.value.name.trim(),...(sourceForm.value.source_url?{source_url:sourceForm.value.source_url}:{})});sourceForm.value={name:'',source_url:'',notes:''};showSourceForm.value=false;notice.value='来源已登记,可继续创建关联文档和知识条目。';await refreshSources()}catch(e){error.value=e instanceof Error?e.message:'保存失败'}} let sourceSearchTimer:number|undefined watch(sourceSearch,()=>{window.clearTimeout(sourceSearchTimer);sourceSearchTimer=window.setTimeout(refreshSources,260)}) onMounted(async()=>{try{await getHealth();apiStatus.value='已连接'}catch{apiStatus.value='未连接'}await Promise.all([refresh(),refreshSources()])}) async function send(){const q=question.value.trim();if(!q||busy.value)return;messages.value.push({role:'user',content:q});question.value='';busy.value=true;try{const r=await askQuestion(q);messages.value.push({role:'assistant',content:r.answer,sources:r.sources})}catch{messages.value.push({role:'assistant',content:'暂时无法连接后端 API。请确认 FastAPI 已启动。'})}finally{busy.value=false}} -async function save(){error.value='';notice.value='';try{await createKnowledge({...form.value,...(form.value.source_url?{source_url:form.value.source_url}:{})});form.value={title:'',category:'旅游资源',content:'',source_url:''};showForm.value=false;notice.value='已保存为待审核条目,尚未进入检索索引。';await refresh()}catch(e){error.value=e instanceof Error?e.message:'保存失败'}} +function validUrl(value:string){try{if(value)new URL(value);return true}catch{return false}} +async function save(){error.value='';notice.value='';if(!form.value.title.trim()||!form.value.content.trim()){error.value='请填写知识标题和正文。';return}if(!validUrl(form.value.source_url)){error.value='来源网址必须是有效的 http(s) 地址。';return}try{await createKnowledge({...form.value,title:form.value.title.trim(),content:form.value.content.trim(),...(form.value.source_url?{source_url:form.value.source_url}:{})});form.value={title:'',category:'旅游资源',content:'',source_url:''};showForm.value=false;notice.value='已保存为待审核条目,尚未进入检索索引。';await refresh()}catch(e){error.value=e instanceof Error?e.message:'保存失败'}}