ChatAgentInput.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. <script setup>
  2. import { ref, unref, onMounted, onUnmounted, computed, watch } from 'vue';
  3. import { useMessage, NInput, NSwitch, NPopover, NScrollbar, NUpload, NTooltip, NProgress, NButton } from 'naive-ui';
  4. import { useUserStore } from '@/stores/modules/userStore';
  5. import { baseURL } from '@/utils/request';
  6. import { getFormatYesterDay } from '@/utils/format';
  7. import { helperApi } from '@/api/helper';
  8. import { TheArchival } from "@/components"
  9. import SvgIcon from '@/components/SvgIcon';
  10. import 'load-awesome/css/ball-running-dots.min.css';
  11. const props = defineProps({
  12. activeItem: {
  13. type: Object,
  14. default: () => ({})
  15. }
  16. });
  17. const emit = defineEmits(['onClick', 'onEnter']);
  18. const MAX_NUM = 5;
  19. const useStore = useUserStore();
  20. const modelLoading = defineModel('loading');
  21. const selectOptions = [
  22. { title: 'LibraAl', subTitle: '水务行业专家', value: 0 },
  23. { title: 'Deepseek', subTitle: '适合深度思考', value: 1 },
  24. ]
  25. const switchStatus = defineModel('switch');
  26. const message = useMessage();
  27. const isTempActiveStatus = ref(false);
  28. const inpVal = ref('');
  29. const inpRef = ref(null);
  30. const isFocusState = ref(false);
  31. const isOpen = ref(false);
  32. const highlightedIndex = ref(0);
  33. const selectedOption = ref(null);
  34. const helperList = ref([]);
  35. const popoverTriggerRef = ref(null);
  36. const popoverInnerRef = ref(null);
  37. const scrollRef = ref(null);
  38. const uploadFileList = ref([]);
  39. const uploadLoading = ref(false);
  40. const agentOptions = computed(() => helperList.value.filter(({ tools }) => tools));
  41. const lastFileListIndex = computed(() => uploadFileList.value.length == 0 ? 0 : uploadFileList.value.length - 1);
  42. const focusInput = _ => isFocusState.value = true;
  43. const blurInput = _ => isFocusState.value = false;
  44. watch(inpVal, (curVal) => {
  45. if (curVal === "@" && curVal.length === 1) {
  46. if ( !unref(agentOptions).length ) {
  47. return message.warning('当前未配置智能体');
  48. }
  49. if ( modelLoading.value ) {
  50. return message.warning('当前对话进行中');
  51. }
  52. isOpen.value = true;
  53. } else {
  54. isOpen.value = false;
  55. }
  56. })
  57. watch(() => props.activeItem, (curVal) => {
  58. selectedOption.value = curVal?.tools ? curVal : null;
  59. })
  60. const handleInpFocus = () => {
  61. inpRef.value?.focus();
  62. }
  63. const commonEmitEvent = (eventName) => {
  64. const val = unref(inpVal);
  65. const len = val.trim().length;
  66. if ( !len ) {
  67. return message.warning('请输入您的问题或需求');
  68. }
  69. if ( len > 2000 ) {
  70. return message.warning('问题限制2000个字以内');
  71. }
  72. if ( modelLoading.value ) {
  73. return message.warning('当前对话进行中');
  74. }
  75. if ( uploadLoading.value ) {
  76. return message.warning('文件上传中,请稍后');
  77. }
  78. emit(eventName, { showVal: val, question: val, selectedOption: selectedOption.value || {}, uploadFileList: uploadFileList.value });
  79. inpVal.value = '';
  80. uploadFileList.value = [];
  81. }
  82. // 回车事件
  83. const handleInpEnter = (event) => {
  84. if (event.key === 'Enter' && !event.shiftKey && inpVal.value) {
  85. event.preventDefault();
  86. commonEmitEvent('onEnter');
  87. inpRef.value?.blur();
  88. }
  89. }
  90. // 点击事件
  91. const handleBtnClick = () => {
  92. commonEmitEvent("onClick");
  93. }
  94. const clearInpVal = () => {
  95. inpVal.value = '';
  96. }
  97. // 键盘上键无限滚动
  98. const scrollKeyUp = (index) => {
  99. const itemLength = agentOptions.value.length;
  100. if ( itemLength - MAX_NUM > index ) {
  101. scrollRef.value.scrollBy({
  102. top: -40 ,
  103. behavior: 'smooth'
  104. })
  105. }
  106. if ( index == itemLength - 1 ) {
  107. scrollRef.value.scrollTo({
  108. top: itemLength * 40,
  109. behavior: 'smooth'
  110. })
  111. }
  112. }
  113. // 键盘下键无限滚动
  114. const scrollKeyDown = (index) => {
  115. if ( index >= MAX_NUM ) {
  116. scrollRef.value.scrollBy({
  117. top: 40,
  118. behavior: 'smooth'
  119. })
  120. }
  121. if ( index == 0 ) {
  122. scrollRef.value.scrollTo({
  123. top: 0,
  124. behavior: 'smooth'
  125. })
  126. }
  127. }
  128. // 键盘事件
  129. const handleKeyDown = (event) => {
  130. const len = unref(agentOptions).length;
  131. if ( !isOpen.value ) return;
  132. switch (event.key) {
  133. case 'ArrowUp':
  134. event.preventDefault();
  135. highlightedIndex.value = (unref(highlightedIndex) - 1 + len) % len;
  136. scrollKeyUp(unref(highlightedIndex), 1)
  137. break;
  138. case 'ArrowDown':
  139. event.preventDefault();
  140. highlightedIndex.value = (unref(highlightedIndex) + 1) % len;
  141. scrollKeyDown(unref(highlightedIndex))
  142. break;
  143. case 'Enter':
  144. event.preventDefault();
  145. selectOption(unref(highlightedIndex));
  146. break;
  147. default:
  148. break;
  149. }
  150. }
  151. const formatFileItem = (file) => {
  152. const { name } = file;
  153. return {
  154. name: name.substring(0, name.lastIndexOf('.')),
  155. url: "",
  156. size: (file.file.size / 1024).toFixed(2) + "KB",
  157. suffix: name.substring( name.lastIndexOf('.') + 1 ).toUpperCase(),
  158. originSuffix:name.substring( name.lastIndexOf('.') )
  159. }
  160. }
  161. // 处理点击空白处关闭
  162. const closePopoverOutside =(event) => {
  163. if (!isOpen.value) return;
  164. const triggerResult = popoverTriggerRef.value.contains(event.target);
  165. const innerResult = popoverInnerRef.value.contains(event.target);
  166. isOpen.value = triggerResult || innerResult;
  167. }
  168. // 选中选项
  169. const selectOption = (index) => {
  170. selectedOption.value = agentOptions.value[index];
  171. highlightedIndex.value = index;
  172. isOpen.value = false;
  173. inpVal.value = selectedOption.value.content;
  174. }
  175. const beforeUpload = ({ file }) => {
  176. if (( file.file?.size / ( 1024 * 1024 ) ) > 5) {
  177. message.warning("文件大小超过5MB,请重新选择");
  178. return false;
  179. }
  180. const index = unref(lastFileListIndex);
  181. uploadFileList.value[index] = {
  182. ...formatFileItem(file),
  183. percentage: 0
  184. }
  185. uploadLoading.value = true
  186. return true;
  187. }
  188. // 上传文件
  189. const handleUploadChange = ({ file }) => {
  190. if (file.status === 'uploading') {
  191. uploadFileList.value[lastFileListIndex.value].percentage = file.percentage;
  192. }
  193. }
  194. // 文件上传完成
  195. const handleFinish = ({ file, event }) => {
  196. try {
  197. const res = JSON.parse( (event?.target).response );
  198. if ( res.code == 200 ) {
  199. uploadFileList.value[lastFileListIndex.value] = {
  200. ...uploadFileList.value[lastFileListIndex.value],
  201. url: res.data,
  202. }
  203. uploadLoading.value = false
  204. }
  205. } catch (error) {
  206. console.log("上传完成, 但是存在错误", error);
  207. }
  208. }
  209. // 上传失败
  210. const handleUploadError = (error) => {
  211. uploadLoading.value = false;
  212. }
  213. // 删除文件
  214. const onRemoveFile = (i) => {
  215. // uploadFileList.value.splice(i, 1);
  216. uploadFileList.value = [];
  217. }
  218. const clearFileList = () => {
  219. uploadFileList.value = [];
  220. }
  221. // 切换智能体
  222. const onChangeAgent = ({ value }) => {
  223. switchStatus.value = value;
  224. }
  225. onMounted(async () => {
  226. const { data } = await helperApi.getHelperList();
  227. const result = getFormatYesterDay(data)
  228. helperList.value = result;
  229. document.addEventListener('keydown', handleKeyDown);
  230. document.addEventListener('click', closePopoverOutside);
  231. })
  232. onUnmounted(() => {
  233. document.removeEventListener('keydown', handleKeyDown);
  234. document.removeEventListener('click', closePopoverOutside);
  235. })
  236. defineExpose({
  237. clearFileList,
  238. clearInpVal,
  239. handleInpFocus,
  240. inpVal,
  241. })
  242. </script>
  243. <template>
  244. <NPopover
  245. trigger="hover"
  246. width="trigger"
  247. display-directive="show"
  248. content-style="padding: 0;"
  249. :show-arrow="false"
  250. :show="isOpen"
  251. >
  252. <template #trigger>
  253. <div class="popover-trigger" ref="popoverTriggerRef">
  254. <div class="chat-inp-outer border-[1px]" :class="[{ 'border-[#2454FF]': isFocusState }]">
  255. <ul class="chat-tools-inner py-[10px] px-[10px] bg-[#fcfcfc]" v-show="selectedOption">
  256. <li class="tools-tips space-x-[10px]">
  257. <span>与</span>
  258. <p class="agent-name space-x-[5px]" @click="isOpen = true">
  259. <img src="https://static.fuxicarbon.com/userupload/db77ffe0cef843278a23b0d2db9505fa.png" alt="">
  260. <span>{{ selectedOption?.title }}</span>
  261. </p>
  262. <span>对话中</span>
  263. </li>
  264. <li class="tools-close" @click="selectedOption = null">
  265. <SvgIcon name="chat-icon-close-btn"></SvgIcon>
  266. </li>
  267. </ul>
  268. <ul class="file-list-wrapper" v-show="uploadFileList.length">
  269. <li class="file-item space-x-[14px]" v-for="(item, index) in uploadFileList" :key="index">
  270. <div class="file-icon"></div>
  271. <div class="file-info">
  272. <p class="title">{{ item.name }}</p>
  273. <p class="info space-x-[8px]">
  274. <span class="suffix">{{ item.suffix }}</span>
  275. <span class="size">{{ item.size }}</span>
  276. </p>
  277. </div>
  278. <span class="close" @click="onRemoveFile(i)" v-show="item.percentage == 100 && !uploadLoading">x</span>
  279. <div class="file-progress" v-if="uploadLoading">
  280. <NProgress
  281. type="line"
  282. color="#3153f5"
  283. :percentage="item.percentage"
  284. :show-indicator="false"
  285. :height="3"
  286. ></NProgress>
  287. </div>
  288. </li>
  289. </ul>
  290. <div class="chat-inp-inner">
  291. <div class="inp-wrapper flex-1" @click="handleInpFocus">
  292. <div class="upload-inner">
  293. <NUpload
  294. accept=".doc, .docx, .pdf, .txt"
  295. :disabled="uploadLoading"
  296. :show-file-list="false"
  297. :action="baseURL + '/qiniuyun/upLoadImage'"
  298. :headers="{
  299. 'Authorization': 'Bearer' + useStore.token,
  300. }"
  301. @on-error="handleUploadError"
  302. @change="handleUploadChange"
  303. @finish="handleFinish"
  304. @before-upload="beforeUpload"
  305. >
  306. <NTooltip trigger="hover">
  307. <template #trigger>
  308. <div class="upload-file-button"></div>
  309. </template>
  310. <span class="text-[12px]">支持上传文件(每次一个, 大小5MB以内)接受.doc、.docx、.pdf、.txt等格式的文件</span>
  311. </NTooltip>
  312. </NUpload>
  313. </div>
  314. <div class="w-full ml-[15px]">
  315. <NInput
  316. class="flex-1"
  317. ref="inpRef"
  318. type="textarea"
  319. size="medium"
  320. placeholder="输入@,召唤智能体"
  321. v-model:value="inpVal"
  322. :autosize="{ minRows: 1, maxRows: 3 }"
  323. @focus="focusInput"
  324. @blur="blurInput"
  325. @keypress="handleInpEnter"
  326. />
  327. </div>
  328. </div>
  329. <div class="option-wrapper">
  330. <div class="option-list space-x-[10px]">
  331. <n-popover trigger="focus" to=".option-wrapper" raw :show-arrow="false" placement="top-start" :width="150" content-class="content-class" content-style="padding: 0;">
  332. <template #trigger>
  333. <n-button text>
  334. <div class="switch-agent option">
  335. <span>{{ selectOptions[switchStatus].title }}</span>
  336. <i class="n-base-icon n-base-suffix__arrow"><svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.14645 5.64645C3.34171 5.45118 3.65829 5.45118 3.85355 5.64645L8 9.79289L12.1464 5.64645C12.3417 5.45118 12.6583 5.45118 12.8536 5.64645C13.0488 5.84171 13.0488 6.15829 12.8536 6.35355L8.35355 10.8536C8.15829 11.0488 7.84171 11.0488 7.64645 10.8536L3.14645 6.35355C2.95118 6.15829 2.95118 5.84171 3.14645 5.64645Z" fill="currentColor"></path></svg></i>
  337. </div>
  338. </n-button>
  339. </template>
  340. <ul class="select-agent-options space-y-[8px]">
  341. <li class="agent-option-item" v-for="item in selectOptions" :key="item.value" @click="onChangeAgent(item)">
  342. <p class="">
  343. <span class="agent-title">{{ item.title }}</span>
  344. <span class="agent-sub-title">{{ item.subTitle }}</span>
  345. </p>
  346. <SvgIcon name="chat-icon-arrow-down" v-show="switchStatus == item.value"></SvgIcon>
  347. </li>
  348. </ul>
  349. </n-popover>
  350. <div class="internet-agent option space-x-[4px]" :class="{'agent-active': isTempActiveStatus}" @click="isTempActiveStatus = !isTempActiveStatus">
  351. <!-- <SvgIcon name="chat-icon-internet"></SvgIcon> -->
  352. <span class="icon"></span>
  353. <span>联网搜索</span>
  354. <span class="circle"></span>
  355. </div>
  356. <!-- <div class="internet-agent option space-x-[4px]">
  357. <SvgIcon name="chat-icon-deep"></SvgIcon>
  358. <span>深度思考(T1)</span>
  359. <span class="circle"></span>
  360. </div> -->
  361. </div>
  362. <div class="submit-btn">
  363. <button class="btn bg-[#1A2029] hover:bg-[#3C4148]" @click="handleBtnClick">
  364. <SvgIcon name="tool-send-plane" size="22" v-show="!modelLoading"></SvgIcon>
  365. <div style="color: #fff" class="la-ball-running-dots la-sm" v-show="modelLoading">
  366. <div v-for="item in 5" :key="item"></div>
  367. </div>
  368. </button>
  369. </div>
  370. </div>
  371. </div>
  372. </div>
  373. <div class="switch-inner pt-[8px] flex justify-center items-center">
  374. <!-- <div class="space-x-[6px]">
  375. <NSwitch size="small" v-model:value="switchStatus"></NSwitch>
  376. <span class="text-[12px] text-[#9E9E9E]">使用deepseek R1</span>
  377. </div> -->
  378. <div>
  379. <TheArchival></TheArchival>
  380. </div>
  381. </div>
  382. <div class="masking-inner text-center text-[#2454FF]"></div>
  383. </div>
  384. </template>
  385. <div class="popover-inner" ref="popoverInnerRef">
  386. <div class="header">
  387. <span>选择智能体</span>
  388. <p class="tools-close" @click="isOpen = false">
  389. <SvgIcon name="chat-icon-close-btn"></SvgIcon>
  390. </p>
  391. </div>
  392. <NScrollbar style="max-height: 200px;" ref="scrollRef" trigger="none">
  393. <div class="item" v-for="item, index in agentOptions" :class="['item', { active: highlightedIndex === index }]" @click="selectOption(index)">
  394. <p class="icon">
  395. <img :src="item.banner" alt="">
  396. </p>
  397. <p class="ml-[10px] space-x-[5px] text">
  398. <span class="text-[15px]">{{item.title}}</span>
  399. <!-- <span class="text-[#888] text-[14px]">这里可以补充个描述</span> -->
  400. </p>
  401. </div>
  402. </NScrollbar>
  403. </div>
  404. </NPopover>
  405. </template>
  406. <style scoped lang="scss">
  407. .chat-inp-outer {
  408. border-radius: 8px;
  409. // overflow: hidden;
  410. box-shadow: 0px 3px 12px 0px #97D3FF40;
  411. .chat-tools-inner {
  412. border-top-left-radius: 8px;
  413. border-top-right-radius: 8px;
  414. margin-bottom: -3px;
  415. @include flex(x, center, between);
  416. .tools-tips {
  417. @include flex(x, center, start);
  418. color: #666;
  419. font-size: 14px;
  420. .agent-name {
  421. @include flex(x, center, start);
  422. font-weight: bold;
  423. color: #333;
  424. cursor: pointer;
  425. img {
  426. width: 14px;
  427. height: 14px;
  428. }
  429. }
  430. }
  431. }
  432. .chat-inp-inner {
  433. position: relative;
  434. border-radius: 8px;
  435. padding: 17px 17px 17px 17px;
  436. background: #fff;
  437. .inp-wrapper {
  438. @include flex(x, start, center);
  439. margin-bottom: 30px;
  440. .upload-inner {
  441. width: 30px;
  442. height: 30px;
  443. padding-top: 2px;
  444. .upload-file-button {
  445. width: 30px;
  446. height: 30px;
  447. background: url("@/assets/svgs/chat/icon-file-default.svg") center center no-repeat;
  448. cursor: pointer;
  449. &:hover {
  450. background: url("@/assets/svgs/chat/icon-file-active.svg") center center no-repeat;
  451. }
  452. }
  453. }
  454. }
  455. .option-wrapper {
  456. @include flex(x, center, between);
  457. .option-list {
  458. @include flex(x, center, between);
  459. .option {
  460. @include flex(x, center, between);
  461. height: 32px;
  462. padding: 0 8px;
  463. border: 1px solid #f5f5f5;
  464. border-radius: 8px;
  465. font-size: 14px;
  466. font-weight: bold;
  467. color: #1A2029;
  468. cursor: pointer;
  469. .icon {
  470. display: block;
  471. width: 16px;
  472. height: 16px;
  473. }
  474. .circle {
  475. width: 6px;
  476. height: 6px;
  477. border-radius: 100%;
  478. background: #BDBDBD;
  479. }
  480. }
  481. .switch-agent {
  482. width: 110px;
  483. background: #f5f5f5;
  484. }
  485. .internet-agent {
  486. transition: all 0.3s ease;
  487. .icon {
  488. background: url("@/assets/svgs/chat/icon-internet.svg");
  489. }
  490. &:hover {
  491. color: #2E5CFF;
  492. .circle {
  493. background: #2E5CFF;
  494. }
  495. .icon {
  496. background: url("@/assets/svgs/chat/icon-internet-active.svg");
  497. }
  498. }
  499. }
  500. .agent-active {
  501. background: #eaeff8;
  502. color: #2E5CFF;
  503. .circle {
  504. background: #2E5CFF;
  505. }
  506. .icon {
  507. background: url("@/assets/svgs/chat/icon-internet-active.svg");
  508. }
  509. }
  510. }
  511. }
  512. .submit-btn {
  513. @include flex(x, center, center);
  514. .btn {
  515. @include flex(x, center, center);
  516. width: 50px;
  517. height: 32px;
  518. border-radius: 32px;
  519. transition: all .3s;
  520. }
  521. }
  522. }
  523. .file-list-wrapper {
  524. padding: 10px;
  525. padding-bottom: 0px;
  526. background: #fff;
  527. .file-item {
  528. position: relative;
  529. @include flex(x, center, start);
  530. width: 30%;
  531. // height: 52px;
  532. padding: 10px;
  533. border-radius: 4px;
  534. background: #f5f5f5;
  535. .file-icon {
  536. flex-shrink: 0;
  537. width: 36px;
  538. height: 36px;
  539. background: url("@/assets/svgs/chat/icon-file.svg") center center no-repeat;
  540. }
  541. .file-info {
  542. flex: 1;
  543. @include flex(y, start, start);
  544. font-size: 12px;
  545. .title {
  546. width: 170px;
  547. color: #1a2029;
  548. text-overflow: ellipsis;
  549. overflow: hidden;
  550. word-break: break-all;
  551. white-space: nowrap;
  552. }
  553. .info {
  554. flex: 1;
  555. @include flex(x, center, between);
  556. color: #838a95;
  557. }
  558. }
  559. .file-progress {
  560. position: absolute;
  561. left: 0;
  562. bottom: 0;
  563. width: 100%;
  564. margin: 0;
  565. }
  566. .close {
  567. position: absolute;
  568. top: 0;
  569. right: 0;
  570. width: 16px;
  571. height: 16px;
  572. border: 1px solid #fff;
  573. border-radius: 100%;
  574. transform: translate(40%, -40%);
  575. background: #c8c8c8;
  576. font-size: 10px;
  577. text-align: center;
  578. line-height: 12px;
  579. cursor: pointer;
  580. color: #fff;
  581. &:hover {
  582. background: #b7b7b7;
  583. }
  584. }
  585. }
  586. }
  587. }
  588. .popover-inner {
  589. .header {
  590. @include flex(x, center, between);
  591. padding-bottom: 8px;
  592. font-size: 14px;
  593. color: #666;
  594. }
  595. .item {
  596. @include flex(x, center, start);
  597. padding: 8px 10px;
  598. cursor: pointer;
  599. &:hover {
  600. background: #f0fafe;
  601. }
  602. .icon {
  603. @include flex(x, center, center);
  604. width: 24px;
  605. height: 24px;
  606. border-radius: 100%;
  607. background: #e9eef8;
  608. img {
  609. width: 16px;
  610. height: 16px;
  611. }
  612. }
  613. .text {
  614. text-align: left;
  615. overflow: hidden;
  616. white-space: nowrap;
  617. text-overflow: ellipsis;
  618. }
  619. }
  620. .active {
  621. background: #f0fafe;
  622. }
  623. }
  624. .tools-close {
  625. @include flex(x, center, center);
  626. width: 28px;
  627. height: 28px;
  628. border-radius: 6px;
  629. background: #fff;
  630. cursor: pointer;
  631. &:hover {
  632. background: #e9eef8;
  633. }
  634. }
  635. .masking-inner {
  636. position: absolute;
  637. top: -30px;
  638. left: 0;
  639. width: 100%;
  640. height: 30px;
  641. background: linear-gradient(180deg, rgba(232, 241, 250, 0) 0%, #E7F0FA 95%);
  642. }
  643. </style>
  644. <style lang="scss">
  645. .chat-inp-outer {
  646. .n-input-wrapper {
  647. padding: 0;
  648. }
  649. }
  650. .option-wrapper {
  651. .n-popover {
  652. border-radius: 8px !important;
  653. box-shadow: none;
  654. }
  655. }
  656. .select-agent-options {
  657. width: 150px;
  658. padding: 12px 19px;
  659. border: 1px solid #ddd;
  660. border-radius: 8px;
  661. background: #fff;
  662. .agent-option-item {
  663. @include flex(x, center, between);
  664. cursor: pointer;
  665. .agent-title, .agent-sub-title {
  666. display: block;
  667. }
  668. .agent-title {
  669. font-size: 14px;
  670. font-weight: bold;
  671. color: #1A2029;
  672. }
  673. .agent-sub-title {
  674. font-size: 12px;
  675. color: #666;
  676. }
  677. }
  678. }
  679. </style>