Files
k3GPT/main/ui/create.html

1052 lines
56 KiB
HTML
Raw Normal View History

2025-11-19 19:43:15 +08:00
<!DOCTYPE html>
<html>
<link href="wang/wangedit.css" rel="stylesheet" />
<style>
#editor—wrapper {
border: 1px solid #ccc;
z-index: 10; /* 按需定义 */
}
#toolbar-container {
border-bottom: 1px solid #ccc;
}
</style>
<style>
/* WebKit 浏览器的滚动条 */
::-webkit-scrollbar {
width: 4px; /* 滚动条宽度 */
height: 10px;
}
::-webkit-scrollbar-track {
background: #f1f1f1; /* 轨道背景色 */
}
::-webkit-scrollbar-thumb {
background: #888; /* 滚动条颜色 */
border-radius: 4px; /* 圆角 */
}
::-webkit-scrollbar-thumb:hover {
background: #555; /* 滚动条悬停时的颜色 */
}
html, body {
overflow: hidden;
}
/* 基础样式 */
h1, h2, h3, h4, h5 {
font-weight: bold;
line-height: 1.2;
margin-top: 1.5em;
margin-bottom: 0.5em;
}
</style>
<!--<script src="https://cdn.tailwindcss.com"></script>-->
<link rel="stylesheet" href="tw.k3gpt.css">
<script src="js/axios.min.js"></script>
<script src="js/marked.js"></script>
<script defer src="js/alpine.js"></script>
<script defer src="js/alp_unit.js"></script>
<body>
<!-- 编辑器的主窗口 -->
<div x-data="$store.ai_job" x-init="init2" class="overflow-hidden">
<!-- 查询器 四角弧形的矩形容器 -->
<div class="flex rounded-lg bg-white shadow-lg p-2 space-x-4 mb-4">
<!-- 横向排列的四个元素 -->
<!--<div class="flex items-center justify-center text-neutral-600 dark:text-zinc-200">标题</div>-->
<div class="relative flex w-full max-w-xs flex-col gap-1 text-neutral-600 dark:text-zinc-200">
<input x-model="title" type="text" class="w-full rounded border border-zinc-300 bg-zinc-100 py-2 pl-2 pr-2 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:focus-visible:outline-sky-600" name="search" placeholder="输入标题" aria-label="search"/>
</div>
<!--<div class="flex items-center justify-center text-neutral-600 dark:text-zinc-200">分类</div>-->
<div class="relative flex w-full max-w-s1 flex-col gap-1 text-neutral-600 dark:text-zinc-200">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="absolute pointer-events-none right-2 top-2 h-5 w-5">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
</svg>
<select id="catalog" x-model="catalog" class="w-full appearance-none rounded border border-zinc-300 bg-zinc-100 px-4 py-2 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:focus-visible:outline-sky-600">
<template x-for="(item,index) in catalogs" :key="index">
<option x-text="item" :selected="catalog==item">词条</option>
</template>
</select>
</div>
<button id="save" @click="do_save" type="button" class="cursor-pointer whitespace-nowrap rounded bg-sky-700 px-4 py-2 text-sm font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">保存</button>
<button id="save_text" @click="do_save_text" type="button" class="cursor-pointer whitespace-nowrap rounded bg-zinc-200 px-4 py-2 text-sm font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">原始</button>
<button @click="window.open('view.html?id='+baike_id)" type="button" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded bg-blue-700 px-4 py-2 text-sm font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
预览
</button>
<button @click="gen_word" type="button" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded bg-sky-700 px-4 py-2 text-sm font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
DOC
</button>
<button @click="gen_pdf" type="button" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded bg-sky-700 px-4 py-2 text-sm font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
PDF
</button>
<button @click="gen_ppt" type="button" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded bg-green-700 px-4 py-2 text-sm font-medium tracking-wide text-white transition text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
PPT
<svg @click="gen_ppt2($event)" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="size-5 fill-white dark:fill-white hover:bg-zinc-900/10" fill="currentColor">
<path fill-rule="evenodd" d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z" clip-rule="evenodd" />
</svg>
</button>
<button @click="gen_poster" type="button" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded bg-green-700 px-4 py-2 text-sm font-medium tracking-wide text-white transition text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
海报
<svg @click="gen_poster2($event)" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="size-5 fill-white dark:fill-white hover:bg-zinc-900/10" fill="currentColor">
<path fill-rule="evenodd" d="M12 3.75a.75.75 0 01.75.75v6.75h6.75a.75.75 0 010 1.5h-6.75v6.75a.75.75 0 01-1.5 0v-6.75H4.5a.75.75 0 010-1.5h6.75V4.5a.75.75 0 01.75-.75z" clip-rule="evenodd" />
</svg>
</button>
<button @click="successModalIsOpen=true" class="rounded-full p-2.5 hover:text-green-700 bg-zinc-900/10 text-neutral-600 focus:outline-none focus-visible:text-neutral-600 focus-visible:zinc-300 focus-visible:outline-offset-0 focus-visible:outline-sky-700 active:bg-zinc-900/5 active:-outline-offset-2 dark:text-zinc-200/75 dark:hover:bg-zinc-50/10 dark:hover:text-zinc-200 dark:focus-visible:text-zinc-200 dark:focus-visible:outline-sky-600 dark:active:bg-zinc-50/5" title="使用技巧" aria-label="使用技巧" >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 shrink-0" aria-hidden="true">
<path fill-rule="evenodd" d="M10 2c-2.236 0-4.43.18-6.57.524C1.993 2.755 1 4.014 1 5.426v5.148c0 1.413.993 2.67 2.43 2.902 1.168.188 2.352.327 3.55.414.28.02.521.18.642.413l1.713 3.293a.75.75 0 0 0 1.33 0l1.713-3.293a.783.783 0 0 1 .642-.413 41.102 41.102 0 0 0 3.55-.414c1.437-.231 2.43-1.49 2.43-2.902V5.426c0-1.413-.993-2.67-2.43-2.902A41.289 41.289 0 0 0 10 2ZM6.75 6a.75.75 0 0 0 0 1.5h6.5a.75.75 0 0 0 0-1.5h-6.5Zm0 2.5a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5h-3.5Z" clip-rule="evenodd"/>
</svg>
</button>
</div>
<!-- 进度条-->
<template x-if="progressbar">
<div class="flex h-4 w-full overflow-hidden rounded-radius bg-slate-100 dark:bg-surface-dark-alt" role="progressbar" aria-label="default progress bar" x-bind:aria-valuenow="currentVal" x-bind:aria-valuemin="minVal" x-bind:aria-valuemax="maxVal">
<div class="h-full rounded-radius bg-sky-700 dark:bg-primary-dark" x-bind:style="`width: ${calcPercentage(minVal, maxVal, currentVal)}%`"></div>
</div>
</template>
<!-- 编辑器部分 -->
<div id="editor—wrapper">
<div id="toolbar-container"><!-- 工具栏 --></div>
<div id="editor-container" class="h-[75vh]"><!-- 编辑器 --></div>
</div>
<!-- AI创作窗口 -->
<template x-if="modalIsOpen">
<div x-cloak x-transition.opacity.duration.200ms x-trap.inert.noscroll="modalIsOpen" @keydown.esc.window="modalIsOpen = false" class="fixed inset-0 z-50 flex items-end justify-center p-4 sm:items-center lg:p-4" role="dialog" aria-modal="true" aria-labelledby="defaultModalTitle">
<!-- Modal Dialog-->
<div x-show="modalIsOpen" x-transition:enter="transition ease-out duration-200 delay-100 motion-reduce:transition-opacity" x-transition:enter-start="opacity-0 scale-50" x-transition:enter-end="opacity-100 scale-100" class="flex w-3/5 max-h-[80vh] flex-col gap-4 overflow-hidden rounded-xl border border-slate-300 bg-white text-slate-700 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300">
<!-- Dialog Header -->
<div class="flex items-center justify-between border-b border-slate-300 bg-slate-100/60 px-2 dark:border-slate-700 dark:bg-slate-900/20">
<h4 id="defaultModalTitle" class="font-semibold tracking-wide text-black dark:text-white">AI创作</h4>
<!-- 搜索框-->
<div class="flex justify-center w-full max-w-md gap-2 border-zinc-300 dark:border-zinc-700" role="tablist" aria-label="tab options">
<div class="relative py-2 flex w-full max-w-md flex-col gap-1 text-neutral-600 dark:text-zinc-200">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="absolute left-2 top-1/2 size-5 -translate-y-1/2 text-neutral-600/50 dark:text-zinc-200/50" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input x-model="search" x-on:input.debounce.900ms="fetchSomething()" placeholder="搜索全站文档,选定一个文档,来进行精准对话" type="search" class="w-full border border-zinc-300 rounded bg-zinc-50 px-2 py-1.5 pl-9 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-900/50 dark:focus-visible:outline-sky-600" name="search" aria-label="Search" placeholder="Search"/>
</div>
</div>
<button @click="modalIsOpen = false" aria-label="close modal">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="1.4" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<!-- 创作完成后的方式-->
<div class="flex justify-center gap-1">
<!-- 下拉框-->
<div x-data="{
options: [
{
value: 'AI续写',
label: 'AI续写',
},
{
value: 'AI摘要',
label: 'AI摘要',
},
{
value: 'AI大纲',
label: 'AI大纲',
},
{
value: 'AI丰富',
label: 'AI丰富',
},
{
value: 'AI优化',
label: 'AI优化',
},
{
value: '拼写检查',
label: '拼写检查',
},
{
value: '智能翻译',
label: '智能翻译',
},
{
value: '指令模式',
label: '指令模式',
},
],
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
highlightFirstMatchingOption(pressedKey) {
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
}" class="w-60 flex flex-col gap-1" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<div class="relative">
<!-- Trigger Button -->
<button type="button" role="combobox" class="inline-flex w-full items-center justify-between gap-2 whitespace-nowrap border-slate-300 bg-slate-100 px-4 py-2 text-sm font-medium capitalize tracking-wide text-slate-700 transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 dark:border-slate-700 dark:bg-slate-800/50 dark:text-slate-300 dark:focus-visible:outline-blue-600 rounded-xl border" aria-haspopup="listbox" aria-controls="industriesList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="text-sm font-normal" x-text="selectedOption ? selectedOption.value : '选择创作模式'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="ai_mode" name="ai_mode" type="text" x-ref="hiddenTextField" hidden/>
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="industriesList" class="absolute z-10 left-0 top-11 flex max-h-44 w-full flex-col overflow-hidden overflow-y-auto border-slate-300 bg-slate-100 py-1.5 dark:border-slate-700 dark:bg-slate-800 rounded-xl border" role="listbox" aria-label="industries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="combobox-option inline-flex cursor-pointer justify-between gap-6 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/5 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0" >
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen Reader 'Selected' Indicator -->
<span class="sr-only" x-text="selectedOption == item ? 'selected' : null"></span>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="size-4" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<!-- Success Button With Icon -->
<button type="button" x-on:click="do_gen" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded-xl bg-green-600 px-4 py-2 text-xs font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-green-600 dark:text-white dark:focus-visible:outline-green-600">
AI生成
</button>
<!-- Primary Button With Icon -->
<button type="button" x-on:click="do_insert" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded-xl bg-blue-700 px-4 py-2 text-xs font-medium tracking-wide text-slate-100 transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-blue-600 dark:text-slate-100 dark:focus-visible:outline-blue-600">
开头插入
</button>
<!-- Info Button With Icon -->
<button type="button" x-on:click="do_cur_insert" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded-xl bg-sky-600 px-4 py-2 text-xs font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
当前插入
</button>
<!-- Info Button With Icon -->
<button type="button" x-on:click="do_replace" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded-xl bg-sky-600 px-4 py-2 text-xs font-medium tracking-wide text-white transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-600 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">
文本替换
</button>
<button type="button" x-on:click="do_end_insert" class="cursor-pointer inline-flex justify-center items-center gap-2 whitespace-nowrap rounded-xl bg-indigo-700 px-4 py-2 text-xs font-medium tracking-wide text-slate-100 transition hover:opacity-75 text-center focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-700 active:opacity-100 active:outline-offset-0 disabled:opacity-75 disabled:cursor-not-allowed dark:bg-indigo-600 dark:text-slate-100 dark:focus-visible:outline-indigo-600">
末尾插入
</button>
</div>
<!-- Dialog Body -->
<div id="ai_creator" x-show="!search_list" class="px-4 flex-grow overflow-y-auto min-h-[40vh] min-w-[24rem] md:min-w-[32rem]" x-html="message"> </div>
<div x-show="search_list" class="flex flex-col gap-2 px-4 flex-grow overflow-y-auto min-h-[40vh] min-w-[24rem]">
<template x-for="(item,index) in aItems" key="index">
<label class="flex w-fit min-w-[14rem] cursor-pointer items-center justify-start gap-2 rounded border border-zinc-300 bg-zinc-100 px-4 py-2 font-medium text-neutral-600 has-[:disabled]:opacity-75 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200">
<input type="radio" name="radioDefault" :value="index" x-model="search_checked" class="before:content[''] relative h-4 w-4 appearance-none rounded-full border border-zinc-300 bg-zinc-50 before:invisible before:absolute before:left-1/2 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:bg-white checked:border-sky-700 checked:bg-sky-700 checked:before:visible focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-zinc-500 checked:focus:outline-sky-700 disabled:cursor-not-allowed dark:border-zinc-700 dark:bg-zinc-900 dark:before:bg-white dark:checked:border-sky-600 dark:checked:bg-sky-600 dark:focus:outline-zinc-500 dark:checked:focus:outline-sky-600">
<span class="text-sm" x-text="item.base+':'+item.f_name">Mac</span>
</label>
</template>
</div>
<!-- Dialog Footer -->
<div class="flex flex-col-reverse justify-between gap-2 border-t border-slate-300 bg-slate-100/60 p-2 dark:border-slate-700 dark:bg-slate-900/20 sm:flex-row sm:items-center md:justify-end">
<!-- AI input-->
<div class="relative w-full">
<label for="aiPromt" for="aiPromt" class="sr-only">ai prompt</label>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" aria-hidden="true" class="absolute left-3 top-1/2 size-4 -translate-y-1/2 fill-blue-700 dark:fill-blue-600">
<path fill-rule="evenodd" d="M5 4a.75.75 0 0 1 .738.616l.252 1.388A1.25 1.25 0 0 0 6.996 7.01l1.388.252a.75.75 0 0 1 0 1.476l-1.388.252A1.25 1.25 0 0 0 5.99 9.996l-.252 1.388a.75.75 0 0 1-1.476 0L4.01 9.996A1.25 1.25 0 0 0 3.004 8.99l-1.388-.252a.75.75 0 0 1 0-1.476l1.388-.252A1.25 1.25 0 0 0 4.01 6.004l.252-1.388A.75.75 0 0 1 5 4ZM12 1a.75.75 0 0 1 .721.544l.195.682c.118.415.443.74.858.858l.682.195a.75.75 0 0 1 0 1.442l-.682.195a1.25 1.25 0 0 0-.858.858l-.195.682a.75.75 0 0 1-1.442 0l-.195-.682a1.25 1.25 0 0 0-.858-.858l-.682-.195a.75.75 0 0 1 0-1.442l.682-.195a1.25 1.25 0 0 0 .858-.858l.195-.682A.75.75 0 0 1 12 1ZM10 11a.75.75 0 0 1 .728.568.968.968 0 0 0 .704.704.75.75 0 0 1 0 1.456.968.968 0 0 0-.704.704.75.75 0 0 1-1.456 0 .968.968 0 0 0-.704-.704.75.75 0 0 1 0-1.456.968.968 0 0 0 .704-.704A.75.75 0 0 1 10 11Z" clip-rule="evenodd" />
</svg>
<input id="aiPromt" x-model="query" x-on:keydown.enter="chat_gen" type="text" class="w-full border-outline bg-slate-100 border border-slate-300 rounded-xl px-2 py-2 pl-10 pr-24 text-sm text-slate-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-slate-700 dark:bg-slate-800/50 dark:text-slate-300 dark:focus-visible:outline-blue-600" name="prompt" placeholder="Ask AI ..." />
<button type="button" x-on:click="chat_gen" class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer bg-blue-700 rounded-xl px-2 py-1 text-xs tracking-wide text-slate-100 transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 active:opacity-100 active:outline-offset-0 dark:bg-blue-600 dark:text-slate-100 dark:focus-visible:outline-blue-600">Go</button>
</div>
</div>
</div>
</div>
</template>
<!-- 通知窗口-->
<!-- Success Alert -->
<template x-if="save_fin">
<div class="fixed bottom-4 left-0 right-0 z-30 mx-auto max-w-2xl w-full overflow-hidden rounded border border-green-700 bg-zinc-50 text-neutral-600 dark:bg-zinc-900 dark:text-zinc-200" role="alert">
<div class="flex w-full items-center gap-2 bg-green-700/10 p-4">
<div class="bg-green-700/15 text-green-700 rounded-full p-1" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-6" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-2">
<h3 class="text-sm font-semibold text-green-700" x-text="doc_length">保存成功</h3>
<p class="text-xs font-medium sm:text-sm">窗口会在3秒后自动消失你也可以手工关闭</p>
</div>
<button @click="save_fin=false" class="ml-auto" aria-label="dismiss alert">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="2.5" class="w-4 h-4 shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<template x-if="chat_modalIsOpen">
<!-- PPT对话设置窗口 -->
<div x-transition.opacity.duration.200ms x-trap.inert.noscroll="chat_modalIsOpen" x-on:keydown.esc.window="chat_modalIsOpen = ppt_modalIsOpen = poster_modalIsOpen = false" x-on:click.self="chat_modalIsOpen = ppt_modalIsOpen = poster_modalIsOpen = false" class="fixed inset-0 z-30 flex w-full h-100vh items-start justify-end bg-black/20 backdrop-blur-md" role="dialog" aria-modal="true" aria-labelledby="defaultModalTitle">
<!-- Modal Dialog -->
<div x-show="chat_modalIsOpen" x-transition:enter="transition ease-out duration-200 delay-100 motion-reduce:transition-opacity" x-transition:enter-start="opacity-0 scale-50" x-transition:enter-end="opacity-100 scale-100" class="flex w-100 flex-col gap-4 overflow-hidden rounded border border-zinc-300 bg-zinc-50 text-neutral-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200">
<!-- Dialog Header -->
<div class="flex items-center justify-between border-b border-zinc-300 bg-zinc-100/60 p-4 dark:border-zinc-700 dark:bg-zinc-900/20">
<h3 id="defaultModalTitle" class="font-semibold tracking-wide text-neutral-900 dark:text-zinc-50">生成助手</h3>
<button x-on:click="chat_modalIsOpen = ppt_modalIsOpen = poster_modalIsOpen = false" aria-label="close modal">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="1.4" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<!-- Dialog Body -->
<template x-if="ppt_modalIsOpen">
<div id="ppt_ai_dialog" alp-unit="ppt_chat.aps"></div>
</template>
<template x-if="poster_modalIsOpen">
<div id="poster_ai_dialog" alp-unit="poster_chat.aps"></div>
</template>
</div>
</div>
</template>
<template x-if="successModalIsOpen">
<div x-cloak x-show="successModalIsOpen" x-transition.opacity.duration.200ms x-trap.inert.noscroll="successModalIsOpen" x-on:keydown.esc.window="successModalIsOpen = false" x-on:click.self="successModalIsOpen = false" class="fixed inset-0 z-30 flex items-end justify-center bg-black/20 p-4 pb-8 backdrop-blur-md sm:items-center lg:p-8" role="dialog" aria-modal="true" aria-labelledby="successModalTitle">
<!-- Modal Dialog -->
<div x-show="successModalIsOpen" x-transition:enter="transition ease-out duration-200 delay-100 motion-reduce:transition-opacity" x-transition:enter-start="opacity-0 scale-50" x-transition:enter-end="opacity-100 scale-100" class="flex max-w-lg flex-col gap-4 overflow-hidden rounded-radius border border-outline bg-surface text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark">
<!-- Dialog Header -->
<div class="flex items-center justify-between border-b border-outline bg-surface-alt/60 px-4 py-2 dark:border-outline-dark dark:bg-surface-dark/20">
<div class="flex items-center justify-center rounded-full bg-success/20 text-success p-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-6" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
</svg>
</div>
<h3 id="successModalTitle" class="mb-2 font-semibold tracking-wide text-on-surface-strong dark:text-on-surface-dark-strong">
<span x-text="share_title"></span>
PPT和海报编辑技巧</h3>
<button x-on:click="successModalIsOpen = false" aria-label="close modal">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="1.4" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<!-- Dialog Body -->
<div class="px-4">
<h3 class="mb-2 font-semibold tracking-wide text-on-surface-strong">标记使用大全</h3>
<p>H1 &nbsp;&nbsp;是PPT和海报的标题(必选)</p>
<p>H3 &nbsp;&nbsp;是PPT和海报的副标题(在第一个H2之前,必选)</p>
<p>H2 &nbsp;&nbsp;是PPT单页主标题或海报的章节主标题(必选)H2在文中会有一横线作为区分</p>
<p>H3 &nbsp;&nbsp;是PPT单页副标题或海报的章节副标题(可选,可以有多个)</p>
<p>H4 &nbsp;&nbsp;是海报的摘要内容,一个海报一般只有一个(可选)</p>
<p>H5 &nbsp;&nbsp;是PPT内容或海报内容一级标题</p>
<p>li &nbsp;&nbsp;是PPT内容或海报内容二级标题可以是有序或无序</p>
<p>p &nbsp;&nbsp;是PPT内容或海报内容三级标题</p>
<p>hr &nbsp;&nbsp;横线表示海报的尾部后续可以添加图片和h5以及p</p>
<p>图片可以添加描述,用做图片的标题显示</p>
</div>
<!-- Dialog Footer -->
<div class="flex items-center justify-center border-outline p-4 dark:border-outline-dark">
<button x-on:click="successModalIsOpen = false" type="button" class="w-full whitespace-nowrap rounded-radius border border-success bg-success px-4 py-2 text-center text-sm font-semibold tracking-wide text-on-success transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-success active:opacity-100 active:outline-offset-0">开始体验</button>
</div>
</div>
</div>
</template>
</div>
</div>
</body>
<script src="wang/wangedit.js"></script>
<script>
const { createEditor, createToolbar,Boot } = window.wangEditor
class MyButtonMenu {
constructor() {
this.title = 'AI' // 自定义菜单标题
// this.iconSvg = '<svg>...</svg>' // 可选
this.tag = 'button'
}
getValue(editor) { // JS 语法
return ''
}
//是否需要激活
isActive(editor) { // JS 语法
return false
}
//是否禁用
isDisabled(editor) { // JS 语法
return false
}
//启用时的动作
exec(editor, value) { // JS 语法
if (this.isDisabled(editor)) return
var text = editor.getSelectionText();
Alpine.store('ai_job').open(text);
//editor.insertText(value) // value 即 this.value(editor) 的返回值
}
}
const menu1Conf = {
key: 'menu-ai', // 定义 menu key :要保证唯一、不重复(重要)
factory() {
return new MyButtonMenu() // 把 `YourMenuClass` 替换为你菜单的 class
},
}
Boot.registerMenu(menu1Conf)
const editorConfig = {
placeholder: '开始输入',
MENU_CONF: {
//上传图片
uploadImage: {
server: '/api/uploadimg',
fieldName: 'file',
base64LimitSize: 2 * 1024,// 2k以下插入 base64
// 单个文件的最大体积限制,默认为 2M
maxFileSize: 50 * 1024 * 1024, // 50M
maxNumberOfFiles: 50, //最多可上传几个文件
},
//上传视频
uploadVideo: {
server: '/api/uploadvideo',
fieldName: 'file',
maxNumberOfFiles: 50, //最多可上传几个文件
// 单个文件的最大体积限制,默认为 10M
maxFileSize: 1000 * 1024 * 1024, // 5M
}
},
onChange(editor) {
const html = editor.getHtml()
//console.log('editor content', html)
// 也可以同步到 <textarea>
},
}
const editor = createEditor({
selector: '#editor-container',
html: '',
config: editorConfig,
mode: 'default', // or 'simple'
})
const toolbarConfig = {}
toolbarConfig.insertKeys = {
index: 0, // 插入的位置,基于当前的 toolbarKeys
keys: ['menu-ai'],
}
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default', // or 'simple'
})
console.info(editor.getConfig().hoverbarKeys);
editor.getConfig().hoverbarKeys.text.menuKeys.unshift("menu-ai");
const url = new URL(window.location.href);
var baike_id = url.searchParams.get('id');
if (baike_id!==undefined){
baike_id=parseInt(baike_id);
}
//可以再外部js调用的一种方式
//A使用Alpine.store('ai_job').open();
document.addEventListener('alpine:init', () => {
Alpine.store('ai_job', {
title:"自动标题",
catalog:"词条",
modalIsOpen: false, //AI创作的窗口
chat_modalIsOpen: false, //ppt和海报对话设置的窗口
ppt_modalIsOpen: false, //ppt对话设置的窗口
poster_modalIsOpen: false, //海报对话设置的窗口
successModalIsOpen: false, //技巧窗口
save_fin: false,
search: "",
search_list: false,
search_checked: "-1",
aItems:[],
progressbar: false, //进度条,显示
currentVal: 20 , //进度条默认值
minVal: 0 ,//进度条最新值
maxVal: 100, //进度条做大值
calcPercentage(min, max, val){return ((val-min)/(max-min))*100},
init(){
//动态加载,AI对话框
this.$watch('ppt_modalIsOpen',value => {
if (value){
this.$nextTick(() => {
var dom = document.getElementById("ppt_ai_dialog")
scan_alp_units(dom);
});
}
});
//动态加载,AI对话框
this.$watch('poster_modalIsOpen',value => {
if (value){
this.$nextTick(() => {
var dom = document.getElementById("poster_ai_dialog")
scan_alp_units(dom);
});
}
});
},//初始化函数
tips: "<span class='bg-green-600 text-white px-4 py-1 text-xs font-medium'>提示:</span>"
+"&nbsp;&nbsp;我是人工智能助手,我可以帮你完成文档创作。请先选择创作的模式然后点击上方的"
+"<span class='bg-green-600 rounded-xl text-white px-4 py-1 text-xs font-medium opacity-50'>AI生成</span>按钮,就会自动生成内容,满意的话可以选择插入或替换文本,不满意的话可以再次点击按钮重新生成。<br><br>"
+"AI续写 根据上文的内容自动续写后续的内容,可以不选中内容<br>"
+"AI摘要 根据上文的内容自动编写一个摘要,可以不选中内容<br>"
+"AI大纲 根据选中的文字一般是标题来进行思维脑图式的创作<br>"
+"AI丰富 对选中的文字内容进行扩写,使其内容变长<br>"
+"AI优化 对选中的文字内容进行缩写,使其更加精简<br>"
+"拼写检查: 对选中的文字的内容进行错别字等检查和修复<br>"
+"智能翻译: 中文翻译成引文或英文翻译成中文<br>"
+"<br>也可以用知识对话的方式来生成所需的内容<br>"
,
message: "",
context: "",
catalogs: "",
query: "",
doc_length:"",
x: 0,
y: 0,
dragging: false,
dragStart(e) {
this.dragging = true;
this.startX = e.clientX - this.x;
this.startY = e.clientY - this.y;
},
drag(e) {
if (!this.dragging) return;
this.x = e.clientX - this.startX;
this.y = e.clientY - this.startY;
},
open(text){
this.modalIsOpen=true;
this.context = text;
this.message = "<span class='bg-yellow-300 text-white'>"+text+"</span><br><br>"+this.tips;
const div = document.getElementById("ai_creator");
div.scrollTop = 0;
},
up_message(msg){
this.message=msg;
},
do_insert(){
editor.focus();
let html = editor.getHtml();
editor.clear();
editor.dangerouslyInsertHtml(this.pre_message())
editor.dangerouslyInsertHtml(html);
const div = document.querySelector(".w-e-scroll")
div.scrollTop = 0;
},
do_cur_insert(){
//editor.blur()
editor.focus();
// if (editor.selection){
// console.info("选中")
// editor.deselect()
// }else{
// console.info("未选")
// }
editor.dangerouslyInsertHtml(this.context+this.pre_message())
},
do_replace(){
editor.focus()
editor.deleteFragment();
editor.dangerouslyInsertHtml(this.pre_message())
},
do_end_insert(){
editor.focus();
let html = editor.getHtml();
editor.clear();
editor.dangerouslyInsertHtml(html);
editor.dangerouslyInsertHtml(this.pre_message())
const div = document.querySelector(".w-e-scroll")
div.scrollTop = div.scrollHeight;;
},
pre_message(){ //纯的message,没有思考过程
let end = this.message.lastIndexOf('padding:5px;">');
var rsp = this.message.slice(end+'padding:5px;">'.length,-6);
return rsp;
},
//初始化
init2(){
this.baike_id= baike_id;
if (baike_id !==0){
axios.get('/api/baike/'+baike_id).then(response => {
this.title= response.data.title;
this.catalog=response.data.catalog;
editor.setHtml(response.data.html);
}).catch(error => {
console.error('Error fetching data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
}
//分类
axios.get('/api/catalogs').then(response => {
//console.log('Data:', response.data);
this.catalogs = response.data.data;
}).catch(error => {
console.error('Error fetching data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
//保存编辑信息
do_save(){
// 数据对象,代表要发送的数据
const newItem = {
id: this.baike_id,
title: this.title,
catalog: this.catalog,
trans: false,
html: editor.getHtml()
};
// 使用 axios 发送 POST 请求
axios.post('/api/baike', newItem)
.then(response =>{
//alert(response.data.msg)
this.doc_length = `保存成功,大约${editor.getText().length}字`;
this.save_fin = true;
this.baike_id = response.data.id
setTimeout(() => {
this.save_fin = false;
}, 3000); // 3000 毫秒 = 3 秒
})
.catch(error => {
alert(error);
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
//保存编辑信息为text格式
do_save_text(){
// 数据对象,代表要发送的数据
const newItem = {
id: this.baike_id,
title: this.title,
catalog: this.catalog,
trans: false,
html: editor.getText()
};
// 使用 axios 发送 POST 请求
axios.post('/api/baike', newItem)
.then(response =>{
//alert(response.data.msg)
this.save_fin = true;
this.doc_length = `保存成功,大约${editor.getText().length}字`;
this.baike_id = response.data.id
setTimeout(() => {
this.save_fin = false;
}, 3000); // 3000 毫秒 = 3 秒
})
.catch(error => {
alert(error);
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
//生成word文档
gen_word(){
// 数据对象,代表要发送的数据
const newItem = {
id: this.baike_id,
title: this.title,
catalog: this.catalog,
html: editor.getHtml()
};
// 使用 axios 发送 POST 请求
axios.post('/api/baike_gen_word', newItem)
.then(response =>{
window.open(`/api/fsd/${response.data.filename}`);
})
.catch(error => {
alert(error.response.data.detail);
});
},
//生成pdf文档
gen_pdf(){
// 数据对象,代表要发送的数据
const newItem = {
id: this.baike_id,
title: this.title,
catalog: this.catalog,
html: editor.getHtml()
};
// 使用 axios 发送 POST 请求
axios.post('/api/baike_gen_pdf', newItem)
.then(response =>{
window.open(`/api/fsd/${response.data.filename}`);
})
.catch(error => {
alert(error.response.data.detail);
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
//生成PPT文档
gen_ppt(){
// 数据对象,代表要发送的数据
const newItem = {
id: this.baike_id,
title: this.title,
catalog: this.catalog,
html: editor.getHtml()
};
this.progressbar = true;
this.currentVal =10;
setInterval(() => {
this.currentVal +=10;
}, 1000); // 1秒
// 使用 axios 发送 POST 请求
axios.post('/api/baike_gen_ppt', newItem)
.then(response =>{
this.progressbar = false;
window.open(`/api/fsd/${response.data.filename}`);
})
.catch(error => {
this.progressbar = false;
alert(error.response.data.detail);
});
},
//打开ppt设置的对话框
gen_ppt2(event){
this.chat_modalIsOpen = true;
this.ppt_modalIsOpen = true;
event.stopPropagation(); // 阻止事件冒泡
return false;
},
//生成海报
gen_poster(){
// 数据对象,代表要发送的数据
const newItem = {
id: this.baike_id,
title: this.title,
catalog: this.catalog,
html: editor.getHtml()
};
this.progressbar = true;
this.currentVal =10;
setInterval(() => {
this.currentVal +=10;
}, 1000); // 1秒
// 使用 axios 发送 POST 请求
axios.post('/api/baike_gen_poster', newItem)
.then(response =>{
this.progressbar = false;
window.open(`/api/fsd/${response.data.filename}`);
})
.catch(error => {
this.progressbar = false;
alert(error.response.data.detail);
});
},
gen_poster2(event){
this.chat_modalIsOpen = true;
this.poster_modalIsOpen = true;
event.stopPropagation(); // 阻止事件冒泡
return false;
},
do_gen(){
this.message="";
this.search_list = false;
const input_ai_mode = document.getElementById("ai_mode");
if (input_ai_mode.value==""){
alert("请选择创作模式");
return;
}
if (input_ai_mode.value=="指令模式"){
alert("请在下方输入框输入指令,点击GO即可完成");
return;
}
if (input_ai_mode.value=="AI续写" && this.context==""){
this.context=editor.getText();
}
if (input_ai_mode.value=="AI摘要" && this.context==""){
this.context=editor.getText();
}
// 数据对象,代表要发送的数据
const newItem = {
question: this.context,
ai_mode: input_ai_mode.value,
};
// 使用 axios 发送 POST 请求
axios.post('/api/ai_create', newItem)
.then(response =>{
rsp = response.data;
// 创建一个EventSource实例连接到服务器发送的事件流
const eventSource = new EventSource('/api/ai_create/'+rsp.chat_id);
// 监听消息事件
eventSource.onmessage = function(event) {
// 将接收到的数据显示在网页上
const message = JSON.parse(event.data);
var gen_rsp ="";
if (message.rsp.indexOf("<think>")==0){
var end = message.rsp.indexOf("</think>");
if (end==-1){ //思考还未结束
var think_mk = marked.parse(message.rsp.slice(7));
gen_rsp = think_mk;
}else{
var think_mk = marked.parse(message.rsp.slice(7,end));
var result= marked.parse(message.rsp.slice(end+8));
var think_all=`${think_mk} <div style="color: white; background-color: rgb(29 78 216 / 0.9); border-radius: 5px; margin: 5px; padding:5px;">${result}</div>`;
gen_rsp= think_all;
}
}else{
gen_rsp =marked.parse(message.rsp);
}
Alpine.store('ai_job').up_message(gen_rsp);
const div = document.getElementById("ai_creator");
div.scrollTop = div.scrollHeight;
};
// 监听错误事件
eventSource.onerror = function(error) {
console.error("EventSource failed:", error);
eventSource.close(); // 关闭连接
};
})
.catch(error => {
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
chat_gen(){
this.search_list = false;
this.message="";
const input_ai_mode = document.getElementById("ai_mode");
var mode="KG"; //知识库问答
if (input_ai_mode.value=="指令模式"){
mode="FA"; //大模型回答
}
if (this.search_checked=="-1" || mode=="FA"){
// 数据对象,代表要发送的数据
const newItem = {
question: this.context+"\n"+this.query,
ai_mode: mode,
};
// 使用 axios 发送 POST 请求
axios.post('/api/chat_search', newItem)
.then(response =>{
this.query="";
rsp = response.data;
// this.Items[this.Items.length-1].chat_id=rsp.chat_id;
// this.Items[this.Items.length-1].rsp=`搜索到${rsp.count}篇文档[${rsp.meta}],正在整理中...`;
// this.Items[this.Items.length-1].rsp_show=true;
update_chat(rsp.chat_id);
})
.catch(error => {
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
}else
{ //单个文档回答
let doc_index=parseInt(this.search_checked);
let doc= this.aItems[doc_index];
//单个文档对话
// 数据对象,代表要发送的数据
const newItem = {
question: this.query,
file: doc.f_name,
base: doc.base,
path: doc.path,
};
// 使用 axios 发送 POST 请求
axios.post('/api/chat_one_doc', newItem)
.then(response =>{
this.query="";
rsp = response.data;
update_chat(rsp.chat_id);
})
.catch(error => {
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
}
},
fetchSomething() {//查询文档
this.search_list = true;
if (this.search==""){
return;
}
axios.get('/api/kg_doc?demo='+this.search).then(response => {
//console.log('Data:', response.data);
this.aItems = response.data.data;
this.search_checked = -1;
this.count=response.data.count;
}).catch(error => {
console.error('Error fetching data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
})
})
function update_chat(chat_id){
// 创建一个EventSource实例连接到服务器发送的事件流
const eventSource = new EventSource('/api/chat/'+chat_id);
// 监听消息事件
eventSource.onmessage = function(event) {
// 将接收到的数据显示在网页上
const message = JSON.parse(event.data);
var gen_rsp ="";
if (message.rsp.indexOf("<think>")==0){
var end = message.rsp.indexOf("</think>");
if (end==-1){ //思考还未结束
var think_mk = marked.parse(message.rsp.slice(7));
gen_rsp = think_mk;
}else{
var think_mk = marked.parse(message.rsp.slice(7,end));
var result= marked.parse(message.rsp.slice(end+8));
var think_all=`${think_mk} <div style="color: white; background-color: rgb(29 78 216 / 0.9); border-radius: 5px; margin: 5px; padding:5px;">${result}</div>`;
gen_rsp= think_all;
}
}else{
gen_rsp =marked.parse(message.rsp);
}
Alpine.store('ai_job').up_message(gen_rsp);
const div = document.getElementById("ai_creator");
div.scrollTop = div.scrollHeight;
};
// 监听错误事件
eventSource.onerror = function(error) {
console.error("EventSource failed:", error);
eventSource.close(); // 关闭连接
};
}
function auto_save(){
var catalog = document.getElementById('catalog').value;
if ( catalog.startsWith("大模型/") )
var button = document.getElementById('save_text');
else{
var button = document.getElementById('save');
}
// 触发点击事件
button.click();
}
//开启30 秒后,自动保存
let timerId = setTimeout(() => {
//定时60秒保存
setInterval(auto_save,60000);
}, 60000);
</script>
</html>