index.vue 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121
  1. <template>
  2. <div class="flex h-full w-full flex-col">
  3. <toBackCom
  4. title="应用中心"
  5. :rou="{
  6. name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe',
  7. }"
  8. />
  9. <div class="bm-main-box">
  10. <div class="grid h-full w-full grid-cols-3 gap-4">
  11. <div
  12. class="col-span-2 flex flex-col gap-2 overflow-hidden rounded-lg bg-[#F6F8FC] p-4"
  13. >
  14. <div
  15. class="flex h-14 w-full items-center gap-2.5 rounded-lg bg-[#ffffff] px-6"
  16. >
  17. <div class="text-2xl font-bold text-[#303133]">编排</div>
  18. <div class="text-[#576275]" v-if="state.autoSaveTimestamp">
  19. 自动保存{{ state.autoSaveTimestamp }}
  20. </div>
  21. <div class="mx-auto" />
  22. <div
  23. class="flex items-center text-[var(--czr-error-color)]"
  24. v-if="
  25. !state.config.publishTime ||
  26. state.config.publishTime !== state.autoSaveTimestamp
  27. "
  28. >
  29. <SvgIcon
  30. name="czr_tip"
  31. color="var(--czr-error-color)"
  32. class="mr-1"
  33. />
  34. 有未发布的修改
  35. </div>
  36. <CzrButton type="primary" title="发布" @click="onPublish" />
  37. <el-popover
  38. :show-arrow="false"
  39. width="26rem"
  40. placement="bottom-end"
  41. :popper-style="{
  42. padding: 0,
  43. }"
  44. >
  45. <template #reference>
  46. <div
  47. class="__hover flex h-8 w-8 items-center justify-center rounded-sm border-1 border-[var(--czr-main-color)]"
  48. >
  49. <SvgIcon name="history" :active="true" size="16" />
  50. </div>
  51. </template>
  52. <div>
  53. <div
  54. class="flex h-11 w-full items-center bg-[url('@/assets/images/knowledge/knowledge-back-test.png')] bg-[length:100%_100%] bg-no-repeat px-4 text-base font-bold text-[#303133]"
  55. >
  56. 历史版本
  57. </div>
  58. <div class="h-[68vh] overflow-y-auto px-4 py-2">
  59. <template v-for="(item, index) in state.history">
  60. <div class="flex">
  61. <div
  62. class="relative mr-3 flex flex-col items-center justify-center"
  63. >
  64. <div
  65. class="h-[50%] w-0.5"
  66. :class="`${index > 0 ? 'bg-[#DEE9FF]' : ''}`"
  67. />
  68. <div
  69. class="h-[50%] w-0.5"
  70. :class="`${index < state.history.length - 1 ? 'bg-[#DEE9FF]' : ''}`"
  71. />
  72. <div
  73. class="absolute flex size-2 items-center justify-center rounded bg-[var(--czr-main-color)]"
  74. >
  75. <div class="size-1 rounded bg-[#DEE9FF]" />
  76. </div>
  77. </div>
  78. <div
  79. class="my-2 flex flex-1 items-center justify-between rounded-lg bg-[#F4F8FF] px-4 py-3"
  80. >
  81. <div
  82. class="text-base font-bold"
  83. :class="{
  84. 'text-[var(--czr-success-color)]':
  85. item.type === 'publish' || item.type === 'apply',
  86. 'text-[var(--czr-warning-color)]':
  87. item.type === 'submit',
  88. 'text-[#2E3238]': item.type === 'create',
  89. }"
  90. >
  91. {{ item.type }}
  92. </div>
  93. <div class="text-[#909399]">{{ item.date }}</div>
  94. </div>
  95. </div>
  96. </template>
  97. </div>
  98. </div>
  99. </el-popover>
  100. </div>
  101. <CzrForm class="form" ref="ref_form">
  102. <div class="grid h-full flex-1 grid-cols-2 gap-4">
  103. <div class="col-span-1 flex flex-col rounded-lg bg-[#ffffff]">
  104. <div
  105. class="flex h-12 w-full items-center gap-2.5 bg-[url('@/assets/images/knowledge/knowledge-back-test.png')] bg-[length:100%_100%] bg-no-repeat px-6"
  106. >
  107. <div class="mr-auto font-bold text-[#303133]">提示词</div>
  108. <CzrButton
  109. type="normal"
  110. title="保存为模板"
  111. @click="onAddTipsTemplate"
  112. />
  113. <CzrButton
  114. type="normal"
  115. title="使用模板"
  116. @click="onTipsTemplate"
  117. />
  118. </div>
  119. <div class="flex-1 p-4">
  120. <textarea
  121. class="h-full w-full"
  122. style="resize: none; line-height: 1.4"
  123. ref="ref_tips"
  124. v-model="state.form.tips"
  125. ></textarea>
  126. </div>
  127. </div>
  128. <div
  129. class="col-span-1 overflow-y-auto rounded-lg bg-[#ffffff] p-4"
  130. >
  131. <template v-if="state.detail.type === 0">
  132. <div class="__czr-title_2">模型选择</div>
  133. <div class="mt-4">
  134. <CzrFormColumn
  135. class="__czr-table-form-column"
  136. required
  137. label="模型选择"
  138. :span="24"
  139. v-model:param="state.form.modelId"
  140. link="select"
  141. :options="DictionaryStore.models.list"
  142. placeholder="点击选择模型"
  143. @click.capture.stop="onModel"
  144. default-error-msg="请选择模型"
  145. :clearable="false"
  146. />
  147. </div>
  148. </template>
  149. <div class="__czr-title_2 mt-4">
  150. 组件
  151. <template v-if="state.form.components.length > 0">
  152. ({{ state.form.components.length }})
  153. </template>
  154. <!-- <CzrButton type="normal" title="新增" class="ml-auto" />-->
  155. </div>
  156. <template v-if="state.form.components.length > 0"> </template>
  157. <template v-else>
  158. <div
  159. class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
  160. >
  161. 暂未添加组件
  162. </div>
  163. </template>
  164. <template v-if="state.detail.type === 0">
  165. <div class="__czr-title_2 mt-4">
  166. 知识库
  167. <template v-if="state.form.datasetIds.length > 0">
  168. ({{ state.form.datasetIds.length }})
  169. </template>
  170. <CzrButton
  171. type="normal"
  172. title="新增"
  173. class="ml-auto"
  174. @click="onAddKnowledge"
  175. />
  176. </div>
  177. <template v-if="state.form.datasetIds.length > 0">
  178. <div
  179. class="mt-2 flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
  180. >
  181. <template v-for="(id, index) in state.form.datasetIds">
  182. <div class="flex items-center gap-1.5">
  183. <div
  184. class="flex flex-1 items-center overflow-hidden rounded-sm bg-[#F6F8FC] p-2.5"
  185. >
  186. <img
  187. src="@/assets/images/knowledge/knowledge-item-icon.png"
  188. class="mr-4 size-6"
  189. />
  190. <div
  191. class="flex-1 text-sm font-bold text-[#2E3238]"
  192. v-title
  193. >
  194. {{ DictionaryStore.knowledges.map.get(id) }}
  195. </div>
  196. </div>
  197. <SvgIcon
  198. class="__hover"
  199. name="czr_del"
  200. color="var(--czr-error-color)"
  201. @click="state.form.datasetIds.splice(index, 1)"
  202. />
  203. </div>
  204. </template>
  205. </div>
  206. </template>
  207. <template v-else>
  208. <div
  209. class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
  210. >
  211. 暂未添加知识库
  212. </div>
  213. </template>
  214. </template>
  215. <template v-if="state.detail.type === 1">
  216. <div class="__czr-title_2 mt-4">
  217. 工作流
  218. <CzrButton
  219. type="normal"
  220. :title="state.form.workflowId ? '选择' : '新增'"
  221. class="ml-auto"
  222. @click="onAddWorkflow"
  223. />
  224. </div>
  225. <template v-if="state.form.workflowId">
  226. <div
  227. class="mt-2 flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
  228. >
  229. <div class="flex items-center gap-1.5">
  230. <div
  231. class="flex flex-1 items-center overflow-hidden rounded-sm bg-[#F6F8FC] p-2.5"
  232. >
  233. <img
  234. src="@/assets/images/workflow/workflow-default-icon.png"
  235. class="mr-4 size-15"
  236. />
  237. <div
  238. class="flex flex-1 flex-col gap-2 overflow-hidden"
  239. >
  240. <div
  241. class="text-sm font-bold text-[#2E3238]"
  242. v-title
  243. >
  244. {{
  245. DictionaryStore.workflows.map.get(
  246. state.form.workflowId,
  247. )
  248. }}
  249. </div>
  250. <div
  251. class="text-sm text-[#6F7889]"
  252. v-title="{ lines: 2 }"
  253. >
  254. {{
  255. DictionaryStore.workflows.objMap.get(
  256. state.form.workflowId,
  257. )?.description
  258. }}
  259. </div>
  260. </div>
  261. </div>
  262. <SvgIcon
  263. class="__hover"
  264. name="czr_del"
  265. color="var(--czr-error-color)"
  266. @click="state.form.workflowId = ''"
  267. />
  268. </div>
  269. </div>
  270. </template>
  271. <template v-else>
  272. <div
  273. class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
  274. >
  275. 暂未添加工作流
  276. </div>
  277. </template>
  278. </template>
  279. <div class="__czr-title_2 mt-4">开场白</div>
  280. <div class="mt-4">
  281. <CzrFormColumn
  282. class="__czr-table-form-column"
  283. required
  284. label-width="0px"
  285. :span="24"
  286. v-model:param="state.form.prologue"
  287. link="rich"
  288. :height="300"
  289. default-error-msg="请输入开场白"
  290. />
  291. </div>
  292. <div class="__czr-title_2 mt-4">
  293. 开场白引导问题
  294. <template v-if="state.prologueQuestionsArr.length > 0">
  295. ({{ state.prologueQuestionsArr.length }})
  296. </template>
  297. <div class="ml-auto">
  298. <el-radio-group
  299. v-model="state.form.prologueNum"
  300. size="small"
  301. >
  302. <el-radio-button :value="3">显示前3条</el-radio-button>
  303. <el-radio-button :value="0">显示全部</el-radio-button>
  304. </el-radio-group>
  305. </div>
  306. </div>
  307. <div class="mt-2 flex flex-col">
  308. <div
  309. class="drag-body col relative flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
  310. ref="ref_prologueBody"
  311. v-if="state.dragRefresh"
  312. >
  313. <template
  314. v-for="(item, index) in state.prologueQuestionsArr"
  315. >
  316. <div
  317. class="flex items-center gap-2"
  318. style="-webkit-user-drag: element"
  319. >
  320. <SvgIcon
  321. name="czr_drag"
  322. color="#999999"
  323. class="drag-icon cursor-move"
  324. />
  325. <div
  326. class="__hover flex h-7 flex-1 items-center rounded-sm bg-[#F6F8FC] px-2.5 text-sm text-[#A7ADB9]"
  327. @click="onPrologue(item)"
  328. >
  329. <template v-if="item.__edit">
  330. <CzrFormColumn
  331. ref="ref_prologue"
  332. class="transparent-input"
  333. label-width="0px"
  334. :span="24"
  335. v-model:param="item.__value"
  336. :transparent="true"
  337. :clearable="false"
  338. maxlength="40"
  339. show-word-limit
  340. @keyup.enter.stop="onEditPrologue(item, index)"
  341. @blur="onEditPrologue(item, index)"
  342. />
  343. </template>
  344. <template v-else> {{ item.value }} </template>
  345. </div>
  346. <SvgIcon
  347. class="__hover"
  348. name="czr_del"
  349. color="var(--czr-error-color)"
  350. @click="
  351. (state.form.prologueQuestions.splice(index, 1),
  352. state.prologueQuestionsArr.splice(index, 1))
  353. "
  354. />
  355. </div>
  356. </template>
  357. </div>
  358. <div class="mt-2 flex items-center gap-2">
  359. <div
  360. class="__hover flex h-7 flex-1 items-center rounded-sm bg-[#F6F8FC] px-2.5 text-sm text-[#A7ADB9]"
  361. @click="onPrologue(null)"
  362. >
  363. <template v-if="state.prologuesAdd.__edit">
  364. <CzrFormColumn
  365. ref="ref_prologue"
  366. class="transparent-input"
  367. label-width="0px"
  368. :span="24"
  369. v-model:param="state.prologuesAdd.value"
  370. :transparent="true"
  371. :clearable="false"
  372. maxlength="40"
  373. show-word-limit
  374. @keyup.enter.stop="onAddPrologue"
  375. @blur="state.prologuesAdd.__edit = false"
  376. />
  377. </template>
  378. <template v-else> 点击输入问题,enter以新增 </template>
  379. </div>
  380. </div>
  381. </div>
  382. <div class="__czr-title_2 mt-4">用户输入方式</div>
  383. <div class="mt-4">
  384. <CzrFormColumn
  385. class="__czr-table-form-column"
  386. required
  387. label="输入方式"
  388. :span="24"
  389. v-model:param="state.form.userInputMethod"
  390. link="select"
  391. :options="[
  392. { value: 0, label: '文字/语音输入' },
  393. { value: 1, label: '语音通话' },
  394. ]"
  395. :clearable="false"
  396. />
  397. <div
  398. class="mt-4 flex items-center"
  399. v-if="state.form.userInputMethod == 1"
  400. >
  401. <div class="flex-1">
  402. <CzrFormColumn
  403. class="__czr-table-form-column"
  404. required
  405. label="语音包"
  406. :span="24"
  407. v-model:param="state.form.voicePackage"
  408. link="select"
  409. :options="[
  410. { value: 1, label: '语音包1' },
  411. { value: 2, label: '语音包2' },
  412. ]"
  413. :clearable="false"
  414. >
  415. <template #row="{ row }">
  416. <div class="flex h-full items-center">
  417. {{ row.label }}
  418. <div
  419. class="__hover ml-auto text-[var(--czr-main-color)]"
  420. @click.stop=""
  421. >
  422. 试听
  423. </div>
  424. </div>
  425. </template>
  426. </CzrFormColumn>
  427. </div>
  428. <div
  429. class="__hover ml-4 text-sm text-[var(--czr-main-color)]"
  430. >
  431. 试听
  432. </div>
  433. </div>
  434. </div>
  435. <div class="__czr-title_2 mt-4">
  436. 问题建议
  437. <div class="ml-auto">
  438. <el-checkbox-group
  439. v-model="state.form.advise.types"
  440. size="small"
  441. >
  442. <el-checkbox-button :value="AdviseType.Open">
  443. 开启
  444. </el-checkbox-button>
  445. <template
  446. v-if="state.form.advise.types.includes(AdviseType.Open)"
  447. >
  448. <el-checkbox-button :value="AdviseType.Tips">
  449. 自定义提示词
  450. </el-checkbox-button>
  451. <el-checkbox-button :value="AdviseType.Knowledge">
  452. 仅从知识库建议
  453. </el-checkbox-button>
  454. </template>
  455. </el-checkbox-group>
  456. </div>
  457. </div>
  458. <template
  459. v-if="state.form.advise.types.includes(AdviseType.Open)"
  460. >
  461. <div class="mt-4">
  462. <CzrFormColumn
  463. class="__czr-table-form-column"
  464. required
  465. label="模型选择"
  466. :span="24"
  467. v-model:param="state.form.advise.modelId"
  468. link="select"
  469. :options="DictionaryStore.models.list"
  470. placeholder="点击选择模型"
  471. @click="onAdviseModel"
  472. default-error-msg="请选择问题建议模型"
  473. :clearable="false"
  474. />
  475. <div
  476. class="mt-2 flex h-10 items-center gap-1 rounded-sm bg-[#FFFAEA] px-3.5 text-sm text-[#6F7889]"
  477. >
  478. <SvgIcon
  479. name="czr_tip"
  480. color="var(--czr-warning-color)"
  481. />
  482. 应用每次回复后,根据对话内容提出最多3条问题建议
  483. </div>
  484. <template
  485. v-if="state.form.advise.types.includes(AdviseType.Tips)"
  486. >
  487. <div class="mt-2">
  488. <CzrFormColumn
  489. class="__czr-table-form-column"
  490. label-width="0px"
  491. :span="24"
  492. v-model:param="state.form.advise.tips"
  493. type="textarea"
  494. :rows="4"
  495. placeholder="问题应该与你最后一轮的回复紧密相关,可以引发进一步的讨论。&#10;问题不要与上文已经提问或者回答过的内容重复。&#10;每句话只包含一个问题,但也可以不是问句而是一句指令。&#10;推荐你有能力回答的问题。"
  496. />
  497. </div>
  498. </template>
  499. </div>
  500. <template
  501. v-if="
  502. state.form.advise.types.includes(AdviseType.Knowledge)
  503. "
  504. >
  505. <div class="__czr-title_2 mt-4">
  506. 问题建议知识库
  507. <template v-if="state.form.advise.datasetIds.length > 0">
  508. ({{ state.form.advise.datasetIds.length }})
  509. </template>
  510. <CzrButton
  511. type="normal"
  512. title="新增"
  513. class="ml-auto"
  514. @click="onAddAdviseKnowledge"
  515. />
  516. </div>
  517. <template v-if="state.form.advise.datasetIds.length > 0">
  518. <div
  519. class="mt-2 flex max-h-42 flex-col gap-2 overflow-y-auto pr-2"
  520. >
  521. <template
  522. v-for="(id, index) in state.form.advise.datasetIds"
  523. >
  524. <div class="flex items-center gap-1.5">
  525. <div
  526. class="flex h-10 flex-1 items-center overflow-hidden rounded-sm bg-[#F6F8FC] px-2.5"
  527. >
  528. <img
  529. src="@/assets/images/knowledge/knowledge-item-icon-2.png"
  530. class="mr-4 size-6"
  531. />
  532. <div
  533. class="flex-1 text-sm font-bold text-[#2E3238]"
  534. v-title
  535. >
  536. {{ DictionaryStore.knowledges.map.get(id) }}
  537. </div>
  538. </div>
  539. <SvgIcon
  540. class="__hover"
  541. name="czr_del"
  542. color="var(--czr-error-color)"
  543. @click="
  544. state.form.advise.datasetIds.splice(index, 1)
  545. "
  546. />
  547. </div>
  548. </template>
  549. </div>
  550. </template>
  551. <template v-else>
  552. <div
  553. class="mt-2 flex h-8 items-center justify-center rounded-sm bg-[#F6F8FC] text-xs text-[#A7ADB9]"
  554. >
  555. 暂未添加问题建议知识库
  556. </div>
  557. </template>
  558. </template>
  559. </template>
  560. </div>
  561. </div>
  562. </CzrForm>
  563. </div>
  564. <div
  565. class="col-span-1 flex flex-col gap-2 overflow-hidden rounded-lg bg-[#F6F8FC] p-4"
  566. >
  567. <div
  568. class="flex h-14 w-full items-center gap-2.5 rounded-lg bg-[#ffffff] px-6"
  569. >
  570. <div class="text-2xl font-bold text-[#303133]">预览</div>
  571. <div
  572. class="flex items-center gap-2 text-sm text-[var(--czr-error-color)]"
  573. v-if="modelApplyCpt"
  574. >
  575. <SvgIcon name="czr_tip" color="var(--czr-error-color)" />
  576. 编排中有申请中的模型,预览暂不可用
  577. </div>
  578. <el-tooltip content="重新开始" :raw-content="true" placement="top">
  579. <SvgIcon class="__hover ml-auto" name="refresh" :active="true" />
  580. </el-tooltip>
  581. </div>
  582. <div class="relative flex-1 overflow-hidden rounded-lg">
  583. <chat :ID="state.ID" />
  584. <div
  585. v-if="!state.isDebug"
  586. class="absolute inset-0 flex h-full w-full flex-col rounded-lg bg-white/50 p-20 backdrop-blur-sm"
  587. >
  588. <div class="text-2xl font-bold text-[#1d2939]">编排已改变</div>
  589. <div class="mt-6 text-[#667085]">
  590. 修改编排将重置调试区域,确定吗?
  591. </div>
  592. <div class="mt-6 flex gap-2">
  593. <CzrButton
  594. type="primary"
  595. title="重新开始"
  596. icon="refresh"
  597. size="18"
  598. @click="state.isDebug = true"
  599. />
  600. <CzrButton
  601. type="normal"
  602. title="取消"
  603. @click="state.isDebug = true"
  604. />
  605. </div>
  606. </div>
  607. </div>
  608. </div>
  609. </div>
  610. </div>
  611. <knowledgeSelect
  612. v-model:show="state.knowledgeSelect.show"
  613. :transfer="state.knowledgeSelect.transfer"
  614. @refresh="getKnowledge"
  615. />
  616. <modelSelect
  617. v-model:show="state.modelSelect.show"
  618. :transfer="state.modelSelect.transfer"
  619. @refresh="getModel"
  620. />
  621. <workflowSelect
  622. v-model:show="state.workflowSelect.show"
  623. :transfer="state.workflowSelect.transfer"
  624. @refresh="getWorkflow"
  625. />
  626. <templateSelect
  627. v-model:show="state.templateSelect.show"
  628. @insert="(val) => ((state.form.tips = val), ref_tips.focus())"
  629. />
  630. <templateDetail
  631. v-model:show="state.templateDetail.show"
  632. :transfer="state.templateDetail.transfer"
  633. />
  634. <CzrDialog
  635. :show="state.publish.show"
  636. title="发布"
  637. @onClose="state.publish.show = false"
  638. @onSubmit="onPublishSubmit"
  639. width="42.5rem"
  640. height="auto"
  641. >
  642. <div class="bm-form">
  643. <CzrForm ref="ref_formPublish">
  644. <CzrFormColumn
  645. required
  646. label="发布渠道"
  647. :span="24"
  648. v-model:param="state.publish.form.name"
  649. link="select"
  650. :options="[
  651. { label: '个人', value: '1' },
  652. { label: '公共空间(需审批)', value: '2' },
  653. ]"
  654. :clearable="false"
  655. />
  656. <CzrFormColumn
  657. v-if="state.publish.form.name == 2"
  658. label="申请留言"
  659. :span="24"
  660. v-model:param="state.publish.form.description"
  661. type="textarea"
  662. :rows="10"
  663. />
  664. </CzrForm>
  665. </div>
  666. </CzrDialog>
  667. </div>
  668. </template>
  669. <script setup lang="ts">
  670. import {
  671. computed,
  672. getCurrentInstance,
  673. inject,
  674. nextTick,
  675. onMounted,
  676. reactive,
  677. ref,
  678. watch,
  679. } from 'vue'
  680. import { useRoute, useRouter } from 'vue-router'
  681. import { ElLoading, ElMessage } from 'element-plus'
  682. import { useAppStore, useDialogStore, useDictionaryStore } from '@/stores'
  683. import { Search } from '@element-plus/icons-vue'
  684. import knowledgeSelect from './knowledge-select.vue'
  685. import modelSelect from './model-select.vue'
  686. import { isValue, YMDHms } from '@/utils/czr-util'
  687. import Sortable from 'sortablejs'
  688. import { debounce } from 'lodash'
  689. import chat from '@/views/chat/index.vue'
  690. import templateSelect from './template-select.vue'
  691. import templateDetail from './template-detail.vue'
  692. import workflowSelect from './workflow-select.vue'
  693. import CzrForm from '@/components/czr-ui/CzrForm.vue'
  694. import CzrDialog from '@/components/czr-ui/CzrDialog.vue'
  695. import toBackCom from '@/views/manage/components/to-back.vue'
  696. import {
  697. appModelConfigDetail,
  698. appModelConfigSave,
  699. } from '@/api/modules/app/make'
  700. import { appDetail } from '@/api/modules/app'
  701. const DictionaryStore = useDictionaryStore()
  702. const AppStore = useAppStore()
  703. const DialogStore = useDialogStore()
  704. const route = useRoute()
  705. const router = useRouter()
  706. const emit = defineEmits([])
  707. const props = defineProps({})
  708. const { proxy }: any = getCurrentInstance()
  709. enum AdviseType {
  710. Open = 1,
  711. Tips = 2,
  712. Knowledge = 3,
  713. }
  714. const state: any = reactive({
  715. ID: route.params.id,
  716. isInit: false,
  717. autoSaveTimestamp: '',
  718. form: {
  719. tips: '',
  720. modelId: '',
  721. components: [],
  722. datasetIds: [],
  723. workflowId: '',
  724. prologue: '',
  725. prologueQuestions: [],
  726. prologueNum: 3,
  727. userInputMethod: 1,
  728. voicePackage: '',
  729. advise: {
  730. types: [],
  731. modelId: '',
  732. tips: '',
  733. datasetIds: [],
  734. },
  735. },
  736. prologueQuestionsArr: [],
  737. detail: {},
  738. config: {},
  739. knowledgeSelect: {
  740. show: false,
  741. transfer: {},
  742. },
  743. modelSelect: {
  744. show: false,
  745. transfer: {},
  746. },
  747. workflowSelect: {
  748. show: false,
  749. transfer: {},
  750. },
  751. templateSelect: {
  752. show: false,
  753. },
  754. templateDetail: {
  755. show: false,
  756. transfer: {},
  757. },
  758. publish: {
  759. show: false,
  760. transfer: {},
  761. form: {},
  762. },
  763. prologuesAdd: {
  764. value: '',
  765. },
  766. dragRefresh: true,
  767. isDebug: true,
  768. history: [
  769. { type: 'submit', date: '2024-03-06 22:24:54' },
  770. { type: 'publish', date: '2024-03-06 22:24:54' },
  771. { type: 'apply', date: '2024-03-06 22:24:54' },
  772. { type: 'publish', date: '2024-03-06 22:24:54' },
  773. { type: 'submit', date: '2024-03-06 22:24:54' },
  774. { type: 'publish', date: '2024-03-06 22:24:54' },
  775. { type: 'submit', date: '2024-03-06 22:24:54' },
  776. { type: 'publish', date: '2024-03-06 22:24:54' },
  777. { type: 'create', date: '2024-03-06 22:24:54' },
  778. ],
  779. })
  780. const ref_form = ref()
  781. const ref_formPublish = ref()
  782. const ref_tips = ref()
  783. const ref_prologue = ref()
  784. const ref_prologueBody = ref()
  785. const modelApplyCpt = computed(() => {
  786. return true
  787. })
  788. watch(
  789. () => state.form,
  790. (n) => {
  791. if (state.isInit) {
  792. console.log('触发自动保存')
  793. autoSave(n)
  794. }
  795. },
  796. { deep: true },
  797. )
  798. const autoSave = debounce((v) => {
  799. const loading = ElLoading.service({
  800. text: '自动保存中……',
  801. background: 'rgba(0, 0,0, 0.3)',
  802. })
  803. const params: any = {
  804. appId: state.ID,
  805. prePrompt: state.form.tips,
  806. openingStatement: state.form.prologue,
  807. showAll: state.form.prologueNum,
  808. suggestedQuestions: state.form.prologueQuestions,
  809. userInputMethod: state.form.userInputMethod,
  810. voice: state.form.voicePackage,
  811. qsType: state.form.advise.types,
  812. qsModelId: state.form.advise.modelId,
  813. qsPrePrompt: state.form.advise.tips,
  814. qsDatasetIds: state.form.advise.datasetIds,
  815. }
  816. if (state.detail.type === 0) {
  817. params.modelId = state.form.modelId
  818. params.datasetIds = state.form.datasetIds
  819. } else {
  820. params.workflowId = state.form.workflowId
  821. }
  822. appModelConfigSave(params)
  823. .then(({ data }: any) => {
  824. state.autoSaveTimestamp = data.updateTime
  825. state.isDebug = false
  826. })
  827. .catch(() => {})
  828. .finally(() => {
  829. loading.close()
  830. })
  831. }, 5000)
  832. const initDetail = () => {
  833. state.isInit = false
  834. if (state.ID) {
  835. appDetail(state.ID)
  836. .then(({ data: appData }: any) => {
  837. state.detail = appData
  838. document.title = state.detail.name
  839. appModelConfigDetail(state.ID)
  840. .then(({ data: configData }: any) => {
  841. state.autoSaveTimestamp = configData.updateTime
  842. state.config = JSON.parse(JSON.stringify(configData))
  843. state.form.tips = configData.prePrompt || ''
  844. state.form.prologue = configData.openingStatement || ''
  845. state.form.prologueNum = Number(configData.showAll) || 3
  846. state.form.prologueQuestions = configData.suggestedQuestions || []
  847. state.prologueQuestionsArr = state.form.prologueQuestions.map(
  848. (v) => ({
  849. value: v,
  850. }),
  851. )
  852. state.form.userInputMethod = Number(configData.userInputMethod) || 0
  853. state.form.voicePackage = configData.voice || ''
  854. state.form.advise.types =
  855. configData.qsType?.map((v) => Number(v)) || []
  856. state.form.advise.modelId = configData.qsModelId || ''
  857. state.form.advise.tips = configData.qsPrePrompt || ''
  858. state.form.advise.datasetIds = configData.qsDatasetIds || []
  859. if (state.detail.type === 0) {
  860. state.form.modelId = configData.modelId || ''
  861. state.form.datasetIds = configData.datasetIds || []
  862. } else {
  863. state.form.workflowId = configData.workflowId
  864. }
  865. setTimeout(() => {
  866. state.isInit = true
  867. }, 100)
  868. })
  869. .catch(() => {})
  870. .finally(() => {
  871. state.loading = false
  872. })
  873. })
  874. .catch(() => {})
  875. .finally(() => {
  876. state.loading = false
  877. })
  878. // pluginDetail(state.ID)
  879. // .then(({ data }: any) => {
  880. // state.detail = data
  881. // })
  882. // .catch(() => {})
  883. // .finally(() => {})
  884. initDrag()
  885. } else {
  886. router.push({ name: 'd446bfb3-4605-477f-a0f4-b7a0a1aa78fe' })
  887. }
  888. }
  889. const initDrag = () => {
  890. nextTick(() => {
  891. const tbody =
  892. ref_prologueBody.value.parentElement.querySelector('.drag-body')
  893. new Sortable(tbody, {
  894. animation: 150,
  895. handle: '.drag-icon', // 设置可拖拽行的类名(el-table自带的类名)
  896. onEnd: ({ newIndex, oldIndex }: any) => {
  897. const d = [...state.prologueQuestionsArr]
  898. const targetRow = d[oldIndex]
  899. d.splice(oldIndex, 1)
  900. d.splice(newIndex, 0, targetRow)
  901. state.prologueQuestionsArr = [...d]
  902. state.dragRefresh = false
  903. setTimeout(() => {
  904. state.dragRefresh = true
  905. initDrag()
  906. }, 0)
  907. },
  908. })
  909. })
  910. }
  911. const onAddKnowledge = () => {
  912. state.knowledgeSelect.transfer = {
  913. ids: state.form.datasetIds,
  914. type: 'knowledge',
  915. }
  916. state.knowledgeSelect.show = true
  917. }
  918. const onAddAdviseKnowledge = () => {
  919. state.knowledgeSelect.transfer = {
  920. ids: state.form.advise.datasetIds,
  921. type: 'advise',
  922. }
  923. state.knowledgeSelect.show = true
  924. }
  925. const onPrologue = (row) => {
  926. if (row) {
  927. row.__value = row.value + ''
  928. row.__edit = true
  929. } else {
  930. state.prologuesAdd.__edit = true
  931. }
  932. const t = setInterval(() => {
  933. const r = ref_prologue.value
  934. if (r) {
  935. if (r.length) {
  936. r[0].focus()
  937. } else {
  938. r.focus()
  939. }
  940. clearInterval(t)
  941. }
  942. }, 100)
  943. }
  944. const onAddPrologue = () => {
  945. state.form.prologueQuestions.push(state.prologuesAdd.value + '')
  946. state.prologueQuestionsArr.push({ value: state.prologuesAdd.value + '' })
  947. state.prologuesAdd.value = ''
  948. }
  949. const onEditPrologue = (row, index) => {
  950. if (isValue(row.__value.trim())) {
  951. row.value = row.__value + ''
  952. state.form.prologueQuestions[index] = row.__value + ''
  953. }
  954. row.__edit = false
  955. }
  956. const getKnowledge = (arr) => {
  957. switch (state.knowledgeSelect.transfer.type) {
  958. case 'knowledge':
  959. {
  960. state.form.datasetIds.push(...arr.map((v) => v.id))
  961. }
  962. break
  963. case 'advise':
  964. {
  965. state.form.advise.datasetIds.push(...arr.map((v) => v.id))
  966. }
  967. break
  968. }
  969. }
  970. const getModel = (val) => {
  971. switch (state.modelSelect.transfer.type) {
  972. case 'model':
  973. {
  974. state.form.modelId = val.value
  975. }
  976. break
  977. case 'advise':
  978. {
  979. state.form.advise.modelId = val
  980. }
  981. break
  982. }
  983. }
  984. const onModel = () => {
  985. state.modelSelect.transfer = {
  986. type: 'model',
  987. id: state.form.modelId,
  988. }
  989. state.modelSelect.show = true
  990. }
  991. const onAdviseModel = () => {
  992. state.modelSelect.transfer = {
  993. type: 'advise',
  994. id: state.form.advise.modelId,
  995. }
  996. state.modelSelect.show = true
  997. }
  998. const onPublish = () => {
  999. ref_form.value
  1000. .submit()
  1001. .then(() => {
  1002. if (state.detail.type == 1) {
  1003. if (!state.form.workflowId) {
  1004. ElMessage.warning('请添加工作流!')
  1005. return
  1006. }
  1007. }
  1008. if (state.form.advise.types.includes(AdviseType.Knowledge)) {
  1009. if (state.form.advise.datasetIds.length === 0) {
  1010. ElMessage.warning('请添加问题建议知识库!')
  1011. return
  1012. }
  1013. }
  1014. // if (modelApplyCpt.value) {
  1015. // DialogStore.confirm({
  1016. // content: `编排中有申请中的模型,暂不可发布!`,
  1017. // props: {
  1018. // showSubmit: false,
  1019. // showCancel: false,
  1020. // },
  1021. // })
  1022. // }
  1023. // if (1) {
  1024. // DialogStore.confirm({
  1025. // title: '发布失败',
  1026. // content: `该应用有发布中的申请,无法提交发布!<br/>申请提交时间:2023-02-02 15:21:23`,
  1027. // onSubmit: () => {},
  1028. // props: {
  1029. // submitText: '撤销上一次的申请,重新发布',
  1030. // cancelText: '等待审批',
  1031. // },
  1032. // })
  1033. // }
  1034. if (1) {
  1035. state.publish.transfer = {}
  1036. state.publish.show = true
  1037. }
  1038. })
  1039. .catch((e) => {
  1040. ElMessage({
  1041. message: e[0].message,
  1042. grouping: true,
  1043. type: 'warning',
  1044. })
  1045. })
  1046. }
  1047. const onPublishSubmit = () => {
  1048. ref_formPublish.value
  1049. .submit()
  1050. .then(() => {
  1051. DialogStore.confirm({
  1052. content: `请确认是否提交?`,
  1053. onSubmit: () => {
  1054. state.publish.show = false
  1055. },
  1056. })
  1057. })
  1058. .catch((e) => {
  1059. ElMessage({
  1060. message: e[0].message,
  1061. grouping: true,
  1062. type: 'warning',
  1063. })
  1064. })
  1065. }
  1066. const onAddTipsTemplate = () => {
  1067. state.templateDetail.transfer = {
  1068. mode: 'add',
  1069. tips: state.form.tips + '',
  1070. }
  1071. state.templateDetail.show = true
  1072. }
  1073. const onTipsTemplate = () => {
  1074. state.templateSelect.show = true
  1075. }
  1076. const onAddWorkflow = () => {
  1077. state.workflowSelect.transfer = {
  1078. id: state.form.workflowId,
  1079. }
  1080. state.workflowSelect.show = true
  1081. }
  1082. const getWorkflow = (val) => {
  1083. state.form.workflowId = val.id
  1084. }
  1085. onMounted(() => {
  1086. initDictionary()
  1087. initDetail()
  1088. })
  1089. const initDictionary = () => {
  1090. DictionaryStore.initWorkflows()
  1091. DictionaryStore.initModels()
  1092. DictionaryStore.initKnowledges(AppStore.tenantInfo?.id)
  1093. }
  1094. </script>
  1095. <style lang="scss" scoped>
  1096. :deep(.form) {
  1097. flex: 1;
  1098. overflow: hidden;
  1099. .el-form {
  1100. width: 100%;
  1101. height: 100%;
  1102. .el-row {
  1103. width: 100%;
  1104. height: 100%;
  1105. .transparent-input {
  1106. .el-input__wrapper {
  1107. padding: 0;
  1108. }
  1109. }
  1110. }
  1111. }
  1112. }
  1113. .__czr-title_2 {
  1114. height: 2rem;
  1115. }
  1116. </style>