Browse Source

虚警抑制、智能问答、知识库管理

Rmengdi 3 months ago
parent
commit
47ee26aea8

+ 17 - 0
package-lock.json

@@ -23,6 +23,7 @@
         "file-saver": "^2.0.5",
         "html2canvas": "^1.4.1",
         "lodash": "^4.17.21",
+        "marked": "^15.0.7",
         "md5": "^2.3.0",
         "pdfvuer": "^1.10.0",
         "qs": "^6.10.3",
@@ -8365,6 +8366,17 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/marked": {
+      "version": "15.0.7",
+      "resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.7.tgz",
+      "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==",
+      "bin": {
+        "marked": "bin/marked.js"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
     "node_modules/material-colors": {
       "version": "1.2.6",
       "resolved": "https://registry.npmmirror.com/material-colors/-/material-colors-1.2.6.tgz",
@@ -19064,6 +19076,11 @@
         "semver": "^6.0.0"
       }
     },
+    "marked": {
+      "version": "15.0.7",
+      "resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.7.tgz",
+      "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg=="
+    },
     "material-colors": {
       "version": "1.2.6",
       "resolved": "https://registry.npmmirror.com/material-colors/-/material-colors-1.2.6.tgz",

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
     "file-saver": "^2.0.5",
     "html2canvas": "^1.4.1",
     "lodash": "^4.17.21",
+    "marked": "^15.0.7",
     "md5": "^2.3.0",
     "pdfvuer": "^1.10.0",
     "qs": "^6.10.3",

+ 16 - 0
src/api/als/LLM.js

@@ -0,0 +1,16 @@
+import { get, postFile, post } from '@/http/index'
+
+// 获取知识库内文件列表
+export const uploadDocs = async (data) => {
+  return await postFile('/knowledge_base/upload_docs', data)
+}
+
+// 获取知识库内文件列表
+export const getListFiles = async (data) => {
+  return await get('/knowledge_base/list_files', data)
+}
+
+// 删除文件
+export const deleteDocs = async (data) => {
+  return await post('/knowledge_base/delete_docs', data)
+}

+ 5 - 0
src/api/als/algorithm.js

@@ -25,6 +25,11 @@ export const executeFalseAlarm = async (data) => {
   return await post('/als/algorithm/execute/falseAlarm', data)
 }
 
+// 获取虚警的数学参数
+export const executeMath = async (data) => {
+  return await post('/als/algorithm/execute/math', data)
+}
+
 // 执行故障诊断算法
 export const executeFault = async (data) => {
   return await post('/als/algorithm/execute/fault', data)

+ 9 - 0
src/router/modules/system.js

@@ -263,6 +263,15 @@ export default [
           title: '文件管理'
         }
       },
+      {
+        name: 'LLM',
+        path: '/dataManage/LLM',
+        component: () => import('@/views/als/LLM/index.vue'),
+        meta: {
+          parent: 'BasicData',
+          title: '知识库管理'
+        }
+      },
       {
         name: 'KnowledgeExtraction',
         path: '/knowledgeGraph/knowledgeExtraction',

+ 239 - 0
src/views/als/LLM/index.vue

@@ -0,0 +1,239 @@
+<template>
+  <div class="view-table-content">
+    <div style="width: 100%">
+      <div class="view-dataType-title">
+        <div class="view-dataType-title-btn">
+          <el-button type="success" @click="openDialog()">上传文件</el-button>
+          <el-button type="warning" @click="remove(tableCheckItems)" :disabled="tableCheckItems.length == 0">删除</el-button>
+        </div>
+      </div>
+      <div class="view-dataType-table">
+        <LTable ref="table" @selection-change="selection" :defaultFetch="false" :fetch="fetch" :columns="columns" :dataSource="tableData" :options="options"></LTable>
+      </div>
+      <!-- 添加或修改模型信息对话框 -->
+      <el-dialog title="上传文件" :visible.sync="dialogVisible" ref="fileUploadRef" width="600px" :before-close="handleClose" style="text-align: center">
+        <el-upload class="upload-demo" name="files" drag :data="carryData" :before-upload="handleBeforeUpload" :file-list="fileList" :headers="headers" :action="uploadFileUrl" :on-success="handleUploadSuccess" :on-error="handleUploadError" multiple>
+          <i class="el-icon-upload"></i>
+          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+          <div class="el-upload__tip" slot="tip">只能上传docx/doc文件,且不超过50MB</div>
+        </el-upload>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getListFiles, deleteDocs } from '@/api/als/LLM'
+import { deepClone, debounce, getItem } from '@/utils/index'
+
+export default {
+  name: 'LLM',
+  data() {
+    // 这里存放数据
+    return {
+      dialogTitle: '新增',
+      dialogVisible: false,
+      keyWordData: '',
+      searchValue: '',
+      columns: [
+        {
+          prop: 'kb_name',
+          label: '知识库名称'
+        },
+        {
+          prop: 'file_name',
+          label: '文件名称'
+        },
+        {
+          prop: 'file_ext',
+          label: '文件类型'
+        },
+        {
+          prop: 'document_loader',
+          label: '文件加载器类型'
+        },
+        {
+          prop: 'docs_count',
+          label: '文件分割数量'
+        },
+        {
+          prop: 'text_splitter',
+          label: '文本分割器类型'
+        },
+        {
+          prop: 'create_time',
+          label: '创建时间'
+        },
+        {
+          prop: 'file_size',
+          label: '文件大小(字节)'
+        },
+        {
+          button: true,
+          label: '操作',
+          width: '240px',
+          group: [
+            {
+              name: '删除',
+              type: 'text',
+              round: false,
+              plain: false,
+              onClick: (row, index, scope) => {
+                this.remove([row])
+              }
+            }
+          ]
+        }
+      ],
+      options: {
+        stripe: false, // 斑马纹
+        mutiSelect: true, // 多选框
+        index: false, // 显示序号, 多选则 mutiSelect
+        loading: false, // 表格动画
+        initTable: false, // 是否一挂载就加载数据
+        border: true,
+        height: 'calc(100vh - 300px)'
+      },
+      tableCheckItems: [],
+      tableData: [],
+      carryData: {
+        to_vector_store: true,
+        override: false,
+        chunk_size: 750,
+        chunk_overlap: 150,
+        zh_title_enhance: false,
+        knowledge_base_name: 'lqbz'
+      },
+      fileList: [],
+      uploadFileUrl: '/api/knowledge_base/upload_docs',
+      headers: { Authorization: getItem('token') }
+    }
+  },
+  mounted() {
+    this.getListFilesAPI()
+  },
+  methods: {
+    async getListFilesAPI() {
+      if (this.$refs.table) this.$refs.table.clearSelection()
+      const { data } = await getListFiles({ knowledge_base_name: 'lqbz' })
+      this.tableData = data
+    },
+
+    fetch() {
+      this.getListFilesAPI()
+    },
+
+    async searchClick() {
+      this.getListFilesAPI({ name: keyWordData })
+    },
+
+    openDialog() {
+      this.dialogTitle = '新增'
+      this.dialogVisible = true
+    },
+
+    async deleteDocsAPI(params) {
+      const data = {
+        knowledge_base_name: 'lqbz',
+        file_names: params,
+        delete_content: true,
+        not_refresh_vs_cache: false
+      }
+      try {
+        const { code } = await deleteDocs(data)
+        if (code === 200) {
+          this.$message({
+            type: 'success',
+            message: '操作成功!'
+          })
+          this.getListFilesAPI()
+          this.handleClose()
+        }
+      } catch (error) {}
+    },
+
+    handleClose() {
+      this.dialogVisible = false
+      this.form = {
+        fileName: '',
+        fileType: ''
+      }
+    },
+
+    handUpdate(row) {
+      this.dialogTitle = '编辑'
+      this.form = deepClone(row)
+      this.dialogVisible = true
+    },
+
+    submit() {},
+
+    selection(val) {
+      this.tableCheckItems = val
+    },
+
+    remove(row) {
+      this.$confirm('是否删除该数据', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(() => {
+          this.deleteDocsAPI(row.map((e) => e.file_name))
+        })
+        .catch(() => {})
+    },
+
+    handleBeforeUpload(file) {
+      const fileType = ['docx', 'doc']
+      const fileName = file.name.split('.')
+      const fileExt = fileName[fileName.length - 1]
+      const isTypeOk = fileType.indexOf(fileExt) >= 0
+      if (!isTypeOk) {
+        this.$message({
+          type: 'error',
+          message: '文件格式不正确, 请上传docx/doc格式文件!'
+        })
+        return false
+      }
+      // 校检文件大小
+      const isLt = file.size / 1024 / 1024 < 50
+      if (!isLt) {
+        this.$message({
+          type: 'error',
+          message: '上传文件大小不能超过 50 MB!'
+        })
+        return false
+      }
+    },
+
+    handleUploadSuccess(res, file) {
+      if (res.code === 200) {
+        this.$message({
+          type: 'success',
+          message: '文件上传成功'
+        })
+      } else {
+        this.$message({
+          type: 'error',
+          message: res.msg
+        })
+      }
+      this.getListFilesAPI()
+      this.fileList = []
+      this.dialogVisible = false
+    },
+
+    handleUploadError(err) {
+      this.$message({
+        type: 'error',
+        message: '上传文件失败,请重试'
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../index.scss';
+</style>

+ 10 - 0
src/views/als/components/Charts/playBackChart.vue

@@ -87,6 +87,16 @@ export default {
             color: '#fff'
           }
         },
+        toolbox: {
+          show: true,
+          feature: {
+            dataZoom: {
+              yAxisIndex: 'none'
+            },
+            restore: {},
+            saveAsImage: {}
+          }
+        },
         xAxis: {
           type: 'category',
           data: xAxisData

+ 134 - 18
src/views/als/falseAlarm/index.vue

@@ -17,19 +17,6 @@
       </div>
       <!-- 警告列表对话框 -->
       <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" :before-close="handleClose" width="1200px">
-        <el-dialog width="80%" title="飞参数据曲线" :visible.sync="innerVisible" :before-close="innerDialogClose" append-to-body>
-          <div style="width: 100%; position: relative; color: #fff">
-            <el-radio-group class="isFalseAlarm" v-model="isFalseAlarm" size="small">
-              <el-radio-button label="0">实警</el-radio-button>
-              <el-radio-button label="1">虚警</el-radio-button>
-            </el-radio-group>
-          </div>
-          <PlayBackChart :chartData="chartData" />
-          <span slot="footer" class="dialog-footer">
-            <el-button @click="innerDialogClose">取 消</el-button>
-            <el-button type="primary" @click="submit">确 定</el-button>
-          </span>
-        </el-dialog>
         <el-form ref="form" :inline="true" :model="form" label-width="80px" disabled>
           <el-form-item label="编目" prop="aircraftId">
             <el-select v-model="form.aircraftId">
@@ -44,6 +31,28 @@
           </el-form-item> -->
         </el-form>
         <LTable ref="warningTable" @selection-change="selection" :defaultFetch="false" :showColumnSetting="false" :columns="warningColumns" :dataSource="warningTableData" :options="warningOptions" :pagination="warningTableRequset"></LTable>
+        <el-dialog width="80%" title="飞参数据曲线" :visible.sync="innerVisible" :before-close="innerDialogClose" append-to-body>
+          <PlayBackChart :chartData="chartData" />
+          <div style="margin-top: 20px">
+            <span style="color: #fff; margin-left: 30px">请选择时间区间:</span>
+            <el-date-picker v-model="mathTime" placement="bottom-start" value-format="yyyy-MM-dd HH:mm:ss.SSS" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="handleMathTime" :clearable="false">
+            </el-date-picker>
+
+            <el-button icon="el-icon-refresh" circle class="refresh" @click="resetTime"></el-button>
+            <el-button type="success" @click="getExecuteMathAPI" style="margin-left: 15px">确 定</el-button>
+            <LTable ref="mathTable" :defaultFetch="false" :columns="mathColumns" :dataSource="mathTableData" :options="mathOptions"></LTable>
+          </div>
+          <div style="width: 100%; color: #fff; text-align: center">
+            <el-radio-group class="isFalseAlarm" v-model="isFalseAlarm" size="small">
+              <el-radio-button label="0">实警</el-radio-button>
+              <el-radio-button label="1">虚警</el-radio-button>
+            </el-radio-group>
+          </div>
+          <span slot="footer" class="dialog-footer">
+            <el-button @click="innerDialogClose">取 消</el-button>
+            <el-button type="primary" @click="submit">确 定</el-button>
+          </span>
+        </el-dialog>
       </el-dialog>
       <el-dialog title="执行进度" :visible.sync="progressVisible" width="800px">
         <el-progress :text-inside="true" :stroke-width="24" :percentage="percentage" status="success"></el-progress>
@@ -59,7 +68,7 @@ import { getAircaftCatalogTree } from '@/api/als/sideTree'
 import { getAircaftCatalogAll } from '@/api/als/aircraft'
 import PlayBackChart from '@/views/als/components/Charts/playBackChart.vue'
 import { deepClone, debounce } from '@/utils/index'
-import { executeFalseAlarm, getOssIdDataAPI } from '@/api/als/algorithm'
+import { executeFalseAlarm, getOssIdDataAPI, executeMath } from '@/api/als/algorithm'
 import { getWarningResult, updateWarningResult } from '@/api/als/falseAlarmResult'
 
 export default {
@@ -242,6 +251,58 @@ export default {
         pageSize: 10,
         searchValue: ''
       },
+      mathColumns: [
+        {
+          prop: 'params',
+          label: '参数'
+        },
+        {
+          prop: 'mean',
+          label: '算数平均数'
+        },
+        {
+          prop: 'variance',
+          label: '方差'
+        },
+        {
+          prop: 'populationVariance',
+          label: '总体方差'
+        },
+        {
+          prop: 'sum',
+          label: '和'
+        },
+        {
+          prop: 'max',
+          label: '最大值'
+        },
+        {
+          prop: 'min',
+          label: '最小值'
+        },
+        {
+          prop: 'mode',
+          label: '众数'
+        },
+        {
+          prop: 'geometricMean',
+          label: '几何平均数'
+        },
+        {
+          prop: 'sumSq',
+          label: '平方和'
+        }
+      ],
+      mathOptions: {
+        stripe: false, // 斑马纹
+        mutiSelect: false, // 多选框
+        index: false, // 显示序号, 多选则 mutiSelect
+        loading: false, // 表格动画
+        initTable: false, // 是否一挂载就加载数据
+        border: true,
+        height: '300px'
+      },
+      mathTableData: [],
       debounceFn: debounce(this.fetch, 500),
       aircaftCatalogAll: [],
       chartData: {
@@ -265,7 +326,17 @@ export default {
       },
       isFalseAlarm: '0',
       currentSortieNo: '',
-      percentage: 0
+      percentage: 0,
+      mathTime: [],
+      mathParams: {
+        code: '',
+        sortieNo: '',
+        beginTime: '',
+        endTime: ''
+      },
+      backupsTime: []
+      // startTime:'',
+      // endTime:'',
     }
   },
   watch: {
@@ -379,6 +450,13 @@ export default {
         id: null,
         resultContent: ''
       }
+      this.mathParams = {
+        code: '',
+        sortieNo: '',
+        beginTime: '',
+        endTime: ''
+      }
+      this.mathTableData = []
       this.isFalseAlarm = false
     },
 
@@ -436,6 +514,9 @@ export default {
     async checkCurve(row) {
       this.isFalseAlarm = row.resultContent
       this.warningForm = deepClone(row)
+      this.mathParams.code = row.code
+      this.mathParams.sortieNo = row.sortieNo
+      this.getExecuteMathAPI()
       try {
         const { code, data } = await getCurveData(row.code, row.sortieNo)
         if (code === 200) {
@@ -451,6 +532,31 @@ export default {
       } catch (error) {}
     },
 
+    handleMathTime(time) {
+      this.mathParams.beginTime = time[0]
+      this.mathParams.endTime = time[1]
+    },
+
+    resetTime() {
+      this.mathTime = this.backupsTime
+    },
+
+    // 获取数学数据
+    async getExecuteMathAPI() {
+      try {
+        const { data, code } = await executeMath(this.mathParams)
+        if (code === 200) {
+          this.mathTableData = []
+          Object.keys(data).forEach((item) => {
+            this.mathTableData.push({
+              params: item,
+              ...data[item]
+            })
+          })
+        }
+      } catch (error) {}
+    },
+
     getResultData(data) {
       this.chartData = {
         title: '',
@@ -465,7 +571,10 @@ export default {
       this.chartData.legendData = headData
       for (var key in contentData) {
         if (key === '时间') {
-          this.chartData.xAxisData = contentData['时间']
+          const timeList = contentData['时间']
+          const time = [timeList[0], timeList[timeList.length - 1]]
+          this.mathTime = this.backupsTime = time
+          this.chartData.xAxisData = timeList
         } else {
           this.chartData.seriesData.push({
             name: key,
@@ -535,10 +644,17 @@ export default {
 
 <style lang="scss" scoped>
 @import '../index.scss';
+
 .isFalseAlarm {
-  position: absolute;
-  right: 20px;
+  // position: absolute;
+  // right: 20px;
+  margin-top: 20px;
   color: #fff;
   z-index: 9999;
 }
+.refresh {
+  padding: 5px !important;
+  border-radius: 50% !important;
+  margin-left: 10px;
+}
 </style>

+ 8 - 2
src/views/als/faultStatistics/echarts.vue

@@ -333,8 +333,14 @@ export default {
           value: e.nameCount
         })
       })
-      this.echartsLeft(names, values)
-      this.echartsRight(data)
+      const sortPieData = data.sort((a, b) => {
+        return b.value - a.value
+      })
+      const sortBarData = values.sort((a, b) => {
+        return b - a
+      })
+      this.echartsLeft(names, sortBarData)
+      this.echartsRight(sortPieData)
     },
 
     fetch() {

+ 5 - 0
src/views/als/index.scss

@@ -58,3 +58,8 @@
   }
 }
 
+::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  display: none;
+}
+

+ 41 - 19
src/views/als/intelligentQA/index.scss

@@ -2,7 +2,7 @@
   display: flex;
   width: 100%;
   height: calc(100vh - 160px);
-  
+  // font-family:Simsun
 }
 
 .history,
@@ -111,15 +111,6 @@
   display: flex;
   flex-direction: column;
 
-  .header{
-    height: 60px;
-    line-height: 60px;
-    width: 100%;
-    text-align: center;
-    color: #fff;
-    background-color: #f8eaea9b;
-  }
-
   .main{
     flex: 1;
     // background-color: #f8f8fc4c;
@@ -130,7 +121,7 @@
     .chatLine {
       flex: 1;
       width: 100%;
-      max-width: 1200px;
+      max-width: 1250px;
       margin: 0 auto;
     }
     
@@ -150,26 +141,56 @@
         color: #fff;
       }
     }
-    .answerData{
-      max-width: 800px;
-      display: inline-block;
+    .chatA{
+      flex-direction: column;
+      min-width: 900px;
+      max-width:90%;
       border-radius: 8px;
       padding: 6px 12px;
       background: #11536771;
       box-shadow: 0 16px 20px 0 rgba(174, 167, 223, 0.06);
       color: #fff;
-      position: relative;
+      display: inline-block;
+      .tipAnswer{
+        background: inherit;
+      }
+    }
+    .answerData{
+      .title{
+        font-size: 2rem;
+        // font-weight: 700;
+        margin: 30px 0px;
+      }
+      // position: relative;
       .answer{
         margin: 20px 0;
+        margin-left: 30px;
+        line-height: 1.8rem;
+      }
+      .markdown{
+        color: #c0c0c0;
+        background:transparent ;
       }
     }
-    
+    ::v-deep .el-collapse {
+      .el-collapse-item__header{
+        background-color: transparent !important;
+        color: #FFF;
+      }
+      .el-collapse-item__wrap{
+        background-color: transparent !important;
+      }
+    }
+    .graph{
+      width: 100%;
+      display: flex;
+      align-items:flex-end;
+      justify-content: center;
+    }
     .more{
       border-radius: 50%;
-      position: absolute;
       font-size: 1.5rem;
-      right: -30px;
-      bottom: 0;
+      margin-left: 20px;
     }
     .more:hover{
       cursor: pointer;
@@ -179,6 +200,7 @@
   .footer{
     width: 100%;
     text-align: center;
+    margin-top: 20px;
 
     .footerContent{
       width: 60%;

File diff suppressed because it is too large
+ 81 - 33
src/views/als/intelligentQA/index.vue


+ 10 - 1
vue.config.js

@@ -16,13 +16,22 @@ module.exports = defineConfig({
         }
       },
       ['/api/kgqa']: {
-        target: 'http://192.168.0.107:7073',
+        // target: 'http://192.168.0.107:7073',
+        target: 'http://localhost:7073',
         ws: false,
         changeOrigin: true,
         pathRewrite: {
           ['^' + '/api/kgqa']: '/kgqa'
         }
       },
+      ['/api/knowledge_base']: {
+        target: 'http://192.168.0.107:7861',
+        ws: false,
+        changeOrigin: true,
+        pathRewrite: {
+          ['^' + '/api/knowledge_base']: '/knowledge_base'
+        }
+      },
       [process.env.VUE_APP_BASE_API]: {
         target: process.env.VUE_APP_BASE_API_target,
         ws: false, //也可以忽略不写,不写不会影响跨域

Some files were not shown because too many files changed in this diff