dialog.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. <template>
  2. <div class="page">
  3. <div class="back adfac" v-if="dialogList.length||idx" @click="handleBack">
  4. <img src="@/assets/images/agent/arrow_left.png">
  5. <span>返回</span>
  6. </div>
  7. <div class="box adffcacjc" v-if="dialogList.length===0">
  8. <img src="@/assets/images/agent/dialog_logo2.gif">
  9. <div class="title">Hi,我是AI团队教练助手~</div>
  10. <div class="tip">任何关于团队、分析报告、教练的问题,你都可以向我提问,可以为你提供全方位多角度的PREILL团队分析,我正在快速学习和进化中,有新功能时,我一定第一时间告诉你~ </div>
  11. <div class="items">
  12. <template v-if="!idx">
  13. <div class="item" :class="{'active':idx===1}" @click="handleChange(1)">
  14. <div class="i_top adfac">
  15. <img src="@/assets/images/agent/dialog_zndh.png">
  16. <span>智能对话</span>
  17. </div>
  18. <p>解答关于团队、分析报告、教练的问题</p>
  19. </div>
  20. <div class="item" :class="{'active':idx===2}" @click="handleChange(2)">
  21. <div class="i_top adfac">
  22. <img src="@/assets/images/agent/dialog_bgfx.png">
  23. <span>报告分析</span>
  24. </div>
  25. <p>智能解读报告,快速找到关键信息,提出教练重点</p>
  26. </div>
  27. <div class="item" :class="{'active':idx===3}" @click="handleChange(3)">
  28. <div class="i_top adfac">
  29. <img src="@/assets/images/agent/dialog_wdwd.png">
  30. <span>文档问答</span>
  31. </div>
  32. <p>提供全方位多角度的PREILL团队分析</p>
  33. </div>
  34. </template>
  35. </div>
  36. </div>
  37. <div class="dialog" ref="scrollableDiv" v-else>
  38. <div class="list">
  39. <div class="l_item" v-for="(item,index) in dialogList" :key="index">
  40. <div class="li_pre my adfac" v-if="item.type===1">
  41. <div class="li_file adfac" v-if="item.askFile">
  42. <img src="@/assets/images/agent/agent_file.png" v-if="idx===2">
  43. <img src="@/assets/images/agent/agent_file.png" v-else-if="idx===3">
  44. <div class="lif_info">
  45. <p>{{ item.qfileName }}</p>
  46. <p><span>{{ item.qfileType }}</span> <span>{{ item.qfileSize }}</span></p>
  47. </div>
  48. </div>
  49. <div class="text" v-else>{{ item.question }}</div>
  50. <img class="img" src="@/assets/images/agent/dialog_avatar.png">
  51. </div>
  52. <div class="li_pre ai" v-else-if="item.type===2">
  53. <div class="adfac">
  54. <img class="img" src="@/assets/images/agent/dialog_logo3.png">
  55. <div class="text" v-if="item.answer" v-html="sanitizeHtml(item.answer)"></div>
  56. <div class="text" v-else>
  57. <img src="@/assets/images/agent/dialog_loading.gif">
  58. <span>正在思考中</span>
  59. </div>
  60. </div>
  61. <div class="icons adfac" v-if="item.answer">
  62. <el-popover popper-class="icon_pop" placement="bottom" trigger="hover" content="重新生成">
  63. <template #reference>
  64. <img alt="重新生成" :src="isSx?require('@/assets/images/agent/dialog_sx2.png'):require('@/assets/images/agent/dialog_sx1.png')" @click="handleSx(index,item?.taskId)" class="f">
  65. </template>
  66. </el-popover>
  67. <el-popover popper-class="icon_pop" placement="bottom" trigger="hover" content="复制">
  68. <template #reference>
  69. <img alt="复制" :src="isFz?require('@/assets/images/agent/dialog_fz2.png'):require('@/assets/images/agent/dialog_fz1.png')" @click="handleFz(index)">
  70. </template>
  71. </el-popover>
  72. <el-popover popper-class="icon_pop" placement="bottom" trigger="hover" content="喜欢">
  73. <template #reference>
  74. <img alt="喜欢" :src="isDz?require('@/assets/images/agent/dialog_dz2.png'):require('@/assets/images/agent/dialog_dz1.png')" @click="handleDz">
  75. </template>
  76. </el-popover>
  77. <el-popover popper-class="icon_pop" placement="bottom" trigger="hover" content="评论">
  78. <template #reference>
  79. <img alt="评论" :src="isPl?require('@/assets/images/agent/dialog_pl2.png'):require('@/assets/images/agent/dialog_pl1.png')" @click="handlePl">
  80. </template>
  81. </el-popover>
  82. </div>
  83. </div>
  84. <div class="li_pre ai adfac" v-else-if="item.type===3">
  85. <img class="img" src="@/assets/images/agent/dialog_logo3.png">
  86. <div class="text">
  87. <div class="title">报告分析</div>
  88. <div class="tip">智能解读报告,快速找到关键信息,提出教练重点</div>
  89. <el-upload
  90. :action="uploadUrl"
  91. :headers="uploadHeaders"
  92. :on-success="e=>uploadFileSuccess(e,'report')"
  93. :before-upload="e=>beforeAvatarUpload(e,'report')"
  94. :file-list="fileList"
  95. :limit="1">
  96. <div class="upload adfac">
  97. <img src="@/assets/images/agent/upload.png">
  98. <div class="span">上传报告</div>
  99. </div>
  100. </el-upload>
  101. </div>
  102. </div>
  103. <div class="li_pre ai adfac" v-else-if="item.type===4">
  104. <img class="img" src="@/assets/images/agent/dialog_logo3.png">
  105. <div class="text">
  106. <div class="title">文档问答</div>
  107. <div class="tip">提供全方位多角度的PREILL团队分析</div>
  108. <el-upload
  109. :action="uploadUrl"
  110. :headers="uploadHeaders"
  111. :on-success="e=>uploadFileSuccess(e,'file')"
  112. :before-upload="e=>beforeAvatarUpload(e,'file')"
  113. :file-list="fileList"
  114. :limit="1">
  115. <div class="upload adfac">
  116. <img src="@/assets/images/agent/upload.png">
  117. <div class="span">上传文档</div>
  118. </div>
  119. </el-upload>
  120. </div>
  121. </div>
  122. </div>
  123. </div>
  124. </div>
  125. <div class="ask">
  126. <div class="a_top">
  127. <el-input type="textarea" :rows="3" :placeholder="placeholder" v-model="question" resize="none" @keyup.enter.native="handleQuestion"></el-input>
  128. </div>
  129. <div class="a_bottom adfacjb">
  130. <div class="ab_l adfac">
  131. <div class="abl_pre adfac active">
  132. <img src="@/assets/images/agent/think3.png">
  133. <span>创衡增强</span>
  134. </div>
  135. <div class="abl_pre adfac active">
  136. <img src="@/assets/images/agent/intenet3.png">
  137. <span>联网搜索</span>
  138. </div>
  139. <!-- <div class="abl_pre adfac" :class="{'active':isScwd}" @click="isScwd=!isScwd">
  140. <el-upload
  141. :action="uploadUrl"
  142. :headers="uploadHeaders"
  143. :on-success="uploadFileSuccess"
  144. :before-upload="beforeAvatarUpload"
  145. :limit="1">
  146. <div>
  147. <img src="@/assets/images/agent/upload.png" v-if="!isScwd">
  148. <img src="@/assets/images/agent/upload2.png" v-else>
  149. <span>上传文档</span>
  150. </div>
  151. </el-upload>
  152. </div> -->
  153. </div>
  154. <div class="ab_r">
  155. <img src="@/assets/images/agent/input_hou.png" v-if="question" @click="handleQuestion" style="cursor: pointer;">
  156. <img src="@/assets/images/agent/input_qian.png" v-else style="cursor: not-allowed;">
  157. </div>
  158. </div>
  159. </div>
  160. </div>
  161. </template>
  162. <script setup name="">
  163. import Cookies from "js-cookie";
  164. import DOMPurify from 'dompurify';
  165. import { ref, getCurrentInstance, watch } from 'vue'
  166. import useClipboard from 'vue-clipboard3';
  167. import { sendChatMessageStream,stopChatMessage } from '@/api/agent'
  168. const { toClipboard } = useClipboard();
  169. const { proxy } = getCurrentInstance();
  170. const uploadUrl = `${window.SITE_CONFIG["apiURL"]}/sys/oss/uploadFile`
  171. const uploadHeaders = {token:Cookies.get("token")};
  172. const idx = ref('')
  173. const placeholder = ref('有什么问题,可以向我提问!')
  174. const question = ref('')
  175. const isChzq = ref(false)
  176. const isLwss = ref(false)
  177. const isScwd = ref(false)
  178. const isSx = ref(false)
  179. const isFz = ref(false)
  180. const isDz = ref(false)
  181. const isPl = ref(false)
  182. const dialogList = ref([])
  183. const fileList = ref([])
  184. const questionFile = ref('')
  185. const scrollableDiv = ref(null);
  186. const askParams = ref({
  187. query:'',
  188. identity:'教练',
  189. })
  190. const askFileName = ref('')
  191. const askFileType = ref('')
  192. const askFileSize = ref('')
  193. const currentTaskId = ref('');
  194. const handleBack = () => {
  195. dialogList.value = [];
  196. idx.value = '';
  197. placeholder.value = '有什么问题,可以向我提问!';
  198. questionFile.value = '';
  199. askParams.value = {
  200. query:'',
  201. identity:'教练',
  202. };
  203. askFileName.value = '';
  204. askFileType.value = '';
  205. askFileSize.value = '';
  206. proxy.$router.push('/agent-dialog');
  207. }
  208. const handleChange = (val) => {
  209. idx.value = val;
  210. dialogList.value = []
  211. if(val===1) placeholder.value = '有什么问题,可以向我提问!';
  212. else if(val===2){
  213. placeholder.value = '上传报告并输入问题提问';
  214. let obj = {type:3}
  215. dialogList.value.unshift(obj)
  216. }
  217. else if(val===3){
  218. placeholder.value = '上传文件并输入问题提问';
  219. let obj = {type:4}
  220. dialogList.value.unshift(obj)
  221. }
  222. }
  223. const uploadFileSuccess = (e,type) =>{
  224. questionFile.value = e.data;
  225. if(type==='report') askParams.value.report = e.data;
  226. else if(type==='file') askParams.value.file = e.data;
  227. proxy?.$modal.msgSuccess('上传成功');
  228. let fnc = JSON.parse(JSON.stringify(askFileName.value));
  229. let fnt = JSON.parse(JSON.stringify(askFileType.value));
  230. let fns = JSON.parse(JSON.stringify(askFileSize.value));
  231. dialogList.value.push({type:1,askFile:true,qfileName:fnc,qfileType:fnt,qfileSize:fns})
  232. fileList.value = [];
  233. }
  234. const beforeAvatarUpload = (e,t) => {
  235. let type = e.name.split('.')[e.name.split('.').length-1];
  236. let isTxt = e.type === 'text/plain';
  237. let isPdf = e.type === 'application/pdf';
  238. let isHtml = e.type === 'text/html';
  239. let isExcel = e.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
  240. let isDocx = e.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
  241. if((type.toLowerCase() === 'xlsx' || type.toLowerCase() === 'xls' && isExcel)
  242. || (type.toLowerCase() === 'docx' && isDocx)
  243. || (type.toLowerCase() === 'txt' && isTxt)
  244. || (type.toLowerCase() === 'pdf' && isPdf)
  245. || (type.toLowerCase() === 'html' && isHtml)
  246. && e.size <= 1024*1024*15){
  247. askFileName.value = e.name;
  248. askFileType.value = type.toUpperCase();
  249. askFileSize.value = (e.size/1024/1024).toFixed(1)+'MB';
  250. }else{
  251. proxy?.$modal.msgError('请上传txt、pdf、html、xlsx、xls或docx格式的文件!且文件大小不超过15MB!');
  252. return false;
  253. }
  254. }
  255. const handleQuestion = () => {
  256. if(question.value.trim()=='') return proxy.$message.warning('请输入问题');
  257. if(idx.value===2&&!questionFile.value) return proxy.$message.warning('请上传报告')
  258. if(idx.value===3&&!questionFile.value) return proxy.$message.warning('请上传文档')
  259. let qc = JSON.parse(JSON.stringify(question.value.trim()));
  260. let obj1 = {
  261. question: question.value.trim(),
  262. type: 1
  263. }
  264. dialogList.value = [...dialogList.value,obj1]
  265. let obj2 = {
  266. answer: '',
  267. type: 2
  268. }
  269. dialogList.value = [...dialogList.value,obj2]
  270. question.value = '';
  271. askParams.value.query = qc;
  272. startStream();
  273. }
  274. const startStream = async (query) => {
  275. try {
  276. const response = await fetch(`${window.SITE_CONFIG['apiURL']}/core/chat/messageByWorkflow`, {//streamingMessage messages messageByWorkflow
  277. method: 'POST',
  278. headers: {
  279. 'token':Cookies.get('token') || '',
  280. 'Content-Type': 'application/json',
  281. // 'Accept': 'text/event-stream',
  282. },
  283. body: JSON.stringify(askParams.value)
  284. });
  285. if (!response.ok || !response.body) {
  286. throw new Error(`请求失败: ${response.status}`);
  287. }
  288. const reader = response.body.getReader();
  289. const decoder = new TextDecoder('utf-8');
  290. let buffer = ''; // 累积缓冲器
  291. while (true) {
  292. const { value, done } = await reader.read();
  293. if (done) break;
  294. buffer += decoder.decode(value, { stream: true });
  295. const events = buffer.split('\n\n');
  296. buffer = events.pop() || '';
  297. events.forEach(event => {
  298. if (event.startsWith('data:')) {
  299. try {
  300. const jsonStr = event.slice(5).trim();
  301. const jsonData = JSON.parse(jsonStr);
  302. currentTaskId.value = jsonData?.task_id || '';
  303. dialogList.value = [...dialogList.value].map((item, idx) => {
  304. if (idx === dialogList.value.length - 1) {
  305. return { ...item, answer: item.answer + (jsonData?.answer.replace(/\n\n+/g, '<br><br>').replace(/\n+/g, '<br>') || ''),taskId: jsonData?.task_id || '' };
  306. }
  307. return item;
  308. });
  309. } catch (e) {
  310. console.error('SSE解析失败', e, '原始数据:', event);
  311. }
  312. }
  313. });
  314. }
  315. } catch (err) {
  316. console.log(err,'err');
  317. }
  318. };
  319. const sanitizeHtml = (html) => {
  320. if (!html) return '';
  321. return DOMPurify.sanitize(html);
  322. }
  323. const handleSx = (index,taskId) => {
  324. stopChatMessage(taskId).then((res) => {
  325. if(res.code!==0) return proxy.$modal.msgError(res.message);
  326. dialogList.value[index].answer = '';
  327. let query = dialogList.value[index-1].question;
  328. startStream(query)
  329. })
  330. }
  331. const handleFz = async (index) => {
  332. try {
  333. await toClipboard(dialogList.value[index].answer);
  334. proxy.$message.success('复制成功');
  335. } catch (e) {
  336. proxy.$message.error('复制失败');
  337. }
  338. }
  339. const handleDz = () => {
  340. isDz.value = !isDz.value;
  341. }
  342. const handlePl = () => {
  343. isPl.value = !isPl.value;
  344. }
  345. watch(() => dialogList.value, (newVal) => {
  346. if(newVal.length>0) {
  347. if(scrollableDiv.value){
  348. setTimeout(() => {
  349. scrollableDiv.value.scrollTop = 999999;
  350. }, 50);
  351. }
  352. }
  353. })
  354. </script>
  355. <style>
  356. .icon_pop{
  357. background:#000 !important;
  358. min-width: inherit !important;
  359. color: #FFFFFF !important;
  360. border-radius: 10px;
  361. padding: 10px !important;
  362. }
  363. .icon_pop .popper__arrow::after{
  364. border-bottom-color:#000 !important;
  365. }
  366. .el-upload-list{
  367. display: none !important;
  368. }
  369. </style>
  370. <style scoped lang="scss">
  371. ::v-deep .el-textarea textarea{
  372. border: none !important;
  373. padding: 0 !important;
  374. }
  375. .page{
  376. width: 100%;
  377. padding: 40px 290px;
  378. box-sizing: border-box;
  379. position: relative;
  380. .back{
  381. position: absolute;
  382. left: 30px;
  383. top: 20px;
  384. cursor: pointer;
  385. img{
  386. width: 36px;
  387. height: 36px;
  388. }
  389. span{
  390. margin-left: 10px;
  391. font-family: PingFang-SC, PingFang-SC;
  392. font-weight: bold;
  393. font-size: 16px;
  394. color: #252525;
  395. line-height: 22px;
  396. }
  397. }
  398. .box{
  399. width: 100%;
  400. height: calc(100vh - 228px);
  401. &>img{
  402. width: 99px;
  403. height: 94px;
  404. }
  405. .title{
  406. font-family: PingFang-SC, PingFang-SC;
  407. font-weight: bold;
  408. font-size: 24px;
  409. color: #252525;
  410. line-height: 33px;
  411. text-align: center;
  412. margin-top: 30px;
  413. }
  414. .tip{
  415. max-width: 647px;
  416. font-family: PingFangSC, PingFang SC;
  417. font-weight: 400;
  418. font-size: 14px;
  419. color: #646464;
  420. line-height: 20px;
  421. text-align: center;
  422. margin-top: 16px;
  423. }
  424. .items{
  425. width: 700px;
  426. min-height: 124px;
  427. margin-top: 40px;
  428. display: flex;
  429. justify-content: space-between;
  430. .item{
  431. width: calc(100% / 3 - 13px);
  432. padding: 17px 16px;
  433. box-sizing: border-box;
  434. background: #FFFFFF;
  435. box-shadow: 0px 2px 12px 0px rgba(0,0,0,0.04);
  436. border-radius: 10px;
  437. cursor: pointer;
  438. &.active,&:hover{
  439. border: 1px solid #33A7A7;
  440. padding: 16px 15px;
  441. }
  442. .i_top{
  443. img{
  444. width: 32px;
  445. height: 32px;
  446. }
  447. span{
  448. font-family: PingFang-SC, PingFang-SC;
  449. font-weight: bold;
  450. font-size: 16px;
  451. color: #252525;
  452. line-height: 22px;
  453. margin-left: 12px;
  454. }
  455. }
  456. p{
  457. font-family: PingFangSC, PingFang SC;
  458. font-weight: 400;
  459. font-size: 14px;
  460. color: #999999;
  461. line-height: 20px;
  462. margin-top: 18px;
  463. }
  464. }
  465. }
  466. }
  467. .dialog{
  468. width: 100%;
  469. height: calc(100vh - 228px);
  470. padding: 24px 0;
  471. flex: 1;
  472. position: relative;
  473. overflow-y: auto;
  474. .list{
  475. .l_item{
  476. margin-top: 36px;
  477. &:first-child{
  478. margin-top: 0;
  479. }
  480. .li_pre{
  481. .text{
  482. max-width: calc(100% - 60px);
  483. padding: 14px 16px;
  484. box-sizing: border-box;
  485. border-radius: 8px;
  486. font-family: PingFangSC, PingFang SC;
  487. font-weight: 400;
  488. font-size: 14px;
  489. line-height: 20px;
  490. .title{
  491. font-family: PingFang-SC, PingFang-SC;
  492. font-weight: bold;
  493. font-size: 16px;
  494. color: #393939;
  495. line-height: 16px;
  496. }
  497. .tip{
  498. font-family: PingFangSC, PingFang SC;
  499. font-weight: 400;
  500. font-size: 14px;
  501. color: #999999;
  502. line-height: 14px;
  503. margin-top: 16px;
  504. }
  505. .upload{
  506. width: 112px;
  507. background: #FFFFFF;
  508. border-radius: 18px;
  509. border: 1px solid #E5E7EB;
  510. margin-top: 24px;
  511. padding: 8px 12px;
  512. box-sizing: border-box;
  513. cursor: pointer;
  514. img{
  515. width: 14px;
  516. height: 14px;
  517. margin: 0;
  518. }
  519. .span{
  520. font-family: PingFangSC, PingFang SC;
  521. font-weight: 400;
  522. font-size: 14px;
  523. color: #252525;
  524. line-height: 20px;
  525. margin-left: 11px;
  526. }
  527. }
  528. }
  529. .li_file{
  530. max-width: calc(100% - 60px);
  531. padding: 14px 16px;
  532. box-sizing: border-box;
  533. background: #FFFFFF;
  534. box-shadow: 0px 2px 12px 0px rgba(0,0,0,0.04);
  535. border-radius: 8px;
  536. &>img{
  537. width: 30px;
  538. height: 30px;
  539. }
  540. .lif_info{
  541. margin-left: 16px;
  542. p{
  543. font-family: PingFangSC, PingFang SC;
  544. font-weight: 400;
  545. font-size: 14px;
  546. color: #1D2129;
  547. line-height: 14px;
  548. &:last-child{
  549. margin-top: 8px;
  550. span{
  551. font-family: PingFangSC, PingFang SC;
  552. font-weight: 400;
  553. font-size: 12px;
  554. color: #A6A6A6;
  555. line-height: 12px;
  556. }
  557. }
  558. }
  559. }
  560. }
  561. .icons{
  562. width: 216px;
  563. margin-top: 16px;
  564. margin-left: 60px;
  565. padding: 10px 20px 10px 0;
  566. background: #FFFFFF;
  567. border-radius: 8px;
  568. border: 1px solid #EFEFEF;
  569. img{
  570. width: 24px;
  571. height: 24px;
  572. margin: 0;
  573. margin-left: 20px;
  574. cursor: pointer;
  575. &.f{
  576. width: 44px;
  577. padding-right: 20px;
  578. border-right: 1px solid #E5E7EB;
  579. }
  580. }
  581. .line{
  582. width: 1px;
  583. height: 20px;
  584. background: #E5E7EB;
  585. }
  586. }
  587. .img{
  588. width: 36px;
  589. height: 36px;
  590. }
  591. &.my{
  592. justify-content: flex-end;
  593. .text{
  594. border: 1px solid #33A7A7;
  595. background: #33A7A7;
  596. color: #FFFFFF;
  597. }
  598. .img{
  599. margin-left: 24px;
  600. }
  601. }
  602. &.ai{
  603. .text{
  604. border: 1px solid #EFEFEF;
  605. background: #FFFFFF;
  606. color: #393939;
  607. &>img{
  608. width: 24px;
  609. height: 24px;
  610. }
  611. span{
  612. font-family: PingFang-SC, PingFang-SC;
  613. font-weight: bold;
  614. font-size: 14px;
  615. color: #393939;
  616. line-height: 20px;
  617. margin-left: 5px;
  618. }
  619. }
  620. .img{
  621. width: 42px;
  622. margin-right: 18px;
  623. }
  624. }
  625. }
  626. }
  627. }
  628. }
  629. .ask{
  630. width: 100%;
  631. height: 148px;
  632. background: #FFFFFF;
  633. box-shadow: 0px 2px 12px 0px rgba(0,0,0,0.04);
  634. border-radius: 16px;
  635. padding: 20px 24px;
  636. box-sizing: border-box;
  637. display: flex;
  638. flex-direction: column;
  639. position: relative;
  640. .a_top{
  641. flex: 1;
  642. padding-bottom: 10px;
  643. }
  644. .a_bottom{
  645. .ab_l{
  646. .abl_pre{
  647. background: #FFFFFF;
  648. border-radius: 18px;
  649. border: 1px solid #009191;
  650. padding: 10px 13px;
  651. margin-left: 16px;
  652. //cursor: pointer;
  653. &:first-child{
  654. margin-left: 0;
  655. }
  656. img{
  657. width: 20px;
  658. height: 20px;
  659. }
  660. span{
  661. font-family: PingFangSC, PingFang SC;
  662. font-weight: 400;
  663. font-size: 15px;
  664. color: #393939;
  665. margin-left: 7px;
  666. }
  667. &.active{
  668. background: #F0F9F9;
  669. span{
  670. color: #393939;
  671. }
  672. }
  673. }
  674. }
  675. .ab_r{
  676. img{
  677. width: 32px;
  678. height: 32px;
  679. border-radius: 7px;
  680. }
  681. }
  682. }
  683. }
  684. }
  685. </style>