1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692 |
- <template>
- <div class="ai-search-detail">
- <a-affix>
- <div class="header">
- <home-header @login="emits('login')" sub-title="自然资源大模型" />
- </div>
- </a-affix>
- <div class="search-panel" id="pageContainer">
- <a-affix
- ref="markdownTocAffixRef"
- :style="`width: 20%;margin-left: 13%;max-height: ${tocHeight}px;`"
- :offset-top="110"
- v-show="!showDoc"
- >
- <markdown-toc :no-data="activeTab === 'original'" :toc="tocs" :class="`markdown-toc-${askType}`" />
- </a-affix>
- <div :class="`search-detail search-detail-${askType}`" ref="searchRef">
- <div
- class="search-result"
- id="searchResult"
- style="overflow-x: hidden; height: auto"
- ref="messageContainer"
- >
- <div class="top">
- <div class="title">
- <div class="icon"></div>
- <div class="question" style="cursor: pointer" @click="openAskModal">
- {{ question }}
- </div>
- <div v-show="askType === 'tdsc'" class="graph-switch">
- <a-switch v-model:checked="graph" @change="questLandMark" />
- 生成图表
- </div>
- </div>
- </div>
- <div :class="`result-panel result-panel-${askType}`">
- <div class="search-steps" v-show="activeIndex !== -1 && activeIndex < 5">
- <div
- v-show="st.key <= activeIndex"
- :class="`step ${st.key < activeIndex ? 'step-done' : ''}`"
- v-for="(st, i) in steps"
- :key="`step-${i}`"
- >
- <div class="info">
- <div class="icon"></div>
- <div class="title">{{ st.name }}</div>
- <div class="tags">
- <div
- v-if="st.key === 2 && askType === 'tdsc'"
- style="width: 20%; font-size: 14px; line-height: 22px"
- class="tag-last"
- >
- {{ st.agent }}
- </div>
- <div
- :class="`tag${st.key === 0 ? ' tag-first' : ''}`"
- v-if="st.key < 3 && askType !== 'tdsc'"
- :title="t"
- v-for="(t, i) in st.tags"
- :key="`tag-${i}`"
- >
- {{ t }}
- </div>
- <div
- class="d"
- style="
- width: 80%;
- overflow: hidden;
- animation: scroll-text-992e3d76 25s linear infinite;
- "
- v-if="st.key < 3 && askType === 'tdsc'"
- >
- <div
- :title="t"
- :class="`tag${askType === 'tdsc' ? ' tag-rol' : ''}`"
- v-for="(t, i) in st.tags"
- :key="`tag-${i}`"
- >
- <div>{{ t }}</div>
- </div>
- </div>
- </div>
- </div>
- <div class="progress">
- <a-progress size="small" :show-info="false" :percent="st.prog" />
- </div>
- </div>
- </div>
- <div class="result">
- <template v-if="askType === 'zcfg'">
- <div class="tabs" style="position: relative">
- <div
- :class="`tab${activeTab === t.key ? ' tab-active' : ''}`"
- v-for="t in tabs"
- :key="t.key"
- @click="changeTab(t.key)"
- >
- <div class="title">{{ t.name }}</div>
- <div class="bottom"></div>
- </div>
- <div
- class="scope-type"
- style="
- display: flex;
- align-items: center;
- position: absolute;
- right: 5px;
- top: 10px;
- "
- >
- <div class="label">搜索模式:</div>
- <a-radio-group
- v-model:value="answerType"
- @change="changeAnswerType"
- name="radioGroup"
- >
- <a-radio value="0">简洁</a-radio>
- <a-radio value="1">深入</a-radio>
- <a-radio value="2">研究</a-radio>
- </a-radio-group>
- <div class="ds-box">
- <div @click="onChange('1')" :class="{active: dsChecked === true}">
- <i class="iconfont icon-deepsee"></i>
- </div>
- <div @click="onChange('0')" :class="{active: dsChecked === false}">
- <i class="iconfont icon-tongyi"></i>
- </div>
- </div>
- </div>
- </div>
- <div class="result-view">
- <div class="q-r">
- <div class="ds-content-box" v-if="dsChecked">
- <div class="icon">
- <img src="/images/icon-ds.png" />
- </div>
- <div class="ds-panel">
- <div class="ds-loading" v-if="dsHintTxt" @click="dsUp = !dsUp">
- {{ dsHintTxt }}
- <DownOutlined class="icon-arrow" :class="{ rotate: dsUp }" />
- </div>
- <div class="ds-con" v-if="!dsUp">
- <vue-markdown-it
- id="dsMarkdown"
- :source="
- currentResponse.streamMock
- ? currentResponse.streamMsg.indexOf('###') > -1
- ? currentResponse.streamMsg.substring(
- 0,
- currentResponse.streamMsg.indexOf('###')
- )
- : currentResponse.streamMsg
- : currentResponse.msg.indexOf('###') > -1
- ? currentResponse.msg.substring(
- 0,
- currentResponse.msg.indexOf('###')
- )
- : currentResponse.msg
- "
- :options="{
- html: true,
- linkify: true
- }"
- />
- </div>
- <a-spin :indicator="indicator" v-if="dsLoading" />
- </div>
- </div>
- <vue-markdown-it
- id="resMarkdown"
- :source="
- currentResponse.streamMock
- ? currentResponse.streamMsg.indexOf('###') > -1
- ? currentResponse.streamMsg.substring(
- currentResponse.streamMsg.indexOf('###')
- )
- : ''
- : dsChecked
- ? currentResponse.msg.indexOf('###') > -1
- ? currentResponse.msg.substring(currentResponse.msg.indexOf('###'))
- : ''
- : currentResponse.msg
- "
- :options="{
- html: true,
- linkify: true
- }"
- />
- </div>
- <a-skeleton
- active
- v-if="!dsLoading && (activeIndex < 2 || !currentResponse.msg)"
- />
- <div v-if="activeIndex >= 5" class="follow">
- <div class="follow-btn" style="cursor: pointer">
- <!-- <a-button v-show="activeTab === 'knowledge'" style="margin-right: 10px;float: left" type="link" color="green" size="small" @click="reAnswer">-->
- <a-button
- style="
- margin-right: 35px;
- float: left;
- font-family: Microsoft YaHei;
- font-weight: 400;
- font-size: 16px;
- color: #465d7c;
- "
- type="link"
- color="green"
- size="small"
- @click="changeTab(activeTab)"
- >
- 重新生成
- </a-button>
- <div class="eval-card" style="float: left">
- <div
- :class="`eval-icon eval-icon-like${evaluate === 'like' ? '-s' : ''}`"
- @click="evalResponse('like')"
- ></div>
- <a-divider type="vertical" />
- <div
- :class="`eval-icon eval-icon-dislike${
- evaluate === 'dislike' ? '-s' : ''
- }`"
- @click="evalResponse('dislike')"
- ></div>
- <a-divider type="vertical" />
- <a-dropdown :trigger="['click']">
- <a class="ant-dropdown-link" @click.prevent>
- <div :class="`eval-icon eval-icon-copy`"></div>
- </a>
- <template #overlay>
- <a-menu>
- <a-menu-item key="0" @click="copy()"> 复制到剪贴板</a-menu-item>
- <a-menu-divider />
- <a-menu-item key="1" @click="exportAnswer('doc')">
- 导出 Word
- </a-menu-item>
- <a-menu-divider />
- <a-menu-item key="2" @click="exportAnswer('pdf')">
- 导出 PDF
- </a-menu-item>
- </a-menu>
- </template>
- </a-dropdown>
- </div>
- </div>
- <div class="follow-input" v-show="followVisible">
- <a-textarea
- v-model:value="followQuestion"
- class="input-text"
- v-model="followQuestion"
- placeholder="继续提问"
- />
- <div class="button" @click="followAsk">发送</div>
- </div>
- </div>
- <!-- <div class="more-questions" v-if="activeTab === 'knowledge' && activeIndex >= 4">-->
- <div class="more-questions" v-if="activeIndex >= 4">
- <div class="title" style="margin-bottom: 14px">
- 你可以继续问我:
- <div class="change-title" @click="changeRecommendedQuestions">
- <div class="change-icon"></div>
- 换一批
- </div>
- </div>
- <div class="questions" style="cursor: pointer">
- <div
- class="question"
- @click="openRecommendedQuestion(q)"
- v-for="(q, i) in questions"
- :key="i"
- style="
- line-height: 14px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- width: fit-content;
- margin-bottom: 12px;
- background: #ffffff;
- box-shadow: 0px 1px 8px 0px #e4e4e4;
- border-radius: 10px;
- border: 1px solid #e9e9e9;
- padding: 16px;
- font-family: PingFang SC;
- font-weight: 500;
- font-size: 14px;
- color: #2185f2;
- "
- >
- {{ q }}
- </div>
- </div>
- </div>
- <div class="source" v-if="activeTab !== 'original'" v-show="activeIndex === 5">
- <div class="title">
- <div class="icon"></div>
- <div class="text">来源</div>
- <div class="num">({{ currentResponse.docs.length }})</div>
- </div>
- <div class="items">
- <div
- class="item"
- v-if="activeTab !== 'net'"
- v-for="(doc, i) in currentResponse.docs"
- :key="'doc-' + i"
- >
- <div class="doc">
- <p>
- <span
- class="ma"
- style="font-weight: bolder; color: #000000; margin-right: 10px"
- >出处 [{{ i + 1 }}] </span
- ><span class="doc-link" @click="openDoc(doc, i)">{{ doc.doc }}</span>
- </p>
- <div
- :class="`doc-icon${
- !doc.showContent ? ' doc-icon-show' : ' doc-icon-hide'
- }`"
- @click="doc.showContent = !doc.showContent"
- ></div>
- </div>
- <div :class="`content${doc.showContent ? '' : ' content-hide'}`">
- <p>{{ doc.content }}</p>
- </div>
- </div>
- <div
- class="item item-url"
- v-if="activeTab === 'net'"
- v-for="(doc, i) in currentResponse.docs"
- :key="'doc-' + i"
- >
- <div class="doc">
- <p>
- <span class="doc-link" @click="openUrl(doc.link)">{{ doc.title }}</span>
- </p>
- </div>
- <div class="bottom">
- <div class="title-icon">
- <div class="icon"></div>
- <div class="title">{{ doc.doc }}</div>
- </div>
- <div class="index">{{ i + 1 }}</div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <template v-else>
- <div v-if="activeIndex >= 1" class="map-answer">
- <div class="left-panel">
- <div class="content">
- <div class="summary-card">
- <div class="summary-title" id="tdscSummaryTitle">总结</div>
- <div class="summary-content">
- <a-skeleton active v-if="!currentResponse.msg" />
- <vue-markdown-it
- :source="
- currentResponse.streamMock
- ? currentResponse.streamMsg
- : currentResponse.msg
- "
- :options="{
- html: true,
- linkify: true
- }"
- />
- <!-- {{currentResponse.msg}}-->
- </div>
- </div>
- <div class="chart-card" v-if="currentResponse.hasChart" id="tdscChartCard">
- <div class="chart-title">生成图表</div>
- <a-skeleton
- active
- style="height: 100%"
- v-if="!currentResponse.chartOption"
- />
- <div v-else class="chart" id="summaryChart"></div>
- </div>
- </div>
- </div>
- <!-- <div class="map-panel" id="tdscMapPanel">-->
- <!-- <div class="map-title">空间位置</div>-->
- <!-- <div class="map">-->
- <!-- <a-i-map />-->
- <!-- </div>-->
- <!-- </div>-->
- </div>
- </template>
- </div>
- </div>
- </div>
- <!-- 历史回答 -->
- <div
- v-show="askType === 'zcfg'"
- class="search-result result-history"
- style="overflow-y: hidden; height: auto"
- v-if="questHistories.length > 0"
- id="messageContainer"
- >
- <template v-for="(qh, i) in [...questHistories].reverse()" :key="`qh-${i}`">
- <div class="top">
- <div class="title">
- <div class="icon"></div>
- <div class="question" style="cursor: pointer">{{ qh.question }}</div>
- </div>
- </div>
- <div class="result-panel">
- <div class="result">
- <div class="tabs">
- <div :class="`tab tab-active`">
- <div class="title">回答</div>
- <div class="bottom"></div>
- </div>
- </div>
- </div>
- <div class="result-view">
- <div class="q-r">
- <vue-markdown-it
- :source="qh.msg"
- :toc="true"
- :options="{
- html: true,
- linkify: true
- }"
- />
- </div>
- </div>
- </div>
- </template>
- </div>
- </div>
- <a-affix v-if="showDoc" style="width: 52%; font-size: 16px" :offset-top="210">
- <div
- class="doc"
- style="width: 100%; background-color: white; height: calc(100vh - 190px); overflow: auto"
- >
- <p-d-f-viewer
- v-if="fileType === 'pdf'"
- :src="pdfSrc"
- @close="closeDoc"
- :content="pdfContent"
- :num="pdfNum"
- />
- <word-viewer
- v-if="fileType === 'docx'"
- :src="pdfSrc"
- @close="closeDoc"
- :content="pdfContent"
- :num="pdfNum"
- >
- </word-viewer>
- <txt-viewer v-if="fileType === 'txt'" :src="pdfSrc" @close="closeDoc" :txt="pdfContent" />
- </div>
- </a-affix>
- </div>
- <a-modal force-render class="input-modal" v-model:open="open" :closable="false" width="700px">
- <template #footer>
- <div class="footer" style="height: 26px; margin: 0 auto">
- <div class="left" style="float: left; cursor: pointer" @click="clearModalTextArea">
- 清空
- </div>
- <a-button style="float: right" type="primary" size="small" @click="sendModalTextArea">
- 发送
- </a-button>
- </div>
- </template>
- <div class="modal-que">
- <ai-textarea :q="question" ref="aiTextAreaRef" @enter="ask" />
- </div>
- </a-modal>
- </div>
- </template>
- <script setup>
- import { fetchEventSource } from '@microsoft/fetch-event-source';
- import PDFViewer from '@/components/pdf/PdfCanvas.vue';
- import WordViewer from '@/components/pdf/WordViewer.vue';
- import AiTextarea from '@/components/TextArea/AiTextarea.vue';
- import { VueMarkdownIt } from '@f3ve/vue-markdown-it';
- import TxtViewer from '@/components/pdf/TxtViewer.vue';
- import { message } from 'ant-design-vue';
- import { ref, defineProps, watch } from 'vue';
- import CommonAPI from '@/api/common';
- import ManagerAPI from '@/api/manager';
- import AIMap from '@/components/Map/AIMap.vue';
- import * as echarts from 'echarts';
- import PubsubService from '@/utils/PubsubService';
- import MarkdownToc from '@/components/markdown-toc/MarkdownToc.vue';
- import HomeHeader from '@/views/home/components/HomeHeader.vue';
- import { useUserStore } from '@/stores';
- const { user: u = {}, updateUser } = useUserStore();
- const { user = {}, token } = u || {};
- import UserAPI from '@/api/user';
- const aiLoading = ref(false);
- const router = useRouter();
- const markdownTocAffixRef = ref(null);
- const tocHeight = ref(530);
- import { LoadingOutlined, DownOutlined } from '@ant-design/icons-vue';
- import { h } from 'vue';
- const indicator = h(LoadingOutlined, {
- style: {
- fontSize: '24px'
- },
- spin: true
- });
- const props = defineProps({
- searchType: '',
- askType: {
- type: String,
- default: 'zcfg'
- }
- });
- const answerType = ref('0');
- const dsChecked = ref(true);
- const scope = ref('net');
- watch(
- () => router.currentRoute.value,
- (value) => {
- const { query } = value;
- if (query.q) {
- ask(decodeURIComponent(query.q));
- answerType.value = query.type || '0';
- scope.value = query.scope || 'net';
- dsHintTxt.value = '';
- }
- }
- );
- watch(
- () => router.currentRoute.value,
- (value) => {
- const { query } = value;
- if (query.q) {
- var ds = query.ds;
- if (ds && ds == '1') {
- ds = true;
- } else {
- ds = false;
- }
- dsChecked.value = ds;
- }
- },
- {
- immediate: true
- }
- );
- watch(props.searchType, (val) => {
- activeTab.value = val;
- });
- const question = ref('国有土地的使用方式有哪些?');
- const followQuestion = ref('');
- const statusText = ref('检索中');
- const activeIndex = ref(0);
- const activeTab = ref(props.searchType);
- let ctr = null;
- const aiTextAreaRef = ref(null);
- const searchRef = ref(null);
- const evaluate = ref(null);
- const scrollToBottom = () => {
- const messageContainer = document.getElementById('messageContainer');
- if (messageContainer && searchRef.value) {
- }
- };
- const clearModalTextArea = () => {
- aiTextAreaRef.value.clear();
- };
- const sendModalTextArea = () => {
- aiTextAreaRef.value.send();
- };
- const tocs = ref([]);
- const open = ref(false);
- const showDoc = ref(false);
- const pdfSrc = ref('');
- const pdfContent = ref('');
- const pdfNum = ref(1);
- const fileType = ref('pdf');
- const dsLoading = ref(true);
- const startTime = ref(0);
- const endTime = ref(0);
- const dsHintTxt = ref('');
- const dsUp = ref(false);
- const times = ref(0);
- const timers = ref([]);
- const currentResponse = ref({
- streamMsg: '',
- streamMock: false,
- msg: '',
- originAnswer: '',
- docs: []
- });
- let streamMockInterval = null;
- const streamToAnswer = () => {
- console.log('streamToAnswer');
- currentResponse.value.index = 0;
- streamMockInterval = setInterval(() => {
- const { originAnswer = '', msg, streamMsg = '', id, index = 0 } = currentResponse.value;
- if (currentResponse.value.mockStart || index <= originAnswer.length) {
- currentResponse.value.streamMsg = originAnswer.substr(0, index + 2).replaceAll('\n', ' \n');
- if (originAnswer) {
- currentResponse.value.index += 2;
- }
- let num = getNum(currentResponse.value.streamMsg);
- while (num) {
- const docsNum = currentResponse.value.docs.length;
- if (docsNum && num > docsNum + 1) {
- }
- currentResponse.value.streamMsg = currentResponse.value.streamMsg.replace(
- `[[${num}]]`,
- `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style=" cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd; width: 20px;
- height: 20px;
- background: #FFFFFF;
- border-radius: 4px 4px 4px 4px;
- border: 1px solid #BACAE3;">${num}</span>`
- );
- num = getNum(currentResponse.value.streamMsg);
- }
- } else {
- if (streamMockInterval) {
- clearInterval(streamMockInterval);
- streamMockInterval = null;
- }
- activeIndex.value = 5;
- searchRef.value.scrollTop = 0;
- generateToc();
- console.log('mock 结束');
- }
- }, 50);
- };
- const tabs = [
- { key: 'knowledge', name: '知识库' },
- // { key: 'net', name: '全网' },
- { key: 'original', name: '原生' },
- ];
- const steps = ref([
- {
- key: 0,
- name: '问题分析',
- tags: [],
- prog: 0
- },
- {
- key: 1,
- name: '知识搜索',
- tags: [],
- prog: 0
- },
- {
- key: 2,
- name: '整理答案',
- tags: [],
- prog: 0
- },
- {
- key: 3,
- name: '回答完成',
- tags: [],
- prog: 0
- }
- ]);
- const doStep = () => {
- setInterval(() => {
- steps.value.forEach((v) => {
- if (v.key < activeIndex.value) {
- v.prog = 100;
- } else if (v.key === activeIndex.value) {
- if (v.prog + 1 === 100) {
- } else {
- v.prog += 1;
- }
- } else {
- v.prog = 0;
- }
- });
- }, 100);
- };
- // 相关案例关键词
- const caseKeyWords = ref([]);
- const setActiveIndexTags = (index, tags) => {
- steps.value[index].tags = [];
- // 把问题分析部分的关键词带出去
- if (index == 0 && tags.length > 0) {
- caseKeyWords.value = tags;
- }
- for (let i = 0; i < tags.length; i++) {
- setTimeout(function () {
- steps.value[index].tags = tags.slice(0, i + 1);
- }, 500 * (i + 1));
- }
- };
- const evalResponse = (evalType) => {
- // ManagerAPI.feedback(currentResponse.value.logId, evalType === 'like' ? 1 : 2).then((res) => {
- // evaluate.value = evalType;
- // message.info('感谢你的反馈!');
- // });
- };
- const changeStatusText = () => {
- let i = 0;
- setInterval(() => {
- statusText.value = '检索中' + '.'.repeat(i);
- if (i === 3) {
- i = 0;
- }
- i++;
- }, 500);
- };
- changeStatusText();
- const askType = ref('zcfg');
- const search = (q, type = 'zcfg') => {
- question.value = q;
- askType.value = type;
- if (ctr) {
- ctr.abort();
- ctr = null;
- if (streamMockInterval) {
- clearInterval(streamMockInterval);
- streamMockInterval = null;
- }
- }
- if (type === 'zcfg') {
- quest();
- } else {
- questLandMark();
- }
- };
- const graph = ref(false);
- const questLandMark = async () => {
- activeIndex.value = 0;
- tocs.value = [];
- steps.value = [
- {
- key: 0,
- name: '问题分析',
- tags: [],
- prog: 0
- },
- {
- key: 1,
- name: '任务拆解',
- tags: [],
- prog: 0
- },
- {
- key: 2,
- name: 'Agent调用',
- tags: [],
- prog: 0
- },
- {
- key: 3,
- name: '回答完成',
- tags: [],
- prog: 0
- }
- ];
- currentResponse.value = {
- question: question.value,
- id: 1,
- msg: '',
- originAnswer: '',
- docs: [],
- msgFinished: false,
- hasChart: false
- };
- await getQuestionKeyWords();
- ctr = new AbortController();
- await fetchEventSource(window.AppGlobalConfig.landMarketUrl, {
- method: 'POST',
- openWhenHidden: true,
- timeout: 300000,
- // redirect: 'follow',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- data: question.value + (graph.value ? ',需要生成图表' : ',不需要生成图表')
- }),
- // mode: 'no-cors',
- signal: ctr.signal,
- async onmessage(msg) {
- const { data } = msg;
- if (!['', '[FINISH]', '[DONE]'].includes(data)) {
- try {
- currentResponse.value.originAnswer = data;
- const response = JSON.parse(data);
- if (response) {
- const { agent_responses = [] } = response;
- for (let agent of agent_responses) {
- // 问题分析
- if (agent.agent_name === 'plan_dispatcher' && activeIndex.value === 0) {
- if (
- agent.choices.length > 0 &&
- agent.choices.filter((v) => v.role === 'assistant').length > 0
- ) {
- const chio = agent.choices.filter((v) => v.role === 'assistant')[0];
- if (chio.finished) {
- activeIndex.value = 1;
- setActiveIndexTags(0, [chio.content]);
- }
- }
- }
- if (agent.agent_name === 'land_supply_planner' && activeIndex.value === 1) {
- if (
- agent.choices.length > 0 &&
- agent.choices.filter((v) => v.role === 'assistant').length > 0
- ) {
- const chio = agent.choices.filter((v) => v.role === 'assistant')[0];
- if (chio.finished) {
- activeIndex.value = 2;
- setActiveIndexTags(1, [chio.content]);
- if (chio.content.includes('generate_chart')) {
- currentResponse.value.hasChart = true;
- }
- }
- }
- }
- if (
- ['generate_chart', 'LandSupplySqlAgent', 'summary'].includes(agent.agent_name) &&
- activeIndex.value === 2
- ) {
- if (
- agent.choices.length > 0 &&
- agent.choices.filter((v) => v.role === 'assistant').length > 0
- ) {
- const chio = agent.choices.filter((v) => v.role === 'assistant')[0];
- if (chio.finished && steps.value[2].agent !== agent.agent_name) {
- steps.value[2].agent = agent.agent_name;
- setActiveIndexTags(2, [chio.content]);
- }
- }
- }
- if (agent.agent_name === 'summary') {
- steps.value[2].agent = 'summary';
- if (activeIndex.value === 2) {
- activeIndex.value = 3;
- }
- if (agent.choices.length === 3) {
- if (!currentResponse.value.msgFinished) {
- currentResponse.value.msg = agent.choices[2].content;
- currentResponse.value.msgFinished = agent.choices[2].finished;
- }
- }
- }
- if (agent.agent_name === 'generate_chart') {
- currentResponse.value.hasChart = true;
- if (agent.executed) {
- const option = JSON.parse(
- agent.exec_res.replace('```json', '').replace('```', '').trim()
- );
- currentResponse.value.chartOption = option;
- setTimeout(() => {
- initChart(option);
- }, 1000);
- }
- }
- }
- }
- } catch (e) {
- console.error(e, data);
- }
- }
- if (data === '[FINISH]') {
- activeIndex.value = 5;
- }
- },
- onclose() {
- activeIndex.value = 5;
- generateToc();
- collectQuestion();
- },
- onerror(err) {
- throw err;
- },
- onopen() {}
- });
- };
- const openAskModal = () => {
- open.value = true;
- nextTick(() => {
- if (aiTextAreaRef.value) {
- aiTextAreaRef.value.setText(question.value);
- }
- });
- };
- const ask = async (q, isFllow) => {
- if (ctr) {
- if (streamMockInterval) {
- clearInterval(streamMockInterval);
- streamMockInterval = null;
- }
- ctr.abort();
- }
- times.value = 0;
- if (timers.value) {
- timers.value.forEach((t) => {
- clearTimeout(t);
- t = null;
- });
- }
- timers.value = [];
- if (dsChecked.value) {
- dsHintTxt.value = '';
- dsLoading.value = true;
- }
- question.value = q;
- open.value = false;
- showDoc.value = false;
- askType.value = 'zcfg';
- // activeTab.value = 'knowledge';
- quest(isFllow);
- };
- const followAsk = () => {
- ask(followQuestion.value, true);
- followVisible.value = false;
- followQuestion.value = '';
- showDoc.value = false;
- };
- let questionUrl = '/chat/kb_chat';
- const changeTab = (tab) => {
- dsLoading.value = dsChecked.value;
- dsHintTxt.value = '';
- times.value = 0;
- if (timers.value) {
- timers.value.forEach((t) => {
- clearTimeout(t);
- t = null;
- });
- }
- timers.value = [];
- if (tab === 'net') {
- questionUrl = '/chat/bing_chat';
- } else if(tab === 'knowledge'){
- questionUrl = '/chat/kb_chat';
- }else if(tab === 'original') {
- questionUrl = '/chat/chat';
- }
- activeTab.value = tab;
- if (ctr) {
- if (streamMockInterval) {
- clearInterval(streamMockInterval);
- streamMockInterval = null;
- }
- ctr.abort();
- }
- quest();
- };
- const onChange = (type) => {
- dsChange(type);
- changeAnswerType();
- };
- //ds绑定用户改变
- const dsChange = (type) => {
- dsChecked.value = type === '0' ? false : true;
- localStorage.setItem("_isDeepSeek", type);
- if (timers.value) {
- timers.value.forEach((t) => {
- clearTimeout(t);
- t = null;
- });
- timers.value = [];
- }
- times.value = 0;
- dsHintTxt.value = '';
- //打字机效果 切换会打印
- aiLoading.value = true;
- };
- const changeAnswerType = () => {
- if (!dsChecked.value) {
- dsLoading.value = false;
- dsHintTxt.value = '';
- } else {
- dsLoading.value = true;
- dsHintTxt.value = '';
- }
- if (ctr) {
- if (streamMockInterval) {
- clearInterval(streamMockInterval);
- streamMockInterval = null;
- }
- ctr.abort();
- }
- quest();
- };
- const questHistories = ref([]);
- const followVisible = ref(false);
- let scb = null;
- const quest = async (isFllow) => {
- startTime.value = Date.now();
- window.scroll({ top: 0, behavior: 'smooth' });
- tocs.value = [];
- question.value = (isFllow ? '追问: ' : '') + question.value;
- evaluate.value = null;
- activeIndex.value = 0;
- steps.value = [
- {
- key: 0,
- name: '问题分析',
- tags: [],
- prog: 0
- },
- {
- key: 1,
- name: '知识搜索',
- tags: [],
- prog: 0
- },
- {
- key: 2,
- name: '整理答案',
- tags: [],
- prog: 0
- },
- {
- key: 3,
- name: '正在生成答案...',
- tags: [],
- prog: 0
- }
- ];
- if (!isFllow) {
- showDoc.value = false;
- }
- searchRef.value.scrollTop = 0;
- if (isFllow) {
- const { id, question, msg, docs, originAnswer, keywords = [] } = currentResponse.value;
- questHistories.value.push({ id, question, msg, docs: docs, originAnswer, keywords });
- } else {
- questHistories.value = [];
- }
- aiLoading.value = true;
- if (scb !== null) {
- clearInterval(scb);
- scb = null;
- } else {
- scb = setInterval(() => {
- scrollToBottom();
- }, 500);
- }
- ctr = new AbortController();
- const id = questHistories.value.length;
- currentResponse.value = {
- question: question.value,
- id,
- msg: '',
- originAnswer: '',
- streamMock: false,
- streamMsg: '',
- docs: []
- };
- activeIndex.value = 0;
- getQuestionKeyWords();
- if (activeTab.value === 'net') {
- questionUrl = '/chat/bing_chat';
- } else if(activeTab.value === 'knowledge'){
- questionUrl = '/chat/kb_chat';
- }else if(activeTab.value === 'original') {
- questionUrl = '/chat/chat';
- }
- const topKs = window?.AppGlobalConfig?.topKs || {
- 0: 5,
- 1: 10,
- 2: 15
- };
- let body = null
- if (activeTab.value === 'net') {
- body = {
- query: question.value,
- stream: true,
- model: dsChecked.value ? 'deepseek-r1' : '',
- search_type: answerType.value
- }
- } else if(activeTab.value === 'knowledge'){
- body = {
- query: question.value,
- mode: 'local_kb',
- kb_name: activeTab.value === 'paper' ? 'compose_paper_material_total' : dsChecked.value ? window?.AppGlobalConfig?.llm?.kb_name : 'policy',
- top_k: topKs[answerType.value],
- search_type: answerType.value,
- score_threshold: 0.5,
- model: dsChecked.value ? 'deepseek-r1' : '',
- history: isFllow ? getFlowHistory() : [],
- stream: true,
- prompt_name: 'rag_context_qa.md',
- return_direct: false
- }
- } else if (activeTab.value === 'original') {
- body = {
- query: question.value,
- stream: true
- }
- }
- if (activeTab.value === 'knowledge' && isFllow) {
- if (questHistories.value.length > 0) {
- body.history_keyword = questHistories.value[questHistories.value.length - 1].keywords;
- }
- }
- if (activeTab.value !== 'net' && answerType.value !== '0') {
- currentResponse.value.streamMock = true;
- currentResponse.value.mockStart = true;
- streamToAnswer();
- }
- const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
- await fetchEventSource(rootUrl + questionUrl, {
- method: 'POST',
- openWhenHidden: true,
- timeout: 300000,
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(body),
- signal: ctr.signal,
- async onmessage(msg) {
- if (activeIndex.value !== 3) {
- activeIndex.value = 3;
- setActiveIndexTags(2, ['知识整理', '要点归纳', '总结摘要']);
- }
- activeTab.value === 'net' ? handleNetResponse(msg, id) : handleKnowledgeResponse(msg, id);
- },
- onclose() {
- if (scb !== null) {
- clearInterval(scb);
- scb = null;
- collectQuestion();
- }
- if (currentResponse.value.streamMock) {
- currentResponse.value.mockStart = false;
- } else {
- activeIndex.value = 5;
- searchRef.value.scrollTop = 0;
- generateToc();
- }
- },
- onerror(err) {
- throw err;
- },
- onopen() {
- setActiveIndexTags(1, ['国家法律法规', '地方性法规', '政府规章', '政策解读', '经典案例']);
- }
- });
- };
- const getFlowHistory = () => {
- const lastHistory = [...questHistories.value].splice(-1);
- const parm = [];
- lastHistory.forEach((item) => {
- parm.push({ role: 'user', content: item.question });
- });
- return parm;
- };
- const handleKnowledgeResponse = (msg, id) => {
- if (!msg || !msg.data) {
- return;
- }
- const rData = JSON.parse(msg.data);
- if (rData?.choices && rData.choices.length > 0) {
- if (activeTab.value === 'net' && rData.status !== 2) {
- return;
- }
- if (rData.status == 3) {
- if (timers.value) {
- timers.value.forEach((t) => {
- clearTimeout(t);
- t = null;
- });
- timers.value = [];
- }
- if (dsChecked.value) {
- endTime.value = Date.now();
- var time = ((endTime.value - startTime.value) / 1000).toFixed(0);
- dsHintTxt.value = `已深度思考(用时 ${time} 秒)`;
- dsLoading.value = false;
- aiLoading.value=true;
- }
- currentResponse.value.originAnswer = rData.choices[0]?.delta?.content.replaceAll(
- '\n',
- ` \n`
- );
- currentResponse.value.msg = rData.choices[0]?.delta?.content.replaceAll('\n', ` \n`);
- let num = getNum(currentResponse.value.msg);
- while (num) {
- const docsNum = currentResponse.value.docs.length;
- if (docsNum && num > docsNum + 1) {
- }
- currentResponse.value.msg = currentResponse.value.msg.replace(
- `[[${num}]]`,
- `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style=" cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd; width: 20px;
- height: 20px;
- background: #FFFFFF;
- border-radius: 4px 4px 4px 4px;
- border: 1px solid #BACAE3;">${num}</span>`
- );
- num = getNum(currentResponse.value.msg);
- }
- } else {
- aiLoading.value = false;
- if (dsChecked.value) {
- dsHintTxt.value = '思考中...';
- //ds模式打字机效果输出
- const timer = setTimeout(() => {
- if (!aiLoading.value) {
- currentResponse.value.originAnswer += rData.choices[0]?.delta?.content.replaceAll(
- '\n',
- ` \n`
- );
- currentResponse.value.msg += rData.choices[0]?.delta?.content.replaceAll('\n', ` \n`);
- let num = getNum(currentResponse.value.msg);
- while (num) {
- const docsNum = currentResponse.value.docs.length;
- if (docsNum && num > docsNum + 1) {
- }
- currentResponse.value.msg = currentResponse.value.msg.replace(
- `[[${num}]]`,
- `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style=" cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd; width: 20px;
- height: 20px;
- background: #FFFFFF;
- border-radius: 4px 4px 4px 4px;
- border: 1px solid #BACAE3;">${num}</span>`
- );
- num = getNum(currentResponse.value.msg);
- }
- }
- clearTimeout(timer);
- timers.value.push(timer);
- }, times.value * 15);
- } else {
- currentResponse.value.originAnswer += rData.choices[0]?.delta?.content.replaceAll(
- '\n',
- ` \n`
- );
- currentResponse.value.msg += rData.choices[0]?.delta?.content.replaceAll('\n', ` \n`);
- let num = getNum(currentResponse.value.msg);
- while (num) {
- const docsNum = currentResponse.value.docs.length;
- if (docsNum && num > docsNum + 1) {
- }
- currentResponse.value.msg = currentResponse.value.msg.replace(
- `[[${num}]]`,
- `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style=" cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd; width: 20px;
- height: 20px;
- background: #FFFFFF;
- border-radius: 4px 4px 4px 4px;
- border: 1px solid #BACAE3;">${num}</span>`
- );
- num = getNum(currentResponse.value.msg);
- }
- }
- // currentResponse.value.msg += rData.choices[0]?.delta?.content.replaceAll('\n', ` \n`);
- }
- }
- if (!currentResponse.value.docs.length) {
- if (rData.docs && rData.docs.length) {
- handleDocs(rData.docs);
- }
- }
- times.value++;
- };
- const handleNetResponse = (msg, id) => {
- const rData = msg.data;
- if (!!rData && rData !== '[DONE]') {
- try {
- const res = JSON.parse(rData);
- if (res.error) {
- activeIndex.value = 4;
- message.error(res.error);
- currentResponse.value.msg = res.error;
- return;
- }
- if (res.rag_finish) {
- if (activeIndex.value < 4) {
- activeIndex.value = 4;
- }
- if (dsChecked.value) {
- endTime.value = Date.now();
- var time = ((endTime.value - startTime.value) / 1000).toFixed(0);
- dsHintTxt.value = `已深度思考(用时 ${time} 秒)`;
- dsLoading.value = false;
- }
- } else {
- if (dsChecked.value && dsHintTxt.value != '思考中...') {
- dsHintTxt.value = '思考中...';
- }
- }
- if (res.result) {
- currentResponse.value.originAnswer = res.result;
- currentResponse.value.msg = res.result.replaceAll('\n', ` \n`);
- let num = getNum(currentResponse.value.msg);
- while (num) {
- const docsNum = currentResponse.value.docs.length;
- if (docsNum && num > docsNum + 1) {
- currentResponse.value.msg = currentResponse.value.msg.replace(`[[${num}]]`, ``);
- }
- currentResponse.value.msg = currentResponse.value.msg.replace(
- `[[${num}]]`,
- `<span onclick="window.openDocByIndex(${num}, ${id})" class="poi" style=" cursor: pointer; display: inline-block; width: 20px; height: 20px; font-size: 12px; line-height: 20px; text-align: center; margin: 0 5px; border-radius: 10px;background: #d0d5dd">${num}</span>`
- );
- num = getNum(currentResponse.value.msg);
- }
- }
- if (res.source_list && res.source_list.length && !currentResponse.value.docs.length) {
- handleDocs(res.source_list);
- }
- } catch (e) {}
- }
- if (rData === '[DONE]') {
- console.log(currentResponse.value.msg);
- }
- };
- const handleDocs = (docs) => {
- if (
- docs.length === 1 &&
- "<span style='color:red'>未找到相关文档,该回答为大模型自身能力解答!</span>" === docs[0]
- ) {
- // message.warn("知识库中未收录相关内容,将为您自动切换全网模式,请稍候......");
- // changeTab('net');
- return;
- }
- currentResponse.value.docs = docs.map((v, i) => {
- if (activeTab.value === 'net') {
- return {
- index: v.num,
- doc: v.name,
- link: v.url,
- title: v.title,
- summary: v.summary,
- content: '',
- showContent: false,
- type: 'url'
- };
- }
- if (v.toLowerCase().indexOf('.pdf') > 0) {
- return {
- index: i++,
- doc: v.substring(v.toLowerCase().indexOf('] [') + 3, v.toLowerCase().indexOf('.pdf]') + 4),
- link: v.substring(v.toLowerCase().indexOf('.pdf]') + 6, v.toLowerCase().indexOf('.pdf)') + 4),
- content: v.substring(v.toLowerCase().indexOf('.pdf)') + 5),
- showContent: false,
- type: 'pdf'
- };
- } else if (v.toLowerCase().indexOf('.txt') > 0) {
- return {
- index: i++,
- doc: v.toLowerCase().substring(v.indexOf('] [') + 3, v.toLowerCase().indexOf('.txt]') + 4),
- link: v.toLowerCase().substring(v.indexOf('.txt]') + 6, v.toLowerCase().indexOf('.txt)') + 4),
- content: v.toLowerCase().substring(v.indexOf('.txt)') + 5),
- showContent: false,
- type: 'txt'
- };
- } else if (v.toLowerCase().indexOf('.docx') > 0) {
- return {
- index: i++,
- doc: v.substring(v.toLowerCase().indexOf('] [') + 3, v.toLowerCase().indexOf('.docx]') + 5),
- link: v.substring(v.toLowerCase().indexOf('.docx]') + 7, v.toLowerCase().indexOf('.docx)') + 5),
- content: v.substring(v.toLowerCase().indexOf('.docx)') + 6),
- showContent: false,
- type: 'docx'
- };
- }
- });
- };
- const openDoc = (doc, i) => {
- if (window.AppGlobalConfig.isDisabledSource) return;
- var link = doc.link;
- var type = doc.type;
- pdfSrc.value = link.replace(
- window.AppGlobalConfig.knowledgeDocUrl.replace(
- '=policy&',
- activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
- ),
- window.AppGlobalConfig.knowledgeDocUrlProxy.replace(
- '=policy&',
- activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
- )
- );
- showDoc.value = true;
- fileType.value = type;
- pdfContent.value = doc.content;
- pdfNum.value = i;
- };
- const closeDoc = () => {
- showDoc.value = false;
- };
- watch(
- () => showDoc.value,
- (newVal) => {
- // 发布打开关闭,在关闭文档的时候打开相关案例
- PubsubService.publish('switch-relevant-cases-box', newVal);
- }
- );
- const openUrl = (url) => {
- window.open(url, '_blank');
- };
- const openDocByIndex = (ind, id) => {
- showDoc.value = false;
- let link = null;
- if (id !== currentResponse.value.id) {
- if (questHistories.value[id].docs[ind - 1].type === 'url') {
- openUrl(questHistories.value[id].docs[ind - 1].link);
- return;
- }
- link = questHistories.value[id].docs[ind - 1].link;
- fileType.value = questHistories.value[id].docs[ind - 1].type;
- } else {
- if (currentResponse.value.docs[ind - 1].type === 'url') {
- openUrl(currentResponse.value.docs[ind - 1].link);
- return;
- }
- link = currentResponse.value.docs[ind - 1].link;
- fileType.value = currentResponse.value.docs[ind - 1].type;
- pdfContent.value = currentResponse.value.docs[ind - 1].content;
- pdfNum.value = currentResponse.value.docs[ind - 1].num;
- }
- pdfNum.value = ind;
- pdfSrc.value = link.replace(
- window.AppGlobalConfig.knowledgeDocUrl.replace(
- '=policy&',
- activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
- ),
- window.AppGlobalConfig.knowledgeDocUrlProxy.replace(
- '=policy&',
- activeTab.value === 'paper' ? '=compose_paper_material_total&' : '=policy&'
- )
- );
- showDoc.value = true;
- };
- window.openDocByIndex = openDocByIndex;
- const getNum = (str) => {
- const matches = str.match(/\[\[(\d+)\]\]/);
- if (matches) {
- return matches[1]; // 输出: 2
- } else {
- return null;
- }
- };
- // 埋点采集数据
- const collectQuestion = () => {
- const { question, originAnswer, keywords = [] } = currentResponse.value;
- const userStore = useUserStore();
- const param = {
- question,
- answer: originAnswer,
- questionType: askType.value === 'zcfg' ? '政策法规' : '土地市场',
- keywords: Array.isArray(keywords) ? keywords.join(',') : keywords,
- user: '游客',
- userId: '-1'
- };
- if (userStore.isLogin) {
- const { id = '-1', displayName = '游客' } = userStore?.user?.user || {};
- param.user = displayName;
- param.userId = id;
- }
- ManagerAPI.collect(param).then((res) => {
- if (res.data) {
- // 记录日志,用来反馈
- currentResponse.value.logId = res.data;
- }
- });
- };
- const questions = ref([]);
- let recommendedQuestions = [];
- let recommendedQuestionsIndex = 0;
- const getRecommendedQuestion = async (b = false) => {
- const { question } = currentResponse.value;
- const myHeaders = new Headers();
- myHeaders.append('Content-Type', 'application/json');
- questions.value = [];
- if (!b) {
- recommendedQuestions = [];
- }
- recommendedQuestionsIndex = 0;
- const raw = JSON.stringify({
- query: `你会收到一个用户提问,请根据问题延伸出10个子问题。
- 在你回答问题的时候,还需要注意推荐给用户的问题必须以列表的形式返回。
- for example:
- [
- "1、南京市在推进产业用地高质量利用方面采取了哪些具体措施?这些措施的效果如何?",
- "2、杭州市创新型产业用地管理的具体实施细则是什么?这些细则如何促进传统产业转型升级?",
- "3、南京市和杭州市在土地供应方式上有哪些不同?这些不同如何影响各自的产业发展?",
- "4、南京市如何通过政策支持和激励措施吸引重大投资项目?这些措施的实际效果如何?",
- "5、杭州市“工业上楼”项目的实施情况如何?这一政策对提升土地利用效率有何影响?",
- ]
- 以下是用户提问:${question}`,
- // model: 'qwen1.5-chat',
- stream: false
- });
- const requestOptions = {
- method: 'POST',
- headers: myHeaders,
- body: raw,
- redirect: 'follow'
- };
- const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
- fetch(rootUrl + '/chat/chat', requestOptions)
- .then((response) => {
- return response.json()
- })
- .then((msgStr) => {
- const msg = JSON.parse(msgStr)
- const str = msg.choices[0]?.message?.content;
- if (str) {
- const str1 = str.slice(str.indexOf("</think>") + 7)
- recommendedQuestions = formatRecommendedQuestions(str1);
- questions.value = recommendedQuestions.slice(0, 5);
- }
- })
- .catch((error) => console.error(error));
- };
- const formatRecommendedQuestions = (str) => {
- return str
- .replace(/`|$$|$$|"|,/g, '') // 清除引号、反引号、方括号和逗号
- .split('\n') // 按换行分割
- .map(line => line.trim()) // 去除首尾空格
- .filter(line => { // 双条件过滤
- return (
- line.length > 0 && // 过滤空行
- /^\d+[、.]/.test(line) // 匹配"1、"或"1."开头
- )
- })
- .map(line => {
- // 统一标点为中文顿号并保留末尾空格
- return line
- .replace(/^\d+\./, m => m.replace('.', '、')) // 英文标点转中文
- .replace(/\s*$/, ' ') // 强制保留末尾3空格
- });
- }
- const changeRecommendedQuestions = () => {
- recommendedQuestionsIndex++;
- if (recommendedQuestionsIndex % 2 === 0) {
- questions.value = recommendedQuestions.slice(0, 5);
- } else {
- questions.value = recommendedQuestions.slice(5);
- getRecommendedQuestion(true)
- }
- };
- const getQuestionKeyWords = async () => {
- const { question } = currentResponse.value;
- const myHeaders = new Headers();
- myHeaders.append('Content-Type', 'application/json');
- questions.value = [];
- const raw = JSON.stringify({
- query: `请从以下文本中提取核心关键词,确保关键词简洁明了且准确反映文本的主要内容。 请按照以下格式输出:关键词:关键词1,关键词2。文本如下:“${question}”`,
- stream: false
- });
- const requestOptions = {
- method: 'POST',
- headers: myHeaders,
- body: raw,
- redirect: 'follow'
- };
- const rootUrl = dsChecked.value ? window.AppGlobalConfig.knowledgeServer : window.AppGlobalConfig.aiServer
- return fetch(rootUrl + '/chat/chat', requestOptions)
- .then((response) => response.json())
- .then((msgStr) => {
- const msg = JSON.parse(msgStr)
- activeIndex.value = 1;
- const str = msg.choices[0]?.message?.content;
- if (str) {
- const str1 = str.slice(str.indexOf("</think>")+7)
- const keywords = splitWords(str1).slice(0, 3);
- setActiveIndexTags(0, keywords);
- currentResponse.value.keywords = keywords;
- }
- })
- .catch((error) => console.error(error));
- };
- const splitWords = (word) => {
- if (word) {
- word = word
- .replaceAll('核心词为:', '')
- .replaceAll('问题核心词:', '')
- .replaceAll('>', '')
- .replaceAll('。', '')
- .trim();
- if (word.indexOf('\n\n') !== -1) {
- return word.split('\n\n');
- }
- if (word.indexOf(',') !== -1) {
- return word.split(',');
- }
- if (word.indexOf('、') !== -1) {
- return word.split('、');
- }
- if (word.indexOf(',') !== -1) {
- return word.split(',');
- }
- return [word];
- }
- return [];
- };
- const exportAnswer = (type) => {
- const { question, originAnswer } = currentResponse.value;
- CommonAPI.answerExport(type, { question, answer: originAnswer });
- };
- const copy = async () => {
- try {
- const input = document.createElement('input');
- input.value = currentResponse.value.msg;
- document.body.appendChild(input);
- input.select();
- document.execCommand('copy');
- document.body.removeChild(input);
- message.info('内容已复制到剪贴板');
- } catch (err) {
- console.error(err);
- // console.error('复制到剪贴板失败', err);
- }
- };
- const changeActiveTab = (tab) => {
- activeTab.value = tab;
- };
- const stopAI = () => {
- if (ctr) {
- ctr.abort();
- }
- };
- const openRecommendedQuestion = (q) => {
- if (q) {
- if (q.charAt(1) === '、') {
- q = q.substring(2);
- }
- window.open(
- `/aisearch/#/ai-search?q=${encodeURI(q.trim())}&scope=${activeTab.value}&type=${answerType.value}`,
- '_blank'
- );
- }
- };
- function initChart(option = {}) {
- let chart = echarts.init(document.getElementById('summaryChart'));
- if (option.option) {
- chart.setOption(option.option);
- } else {
- chart.setOption(option);
- }
- }
- const generateToc = () => {
- if (askType.value === 'tdsc') {
- const summaryTitle = document.getElementById('tdscSummaryTitle');
- const chartCard = document.getElementById('tdscChartCard');
- const mapPanel = document.getElementById('tdscMapPanel');
- tocs.value = [];
- const offset = 550 + 210;
- tocs.value.push({
- id: 'tdscSummaryTitle',
- children: [],
- text: '总结',
- top: summaryTitle.offsetTop + offset
- });
- if (chartCard.style.display !== 'none') {
- tocs.value.push({
- id: 'tdscChartCard',
- children: [],
- text: '生成图表',
- top: chartCard.offsetTop + offset
- });
- }
- // tocs.value.push({
- // id: 'tdscMapPanel',
- // children: [],
- // text: '空间位置',
- // top: mapPanel.offsetTop + offset
- // });
- return;
- }
- const resMarkdownDom = document.getElementById("resMarkdown");
- let thinkDoms = resMarkdownDom.getElementsByTagName("think");
- let markdowns = null;
- if (!thinkDoms || thinkDoms.length === 0) {
- markdowns = resMarkdownDom.children;
- } else {
- markdowns = thinkDoms[0].children
- }
- let index = 1;
- let children = [];
- let toc = [];
- for (const markdown of markdowns) {
- if (markdown.tagName.length === 2 && markdown.tagName.toLocaleUpperCase().startsWith('H')) {
- const id = 'title' + index++ + new Date().getTime();
- markdown.setAttribute('id', id);
- const tocItem = {
- id,
- text: markdown.innerText.trim(),
- children: [],
- top: markdown.offsetTop
- };
- children = tocItem.children;
- toc.push(tocItem);
- }
- if (markdown.tagName.toLocaleUpperCase().trim() === 'P') {
- const pChildren = markdown.children;
- for (const pChild of pChildren) {
- if (pChild.tagName.toLocaleUpperCase().trim() === 'STRONG') {
- const id = 'strong' + index++ + new Date().getTime();
- pChild.setAttribute('id', id);
- const strongItem = {
- id,
- text: pChild?.innerText?.trim(),
- children: [],
- top: pChild.offsetTop
- };
- children.push(strongItem);
- // children = strongItem.children
- }
- }
- }
- }
- tocs.value = toc;
- console.log('toc', toc);
- };
- onMounted(() => {
- tocHeight.value = window.document.body.offsetHeight - 210 - 51;
- doStep();
- const { query } = router.currentRoute.value;
- if (query.q) {
- answerType.value = query.type || '0';
- scope.value = query.scope || 'net';
- var ds = '0';
- if (!useUserStore().isLogin) {
- ds = query.ds;
- } else {
- ds = useUserStore().user.user.enableDeepseek
- ? useUserStore().user.user.enableDeepseek + ''
- : query.ds;
- }
- dsHintTxt.value = '';
- if (ds && ds == '1') {
- ds = true;
- dsLoading.value = true;
- } else {
- ds = false;
- dsLoading.value = false;
- }
- dsChecked.value = ds;
- activeTab.value = scope.value;
- ask(decodeURIComponent(query.q));
- }
- window.addEventListener(
- 'scroll',
- () => {
- if (markdownTocAffixRef.value && markdownTocAffixRef.value.updatePosition) {
- markdownTocAffixRef.value.updatePosition();
- }
- },
- true
- );
- });
- defineExpose({ search, changeActiveTab, stopAI });
- </script>
- <style scoped lang="scss">
- @import 'src/assets/scss/variables';
- .ai-search-detail {
- width: 100%;
- background: $background_color;
- height: 100%;
- .header {
- width: 100%;
- height: 60px;
- background: #F4F6F9;
- }
- .search-panel {
- width: 100%;
- height: calc(100% - 60px - 50px);
- display: flex;
- margin-top: 50px;
- padding-bottom: 50px;
- .markdown-toc-tdsc {
- :deep(.toc-list) {
- margin: 30px 0;
- }
- }
- .search-detail {
- width: calc(52%);
- background: #ffffff;
- margin-left: 52px;
- &-tdsc {
- width: calc(52%);
- }
- .search-result {
- height: calc(100% - 40px);
- .top {
- width: 100%;
- //height: 105px;
- background: #fff;
- //margin-bottom: 5px;
- border-radius: 4px 4px 4px 4px;
- .title {
- width: calc(100% - 7px - 2px);
- display: flex;
- //margin-bottom: 24PX;
- align-items: center;
- min-height: 33px;
- .icon {
- width: 33px;
- height: 29px;
- background: url('@/assets/images/ai-search/question-prefixicon.png') no-repeat;
- background-size: 100% 100%;
- margin-right: 16px;
- }
- .question {
- width: calc(100% - 32px - 150px);
- //line-height: 20px;
- font-family: PingFang SC, PingFang SC;
- font-weight: 600;
- font-size: 24px;
- color: #212121;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
- .graph-switch {
- display: flex;
- align-items: center;
- margin-left: 10px;
- width: 110px;
- button {
- margin-right: 2px;
- }
- }
- }
- .status {
- display: flex;
- align-items: center;
- margin-top: 15px;
- justify-content: center;
- @keyframes shake {
- 0% {
- transform: translateX(0);
- }
- 25% {
- transform: translateX(0px);
- }
- 50% {
- transform: translateX(2px);
- }
- 75% {
- transform: translateX(0px);
- }
- 100% {
- transform: translateX(2px);
- }
- }
- .text {
- width: 55px;
- font-family: Alibaba PuHuiTi 3, Alibaba PuHuiTi 30;
- font-weight: normal;
- font-size: 14px;
- color: #9da6b5;
- line-height: 20px;
- text-stroke: 1px rgba(0, 0, 0, 0);
- text-align: left;
- font-style: normal;
- text-transform: none;
- -webkit-text-stroke: 1px rgba(0, 0, 0, 0);
- }
- }
- }
- .result-panel {
- width: 100%;
- background: #fff;
- padding: 29px 0;
- .search-steps {
- width: 100%;
- padding: 4px 0;
- display: flex;
- align-items: center;
- margin-bottom: 30px;
- flex-direction: column;
- .step {
- width: 100%;
- .info {
- width: 100%;
- padding: 0;
- display: flex;
- align-items: center;
- .icon {
- width: 18px;
- height: 18px;
- background: url('@/assets/images/ai-search/step-wait-icon.png') no-repeat;
- background-size: 100% 100%;
- margin-right: 7px;
- }
- .title {
- width: 120px;
- font-family: PingFang SC, PingFang SC;
- font-weight: 600;
- font-size: 16px;
- color: #212121;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
- .tags {
- width: calc(100% - 10px - 27px - 100px);
- display: flex;
- justify-content: end;
- overflow: hidden;
- height: 30px;
- align-items: center;
- .tag {
- padding: 4px 14px;
- margin-left: 8px;
- line-height: 14px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 200px;
- cursor: pointer;
- font-weight: 400;
- font-size: 14px;
- color: #2185f2;
- height: 24px;
- background: #f5f9fc;
- border: 1px solid #e9eef5;
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 14px;
- color: #525b74;
- /* line-height: 0px; */
- text-align: left;
- font-style: normal;
- text-transform: none;
- &-first {
- background: none;
- font-family: Microsoft YaHei;
- font-weight: 400;
- font-size: 16px;
- color: #666666;
- }
- }
- .tag-rol {
- display: inline-block;
- white-space: nowrap;
- box-sizing: border-box;
- //animation: scroll-text 25s linear infinite;
- //width: 100%;
- background: none;
- height: 22px;
- max-width: none;
- overflow: unset;
- }
- @keyframes scroll-text {
- 0% {
- transform: translateX(100%);
- }
- 100% {
- transform: translateX(-100%);
- }
- }
- }
- }
- &-done {
- .info {
- .icon {
- width: 18px;
- height: 18px;
- background: url('@/assets/images/ai-search/step-done-icon.png') no-repeat;
- background-size: 100% 100%;
- margin-right: 7px;
- }
- .title {
- font-family: Microsoft YaHei;
- font-weight: 400;
- font-size: 16px;
- color: #333333;
- line-height: 30px;
- }
- }
- }
- }
- }
- .result {
- width: 100%;
- .tabs {
- display: flex;
- border-bottom: 1px solid #ccc;
- .ds-box {
- display: flex;
- height: 36px;
- align-items: center;
- border: 1px solid #E3E4E4;
- background: #f0f0f0;
- border-radius: 5px;
- overflow: hidden;
- >div {
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 108px;
- cursor: pointer;
- &.active {
- background: #fff;
- &:nth-child(1) {
- .iconfont {
- color: #4f6bfe;
- }
- }
- &:nth-child(2) {
- .iconfont {
- color: #605BEC;
- }
- }
- }
- .iconfont {
- color: #898D93;
- margin-top: 2px;
- }
- &:nth-child(1) {
- .iconfont {
- font-size: 16px;
- }
- }
- &:nth-child(2) {
- .iconfont {
- font-size: 22px;
- }
- }
- }
- .sw {
- :deep(.ant-switch) {
- background-color: #eaeaea;
- }
- :deep(.ant-switch-handle::before) {
- background-color: #fff;
- }
- }
- .sw-checked {
- :deep(.ant-switch-checked) {
- background-color: #fff;
- }
- :deep(.ant-switch-handle::before) {
- background-color: #1890ff;
- }
- }
- }
- .tab {
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 24px;
- color: #212121;
- text-align: left;
- font-style: normal;
- text-transform: none;
- margin-right: 28px;
- display: flex;
- justify-content: center;
- flex-direction: column;
- align-items: center;
- cursor: pointer;
- .title {
- margin-bottom: 9px;
- }
- .bottom {
- height: 0;
- margin-top: 10px;
- }
- &-active,
- &:hover {
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 24px;
- color: #1586fa;
- text-align: left;
- font-style: normal;
- text-transform: none;
- .bottom {
- width: 100%;
- height: 4px;
- background: #3987F4;
- border-radius: 2px;
- }
- }
- }
- }
- .result-view {
- width: 100%;
- height: 50%;
- overflow-y: auto;
- .q-r {
- padding: 20px;
- line-height: 30px;
- background: #f5f8fa;
- border-radius: 4px 4px 4px 4px;
- margin: 20px 0;
- .ds-content-box {
- display: flex;
- .icon {
- width: 26px;
- height: 26px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 50%;
- background: white;
- border: 1px solid #117ff9;
- img {
- width: 18px;
- height: 18px;
- }
- }
- .ds-panel {
- margin-top: -5px;
- margin-left: 10px;
- width: calc(100% - 40px);
- .ds-loading {
- display: flex;
- align-items: center;
- .icon-arrow {
- padding-left: 5px;
- cursor: pointer;
- }
- }
- .rotate {
- transform: rotate(180deg);
- }
- }
- .ds-con {
- border-left: 1px solid #ccc;
- padding-left: 10px;
- }
- }
- }
- // 追问
- .follow {
- padding: 0 20px 20px 0;
- width: 100%;
- text-align: right;
- height: auto;
- min-height: 40px;
- .eval-card {
- width: auto;
- display: flex;
- /* float: left; */
- justify-content: center;
- align-items: center;
- .eval-icon {
- width: 22px;
- height: 22px;
- &-like {
- background: url('@/assets/images/ai-search/eval-like-icon.png') no-repeat;
- background-size: 100% 100%;
- &-s,
- &:hover {
- background: url('@/assets/images/ai-search/eval-like-icon.png') no-repeat;
- background-size: 100% 100%;
- }
- }
- &-dislike {
- background: url('@/assets/images/ai-search/eval-dislike-icon.png') no-repeat;
- background-size: 100% 100%;
- &-s,
- &:hover {
- background: url('@/assets/images/ai-search/eval-dislike-icon.png') no-repeat;
- background-size: 100% 100%;
- }
- }
- &-copy {
- background: url('@/assets/images/ai-search/eval-copy-icon.png') no-repeat;
- background-size: 100% 100%;
- &-s,
- &:hover {
- background: url('@/assets/images/ai-search/eval-copy-icon.png') no-repeat;
- background-size: 100% 100%;
- }
- }
- }
- }
- .follow-input {
- margin-top: 10px;
- position: relative;
- textarea {
- width: 100%;
- height: 120px;
- border-color: white;
- border-radius: 10px;
- resize: none;
- padding: 10px;
- background: transparent;
- font-size: 19px;
- border-color: #4096ff;
- box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
- textarea:focus-visible,
- textarea:focus {
- border: none !important;
- outline: none;
- }
- textarea:focus {
- border: none !important;
- outline: none;
- }
- }
- .button {
- width: 110px;
- height: 40px;
- background: linear-gradient(90deg, #5c62ea, #517de2);
- border-radius: 20px;
- position: absolute;
- right: 10px;
- bottom: 12px;
- display: flex;
- justify-content: center;
- align-items: center;
- font-family: Alibaba PuHuiTi 2;
- font-weight: normal;
- font-size: 16px;
- color: #ffffff;
- cursor: pointer;
- }
- }
- }
- .more-questions {
- .title {
- display: flex;
- margin-bottom: 14px;
- .change-title {
- margin-left: 20px;
- display: flex;
- cursor: pointer;
- font-size: 13px;
- color: #3c3c3c;
- .change-icon {
- width: 16px;
- height: 16px;
- margin-right: 4px;
- background: url('@/assets/images/ai-search/icon-chang-question.png') no-repeat;
- background-size: 100% 100%;
- }
- }
- }
- }
- .source {
- width: 100%;
- .title {
- display: flex;
- align-items: center;
- font-family: PingFang SC;
- font-weight: bold;
- font-size: 20px;
- color: #333333;
- line-height: 30px;
- .icon {
- width: 20px;
- height: 20px;
- margin-right: 8px;
- background: url('@/assets/images/ai-search/source-icon.png') no-repeat;
- background-size: 100% 100%;
- }
- .text {
- font-family: Alibaba PuHuiTi 3, Alibaba PuHuiTi 30;
- font-weight: normal;
- //font-size: 16px;
- color: #404557;
- line-height: 30px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- margin-right: 5px;
- }
- }
- .items {
- .item {
- width: calc(100% - 10px);
- min-height: 82px;
- background: rgba(255, 255, 255, 0.5);
- border-radius: 10px;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 20px;
- color: #888;
- display: flex;
- line-height: 25px;
- margin-bottom: 20px;
- margin: 12px 0;
- background: #f1f9ff;
- border-radius: 8px 8px 8px 8px;
- .doc {
- display: flex;
- width: 100%;
- p {
- width: calc(100% - 25px);
- .doc-link {
- cursor: pointer;
- color: #3987F4;
- &:hover {
- font-weight: bold;
- }
- }
- }
- .doc-icon {
- width: 20px;
- height: 20px;
- margin-left: 5px;
- cursor: pointer;
- &-show {
- background: url('/images/zcbd/ai-search/icon-doc-show.png') no-repeat;
- background-size: 100% 100%;
- }
- &-hide {
- background: url('/images/zcbd/ai-search/icon-doc-hide.png') no-repeat;
- background-size: 100% 100%;
- }
- }
- }
- .content {
- //white-space: pre-line;
- width: 100%;
- font-family: Alibaba PuHuiTi 3, Alibaba PuHuiTi 30;
- font-weight: normal;
- font-size: 14px;
- color: #757575;
- line-height: 28px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- &-hide {
- display: none;
- }
- }
- }
- .item-url {
- margin: 12px 0;
- background: #f1f9ff;
- border-radius: 8px 8px 8px 8px;
- .bottom {
- width: 100%;
- .title-icon {
- display: flex;
- justify-content: left;
- align-items: center;
- width: 90%;
- float: left;
- .icon {
- width: 23px;
- height: 23px;
- background: url('/images/zcbd/ai-search/icon-net-url.png') no-repeat;
- background-size: 100% 100%;
- }
- .title {
- color: #a9a3a3cc;
- font-size: 16px;
- margin-left: 10px;
- }
- }
- .index {
- float: right;
- width: 20px;
- height: 20px;
- font-size: 12px;
- line-height: 20px;
- text-align: center;
- margin: 0 5px;
- border-radius: 10px;
- background: #d0d5dd;
- color: #000;
- }
- }
- }
- }
- }
- }
- // 土地市场
- .map-answer {
- position: relative;
- width: 100%;
- min-height: 600px;
- .left-panel {
- width: 100%;
- background: #ffffff;
- z-index: 1000;
- //position: relative;
- left: 12px;
- top: 15px;
- border-radius: 10px;
- .title {
- width: 100%;
- height: 30px;
- text-align: left;
- line-height: 30px;
- font-size: 18px;
- font-weight: bolder;
- }
- .content {
- width: 100%;
- height: auto;
- .chart-card {
- width: 100%;
- height: 330px;
- .chart-title {
- width: 100%;
- height: 30px;
- font-size: 18px;
- font-weight: bolder;
- padding: 0 5px;
- line-height: 30px;
- margin: 10px 0;
- }
- .chart {
- width: 100%;
- height: 300px;
- }
- //background: #0a84ff;
- }
- .summary-card {
- width: 100%;
- height: 50%;
- .summary-title {
- width: 100%;
- height: 30px;
- font-size: 18px;
- font-weight: bolder;
- padding: 0 5px;
- line-height: 30px;
- margin: 10px 0;
- }
- .summary-content {
- width: calc(100% - 20px);
- height: calc(100% - 30px);
- padding: 0;
- white-space: pre-wrap;
- overflow-y: auto;
- border-radius: 10px;
- margin: 10px;
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Noto Sans, Helvetica,
- Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
- font-size: 16px;
- line-height: 1.5;
- }
- }
- }
- }
- .map-panel {
- width: 100%;
- height: 630px;
- .map-title {
- width: 100%;
- height: 30px;
- font-size: 18px;
- font-weight: bolder;
- padding: 0 5px;
- line-height: 30px;
- margin: 10px 0;
- }
- .map {
- width: 100%;
- height: 600px;
- }
- }
- }
- }
- }
- }
- }
- }
- ::-webkit-scrollbar {
- width: 8px;
- }
- ::-webkit-scrollbar-thumb {
- background-color: rgb(0 0 0 / 10%);
- border-radius: 10px;
- }
- }
- ::v-deep .input-modal {
- .modal-que {
- background: #0e91fd;
- textarea {
- width: 100%;
- height: 257px !important;
- border: none;
- border-radius: 10px;
- resize: none;
- padding: 10px;
- background: transparent;
- font-size: 19px;
- &:focus-visible {
- border: none !important;
- outline: none;
- }
- }
- }
- }
- </style>
|