Browse Source

Merge branch 'xiaoshi_AI' into product

zhuliu 5 months ago
parent
commit
cfcddc7af9

+ 3 - 1
src/api/index.js

@@ -25,6 +25,7 @@ import examine from "./newApi/examine";
 import IPREmail from "./newApi/IPREmail";
 import litigation from "./newApi/litigation";
 import reportAffair from "./newApi/reportAffair";
+import technicalDisclosure from './newApi/technicalDisclosure'
 
 export default {
   ...client,
@@ -50,5 +51,6 @@ export default {
   ...examine,
   ...IPREmail,
   ...litigation,
-  ...reportAffair
+  ...reportAffair,
+  ...technicalDisclosure
 }

+ 8 - 0
src/api/newApi/noveltySearch.js

@@ -27,6 +27,11 @@ export default {
         return axios.post('/xiaoshi/noveltyProject/queryTechnicalCase', data)
     },
 
+    //修改发明点
+    updateInventionPoint(data){
+        return axios.post('/xiaoshi/noveltyProject/updateInventionPoint', data)
+    },
+
     //查询查新报告对比专利
     queryComparePatent(data){
         return axios.post('/xiaoshi/noveltyProject/queryNoveltyCompareLiterature', data)
@@ -115,4 +120,7 @@ export default {
     copyNoveltyMessage(data){
         return axios.post('/xiaoshi/noveltyProject/copyMessage', data)
     },
+
+
+   
 };

+ 48 - 0
src/api/newApi/technicalDisclosure.js

@@ -0,0 +1,48 @@
+import axios from "@/utils/axios";
+export default {
+     //上传技术交底书
+     addConfession(data){
+        return axios.post('/xiaoshi/noveltyProject/addConfession', data)
+    },
+    //查询技术交底书
+    queryConfession(params){
+        return axios.get('/xiaoshi/noveltyProject/queryConfession', {params})
+    },
+    //查询会话id
+    querySessionId(params){
+        return axios.get('/xiaoshi/dify/querySessionId', {params})
+    },
+    //查询历史会话
+    queryHistoryMessage(data){
+        return axios.post('/xiaoshi/dify/queryHistoryMessage', data)
+    },
+    //发送消息-技术交底书
+    chatMessage(data){
+        return axios.post('/xiaoshi/dify/chatMessage', data)
+    },
+    //停止响应
+    stopMessage(params){
+        return axios.get('/xiaoshi/dify/stopMessage', {params})
+    },
+    //生成发明点
+    generateInventionPoint(data){
+        return axios.post('/xiaoshi/dify/generateInventionPoint', data)
+    },
+
+    //生成技术交底书会话记录
+    addConfessionSession(data){
+        return axios.post('/xiaoshi/confessionSession/addConfessionSession', data)
+    },
+    //更新交底书会话记录
+    updateConfessionSession(data){
+        return axios.post('/xiaoshi/confessionSession/updateConfessionSession', data)
+    },
+    //查询交底书会话记录
+    queryConfessionSession(data){
+        return axios.post('/xiaoshi/confessionSession/queryConfessionSession', data)
+    },
+    //删除交底书会话记录
+    deleteConfessionSessions(data){
+        return axios.post('/xiaoshi/confessionSession/deleteConfessionSessions', data)
+    },
+}

+ 186 - 0
src/components/xiaoshi_AI/CodeBlock.vue

@@ -0,0 +1,186 @@
+<template>
+  <div class="code-block">
+    <div class="code-block-header">
+      <span class="language-label">{{ displayLanguage }}</span>
+      <button class="copy-button" @click="copyCode">
+        {{ copied ? '已复制' : '复制' }}
+      </button>
+    </div>
+    <pre><code :class="['hljs', language]" v-html="highlightedCode" ref="codeEl"></code></pre>
+  </div>
+</template>
+
+<script>
+import hljs from 'highlight.js';
+
+export default {
+  name: 'CodeBlock',
+  props: {
+    code: {
+      type: String,
+      required: true,
+      default: ''
+    },
+    language: {
+      type: String,
+      default: ''
+    }
+  },
+  
+  data() {
+    return {
+      copied: false
+    }
+  },
+
+  computed: {
+    displayLanguage() {
+      return this.language || '文本';
+    },
+
+    highlightedCode() {
+      if (this.language && hljs.getLanguage(this.language)) {
+        try {
+          return hljs.highlight(this.code, { language: this.language }).value;
+        } catch (e) {
+          console.error('代码高亮失败:', e);
+        }
+      }
+      // 如果没有指定语言或高亮失败,返回转义后的代码
+      return hljs.highlightAuto(this.code).value;
+    }
+  },
+
+  methods: {
+    async copyCode() {
+      try {
+        // await navigator.clipboard.writeText(this.code);
+        await this.copyToClipboard(this.code);
+        this.copied = true;
+        setTimeout(() => {
+          this.copied = false;
+        }, 2000);
+      } catch (err) {
+        console.error('复制失败:', err);
+        this.fallbackCopy();
+      }
+    },
+    async copyToClipboard(text) {
+      if (navigator.clipboard && window.isSecureContext) {
+        // 使用现代 Clipboard API
+        return navigator.clipboard.writeText(text);
+      } else {
+        // 使用传统方法
+        return this.fallbackCopy();
+      }
+    },
+
+    fallbackCopy() {
+      try {
+        // 创建临时文本区域
+        const textArea = document.createElement('textarea');
+        textArea.value = this.code;
+        
+        // 确保文本区域不可见
+        textArea.style.position = 'fixed';
+        textArea.style.left = '-999999px';
+        textArea.style.top = '-999999px';
+        document.body.appendChild(textArea);
+        
+        // 选择并复制文本
+        textArea.select();
+        document.execCommand('copy');
+        
+        // 清理
+        document.body.removeChild(textArea);
+        
+        // 更新状态
+        this.copied = true;
+        setTimeout(() => {
+          this.copied = false;
+        }, 2000);
+
+        return Promise.resolve();
+      } catch (err) {
+        return Promise.reject(err);
+      }
+    },
+
+    // 解码 HTML 实体
+    decodeHtml(html) {
+      const txt = document.createElement('textarea');
+      txt.innerHTML = html;
+      return txt.value;
+    }
+  },
+
+  created() {
+    // 解码传入的代码(如果包含 HTML 实体)
+    // this.code = this.decodeHtml(this.code);
+  }
+}
+</script>
+
+<style scoped>
+.code-block {
+  margin: 1em 0;
+  border-radius: 6px;
+  overflow: hidden;
+  background: #f6f8fa;
+  border: 1px solid #e1e4e8;
+}
+
+.code-block-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 16px;
+  background: #f1f1f1;
+  border-bottom: 1px solid #e1e4e8;
+}
+
+.language-label {
+  font-size: 12px;
+  color: #666;
+  text-transform: uppercase;
+}
+
+.copy-button {
+  padding: 4px 8px;
+  font-size: 12px;
+  color: #666;
+  background: transparent;
+  border: 1px solid #ddd;
+  border-radius: 3px;
+  cursor: pointer;
+  transition: all 0.2s;
+  position: relative;
+}
+
+.copy-button:hover {
+  background: #e9e9e9;
+}
+.copy-button.copied {
+  background: #28a745;
+  color: white;
+  border-color: #28a745;
+}
+
+.copy-button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.code-block pre {
+  margin: 0;
+  padding: 16px;
+  overflow-x: auto;
+}
+
+.code-block code {
+  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
+  font-size: 13px;
+  line-height: 1.45;
+  display: block;
+}
+</style> 

File diff suppressed because it is too large
+ 911 - 0
src/components/xiaoshi_AI/index.vue


+ 1 - 1
src/config/index.js

@@ -4,7 +4,7 @@ export default {
     updateTime:'2023-08-04 08:00:00',
     specialDays:['05-12','09-18','12-13'],
     host: window.location.host,
-    url:process.env.NODE_ENV === 'production' ? 'https://xsip.cn' : 'http://192.168.2.105:8803',
+    url:process.env.NODE_ENV === 'production' ? 'https://xsip.cn' : 'http://192.168.2.24:8803',
     staticURL: process.env.NODE_ENV === 'production' ? 'https://xsip.cn/onlinePreview' : 'http://192.168.2.24:8879/onlinePreview',
     WebSocketPath: process.env.NODE_ENV === 'production' ? 'wss://xsip.cn' : 'ws://192.168.2.24:8879',
     WebSocketPathFMS: process.env.NODE_ENV === 'production' ? 'wss://xsip.cn' : 'ws://192.168.2.105:8803',

File diff suppressed because it is too large
+ 1 - 0
src/icons/svg/ai.svg


+ 11 - 0
src/router/index.js

@@ -522,6 +522,17 @@ const routes = [
             },
             component: () => import('@/views/noveltySearch/components/reuseResults/index.vue'),
           },
+          //理解技术交底书
+          {
+            path: '/technicalDisclosure',
+            name: 'technicalDisclosure',
+            meta: {
+              title: '理解技术交底书',
+              sign: 'technicalDisclosure',
+              belong: 'AllReport',
+            },
+            component: () => import('@/views/noveltySearch/components/technicalDisclosure/index.vue'),
+          }
         ]
       },
       //任务模块

+ 30 - 0
src/utils/markdown.js

@@ -0,0 +1,30 @@
+
+import MarkdownIt from 'markdown-it'
+
+function escapeHtml(text) {
+    return text
+      .replace(/&/g, '&amp;')
+      .replace(/"/g, '&quot;')
+      .replace(/'/g, '&#39;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;');
+}
+
+// 初始化 markdown-it
+const md = new MarkdownIt({
+    highlight: (str, lang) => {
+      // 返回一个占位符,后续会被替换为组件
+      return `<code-block language="${lang}" code="${escapeHtml(str)}"></code-block>`;
+    },
+    html: true, // 允许 HTML 标签
+    breaks: true,
+    linkify: true,
+});
+
+export function renderMarkdown(text) {
+    if(!text){
+      return '';
+    }
+    // 只渲染非代码块的文本
+    return md.render(text);
+}

File diff suppressed because it is too large
+ 270 - 0
src/utils/model/AIHelper/index copy.vue


File diff suppressed because it is too large
+ 128 - 16
src/utils/model/AIHelper/index.vue


+ 10 - 3
src/views/components/text.vue

@@ -1,5 +1,9 @@
 <template>
-  <div style="width:500px;margin:50px auto;">
+    <div>
+        <xiaoshiAI></xiaoshiAI>
+    </div>
+    
+  <!-- <div style="width:500px;margin:50px auto;">
     <el-form label-width="120px" :model="form">
         <el-form-item label="智能助手名称">
             <el-input type="textarea" v-model="form.name"></el-input>
@@ -34,12 +38,15 @@
         </el-form-item>
     </el-form>
     <el-button type="primary" size="small" :loading="loading" @click="submit">提交</el-button>
-  </div>
+  </div> -->
 </template>
 
 <script>
+import xiaoshiAI from '@/components/xiaoshi_AI'
 export default {
-  components: {},
+  components: {
+    xiaoshiAI
+  },
   props: {},
   data() {
     return {

+ 176 - 7
src/views/noveltySearch/components/dialog/inventionPoint/inventionPoint.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="inventionPointDialog" v-if="show2">
-    <div class="mini" :style="miniStyle" @click="changeDom('mini')" v-if="!show" v-drag="getPosition" ref="mini">查新</div>
-    <div class="plus" v-else v-drag2="getPosition" ref="plus" :style="plusStyle">
+    <div class="mini" :style="miniStyle" @click="changeDom('mini')" v-show="!show" v-drag="getPosition" ref="mini" :class="!show?'show':''">查新</div>
+    <div class="plus" v-show="show" v-drag2="getPosition" ref="plus" :style="plusStyle" :class="show?'show':''">
       <div class="head" ondragstart="return false">
         <div>{{ text }}</div>
         <div style="width: 20px; height: 20px">
@@ -11,7 +11,7 @@
       <div class="main">
         <div class="main_head" ondragstart="return false">
           <div>
-            <span style="font-size:12px">发明点:</span>
+            <span style="font-size:12px">技术交底书:</span>
             <el-button v-if="projectId" style="margin-left:10px" type="text" size="mini" @click="intoProject" >进入项目</el-button>
           </div>
           <div>
@@ -21,9 +21,34 @@
             <el-button  type="text"  size="mini"  :loading="loading" v-if="btns.indexOf(3)!=-1"  @click="submit(3)">保存</el-button>
           </div>
         </div>
+        <!-- 技术交底书 -->
+        <div class="confession">
+          <div v-if="confession.guid" class="confessionBox" :title="confession.originalName">
+            <div class="confessionName">{{ confession.originalName }}</div>
+            <div class="confessionBtn">
+              <el-button class="understandConfession" type="text" size="mini" @click="understandConfession">AI智能解析</el-button>
+              <!-- <el-button type="text" size="mini" @click="viewConfession">预览</el-button> -->
+              <el-button type="text" size="mini" @click="delConfession">删除</el-button>
+            </div>
+          </div>
+          <div v-else>
+            <el-upload
+                class="upload-demo"
+                ref="uploadConfession"
+                action="#"
+                :auto-upload="false"
+                :show-file-list="false"
+                :on-change="onChangeConfession"
+                :limit="1">
+                <el-button type="text" size="mini">点击上传</el-button>
+            </el-upload>
+          </div>
+        </div>
+        <!-- 发明点输入 -->
         <div class="main_input">
           <myRichText  type="textarea" v-model="technicalCase.inventionPoint"  resize="none" customStyle="height:220px;font-size: 14px;color:#606266" :rows="10"  placeholder="请输入发明点"></myRichText>
         </div>
+
         <div class="main_result" v-if="showResult">
           <div class="description">{{description[clickType]}}</div>
           <div class="result" v-loading="loading">
@@ -222,7 +247,8 @@ export default {
         3:'以下是根据您输入的发明点找到的可能相同的案件,您确认一下是否是相同案件,如果是请相同案件的点击“进入”按钮,如果没有相同的案件就点击“保存”按钮保存输入的发明点'
       },
       chooseContrastFile:[],
-      cWidth:document.body.clientWidth
+      cWidth:document.body.clientWidth,
+      confession:{}
     };
   },
   watch: {
@@ -312,8 +338,10 @@ export default {
         this.technicalCase={
           inventionPoint:'',
         }
+        this.confession = {}
         return
       }
+      this.getConfession()
       var params = {
         projectId:this.projectId
       }
@@ -327,6 +355,79 @@ export default {
         }
       })
     },
+    //查询报告的技术交底书
+    getConfession(){
+      var params = {
+        projectId:this.projectId
+      }
+      this.$api.queryConfession(params).then(response=>{
+        if(response.code == 200){
+          this.confession = response.data || {}
+        }
+      }).catch(error=>{
+        this.confession = {}
+      })
+    },
+    //上传技术交底书
+    onChangeConfession(file){
+        let formData = new FormData()
+        formData.append('sourceId',this.$constants.sourceId)
+        formData.append('files',file.raw)
+        var message = this.$message({
+            message: '文件上传中...',
+            type: 'warning',
+            duration:0
+        });
+        this.$api.uploadFile(formData).then(response=>{
+            if(response.code == 200){
+                let guid = response.data[0]
+                this.$refs.uploadConfession.clearFiles()
+                var params = {
+                  projectId:this.projectId,
+                  fileGuid:guid
+                }
+                this.$api.addConfession(params).then(response=>{
+                  if(response.code == 200){
+                    message.close()
+                    this.$message.success('文件上传成功')
+                    if(!this.projectId){
+                      this.$emit('getProjectId',response.data)
+                    }else{
+                      this.getConfession()
+                    }
+                  }
+                }).catch(error=>{
+                  message.close()
+                })
+            }
+        })
+    },
+    //理解技术交底书
+    understandConfession(){
+      let router = this.$router.resolve({
+          path: '/technicalDisclosure',
+          query: {
+            projectId: this.projectId,
+          }
+        })
+        window.open(router.href, '_blank')
+    },
+    //查看技术交底书
+    viewConfession(){},
+    //删除技术交底书
+    delConfession(){
+      var params = {
+        projectId:this.projectId,
+        fileGuid:''
+      }
+      this.$api.addConfession(params).then(response=>{
+        if(response.code == 200){
+          this.$message.success('文件删除成功')
+          this.getConfession()
+        }
+      }).catch(error=>{
+      })
+    },
     //选择关键词
     chooseKeyword(item) {
       var index = this.checkList.indexOf(item);
@@ -386,18 +487,25 @@ export default {
     // 保存发明点
     saveInventionPoint(){
       var params = this.technicalCase
+      var api = 'addOrUpdateTechnicalCase'
       if(this.projectId){
         params.projectId = this.projectId
+        api = 'updateInventionPoint'
       }
-      this.$api.addOrUpdateTechnicalCase(params).then(response=>{
+      this.$api[api](params).then(response=>{
         if(response.code == 200){
           this.$message.success('保存成功')
           if(!this.technicalCase.projectId){
             this.$emit('getProjectId',response.data.projectId)
           }
           this.$emit('updateInventionPoint')
-          this.technicalCase.technicalCaseId = response.data.technicalCaseId
-          this.technicalCase.projectId = response.data.projectId
+          if(response.data.technicalCaseId){
+            this.technicalCase.technicalCaseId = response.data.technicalCaseId
+          }
+          if(response.data.projectId){
+            this.technicalCase.projectId = response.data.projectId
+          }
+          
         }
       }).catch(error=>{
         this.$message.error('保存失败')
@@ -436,6 +544,7 @@ export default {
 <style lang="scss" scoped>
 .inventionPointDialog {
   z-index: 99999;
+  
   .mini {
     position: fixed;
     // top: 0;
@@ -450,6 +559,10 @@ export default {
     font-weight: bold;
     cursor: pointer;
     box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
+    opacity: 0;
+    transform: scale(2);
+    visibility: hidden;
+    transition: opacity  0.5s ease,transform 0.5s ease;
   }
   .plus {
     padding: 10px;
@@ -460,6 +573,10 @@ export default {
     width: 350px;
     height: auto;
     border-radius: 10px;
+    opacity: 0;
+    transform: scale(0.1);
+    visibility: hidden;
+    transition: opacity  0.5s ease,transform 0.5s ease;
     .head {
       width: 100%;
       display: flex;
@@ -478,6 +595,53 @@ export default {
         align-items: center;
         line-height: 30px;
       }
+
+      .confession{
+        margin: 0 0 10px 0;
+        .confessionBox{
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          flex-wrap: nowrap;
+          .confessionBtn{
+            // width:0;
+            // height: 0;
+            // opacity: 0;
+            // pointer-events: none;
+            .understandConfession{
+              font-size: 15px;
+              background: linear-gradient(to right, blue, red);
+              -webkit-background-clip: text;
+              color: transparent;
+            }
+          }
+          .confessionName{
+            font-size: 12px;
+            flex:1;
+            // width:calc(100% - 1px);
+            width:calc(100% - 30px);
+            line-height:28px;
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow:ellipsis;
+          }
+          &:hover{
+            // color:#66b1ff;
+            transform: scale(1.02) translateY(-2px);
+            transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 1.94);
+            // &> .confessionName{
+            //   width:calc(100% - 30px);
+            // }
+          }
+          // &:hover> .confessionBtn{
+          //   width:auto;
+          //   height: auto;
+          //   opacity: 1;
+          //   pointer-events: all;
+          // }
+        }
+      }
+
       .main_result {
         margin-top: 20px;
         background: white;
@@ -512,5 +676,10 @@ export default {
       }
     }
   }
+  .show{
+    opacity: 1;
+    transform: scale(1) ;
+    visibility: visible ;
+  }
 }
 </style>

+ 7 - 1
src/views/noveltySearch/components/noveltySearchCard.vue

@@ -30,7 +30,7 @@
                     </div>
                     <el-form label-width="100px">
                         <el-form-item label="发明点:">
-                              <div v-html="item.inventionPoint"></div>
+                              <div class="inventionPoint" v-html="item.inventionPoint"></div>
                         </el-form-item>
                         <el-form-item label="创建人:">
                               <div v-html="item.createName"></div>
@@ -313,6 +313,12 @@ import createNoveltySearch from './dialog/createNoveltySearch.vue';
     color: #958b8b8c;
     font-size: 20px;
   }
+  .box-card{
+    .inventionPoint{
+      max-height: 200px;
+      overflow-y: auto;
+    }
+  }
   </style>
   <style lang="scss">
     .box-card{

+ 65 - 0
src/views/noveltySearch/components/technicalDisclosure/dialog/renameConversation.vue

@@ -0,0 +1,65 @@
+<template>
+    <div class="responseDialog">
+      <el-dialog title="重命名会话" :visible.sync="showDialog" width="500px" :close-on-click-modal="false"  :before-close="handleClose" append-to-body>
+        
+        <div>
+            <p>会话名称</p>
+            <el-input v-model="form.conversationName" placeholder="请输入会话名称"></el-input>
+        </div>
+        <span slot="footer" class="dialog-footer">
+          <el-button @click="handleClose">取 消</el-button>
+          <el-button type="primary" @click="updateConfessionSession" >确 定</el-button>
+        </span>
+      </el-dialog>
+    </div>
+</template>
+  
+<script>
+  export default {
+    props:{},
+    data() {
+      return {
+        showDialog:false,
+        form:{}
+      }
+    },
+    watch: {
+    },
+    mounted() {
+  
+    },
+    methods: {
+        open(row){
+            this.form = JSON.parse(JSON.stringify(row))
+            this.showDialog = true
+        },
+        handleClose(){
+            this.showDialog = false
+        },
+        //更新技术交底书会话记录
+        updateConfessionSession(){
+            var params = {
+                confessionSessionId:this.form.id,
+                inventionPoint:this.form.inventionPoint,
+                conversationName:this.form.conversationName
+            }
+            this.$api.updateConfessionSession(params).then(response=>{
+                if(response.code == 200){
+                    this.$emit('rename',this.form)
+                    this.handleClose()
+                }
+            }).catch(error=>{
+                this.$message.error('更新失败')
+            })
+        },
+    },
+  }
+</script>
+  
+<style lang="scss">
+  .responseDialog{
+    .el-dialog__body{
+      padding-bottom: 0px;
+    }
+  }
+</style>

+ 30 - 0
src/views/noveltySearch/components/technicalDisclosure/index.vue

@@ -0,0 +1,30 @@
+<template>
+  <div class="height_100">
+    <technicalDisclosure :projectId="projectId"></technicalDisclosure>
+  </div>
+</template>
+
+<script>
+import technicalDisclosure from './technicalDisclosure.vue';
+export default {
+  components: {
+    technicalDisclosure
+  },
+  props: {},
+  data() {
+    return {
+    };
+  },
+  watch: {},
+  computed: {
+    projectId(){
+        return this.$route.query.projectId
+    },
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

File diff suppressed because it is too large
+ 739 - 0
src/views/noveltySearch/components/technicalDisclosure/technicalDisclosure.vue