石广澎
2025-06-18 e05ab401650433cc7e767e08b482fb7a00be7bbd
pages/aiQuestions/aiQuestions.vue
@@ -54,18 +54,18 @@
                     <u-loading mode="circle" v-if="!item.aiLog" size="40" color="#0079FE"
                        class="u-m-r-10"></u-loading>
                     <u-collapse-item :open='true' :title="item.aiLog ? 'AI思考完成' : 'AI思考中...'">
                        <view v-html="markdown.render(item.thinkLog)"></view>
                        <view style="font-size: 26rpx;"  v-html="markdown.render(item.thinkLog)"></view>
                     </u-collapse-item>
                  </u-collapse>
                  <view v-else-if="!item.aiLog" class="u-flex" style="color: #0079FE;">
                     <u-loading mode="circle" size="40" color="#0079FE" class="u-m-r-10"></u-loading>
                     AI思考中···
                  </view>
                  <view v-html="markdown.render(item.aiLog)"></view>
                  <view style="font-size: 26rpx;"  v-html="markdown.render(item.aiLog)"></view>
                  <view class="file-list" v-if="item.docInfo.length&&item.isEnd">
                     <view class="one-file" v-for="(file,findex) in item.docInfo" :key='findex'>
                     <view class="one-file" @click="uploadFile(file)" v-for="(file,findex) in item.docInfo" :key='findex'>
                        <image :src="getImg(file.docFormat)" mode=""></image>
                        <text>{{file.docName}}</text>
                        <text style="font-size: 26rpx;">{{file.docName}}</text>
                     </view>
                  </view>
                  <view v-if="item.isEnd||item.logSound" class="u-flex u-p-h-10 function-box">
@@ -74,7 +74,7 @@
                           <image src="/static/question/copy.png" mode=""></image>
                        </view>
                     </view>
                     <!--
                     <!--
                        <view class="function-right">
                        <u-icon name="thumb-up-fill" @click='clickLikes(item)' v-if="item.isLike"
                           color="#2468F2" size="40"></u-icon>
@@ -84,7 +84,7 @@
                           color="#2468F2" size="40"></u-icon>
                        <u-icon name="thumb-down" @click="clickDislikes(item)" v-else color="#98a1b2"
                           size="40"></u-icon>
                     </view>
                     </view>
                     -->
                  </view>
               </view>
@@ -100,15 +100,15 @@
      <view v-else class="tips">
         我是财政AI助手,很高兴见到你!我可以回答项目文档中的各种问题,输入问题快来体验吧!
      </view>
      <audio id="audio" src=""></audio>
      <view class="btmbox" :class="{'isOn':showKnow}">
         <view v-if="!voiceFlg" class="know-btn" :class="{'active-btn':showKnow}" @click="showKnow=true">
         <!--  <view v-if="!voiceFlg" class="know-btn" :class="{'active-btn':showKnow}" @click="showKnow=true">
            <view class="btn-box">
               <image v-if="showKnow" src="/static/wd/book-open.png" mode=""></image>
               <image v-else src="/static/wd/book.png" mode=""></image>
            </view>
         </view>
         </view> -->
         <view :class="['btm-btn',{'voice-btn':recording},{'voice-cancel-btn': voiceStop}]">
            <block v-if="!recording">
               <image v-if="voiceFlg" src="/static/wd/i03.png" mode="widthFix" class="btnimg2" @click="clickType"
@@ -130,8 +130,9 @@
               <image src="/static/wd/i02.png" v-if="msg&&!isDisabled" class="btnimg" @click="tiWen"
                  mode="widthFix" alt=""></image>
            </block>
         </view>
            </view>
      </view>
      <view class="tipsMsg"> 内容由 AI 生成,仅供参考,您据此所作判断及操作均由您自行承担责任。</view>
      <!--设置弹窗 -->
      <u-popup v-model="showSet" mode='top' border-radius="20">
         <view class='set-box'>
@@ -385,6 +386,9 @@
         this.appId = options.id || 1
         this.userId = options.userId || 1
         // this.toCheck(this.appId)
         if(options.knowId) {
            this.knowIds = options.knowId.split(',')
         }
         this.openRecord();
         this.getDetails()
         this.getTalkList();
@@ -405,6 +409,9 @@
         document.removeEventListener('contextmenu', this.noContextmenu)
      },
      methods: {
         uploadFile(file) {
            window.open(this.baseUrl + file.docPath)
         },
         chooseThisLLM(e) {
            this.chooseLlm = e;
         },
@@ -413,7 +420,6 @@
            let res = await checkAppScope({
               appId: e
            })
            console.log(res, '验证');
            if (res.data) {
               this.openRecord();
               this.getDetails()
@@ -452,7 +458,6 @@
               pageSize: 10,
               pageNum: 1
            })
            console.log(res);
            this.talkList = res.rows;
         },
         toBack() {
@@ -582,7 +587,6 @@
            this.voicePageY = 0
            this.voiceText = '松开 发送'
            this.startRec()
            console.log('touchstartVoice', this.voicePageY)
         },
         // 搜索知识库
         toSearch() {
@@ -590,7 +594,6 @@
         },
         // 滑动触发
         touchmoveVoice(e) {
            // console.log(e.changedTouches[0])
            if (!this.voicePageY) {
               this.voicePageY = (e.changedTouches[0].pageY).toFixed(2)
            }
@@ -611,7 +614,7 @@
         // 松开触发
         touchendVoice() {
            this.stopRec(this.voiceStop); //录音结束
            console.log('松开触发')
            // console.log('松开触发')
         },
         // 中止回答
         async isSuspend() {
@@ -642,7 +645,7 @@
         // 打断触发
         touchcancelVoice() {
            this.stopRec(this.voiceStop); //录音结束
            console.log('打断触发')
            // console.log('打断触发')
         },
         async tiWen() {
            if (this.$u.test.isEmpty(this.msg) && this.$u.test.isEmpty(this.recMsg)) {
@@ -719,10 +722,8 @@
               const reader = response.body.getReader();
               if (this.suspend) {
                  reader.cancel().then(() => {
                     console.log('流式响应已取消');
                     if (controller) {
                        controller.abort();
                        console.log('中止fetch请求');
                     }
                  })
                  return
@@ -739,7 +740,9 @@
               let docIds = null
               let isGetId = false
               let isThink = false
          let count = 0
               while (result) {
            count++
                  if (this.suspend) break
                  // done表示流是否已经完成读取  value包含读取到的数据块
                  const {
@@ -768,7 +771,7 @@
                        let asIdx = this.getContainedIdx(value, ansCode)
                        if (asIdx !== -1) {
                           isThink = false
                           curMsg.thinkLog += textDecoder.decode(value.slice(0, asIdx))
                           curMsg.thinkLog += textDecoder.decode(value.slice(thkCode.length, asIdx))
                           dataArr = new Uint8Array([...value.slice(asIdx)])
                        } else {
                  if(thkIdx===0){
@@ -783,9 +786,12 @@
                        // 检查数据数组中是否包含值为10的元素,以确定是否需要获取文档ID
                        isGetId = dataArr.includes(10)
                     }
                     // dataArr = new Uint8Array([...dataArr, ...value])
                     // isGetId = dataArr.includes(10)
                  }
            if (count === 1) {
              const idx = dataArr.indexOf(10)
              // 将数据数组从索引位置+1开始的部分与当前值合并,并解码为字符串
              curMsg.aiLog = textDecoder.decode(dataArr.slice(idx + 1))
            }
                  curMsg.debug = 0
                  this.intoView = "";
                  this.$nextTick(() => {
@@ -807,7 +813,6 @@
               this.msgList[this.msgList.length - 1].isPlay = 0
               this.resetRec()
            } catch (err) {
               console.log(err);
               if (this.msgList[this.msgList.length - 1].aiLog === '' || !this.msgList[this.msgList.length - 1]
                  .isEnd) {
                  this.msgList.pop()
@@ -828,8 +833,9 @@
            }
            // 解码数据数组的前半部分,并提取文档ID
            const idStr = textDecoder.decode(dataArr.slice(0, idx)).trim()
            const idArr = idStr.substring(idStr.indexOf(":") + 1).match(/\d+/g)
            const docIds = idArr.join(',')
            // const idArr = idStr.substring(idStr.indexOf(":") + 1).match(/\d+/g)
            const idArr = idStr.substring(idStr.indexOf(":") + 1)
            const docIds = idArr.split(',')
            // 如果文档ID有效且不为'0',则查询文档
            if (docIds && docIds !== '0' && !this.suspend) {
               // 根据文档ID列表获取文档信息
@@ -851,7 +857,7 @@
                     // lastMsgBot.scrollIntoView(false)
                  })
               })
               return idArr.join(',')
               return idArr.split(',')
            }
            return docIds
         },
@@ -1128,6 +1134,7 @@
   }
</style>
<style lang="scss" scoped>
   // 设置弹窗
   .set-box {
      padding: 0 34rpx;
@@ -1161,7 +1168,7 @@
               }
               text {
                  font-size: 32rpx;
                  font-size: 26rpx;
                  color: #000000;
                  font-weight: 400;
               }
@@ -1178,7 +1185,7 @@
               }
               text {
                  font-size: 32rpx;
                  font-size: 26rpx;
                  color: #000000;
                  font-weight: 400;
               }
@@ -1201,7 +1208,7 @@
         display: flex;
         align-items: center;
         justify-content: space-between;
         font-size: 34rpx;
         font-size: 30rpx;
         color: #000000;
         font-weight: 700;
         line-height: 50rpx;
@@ -1243,7 +1250,7 @@
               .details {
                  .know-name {
                     font-size: 32rpx;
                     font-size: 28rpx;
                     color: #000000;
                     line-height: 50rpx;
                     font-weight: 500;
@@ -1258,6 +1265,7 @@
                     display: inline-block;
                     line-height: 42rpx;
                     border-radius: 8rpx;
                     font-size: 28rpx;
                  }
               }
            }
@@ -1359,10 +1367,17 @@
         }
      }
   }
   .tipsMsg{
      position: fixed;
      bottom: 0rpx;
      text-align: center;
      color: #c0c4cc;
      height: 80rpx;
      font-size: 26rpx;
   }
   .btmbox {
      position: fixed;
      bottom: 0;
      bottom: 80rpx;
      height: 120rpx;
      background-color: #F3F5F9;
      display: flex;
@@ -1443,7 +1458,7 @@
      border-radius: 10rpx;
      background: #0AAFB0;
      padding: 10rpx 18rpx;
      font-size: 30rpx;
      font-size: 28rpx;
      color: white;
      text-align: center;
   }
@@ -1453,7 +1468,7 @@
      flex-direction: row;
      justify-content: flex-end;
      width: 100%;
      font-size: 26rpx;
      .div {
         background: linear-gradient(273deg, #2F7AFA 0.22%, #226FF4 99.8%);
         color: #fff;
@@ -1462,6 +1477,7 @@
         max-width: 100%;
         padding: 12px 16px;
         white-space: pre-wrap;
         font-size: 26rpx;
      }
   }
@@ -1502,6 +1518,7 @@
      background: #fff;
      border-radius: 24rpx;
      margin: 40rpx;
      font-size: 26rpx;
   }
   .msgbox1 {
      margin-bottom: 20rpx;
@@ -1560,7 +1577,7 @@
            }
            text {
               font-size: 30rpx;
               font-size: 26rpx;
               color: #101828;
               line-height: 50rpx;
               font-weight: 400;
@@ -1716,7 +1733,7 @@
         position: relative;
         z-index: 2;
         padding-top: 70rpx;
         font-size: 32rpx;
         font-size: 28rpx;
         text-indent: 4em;
      }
@@ -1743,7 +1760,7 @@
            .top {
               color: #000000;
               font-size: 32rpx;
               font-size: 28rpx;
               font-weight: bold;
               span {