from pathlib import Path root=Path(r'C:\kefu') p=root/'admin-web/apps/web-antd/src/api/knowledge.ts' s=p.read_text(encoding='utf-8').replace(' options: { engine: string; max_messages: number };',''' total_messages: null | number; progress_percent: null | number; completion_reason: string; options: { engine: string; max_messages: number; source_name?: string; incremental?: boolean }; } export interface KnowledgePreview { available_messages: number; eligible_text_messages: number; conversations: number; total_messages: number; limited: boolean; cutoff_at: string; source_name: string;''') s += ''' export const previewKnowledgeJob = (id: number, body: Record) => requestClient.post(path('jobs/preview', id), body, { timeout: 60000 }); ''' p.write_text(s,encoding='utf-8',newline='\n') p=root/'admin-web/apps/web-antd/src/components/archive-account-selector.vue' s=p.read_text(encoding='utf-8').replace('defineProps<{ modelValue: number }>()','defineProps<{ modelValue: number; disabled?: boolean }>()') s=s.replace('const loading = ref(false);','''const loading = ref(false); const dropdownOpen = ref(false); defineExpose({ open: () => { dropdownOpen.value = true; } });''') s=s.replace('function choose(value: number) {','function choose(value: number) {\n dropdownOpen.value = false;') s=s.replace('saved === ALL_ACCOUNTS || accounts.value.some','saved === ALL_ACCOUNTS || (saved === 0 && canViewLegacy.value) || accounts.value.some') s=s.replace(' :loading="loading"',''' :loading="loading" :disabled="disabled" :open="dropdownOpen" @dropdown-visible-change="(visible) => { dropdownOpen = visible; }"''') p.write_text(s,encoding='utf-8',newline='\n') p=root/'admin-web/apps/web-antd/src/views/knowledge/center.vue' s=p.read_text(encoding='utf-8').replace('onUnmounted, reactive, ref','onUnmounted, ref') s=s.replace('InputNumber, message, Modal, Select, Space, Switch, Table, Tabs, Tag','message, Modal, Progress, Select, Space, Steps, Switch, Table, Tabs, Tag') s=s.replace('createKnowledgeJob, ', '') s=s.replace("import ArchiveAccountSelector from '#/components/archive-account-selector.vue';", """import ArchiveAccountSelector from '#/components/archive-account-selector.vue'; import KnowledgeJobWizard from './job-wizard.vue'; import KnowledgeBatchDialog from './batch-dialog.vue'; import type { BatchAction } from './batch'; import { actionProblem } from './batch';""") s=s.replace("const createOpen = ref(false);", """const createOpen = ref(false); const accountSelector = ref>(); let pendingCreate = false; const selectedKeys = ref([]); const batchOpen = ref(false); const batchAction = ref('approve'); const batchEntries = ref([]); const selectedRows = computed(() => items.value.filter((item) => selectedKeys.value.includes(item.id))); const rowSelection = computed(() => single.value && (canReview.value || canPublish.value) ? { selectedRowKeys: selectedKeys.value, onChange: (keys: (number | string)[]) => { selectedKeys.value = keys.map(String); }, } : undefined); const guideStep = computed(() => !single.value ? 0 : (overview.value?.counts.published || 0) > 0 ? 3 : (overview.value?.counts.draft || 0) + (overview.value?.counts.approved || 0) > 0 ? 2 : 1); const guideText = computed(() => !single.value ? '先选择一个客服账号,系统会预览该账号可整理的聊天范围。' : !overview.value?.source_accounts.length ? '当前账号尚无聊天归档,请先上传归档或更换账号。' : guideStep.value === 1 ? '先预览范围并生成草稿,再逐条核对来源和适用条件。' : guideStep.value === 2 ? '草稿补全适用条件后审核,通过后发布;也可以勾选列表中的多条知识集中处理。' : '已发布知识可在检索测试中验证;确认结果后开启客服使用知识。'); function batchAllowed(action: BatchAction) { return selectedRows.value.length > 0 && selectedRows.value.every((item) => !actionProblem(item, action)); } function openBatch(action: BatchAction) { batchAction.value = action; batchEntries.value = selectedRows.value.map((item) => ({ ...item })); batchOpen.value = true; } function batchFinished() { selectedKeys.value = []; void load(); } async function startCreate() { if (!single.value) { pendingCreate = true; accountSelector.value?.open(); return; } if (!overview.value) await load(); createOpen.value = true; } function created() { tab.value = 'jobs'; void load(); } function listChanged() { selectedKeys.value = []; void load(); } function cancelJob(jobId: string) { Modal.confirm({ title: '取消此整理任务?', content: '已生成的草稿会保留。取消后不再继续处理;已经开始的模型请求可能仍会计费。持续整理开关需要单独关闭。', okText: '取消任务', cancelText: '保留任务', onOk: () => jobAction(jobId, 'cancel') }); }""") start=s.index('const form = reactive(') end=s.index('\n',start) s=s[:start]+s[end:] s=s.replace("failed: '失败'", "failed: '失败', cancelled: '已取消'") s=s.replace('function accountChanged() {','async function accountChanged() {') s=s.replace(" form.source_account_id = ''; form.staff_confirmed = false; page.value = 1;\n void load();", """ selectedKeys.value = []; batchOpen.value = false; batchEntries.value = []; page.value = 1; await load(); if (pendingCreate && single.value) { pendingCreate = false; createOpen.value = true; } else pendingCreate = false;""") start=s.index('async function create() {') end=s.index('async function jobAction(',start) s=s[:start]+s[end:] s=s.replace('','') s=s.replace('

把历史聊天整理成可追溯的知识,审核发布后供客服回答时参考。

', '''

把历史聊天整理成可追溯的知识,审核发布后供客服回答时参考。

{{ guideText }}

''') s=s.replace(':disabled="!single || !canPublish || busy"', ':disabled="!single || !canPublish || busy || (!overview?.settings?.enabled && !overview?.counts.published)"') s=s.replace('''''','''''') s=s.replace('page = 1; load();', 'page = 1; listChanged();') s=s.replace(''' 已选 {{ selectedKeys.length }} 条(当前页)
''','''''') s=s.replace(''' ''',''' ''') start=s.index(' '''+s[end:] p.write_text(s,encoding='utf-8',newline='\n') print('Updated workflow UI and API types.')