1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288 |
- <template>
- <div class="flex h-full w-full flex-col">
- <toBackCom
- title="应用中心"
- :rou="{
- name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe',
- }"
- />
- <div class="bm-main-box">
- <div class="grid h-full w-full grid-cols-3 gap-4">
- <div
- class="col-span-2 flex flex-col gap-2 overflow-hidden rounded-lg bg-[#F6F8FC] p-4"
- >
- <div
- class="flex h-14 w-full items-center gap-2.5 rounded-lg bg-[#ffffff] px-6"
- >
- <div class="text-2xl font-bold text-[#303133]">编排</div>
- <div class="text-[#576275]" v-if="state.autoSaveTimestamp">
- 自动保存{{ state.autoSaveTimestamp }}
- </div>
- <div class="mx-auto" />
- <div
- class="flex items-center text-[var(--czr-error-color)]"
- v-if="canPublishCpt"
- >
- <SvgIcon
- name="czr_tip"
- color="var(--czr-error-color)"
- class="mr-1"
- />
- 有未发布的修改
- </div>
- <CzrButton
- v-if="AppStore.hasPermission($route.name, 'publish')"
- type="primary"
- title="发布"
- @click="onPublish"
- />
- <el-popover
- :show-arrow="false"
- width="26rem"
- placement="bottom-end"
- :popper-style="{
- padding: 0,
- }"
- >
- <template #reference>
- <div
- class="__hover flex h-8 w-8 items-center justify-center rounded-sm border-1 border-[var(--czr-main-color)]"
- >
- <SvgIcon name="history" :active="true" size="16" />
- </div>
- </template>
- <div>
- <div
- class="flex h-11 w-full items-center bg-[url('@/assets/images/global/head-bg-1.png')] bg-[length:100%_100%] bg-no-repeat px-4 text-base font-bold text-[#303133]"
- >
- 历史版本
- </div>
- <div class="h-[68vh] overflow-y-auto px-4 py-2">
- <template v-for="(item, index) in state.history">
- <div class="flex">
- <div
- class="relative mr-3 flex flex-col items-center justify-center"
- >
- <div
- class="h-[50%] w-0.5"
- :class="`${index > 0 ? 'bg-[#DEE9FF]' : ''}`"
- />
- <div
- class="h-[50%] w-0.5"
- :class="`${index < state.history.length - 1 ? 'bg-[#DEE9FF]' : ''}`"
- />
- <div
- class="absolute flex size-2 items-center justify-center rounded bg-[var(--czr-main-color)]"
- >
- <div class="size-1 rounded bg-[#DEE9FF]" />
- </div>
- </div>
- <div
- class="my-2 flex flex-1 items-center justify-between rounded-lg bg-[#F4F8FF] px-4 py-3"
- >
- <div
- class="text-base font-bold"
- :class="{
- 'text-[var(--czr-success-color)]':
- item.type === 1 || item.type === 3,
- 'text-[var(--czr-warning-color)]': item.type === 2,
- 'text-[#2E3238]': item.type === 0,
- }"
- >
- {{
- DictionaryStore.getStaticDict(
- 'appPublishType',
- item.type,
- )
- }}
- </div>
- <div class="text-[#909399]">{{ item.time }}</div>
- </div>
- </div>
- </template>
- </div>
- </div>
- </el-popover>
- </div>
- <CzrForm class="form" ref="ref_form">
- <div class="grid h-full flex-1 grid-cols-2 gap-4">
- <div class="col-span-1 flex flex-col rounded-lg bg-[#ffffff]">
- <div
- class="flex h-12 w-full items-center gap-2.5 bg-[url('@/assets/images/global/head-bg-1.png')] bg-[length:100%_100%] bg-no-repeat px-6"
- >
- <div class="mr-auto font-bold text-[#303133]">提示词</div>
- <CzrButton
- v-if="AppStore.hasPermission($route.name, 'template')"
- type="normal"
- title="保存为模板"
- @click="onAddTipsTemplate"
- />
- <CzrButton
- type="normal"
- title="使用模板"
- @click="onTipsTemplate"
- />
- </div>
- <div class="flex-1 p-4">
- <textarea
- class="h-full w-full"
- style="resize: none; line-height: 1.4"
- ref="ref_tips"
- v-model="state.form.tips"
- ></textarea>
- </div>
- </div>
- <div
- class="col-span-1 overflow-y-auto rounded-lg bg-[#ffffff] p-4"
- >
- <template v-if="state.detail.type === 0">
- <div class="__czr-title_2">模型选择</div>
- <div class="mt-4">
- <CzrFormColumn
- class="__czr-table-form-column"
- required
- label="模型选择"
- :span="24"
- v-model:param="state.form.modelId"
- link="select"
- :options="DictionaryStore.models.list"
- placeholder="点击选择模型"
- @click.capture.stop="onModel"
- default-error-msg="请选择模型"
- :clearable="false"
- />
- </div>
- </template>
- <div class="__czr-title_2 mt-4">
- 组件
- <template v-if="state.form.components.length > 0">
- ({{ state.form.components.length }})
- </template>
- <!-- <CzrButton type="normal" title="新增" class="ml-auto" />-->
- </div>
- <template v-if="state.form.components.length > 0"> </template>
- <template v-else>
- <div
- class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
- >
- 暂未添加组件
- </div>
- </template>
- <template v-if="state.detail.type === 0">
- <div class="__czr-title_2 mt-4">
- 知识库
- <template v-if="state.form.datasetIds.length > 0">
- ({{ state.form.datasetIds.length }})
- </template>
- <div
- class="__hover ml-4 text-sm font-normal text-[var(--czr-main-color)]"
- @click="onRecall"
- >
- 知识库设置
- </div>
- <CzrButton
- type="normal"
- title="新增"
- class="ml-auto"
- @click="onAddKnowledge"
- />
- </div>
- <template v-if="state.form.datasetIds.length > 0">
- <div
- class="mt-2 flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
- >
- <template v-for="(id, index) in state.form.datasetIds">
- <div class="flex items-center gap-1.5">
- <div
- class="flex flex-1 items-center overflow-hidden rounded-sm bg-[#F6F8FC] p-2.5"
- >
- <img
- src="@/assets/images/knowledge/knowledge-item-icon.png"
- class="mr-4 size-6"
- />
- <div
- class="flex-1 text-sm font-bold text-[#2E3238]"
- v-title
- >
- {{ DictionaryStore.knowledges.map.get(id) }}
- </div>
- </div>
- <el-tooltip content="删除" placement="top">
- <SvgIcon
- class="__hover"
- name="czr_del"
- color="var(--czr-error-color)"
- @click="state.form.datasetIds.splice(index, 1)"
- />
- </el-tooltip>
- </div>
- </template>
- </div>
- </template>
- <template v-else>
- <div
- class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
- >
- 暂未添加知识库
- </div>
- </template>
- </template>
- <template v-if="state.detail.type === 1">
- <div class="__czr-title_2 mt-4">
- 工作流
- <CzrButton
- type="normal"
- :title="state.form.workflowId ? '选择' : '新增'"
- class="ml-auto"
- @click="onAddWorkflow"
- />
- </div>
- <template v-if="state.form.workflowId">
- <div
- class="mt-2 flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
- >
- <div class="flex items-center gap-1.5">
- <div
- class="flex flex-1 items-center overflow-hidden rounded-sm bg-[#F6F8FC] p-2.5"
- >
- <img
- src="@/assets/images/workflow/workflow-default-icon.png"
- class="mr-4 size-15"
- />
- <div
- class="flex flex-1 flex-col gap-2 overflow-hidden"
- >
- <div
- class="text-sm font-bold text-[#2E3238]"
- v-title
- >
- {{
- DictionaryStore.workflows.map.get(
- state.form.workflowId,
- )
- }}
- </div>
- <div
- class="text-sm text-[#6F7889]"
- v-title="{ lines: 2 }"
- >
- {{
- DictionaryStore.workflows.objMap.get(
- state.form.workflowId,
- )?.description
- }}
- </div>
- </div>
- </div>
- <el-tooltip content="查看" placement="top">
- <SvgIcon
- class="__hover"
- name="view"
- :active="true"
- @click="onWorkflowView(state.form.workflowId)"
- />
- </el-tooltip>
- <el-tooltip content="删除" placement="top">
- <SvgIcon
- class="__hover"
- name="czr_del"
- color="var(--czr-error-color)"
- @click="state.form.workflowId = ''"
- />
- </el-tooltip>
- </div>
- </div>
- </template>
- <template v-else>
- <div
- class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
- >
- 暂未添加工作流
- </div>
- </template>
- </template>
- <div class="__czr-title_2 mt-4">开场白</div>
- <div class="mt-4">
- <CzrFormColumn
- class="__czr-table-form-column"
- label-width="0px"
- :span="24"
- v-model:param="state.form.prologue"
- link="rich"
- :height="300"
- default-error-msg="请输入开场白"
- />
- </div>
- <div class="__czr-title_2 mt-4">
- 开场白引导问题
- <template v-if="state.prologueQuestionsArr.length > 0">
- ({{ state.prologueQuestionsArr.length }})
- </template>
- <div class="ml-auto">
- <el-radio-group
- v-model="state.form.prologueNum"
- size="small"
- >
- <el-radio-button :value="3">显示前3条</el-radio-button>
- <el-radio-button :value="0">显示全部</el-radio-button>
- </el-radio-group>
- </div>
- </div>
- <div class="mt-2 flex flex-col">
- <div
- class="drag-body col relative flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
- ref="ref_prologueBody"
- v-if="state.dragRefresh"
- >
- <template
- v-for="(item, index) in state.prologueQuestionsArr"
- >
- <div
- class="flex items-center gap-2"
- style="-webkit-user-drag: element"
- >
- <SvgIcon
- name="czr_drag"
- color="#999999"
- class="drag-icon cursor-move"
- />
- <div
- class="__hover flex h-7 flex-1 items-center rounded-sm bg-[#F6F8FC] px-2.5 text-sm text-[#A7ADB9]"
- @click="onPrologue(item)"
- >
- <template v-if="item.__edit">
- <CzrFormColumn
- ref="ref_prologue"
- class="transparent-input"
- label-width="0px"
- :span="24"
- v-model:param="item.__value"
- :transparent="true"
- :clearable="false"
- maxlength="40"
- show-word-limit
- @keyup.enter.stop="onEditPrologue(item, index)"
- @blur="onEditPrologue(item, index)"
- />
- </template>
- <template v-else> {{ item.value }} </template>
- </div>
- <el-tooltip content="删除" placement="top">
- <SvgIcon
- class="__hover"
- name="czr_del"
- color="var(--czr-error-color)"
- @click="
- (state.form.prologueQuestions.splice(index, 1),
- state.prologueQuestionsArr.splice(index, 1))
- "
- />
- </el-tooltip>
- </div>
- </template>
- </div>
- <div class="mt-2 flex items-center gap-2">
- <div
- class="__hover flex h-7 flex-1 items-center rounded-sm bg-[#F6F8FC] px-2.5 text-sm text-[#A7ADB9]"
- @click="onPrologue(null)"
- >
- <template v-if="state.prologuesAdd.__edit">
- <CzrFormColumn
- ref="ref_prologue"
- class="transparent-input"
- label-width="0px"
- :span="24"
- v-model:param="state.prologuesAdd.value"
- :transparent="true"
- :clearable="false"
- maxlength="40"
- show-word-limit
- @keyup.enter.stop="onAddPrologue"
- @blur="state.prologuesAdd.__edit = false"
- />
- </template>
- <template v-else> 点击输入问题,enter以新增 </template>
- </div>
- </div>
- </div>
- <div class="__czr-title_2 mt-4">用户输入方式</div>
- <div class="mt-4">
- <CzrFormColumn
- class="__czr-table-form-column"
- label="输入方式"
- :span="24"
- v-model:param="state.form.userInputMethod"
- link="select"
- :options="[
- { value: 0, label: '文字/语音输入' },
- { value: 1, label: '语音通话' },
- ]"
- :clearable="false"
- />
- <div
- class="mt-4 flex items-center"
- v-if="state.form.userInputMethod == 1"
- >
- <div class="flex-1">
- <CzrFormColumn
- class="__czr-table-form-column"
- required
- label="语音包"
- :span="24"
- v-model:param="state.form.voicePackage"
- link="select"
- :options="[
- { value: 'Echo', label: 'Echo' },
- { value: 'Alloy', label: 'Alloy' },
- { value: 'Fable', label: 'Fable' },
- { value: 'Onyx', label: 'Onyx' },
- { value: 'Nova', label: 'Nova' },
- { value: 'Shimmer', label: 'Shimmer' },
- ]"
- :clearable="false"
- >
- <template #row="{ row }">
- <div class="flex h-full items-center">
- {{ row.label }}
- <div
- class="__hover ml-auto text-[var(--czr-main-color)]"
- @click.stop="onTryVoice(row.value)"
- >
- 试听
- </div>
- </div>
- </template>
- </CzrFormColumn>
- </div>
- <div
- class="__hover ml-4 text-sm text-[var(--czr-main-color)]"
- @click="onTryVoice(state.form.voicePackage)"
- >
- 试听
- </div>
- </div>
- </div>
- <div class="__czr-title_2 mt-4">
- 问题建议
- <div class="ml-auto">
- <el-checkbox-group
- class="advise"
- v-model="state.form.advise.types"
- size="small"
- >
- <el-checkbox :value="AdviseType.Open"> 开启 </el-checkbox>
- <template
- v-if="state.form.advise.types.includes(AdviseType.Open)"
- >
- <el-checkbox :value="AdviseType.Tips">
- 自定义提示词
- </el-checkbox>
- <el-checkbox :value="AdviseType.Knowledge">
- 仅从知识库建议
- </el-checkbox>
- </template>
- </el-checkbox-group>
- </div>
- </div>
- <template
- v-if="state.form.advise.types.includes(AdviseType.Open)"
- >
- <div class="mt-4">
- <CzrFormColumn
- class="__czr-table-form-column"
- required
- label="模型选择"
- :span="24"
- v-model:param="state.form.advise.modelId"
- link="select"
- :options="DictionaryStore.models.list"
- placeholder="点击选择模型"
- @click.capture.stop="onAdviseModel"
- default-error-msg="请选择问题建议模型"
- :clearable="false"
- />
- <div
- class="mt-2 flex h-10 items-center gap-1 rounded-sm bg-[#FFFAEA] px-3.5 text-sm text-[#6F7889]"
- >
- <SvgIcon
- name="czr_tip"
- color="var(--czr-warning-color)"
- />
- 应用每次回复后,根据对话内容提出最多3条问题建议
- </div>
- <template
- v-if="state.form.advise.types.includes(AdviseType.Tips)"
- >
- <div class="mt-2">
- <CzrFormColumn
- class="__czr-table-form-column"
- label-width="0px"
- :span="24"
- v-model:param="state.form.advise.tips"
- type="textarea"
- :rows="4"
- placeholder="问题应该与你最后一轮的回复紧密相关,可以引发进一步的讨论。 问题不要与上文已经提问或者回答过的内容重复。 每句话只包含一个问题,但也可以不是问句而是一句指令。 推荐你有能力回答的问题。"
- />
- </div>
- </template>
- </div>
- <template
- v-if="
- state.form.advise.types.includes(AdviseType.Knowledge)
- "
- >
- <div class="__czr-title_2 mt-4">
- 问题建议知识库
- <template v-if="state.form.advise.datasetIds.length > 0">
- ({{ state.form.advise.datasetIds.length }})
- </template>
- <CzrButton
- type="normal"
- title="新增"
- class="ml-auto"
- @click="onAddAdviseKnowledge"
- />
- </div>
- <template v-if="state.form.advise.datasetIds.length > 0">
- <div
- class="mt-2 flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
- >
- <template
- v-for="(id, index) in state.form.advise.datasetIds"
- >
- <div class="flex items-center gap-1.5">
- <div
- class="flex h-10 flex-1 items-center overflow-hidden rounded-sm bg-[#F6F8FC] px-2.5"
- >
- <img
- src="@/assets/images/knowledge/knowledge-item-icon.png"
- class="mr-4 size-6"
- />
- <div
- class="flex-1 text-sm font-bold text-[#2E3238]"
- v-title
- >
- {{ DictionaryStore.knowledges.map.get(id) }}
- </div>
- </div>
- <el-tooltip content="删除" placement="top">
- <SvgIcon
- class="__hover"
- name="czr_del"
- color="var(--czr-error-color)"
- @click="
- state.form.advise.datasetIds.splice(index, 1)
- "
- />
- </el-tooltip>
- </div>
- </template>
- </div>
- </template>
- <template v-else>
- <div
- class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
- >
- 暂未添加问题建议知识库
- </div>
- </template>
- </template>
- </template>
- </div>
- </div>
- </CzrForm>
- </div>
- <div
- class="col-span-1 flex flex-col gap-2 overflow-hidden rounded-lg bg-[#F6F8FC] p-4"
- >
- <div
- class="flex h-14 w-full items-center gap-2.5 rounded-lg bg-[#ffffff] px-6"
- >
- <div class="text-2xl font-bold text-[#303133]">预览</div>
- <div
- class="flex items-center gap-2 text-sm text-[var(--czr-error-color)]"
- v-if="!state.canModelApply"
- >
- <SvgIcon name="czr_tip" color="var(--czr-error-color)" />
- 编排中有申请中的模型,预览暂不可用
- </div>
- <el-tooltip content="重新开始" :raw-content="true" placement="top">
- <SvgIcon
- class="__hover ml-auto"
- name="refresh"
- :active="true"
- @click="onRestart"
- />
- </el-tooltip>
- </div>
- <div class="relative flex-1 overflow-hidden rounded-lg">
- <chat :ID="state.ID" :test="true" ref="ref_chat" />
- <div
- v-if="!state.canDebug"
- class="absolute inset-0 flex h-full w-full flex-col rounded-lg bg-white/50 p-20 backdrop-blur-sm"
- >
- <div class="text-2xl font-bold text-[#1d2939]">编排已改变</div>
- <div class="mt-6 text-[#667085]">
- 修改编排将重置调试区域,确定吗?
- </div>
- <div class="mt-6 flex gap-2">
- <CzrButton
- type="primary"
- title="重新开始"
- icon="refresh"
- size="18"
- @click="onRestart"
- />
- <CzrButton
- type="normal"
- title="取消"
- @click="state.canDebug = true"
- />
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <recallConfig
- ref="ref_recalConfig"
- v-model:show="state.recallConfig.show"
- :transfer="state.recallConfig.transfer"
- @refresh="getRecallConfig"
- />
- <knowledgeSelect
- v-model:show="state.knowledgeSelect.show"
- :transfer="state.knowledgeSelect.transfer"
- @refresh="getKnowledge"
- />
- <modelSelect
- v-model:show="state.modelSelect.show"
- :transfer="state.modelSelect.transfer"
- @refresh="getModel"
- />
- <workflowSelect
- v-model:show="state.workflowSelect.show"
- :transfer="state.workflowSelect.transfer"
- @refresh="getWorkflow"
- />
- <templateSelect
- v-model:show="state.templateSelect.show"
- @insert="(val) => ((state.form.tips = val), ref_tips.focus())"
- />
- <templateDetail
- v-model:show="state.templateDetail.show"
- :transfer="state.templateDetail.transfer"
- />
- <CzrDialog
- :show="state.publish.show"
- title="发布"
- @onClose="() => (state.publish.show = false)"
- @onSubmit="onPublishSubmit"
- width="42.5rem"
- height="auto"
- >
- <div class="bm-form">
- <CzrForm ref="ref_formPublish">
- <CzrFormColumn
- required
- label="发布渠道"
- :span="24"
- v-model:param="state.publish.form.type"
- link="select"
- :options="[
- { label: '个人空间', value: 0 },
- { label: '部门空间(需审批)', value: 1 },
- ]"
- :clearable="false"
- />
- <CzrFormColumn
- v-if="state.publish.form.type == 1"
- label="申请留言"
- :span="24"
- v-model:param="state.publish.form.remark"
- type="textarea"
- :rows="10"
- />
- </CzrForm>
- </div>
- </CzrDialog>
- </div>
- </template>
- <script setup lang="ts">
- import {
- computed,
- getCurrentInstance,
- inject,
- nextTick,
- onMounted,
- reactive,
- ref,
- watch,
- } from 'vue'
- import { useRoute, useRouter } from 'vue-router'
- import { ElLoading, ElMessage } from 'element-plus'
- import { useAppStore, useDialogStore, useDictionaryStore } from '@/stores'
- import { Search } from '@element-plus/icons-vue'
- import knowledgeSelect from './knowledge-select.vue'
- import modelSelect from './model-select.vue'
- import { isValue, YMDHms } from '@/utils/czr-util'
- import Sortable from 'sortablejs'
- import { debounce } from 'lodash'
- import chat from '@/views/chat/index.vue'
- import templateSelect from './template-select.vue'
- import templateDetail from './template-detail.vue'
- import workflowSelect from './workflow-select.vue'
- import CzrForm from '@/components/czr-ui/CzrForm.vue'
- import CzrDialog from '@/components/czr-ui/CzrDialog.vue'
- import toBackCom from '@/views/manage/components/to-back.vue'
- import {
- appHistory,
- appTemporarily,
- appModelConfigSave,
- appPublish,
- appValidatePublish,
- appModelConfigDetail,
- } from '@/api/modules/app/make'
- import { appDetail } from '@/api/modules/app'
- import { appTextToAudio } from '@/api/modules/app/chat'
- import recallConfig from '@/views/manage/knowledge/recall-config.vue'
- const DictionaryStore = useDictionaryStore()
- const AppStore = useAppStore()
- const DialogStore = useDialogStore()
- const route = useRoute()
- const router = useRouter()
- const emit = defineEmits([])
- const props = defineProps({})
- const { proxy }: any = getCurrentInstance()
- enum AdviseType {
- Open = 1,
- Tips = 2,
- Knowledge = 3,
- }
- const state: any = reactive({
- ID: route.params.id,
- isInit: false,
- autoSaveTimestamp: '',
- form: {
- tips: '',
- modelId: '',
- components: [],
- datasetIds: [],
- datasetConfigs: null,
- workflowId: '',
- prologue: '',
- prologueQuestions: [],
- prologueNum: 3,
- userInputMethod: 1,
- voicePackage: '',
- advise: {
- types: [],
- modelId: '',
- tips: '',
- datasetIds: [],
- },
- },
- prologueQuestionsArr: [],
- detail: {},
- config: {},
- knowledgeSelect: {
- show: false,
- transfer: {},
- },
- modelSelect: {
- show: false,
- transfer: {},
- },
- workflowSelect: {
- show: false,
- transfer: {},
- },
- templateSelect: {
- show: false,
- },
- templateDetail: {
- show: false,
- transfer: {},
- },
- publish: {
- show: false,
- form: {},
- },
- prologuesAdd: {
- value: '',
- },
- dragRefresh: true,
- canDebug: true,
- canModelApply: true,
- history: [],
- recallConfig: {
- show: false,
- transfer: {},
- },
- })
- const ref_form = ref()
- const ref_formPublish = ref()
- const ref_tips = ref()
- const ref_prologue = ref()
- const ref_prologueBody = ref()
- const ref_recalConfig = ref()
- const ref_chat = ref()
- const canPublishCpt = computed(() => {
- if (state.config.createTime === state.config.updateTime) {
- return false
- }
- if (state.history.length === 0) {
- return false
- } else {
- if (
- new Date(state.autoSaveTimestamp).getTime() <
- new Date(state.history[0]?.time).getTime()
- ) {
- return false
- }
- }
- return true
- })
- watch(
- () => state.form,
- (n) => {
- if (state.isInit) {
- console.log('触发自动保存')
- autoSave(n)
- }
- },
- { deep: true },
- )
- const autoSave = debounce((v) => {
- const loading = ElLoading.service({
- text: '自动保存中……',
- background: 'rgba(0, 0,0, 0.3)',
- })
- const params: any = {
- appId: state.ID,
- prePrompt: state.form.tips,
- openingStatement: state.form.prologue,
- showAll: state.form.prologueNum,
- suggestedQuestions: state.form.prologueQuestions,
- userInputMethod: state.form.userInputMethod,
- voice: state.form.voicePackage,
- qsType: state.form.advise.types,
- qsModelId: state.form.advise.modelId,
- qsPrePrompt: state.form.advise.tips,
- qsDatasetIds: state.form.advise.datasetIds,
- }
- if (state.detail.type === 0) {
- params.modelId = state.form.modelId
- params.datasetIds = state.form.datasetIds
- console.log(state.form.datasetConfigs)
- if (!state.form.datasetConfigs) {
- state.form.datasetConfigs = ref_recalConfig.value.getData()
- }
- params.datasetConfigs = state.form.datasetConfigs
- } else {
- params.workflowId = state.form.workflowId
- }
- appModelConfigSave(params)
- .then(({ data }: any) => {
- state.canDebug = false
- verifyPublish(false)
- state.autoSaveTimestamp = data.updateTime
- })
- .catch(() => {})
- .finally(() => {
- loading.close()
- })
- }, 5000)
- const initDetail = () => {
- state.isInit = false
- if (state.ID) {
- appDetail(state.ID)
- .then(({ data: appData }: any) => {
- state.detail = appData
- document.title = state.detail.name
- initHistory()
- appModelConfigDetail(state.ID, 0)
- .then(({ data: configData }: any) => {
- verifyPublish(false).finally(() => {
- if (state.canModelApply) {
- onRestart()
- }
- })
- if (configData.createTime !== configData.updateTime) {
- state.autoSaveTimestamp = configData.updateTime
- }
- state.config = JSON.parse(JSON.stringify(configData))
- state.form.tips = configData.prePrompt || ''
- state.form.prologue = configData.openingStatement || ''
- state.form.prologueNum = Number(configData.showAll) || 3
- state.form.prologueQuestions = configData.suggestedQuestions || []
- state.prologueQuestionsArr = state.form.prologueQuestions.map(
- (v) => ({
- value: v,
- }),
- )
- state.form.userInputMethod = Number(configData.userInputMethod) || 0
- state.form.voicePackage = configData.voice || ''
- state.form.advise.types =
- configData.qsType?.map((v) => Number(v)) || []
- state.form.advise.modelId = configData.qsModelId || ''
- state.form.advise.tips = configData.qsPrePrompt || ''
- state.form.advise.datasetIds = configData.qsDatasetIds || []
- if (state.detail.type === 0) {
- state.form.modelId = configData.modelId || ''
- state.form.datasetIds = configData.datasetIds || []
- state.form.datasetConfigs = configData.datasetConfigs
- } else {
- state.form.workflowId = configData.workflowId
- }
- setTimeout(() => {
- state.isInit = true
- }, 100)
- })
- .catch(() => {
- ElMessage.error('详情获取异常!')
- router.push({ name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe' })
- })
- .finally(() => {
- state.loading = false
- })
- })
- .catch(() => {
- ElMessage.error('详情获取异常!')
- router.push({ name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe' })
- })
- .finally(() => {
- state.loading = false
- })
- initDrag()
- } else {
- router.push({ name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe' })
- }
- }
- const initHistory = () => {
- appHistory(state.ID).then(({ data }: any) => {
- state.history = data || []
- })
- }
- const initDrag = () => {
- nextTick(() => {
- const tbody =
- ref_prologueBody.value.parentElement.querySelector('.drag-body')
- new Sortable(tbody, {
- animation: 150,
- handle: '.drag-icon', // 设置可拖拽行的类名(el-table自带的类名)
- onEnd: ({ newIndex, oldIndex }: any) => {
- const d = [...state.prologueQuestionsArr]
- const targetRow = d[oldIndex]
- d.splice(oldIndex, 1)
- d.splice(newIndex, 0, targetRow)
- state.prologueQuestionsArr = [...d]
- state.dragRefresh = false
- setTimeout(() => {
- state.dragRefresh = true
- initDrag()
- }, 0)
- },
- })
- })
- }
- const onAddKnowledge = () => {
- state.knowledgeSelect.transfer = {
- ids: state.form.datasetIds,
- type: 'knowledge',
- }
- state.knowledgeSelect.show = true
- }
- const onAddAdviseKnowledge = () => {
- state.knowledgeSelect.transfer = {
- ids: state.form.advise.datasetIds,
- type: 'advise',
- }
- state.knowledgeSelect.show = true
- }
- const onPrologue = (row) => {
- if (row) {
- row.__value = row.value + ''
- row.__edit = true
- } else {
- state.prologuesAdd.__edit = true
- }
- const t = setInterval(() => {
- const r = ref_prologue.value
- if (r) {
- if (r.length) {
- r[0].focus()
- } else {
- r.focus()
- }
- clearInterval(t)
- }
- }, 100)
- }
- const onAddPrologue = () => {
- if (state.prologuesAdd.value.trim()) {
- state.form.prologueQuestions.push(state.prologuesAdd.value + '')
- state.prologueQuestionsArr.push({ value: state.prologuesAdd.value + '' })
- state.prologuesAdd.value = ''
- }
- }
- const onEditPrologue = (row, index) => {
- if (isValue(row.__value.trim())) {
- row.value = row.__value + ''
- state.form.prologueQuestions[index] = row.__value + ''
- }
- row.__edit = false
- }
- const getKnowledge = (arr) => {
- switch (state.knowledgeSelect.transfer.type) {
- case 'knowledge':
- {
- state.form.datasetIds.push(...arr.map((v) => v.id))
- }
- break
- case 'advise':
- {
- state.form.advise.datasetIds.push(...arr.map((v) => v.id))
- }
- break
- }
- }
- const getModel = (val) => {
- switch (state.modelSelect.transfer.type) {
- case 'model':
- {
- state.form.modelId = val.id
- }
- break
- case 'advise':
- {
- state.form.advise.modelId = val.id
- }
- break
- }
- }
- const onModel = () => {
- state.modelSelect.transfer = {
- type: 'model',
- id: state.form.modelId,
- }
- state.modelSelect.show = true
- }
- const onAdviseModel = () => {
- state.modelSelect.transfer = {
- type: 'advise',
- id: state.form.advise.modelId,
- }
- state.modelSelect.show = true
- }
- const onPublish = () => {
- ref_form.value
- .submit()
- .then(() => {
- if (state.detail.type == 1) {
- if (!state.form.workflowId) {
- ElMessage.warning('请添加工作流!')
- return
- }
- }
- if (
- state.form.advise.types.includes(AdviseType.Open) &&
- state.form.advise.types.includes(AdviseType.Knowledge)
- ) {
- if (state.form.advise.datasetIds.length === 0) {
- ElMessage.warning('请添加问题建议知识库!')
- return
- }
- }
- verifyPublish(true).then(() => {
- state.publish.form = {}
- state.publish.show = true
- })
- })
- .catch((e) => {
- ElMessage({
- message: e[0].message,
- grouping: true,
- type: 'warning',
- })
- })
- }
- const onPublishSubmit = () => {
- ref_formPublish.value
- .submit()
- .then(() => {
- DialogStore.confirm({
- content: `请确认是否发布?`,
- onSubmit: () => {
- verifyPublish(true).then(() => {
- appPublish({
- appId: state.ID,
- ...state.publish.form,
- }).then(() => {
- initHistory()
- state.publish.show = false
- ElMessage.success(
- state.publish.form.type == 0 ? '发布成功!' : '提交发布成功!',
- )
- })
- })
- },
- })
- })
- .catch((e) => {
- ElMessage({
- message: e[0].message,
- grouping: true,
- type: 'warning',
- })
- })
- }
- const onAddTipsTemplate = () => {
- state.templateDetail.transfer = {
- mode: 'add',
- tips: state.form.tips + '',
- }
- state.templateDetail.show = true
- }
- const onTipsTemplate = () => {
- state.templateSelect.show = true
- }
- const onAddWorkflow = () => {
- state.workflowSelect.transfer = {
- id: state.form.workflowId,
- }
- state.workflowSelect.show = true
- }
- const getWorkflow = (val) => {
- state.form.workflowId = val.id
- }
- const onTryVoice = (type) => {
- const loading = ElLoading.service({
- text: '语音包加载中……',
- background: 'rgba(0, 0,0, 0.3)',
- })
- appTextToAudio({
- voice: type,
- text: `您好,欢迎使用${(import.meta as any).env.VITE_TITLE}。`,
- })
- .then((bolb: any) => {
- const url = URL.createObjectURL(new Blob([bolb], { type: 'audio/mp3' }))
- const audio = new Audio(url)
- audio.play()
- // 播放结束后释放内存
- audio.onended = function () {
- URL.revokeObjectURL(url)
- }
- })
- .catch(() => {})
- .finally(() => {
- loading.close()
- })
- }
- const onWorkflowView = (id) => {
- const routerUrl = router.resolve({
- name: '37d34d52-78c7-4272-8715-fdc88f599c4f',
- params: {
- id,
- },
- })
- window.open(routerUrl.href, '_blank')
- }
- const verifyPublish = (showDialog) => {
- return new Promise((resolve, reject) => {
- appValidatePublish(state.ID)
- .then(({ data }: any) => {
- if (data.status) {
- resolve(null)
- state.canModelApply = true
- } else {
- if (data.type == 1) {
- state.canModelApply = false
- if (showDialog) {
- DialogStore.confirm({
- title: '发布失败',
- content: `编排中有申请中的模型,暂不可发布!`,
- props: {
- showSubmit: false,
- showCancel: false,
- },
- })
- }
- } else if (data.type == 2) {
- state.canModelApply = true
- if (showDialog) {
- DialogStore.confirm({
- title: '发布失败',
- content: `该应用有发布中的申请,无法提交发布!<br/>申请提交时间:${data.time}`,
- onSubmit: () => {},
- props: {
- submitText: '撤销上一次的申请,重新发布',
- cancelText: '等待审批',
- },
- })
- }
- }
- reject(data)
- }
- })
- .finally(() => {})
- })
- }
- const onRestart = () => {
- state.canDebug = true
- ref_chat.value?.init()
- }
- const onRecall = () => {
- state.recallConfig.transfer = {
- config: null,
- }
- state.recallConfig.show = true
- }
- const getRecallConfig = (config) => {
- state.form.datasetConfigs = { ...config }
- }
- onMounted(() => {
- initDictionary()
- initDetail()
- })
- const initDictionary = () => {
- DictionaryStore.initWorkflows()
- DictionaryStore.initModels()
- DictionaryStore.initKnowledges(AppStore.tenantInfo?.id)
- }
- </script>
- <style lang="scss" scoped>
- :deep(.form) {
- flex: 1;
- overflow: hidden;
- .el-form {
- width: 100%;
- height: 100%;
- .el-row {
- width: 100%;
- height: 100%;
- .transparent-input {
- .el-input__wrapper {
- padding: 0;
- }
- }
- .advise {
- .el-checkbox {
- margin-right: 10px;
- .el-checkbox__label {
- padding-left: 4px;
- }
- }
- }
- }
- }
- }
- .__czr-title_2 {
- height: 2rem;
- }
- </style>
|