瀏覽代碼

Merge branch 'master' of http://60.171.161.56:20000/tjf/deviceManage

wangmengwei 2 天之前
父節點
當前提交
34fa63eaf0

+ 18 - 0
ruoyi-ui/src/api/manage/sousu.js

@@ -0,0 +1,18 @@
+import request from '@/utils/requesta'
+
+// 查询模型列表
+export function getModelsList(query) {
+  return request({
+    url: '/v1/models',
+    method: 'get',
+    params: query
+  })
+}
+// 模型返回结果
+export function getModelsChat(query) {
+  return request({
+    url: '/v1/chat/completions',
+    method: 'post',
+    data: query
+  })
+}

二進制
ruoyi-ui/src/assets/images/_icon_aijs_jg_yy.png


二進制
ruoyi-ui/src/assets/images/icon_aijs_close.png


二進制
ruoyi-ui/src/assets/images/icon_aijs_wjmc.png


二進制
ruoyi-ui/src/assets/images/icon_jsl_sp.png


二進制
ruoyi-ui/src/assets/images/icon_jsl_wj.png


二進制
ruoyi-ui/src/assets/images/searchbg.png


二進制
ruoyi-ui/src/assets/images/sousu/icon_js_lstw.png


二進制
ruoyi-ui/src/assets/images/sousu/icon_js_qcls.png


二進制
ruoyi-ui/src/assets/images/sousu/icon_js_zk.png


+ 263 - 0
ruoyi-ui/src/components/ImageUploaddsj/index.vue

@@ -0,0 +1,263 @@
+<template>
+  <div class="component-upload-image">
+    <el-tooltip class="item" effect="dark" :content="'请上传大小不超过'+ fileSize +'mb,格式为png,jpg,jpeg' " placement="top-start">
+    <el-upload
+      multiple
+      :action="uploadImgUrl"
+      list-type="picture-card"
+      :on-success="handleUploadSuccess"
+      :before-upload="handleBeforeUpload"
+      :data="data"
+      :limit="limit"
+      :on-error="handleUploadError"
+      :on-exceed="handleExceed"
+      ref="imageUpload"
+      :on-remove="handleDelete"
+      :show-file-list="true"
+      :headers="headers"
+      :file-list="fileList"
+      :on-preview="handlePictureCardPreview"
+      :class="{hide: this.fileList.length >= this.limit}"
+    >
+      <i class="el-icon-plus"></i>
+    </el-upload>
+    </el-tooltip>
+
+    <!-- 上传提示 -->
+    <!-- <div class="el-upload__tip" slot="tip" v-if="showTip">
+      请上传
+      <template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
+      <template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
+      的文件
+    </div> -->
+
+    <el-dialog
+      :visible.sync="dialogVisible"
+      title="预览"
+      width="800"
+      append-to-body
+    >
+      <img
+        :src="dialogImageUrl"
+        style="display: block; max-width: 100%; margin: 0 auto"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth"
+import { isExternal } from "@/utils/validate"
+import Sortable from 'sortablejs'
+
+export default {
+  props: {
+    value: [String, Object, Array],
+    // 上传接口地址
+    action: {
+      type: String,
+      default: "/common/upload"
+    },
+    // 上传携带的参数
+    data: {
+      type: Object
+    },
+    // 图片数量限制
+    limit: {
+      type: Number,
+      default: 5
+    },
+    // 大小限制(MB)
+    fileSize: {
+       type: Number,
+      default: 5
+    },
+    // 文件类型, 例如['png', 'jpg', 'jpeg']
+    fileType: {
+      type: Array,
+      default: () => ["png", "jpg", "jpeg"]
+    },
+    // 是否显示提示
+    isShowTip: {
+      type: Boolean,
+      default: true
+    },
+      // 拖动排序
+    drag: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      number: 0,
+      uploadList: [],
+      dialogImageUrl: "",
+      dialogVisible: false,
+      hideUpload: false,
+      baseUrl: process.env.VUE_APP_BASE_API,
+      uploadImgUrl: process.env.VUE_APP_BASE_API + this.action, // 上传的图片服务器地址
+      headers: {
+        Authorization: "Bearer " + getToken(),
+      },
+      fileList: []
+    }
+  },
+  mounted() {
+    if (this.drag) {
+      this.$nextTick(() => {
+        const element = document.querySelector('.el-upload-list')
+        Sortable.create(element, {
+          onEnd: (evt) => {
+            const movedItem = this.fileList.splice(evt.oldIndex, 1)[0]
+            this.fileList.splice(evt.newIndex, 0, movedItem)
+            this.$emit("input", this.listToString(this.fileList))
+          }
+        })
+      })
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        if (val) {
+          // 首先将值转为数组
+          const list = Array.isArray(val) ? val : this.value.split(',')
+          // 然后将数组转为对象数组
+          this.fileList = list.map(item => {
+            if (typeof item === "string") {
+              if (item.indexOf(this.baseUrl) === -1 && !isExternal(item)) {
+                  item = { name: this.baseUrl + item, url: this.baseUrl + item }
+              } else {
+                  item = { name: item, url: item }
+              }
+            }
+            return item
+          })
+        } else {
+          this.fileList = []
+          return []
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  computed: {
+    // 是否显示提示
+    showTip() {
+      return this.isShowTip && (this.fileType || this.fileSize)
+    },
+  },
+  methods: {
+    // 上传前loading加载
+    handleBeforeUpload(file) {
+      let isImg = false
+      if (this.fileType.length) {
+        let fileExtension = ""
+        if (file.name.lastIndexOf(".") > -1) {
+          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1)
+        }
+        isImg = this.fileType.some(type => {
+          if (file.type.indexOf(type) > -1) return true
+          if (fileExtension && fileExtension.indexOf(type) > -1) return true
+          return false
+        })
+      } else {
+        isImg = file.type.indexOf("image") > -1
+      }
+
+      if (!isImg) {
+        this.$modal.msgError(`文件格式不正确,请上传${this.fileType.join("/")}图片格式文件!`)
+        return false
+      }
+      if (file.name.includes(',')) {
+        this.$modal.msgError('文件名不正确,不能包含英文逗号!')
+        return false
+      }
+      if (this.fileSize) {
+        const isLt = file.size / 1024 / 1024 < this.fileSize
+        if (!isLt) {
+          this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`)
+          return false
+        }
+      }
+      this.$modal.loading("正在上传图片,请稍候...")
+      this.number++
+    },
+    // 文件个数超出
+    handleExceed() {
+      this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`)
+    },
+    // 上传成功回调
+    handleUploadSuccess(res, file) {
+      if (res.code === 200) {
+        this.uploadList.push({ name: res.fileName, url: res.fileName })
+        this.uploadedSuccessfully()
+      } else {
+        this.number--
+        this.$modal.closeLoading()
+        this.$modal.msgError(res.msg)
+        this.$refs.imageUpload.handleRemove(file)
+        this.uploadedSuccessfully()
+      }
+    },
+    // 删除图片
+    handleDelete(file) {
+      const findex = this.fileList.map(f => f.name).indexOf(file.name)
+      if (findex > -1) {
+        this.fileList.splice(findex, 1)
+        this.$emit("input", this.listToString(this.fileList))
+      }
+    },
+    // 上传失败
+    handleUploadError() {
+      this.$modal.msgError("上传图片失败,请重试")
+      this.$modal.closeLoading()
+    },
+    // 上传结束处理
+    uploadedSuccessfully() {
+      if (this.number > 0 && this.uploadList.length === this.number) {
+        this.fileList = this.fileList.concat(this.uploadList)
+        this.uploadList = []
+        this.number = 0
+        this.$emit("input", this.listToString(this.fileList))
+        this.$modal.closeLoading()
+      }
+    },
+    // 预览
+    handlePictureCardPreview(file) {
+      this.dialogImageUrl = file.url
+      this.dialogVisible = true
+    },
+    // 对象转成指定字符串分隔
+    listToString(list, separator) {
+      let strs = ""
+      separator = separator || ","
+      for (let i in list) {
+        if (list[i].url) {
+          strs += list[i].url.replace(this.baseUrl, "") + separator
+        }
+      }
+      return strs != '' ? strs.substr(0, strs.length - 1) : ''
+    }
+  }
+}
+</script>
+<style scoped lang="scss">
+// .el-upload--picture-card 控制加号部分
+::v-deep.hide .el-upload--picture-card {
+    display: none;
+}
+// 去掉动画效果
+::v-deep .el-list-enter-active,
+::v-deep .el-list-leave-active {
+    transition: all 0s;
+}
+
+::v-deep .el-list-enter, .el-list-leave-active {
+  opacity: 0;
+  transform: translateY(0);
+}
+</style>
+

+ 1 - 0
ruoyi-ui/src/settings.js

@@ -7,6 +7,7 @@ module.exports = {
   // http://192.168.9.240:5010
   // http://192.168.1.66:8056
   // urls:`http://192.168.9.240:5010`,
+   modelurl:"http://10.90.90.90:1234",
   /**
    * 侧边栏主题 深色主题theme-dark,浅色主题theme-light
    */

+ 154 - 0
ruoyi-ui/src/utils/requesta.js

@@ -0,0 +1,154 @@
+import axios from 'axios'
+import { Notification, MessageBox, Message, Loading } from 'element-ui'
+import store from '@/store'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import { tansParams, blobValidate } from "@/utils/ruoyi"
+import cache from '@/plugins/cache'
+import { saveAs } from 'file-saver'
+
+let downloadLoadingInstance
+// 是否显示重新登录
+export let isRelogin = { show: false }
+
+axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
+// application/json;charset=utf-8
+// application/x-www-form-urlencoded
+// 创建axios实例
+const service = axios.create({
+  // axios中请求配置有baseURL选项,表示请求URL公共部分
+  // baseURL: process.env.VUE_APP_BASE_API,
+  // 超时
+  timeout: 10000
+})
+
+// request拦截器
+service.interceptors.request.use(config => {
+  // 是否需要设置 token
+  const isToken = (config.headers || {}).isToken === false
+  // 是否需要防止数据重复提交
+  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
+  if (getToken() && !isToken) {
+    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+  }
+  // get请求映射params参数
+  if (config.method === 'get' && config.params) {
+    let url = config.url + '?' + tansParams(config.params)
+    url = url.slice(0, -1)
+    config.params = {}
+    config.url = url
+  }
+  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
+    const requestObj = {
+      url: config.url,
+      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
+      time: new Date().getTime()
+    }
+    const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小
+    const limitSize = 5 * 1024 * 1024 // 限制存放数据5M
+    if (requestSize >= limitSize) {
+      console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
+      return config
+    }
+    const sessionObj = cache.session.getJSON('sessionObj')
+    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
+      cache.session.setJSON('sessionObj', requestObj)
+    } else {
+      const s_url = sessionObj.url                  // 请求地址
+      const s_data = sessionObj.data                // 请求数据
+      const s_time = sessionObj.time                // 请求时间
+      const interval = 1000                         // 间隔时间(ms),小于此时间视为重复提交
+      if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
+        const message = '数据正在处理,请勿重复提交'
+        console.warn(`[${s_url}]: ` + message)
+        return Promise.reject(new Error(message))
+      } else {
+        cache.session.setJSON('sessionObj', requestObj)
+      }
+    }
+  }
+  return config
+}, error => {
+    console.log(error)
+    Promise.reject(error)
+})
+
+// 响应拦截器
+service.interceptors.response.use(res => {
+    // 未设置状态码则默认成功状态
+    const code = res.data.code || 200
+    // 获取错误信息
+    const msg = errorCode[code] || res.data.msg || errorCode['default']
+    // 二进制数据则直接返回
+    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {
+      return res.data
+    }
+    if (code === 401) {
+      if (!isRelogin.show) {
+        isRelogin.show = true
+        MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
+          isRelogin.show = false
+          store.dispatch('LogOut').then(() => {
+            location.href = '/index'
+          })
+      }).catch(() => {
+        isRelogin.show = false
+      })
+    }
+      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
+    } else if (code === 500) {
+      Message({ message: msg, type: 'error' })
+      return Promise.reject(new Error(msg))
+    } else if (code === 601) {
+      Message({ message: msg, type: 'warning' })
+      return Promise.reject('error')
+    } else if (code !== 200) {
+      Notification.error({ title: msg })
+      return Promise.reject('error')
+    } else {
+      return res.data
+    }
+  },
+  error => {
+    console.log('err' + error)
+    let { message } = error
+    if (message == "Network Error") {
+      message = "后端接口连接异常"
+    } else if (message.includes("timeout")) {
+      message = "系统接口请求超时"
+    } else if (message.includes("Request failed with status code")) {
+      message = "系统接口" + message.substr(message.length - 3) + "异常"
+    }
+    Message({ message: message, type: 'error', duration: 5 * 1000 })
+    return Promise.reject(error)
+  }
+)
+
+// 通用下载方法
+export function download(url, params, filename, config) {
+  downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
+  return service.post(url, params, {
+    transformRequest: [(params) => { return tansParams(params) }],
+    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+    responseType: 'blob',
+    ...config
+  }).then(async (data) => {
+    const isBlob = blobValidate(data)
+    if (isBlob) {
+      const blob = new Blob([data])
+      saveAs(blob, filename)
+    } else {
+      const resText = await data.text()
+      const rspObj = JSON.parse(resText)
+      const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
+      Message.error(errMsg)
+    }
+    downloadLoadingInstance.close()
+  }).catch((r) => {
+    console.error(r)
+    Message.error('下载文件出现错误,请联系管理员!')
+    downloadLoadingInstance.close()
+  })
+}
+
+export default service

+ 405 - 127
ruoyi-ui/src/views/shipinggaoj/sousu/index.vue

@@ -1,82 +1,106 @@
 <template>
-  <div class="app-container" style="padding-top: 20px;height: (120vh - 100px);"  >
+  <div class="app-container" style="height: calc(100vh - 50px);"  >
     <div class="ntgs nhgytw">
       <img src="../../../assets/images/pic_jsbt_db.png" alt="" style="margin-left: 50%;transform: translateX(-50%);margin-top: 50px;">
-      <div style="width: 70%; margin-left: 50%;transform: translateX(-50%);margin-top: 50px;">
-        <div style="display: flex; justify-content: space-between;">
-          <div style="display: flex; ">
-            <div  class="soush" style="" @click="istuw(0)">
-              <img src="../../../assets/images/pic_jsl_lxxz_selected.png" alt="" v-if="iszhao == 0">
-              <img src="../../../assets/images/pic_jsl_lxxz_normal.png" alt="" v-else>
-              <p style="margin: 0;">找人</p>
+      <div style="height: 48px;"></div>
+      <div class="acitwbox">
+        <!-- 显示结果 -->
+        <div class="resbox">
+          <div class="resboxa">
+            <!-- v-if="is_loading" -->
+             <div class="ambox" v-if="is_loading">
+               <div class="item "></div>
+               <div class="item "></div>
+               <div class="item "></div>
+               <div class="item "></div>
+             </div>
+            <img  src="../../../assets/images/_icon_aijs_jg_yy.png" v-else/>
+            <div class="restit" id="result">请下方文本框输入信息,进行分析</div>
+
+          </div>
+            <!-- 历史数据 -->
+          <div class="hisbox">
+            <div class="hisboxa flexc">
+              <img src="../../../assets/images/sousu/icon_js_lstw.png"/>
+              <div>历史记录</div>
             </div>
-            <div  class="soush" @click="istuw(1)">
-              <img src="../../../assets/images/pic_jsl_lxxz_selected.png" alt="" v-if="iszhao == 1">
-              <img src="../../../assets/images/pic_jsl_lxxz_normal.png" alt="" v-else>
-              <p style="margin: 0;">找机动车</p>
+            <div class="hisboxb flexc" @click="getRemove">
+              <img src="../../../assets/images/sousu/icon_js_qcls.png"/>
+              <div>清除历史</div>
             </div>
-            <div  class="soush" @click="istuw(2)">
-              <img src="../../../assets/images/pic_jsl_lxxz_selected.png" alt="" v-if="iszhao == 2">
-              <img src="../../../assets/images/pic_jsl_lxxz_normal.png" alt="" v-else>
-              <p style="margin: 0;">找非机动车</p>
+            <div class="hisboxc flexc" @click="getZhanFn">
+              <div>{{zhanFlag?'收起':'展开'}}</div>
+              <img src="../../../assets/images/sousu/icon_js_zk.png" :class="zhanFlag?'act':''"/>
             </div>
           </div>
-          <div style="display: flex;">
-            <div style="display: flex; align-items: center;margin-right: 10px;" @click="ditun(0)" >
-              <img src="../../../assets/images/icon_jsl_fwxz_selected.png" alt="" style="margin-right: 5px;" v-if="ishoewdit == 0" >
-              <img src="../../../assets/images/icon_jsl_fwxz_normal.png" alt="" style="margin-right: 5px;" v-else>
-
-              <p class="dit">地图内查找</p>
-            </div>
-            <div style="display: flex; align-items: center;" @click="ditun(1)">
-              <img src="../../../assets/images/icon_jsl_fwxz_selected.png" alt="" style="margin-right: 5px;" v-if="ishoewdit == 1">
-             <img src="../../../assets/images/icon_jsl_fwxz_normal.png" alt="" style="margin-right: 5px;" v-else >
+          <!-- 展开的历史记录 -->
+          <div v-if="zhanFlag" class="histit">
+            <template v-for="(ite,idx) in resuleList" >
+              <div v-if="idx!=0||idx==0&&firstfalg" class="histlist">
+                <div class="tit">问题:{{ite.tit}}</div>
+                <div class="result">{{ite.result}}</div>
+              </div>
+            </template>
 
-              <p class="dit">园区内查找</p>
-            </div>
           </div>
+
         </div>
-      </div>
-      <div :class="isshe == true? 'acitw acitwis' : 'acitw'" >
-        <div class="shutw ">
-           <div style="position: relative;">
-             <el-input style="width: 92%;" v-model="form.staffPhone" placeholder="请输入外貌特征(如:身着服装款式、颜色等)" />
-             <div style="position: absolute;right: -5px;top: -8px;">
-              <img src="../../../assets/images/icon_jsl_jss.png" alt="" style="margin-right: 5px;">
+
+        <div :class="isshe == true? 'acitw acitwis' : 'acitw'" >
+          <div class="shutw " >
+             <div style="position: relative;">
+               <el-input style="width: 92%;" v-model="inputtext" placeholder="请输入外貌特征(如:身着服装款式、颜色等)" />
+               <div style="position: absolute;right: -5px;top: -8px;" @click="callModel">
+                <img src="../../../assets/images/icon_jsl_jss.png" alt="" style="margin-right: 5px;">
+               </div>
              </div>
-           </div>
-           <div style="margin-top: 20px;width: 100%; display: flex;justify-content: space-between;">
-             <div class="inmghe" style="cursor: pointer; display: flex;align-items: center;position: relative;width: 100%;">
-               <img src="../../../assets/images/icon_jsl_sj.png" alt="">
-               <el-select style="width: 90px;" v-model="value" placeholder="">
-                   <el-option
-                     v-for="item in options"
-                     :key="item.value"
-                     :label="item.label"
-                     :value="item.value">
-                   </el-option>
-                 </el-select>
-                 <img src="../../../assets/images/icon_jsl_xl.png" alt="" style="position: absolute; left: 90px;">
-                 <el-time-picker
-                 style="width: 200px;"
-                     is-range
-                     v-model="value1"
-                     range-separator="至"
-                     start-placeholder="开始时间"
-                     end-placeholder="结束时间"
-                     placeholder="选择时间范围">
-                   </el-time-picker>
-                   <img src="../../../assets/images/icon_jsl_xl.png" alt="" style="position: absolute; left: 290px;">
-                    <img src="../../../assets/images/icon_jsl_fg.png" alt=""  style="margin: 10px;">
-                    <img src="../../../assets/images/icon_jsl_tp.png" alt=""  style="margin: 10px;">
-                    <img src="../../../assets/images/icon_jsl_xsd.png" alt=""  style="margin: 10px;">
-                    <img src="../../../assets/images/icon_jsl_qc.png" alt=""  style="margin: 10px;">
-                    <img src="../../../assets/images/icon_jsl_cx.png" alt="" >
+             <div style="margin-top: 20px;width: 100%; display: flex;justify-content: space-between;">
+               <div class="inmghe" style="cursor: pointer; display: flex;align-items: center;position: relative;width: 100%;">
+                 <!-- <el-upload
+                   class="upload-demo"
+                   action="https://jsonplaceholder.typicode.com/posts/"
+                   :on-preview="handlePreview"
+                   :on-remove="handleRemove"
+                   :before-remove="beforeRemove"
+                   :auto-upload="autoUpload"
+                   multiple
+                   :limit="3"
+                   :on-exceed="handleExceed"
+                   :on-change="getChange"
+                   :file-list="fileLista">
+                   <img src="../../../assets/images/icon_jsl_tp.png" alt="">
+                 </el-upload> -->
+                   <!-- <img src="../../../assets/images/icon_jsl_xl.png" alt="" style="position: absolute; left: 90px;">
+                     <img src="../../../assets/images/icon_jsl_xl.png" alt="" style="position: absolute; left: 290px;"> -->
+                     <div class="fileupload">
+                       <img src="../../../assets/images/icon_jsl_tp.png" alt="">
+                       <input type="file" ref="fileInputa" accept="image/*" tabindex="-1"  class="file-input" @change="handleFileChange">
+                     </div>
+                     <div class="fileupload">
+                       <img src="../../../assets/images/icon_jsl_sp.png" alt="">
+                       <input type="file" ref="fileInput" accept="video/*" class="file-input" @change="handleFileChange">
+                     </div>
+                    <!-- <input type="file" ref="fileInput" accept="video/*" class="file-input" @change="handleFileChange"> -->
+                      <!-- <img src="../../../assets/images/icon_jsl_sp.png" alt=""  style="margin: 10px;"> -->
+                      <img src="../../../assets/images/icon_jsl_wj.png" alt=""  >
+                      <!-- <img src="../../../assets/images/icon_jsl_xsd.png" alt=""  style="margin: 10px;">
+                      <img src="../../../assets/images/icon_jsl_qc.png" alt=""  style="margin: 10px;">
+                      <img src="../../../assets/images/icon_jsl_cx.png" alt="" > -->
+               </div>
+               <!-- <div style="display: flex;align-items: center; cursor: pointer;">
+                  <img src="../../../assets/images/icon_jsl_zk.png" alt=""  st @click="nglqw" >
+                </div> -->
              </div>
-              <div style="display: flex;align-items: center; cursor: pointer;">
-                <img src="../../../assets/images/icon_jsl_zk.png" alt=""  st @click="nglqw" >
-              </div>
-           </div>
+
+
+          </div>
+          <div class="files"  v-if="selectedFile">
+            <div class="file" >
+              <img class="imga"  src="../../../assets/images/icon_aijs_wjmc.png"/>
+              <div class="tit">{{selectedFile.name}}</div>
+              <img class="imgb" src="../../../assets/images/icon_aijs_close.png" @click="getDel"/>
+            </div>
+          </div>
         </div>
 
       </div>
@@ -97,51 +121,6 @@
                 <image-uploadrss :limit="10" v-model="form.staffImage"/>
             </div>
         </div>
-        <div style="width: 33.33%; display: flex;position: relative;">
-          <div>
-                    <img src="../../../assets/images/icon_jsdb_gn1.png" alt=""  style="margin-top: 20px;margin-right: 20px;">
-                  </div>
-                  <div>
-                    <p style="font-weight: bold;
-          font-size: 13px;
-          color: #3D455B;">时空回溯 动态还原</p>
-                    <p style="font-weight: 500;
-          font-size: 13px; margin: 0;
-          color: #9DA2B0;">跨摄像头智能串联<br>
-          一键完整还原动态移动轨迹</p>
-                  <img src="../../../assets/images/icon_jsdb_fg.png" alt="" style="position: absolute;right: 0;top:50%;transform: translateY(-50%);">
-                  </div>
-        </div>
-        <div style="width: 33.33%; display: flex;position: relative; justify-content: center;">
-          <div>
-                    <img src="../../../assets/images/icon_jsdb_gn2.png" alt=""  style="margin-top: 20px;margin-right: 20px;">
-                  </div>
-                  <div>
-                    <p style="font-weight: bold;
-          font-size: 13px;
-          color: #3D455B;">以图搜寻 特征解析</p>
-                    <p style="font-weight: 500;
-          font-size: 13px; margin: 0;
-          color: #9DA2B0;">无需文字描述,用图片来说话<br>
-上传图片,自动提取特征
-</p>
-                  <img src="../../../assets/images/icon_jsdb_fg.png" alt="" style="position: absolute;right: 0;top:50%;transform: translateY(-50%);">
-                  </div>
-        </div>
-        <div style="width: 33.33%; display: flex;position: relative;justify-content: center;">
-          <div>
-                    <img src="../../../assets/images/icon_jsdb_gn1.png" alt=""  style="margin-top: 20px;margin-right: 20px;">
-                  </div>
-                  <div>
-                    <p style="font-weight: bold;
-          font-size: 13px;
-          color: #3D455B;">模糊搜索 概率可调</p>
-                    <p style="font-weight: 500;
-          font-size: 13px; margin: 0;
-          color: #9DA2B0;">从“大概特征”到“精确匹配”<br> 自由滑动阈值,智能筛选结果</p>
-                  <!-- <img src="../../../assets/images/icon_jsdb_fg.png" alt="" style="position: absolute;right: 0;top:50%;transform: translateY(-50%);"> -->
-                  </div>
-        </div>
       </div>
 	</div>
 
@@ -151,6 +130,7 @@
 <script>
   import CProgress from '../../../components/Cprogress'
   import { listWarnManage, getWarnManage, delWarnManage, addWarnManage, updateWarnManage } from "@/api/manage/warnManage"
+  import {getModelsList,getModelsChat} from "@/api/manage/sousu"
 // import { listPost, getPost, delPost, addPost, updatePost,updateFs,updateGx } from "@/api/kaoch/renyuan";
 // import { listReservat,camera,cameraIndexCode, listReservatd, getReservat, delReservat, addReservat, updateReservat,setPass,delReservathx,openDz,closeDz } from "@/api/tonggi/houtai";
 // import vueQr from "vue-qr";
@@ -160,6 +140,9 @@
 // import 'videojs-flash'
 // import flvjs from 'flv.js/dist/flv.min.js'
 // import flvjs from "flv.js";
+const config = require('../../../settings.js')
+const modelurl=config.modelurl // 模型接口
+import Cookies from "js-cookie"
 import Bscroll from "better-scroll";
 export default {
   name: "Post",
@@ -184,32 +167,225 @@ export default {
     },
     buzhout:0,
     iszhao:0,
-    ishoewdit:0
-
+    ishoewdit:0,
+    modelList:[],
+    selectedModel:'qwen/qwen2.5-vl-7b:2',
+    inputtext:'',
+    is_loading:false,
+    autoUpload:false,
+    fileLista:[],
+    selectedFile:'',
+    resuleList:[],
+    firstfalg:true,
+    zhanFlag:false
     };
   },
   created() {
-
+  if(Cookies.get("modelList")){
+    this.resuleList = JSON.parse(Cookies.get("modelList"))||[];
+    if(this.resuleList&&this.resuleList.length){
+      this.firstfalg=true;
+    }
+  }
     // this.getList();
-
   },
   mounted() {
-
+    // this.getModelsList()
 
   },
    beforeDestroy() {
 
     },
   methods: {
-    // 地图内
-    ditun(row){
-      this.ishoewdit = row
+     handleRemove(file, fileList) {
+            console.log(file, fileList);
+          },
+          handlePreview(file) {
+            console.log(file);
+          },
+          handleExceed(files, fileList) {
+            this.$message.warning(`当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
+          },
+          beforeRemove(file, fileList) {
+            return this.$confirm(`确定移除 ${ file.name }?`);
+          },
+    getModelsList(){
+      getModelsList().then(res=>{
+        this.modelList=res.data;
+        if(res.data&&res.data.length){
+          this.selectedModel=res.data[0].id
+        }
+      })
+    },
+    getZhanFn(){
+      this.zhanFlag=!this.zhanFlag
+    },
+    getRemove(){
+      Cookies.remove('modelList')
+      this.resuleList=[];
+    },
+    getChange(e){
+      console.log(e)
+    },
+    handleFileChange(e){
+      console.log(e)
+      if (e.target.files.length) {
+          const file = e.target.files[0];
+          // if (!file.type.startsWith('video/')) {
+          //     this.errorMessage = '请上传有效的视频文件';
+          //     return;
+          // }
+          this.selectedFile = file;
+          console.log(typeof file)
+      	console.log( this.selectedFile)
+          this.errorMessage = '';
+      }
     },
-    // 找人
-    istuw(row){
-      this.iszhao = row
+    getDel(){
+      this.selectedFile=''
     },
+    getSubmitFn(){
+      var prompt=this.inputtext
+      var messages=[{ "role": "user", "content": prompt }];
+      var params={
+        model:this.selectedModel,
+        messages: JSON.stringify(messages),
+        stream: true // 启用流式输出
+      }
+      getModelsChat(params).then(res=>{
+        console.log(res)
+      })
+    },
+    tobase64(){
+      var selectedFile=this.selectedFile
+      var imageBase64=''
+      if (selectedFile) {
+          // 将图片转为Base64编码
+          const reader = new FileReader();
+          reader.onload = function(e) {
+              imageBase64 = e.target.result; // 结果为Base64字符串(如"data:image/png;base64,xxxx...")
+              console.log(e,imageBase64)
+          };
+         reader.readAsDataURL(selectedFile);
+      }
+    },
+    /**
+     * 处理流式响应的核心函数
+     */
+    async callModel() {
+       this.$modal.msgError("模块建设中~");
+       return
+      if(!this.inputtext){
+        this.$modal.msgError("请先输入文本");
+        return
+      }
+        var inferenceApiUrl=modelurl+'/v1/chat/completions'
+        var selectedModel=this.selectedModel;
+         const resultElement = document.getElementById('result');
+        const prompt = this.inputtext;
+        this.is_loading=true;
+
+        //  var resultList=this.resuleList;
+        //  var tits="你好!我是天柱山旅游文化数字代言人,具备一系列核心能力,可以全面、细致地为你提供关于天柱山的相关信息和服务。从地质特征到景观布局、再到人文历史,我都能帮助你深入了解这片自然与文化的交融之地 ";
+        // var obj={
+        //   tit:prompt,
+        //   result:tits
+        // }
+        // var len=resultList.length;
+        // if(len>0){
+        //   resultList.unshift(obj);
+        // }else{
+        //   resultList.push(obj);
+        // }
+        // this.resultList=JSON.parse(JSON.stringify(resultList))
+        // console.log()
+        // if(resultList&&resultList.length){
+        //   console.log(22)
+        //    Cookies.set("modelList", JSON.stringify(resultList), { expires: 30 })
+        //    console.log(22)
+        // }
+        // this.firstfalg=false;
+        // this.is_loading=false;
+        // return
+        // var content=[
+        //   {tyep:'text',"text":this.inputtext},
+        //   {tyep:'imgage_url',"imgage_url":this.inputtext},
+
+        //   ]
+        try {
+            const response = await fetch(inferenceApiUrl, {
+                method: 'POST',
+                headers: { 'Content-Type': 'application/json' },
+                body: JSON.stringify({
+                    model: selectedModel,
+                    messages: [{ "role": "user", "content": prompt }],
+                    stream: true // 启用流式输出
+                })
+            });
+
+            if (!response.ok) throw new Error(`推理失败:${response.status}`);
 
+            // 关键:处理流式响应(ReadableStream)
+            const reader = response.body.getReader();
+            const decoder = new TextDecoder(); // 解码二进制流为文本
+            let result = ''; // 累加最终结果
+            var resultList=this.resuleList;
+            // 循环读取流数据
+            while (true) {
+                const { done, value } = await reader.read();
+                if (done) break; // 流结束
+
+                // 解码当前块数据
+                const chunk = decoder.decode(value, { stream: true });
+                // 按行分割(SSE 格式每行以 data: 开头)
+                const lines = chunk.split('\n');
+
+                for (const line of lines) {
+                    const trimmedLine = line.trim();
+                    if (trimmedLine === '') continue; // 跳过空行
+                    if (trimmedLine === 'data: [DONE]') break; // 结束标记
+
+                    // 去除 "data: " 前缀,获取纯 JSON 字符串
+                    if (trimmedLine.startsWith('data: ')) {
+                        const jsonStr = trimmedLine.slice(6); // 从第6个字符开始(跳过 "data: ")
+                        try {
+                            const data = JSON.parse(jsonStr);
+                            // 提取当前片段的内容(LM Studio 格式与 OpenAI 一致)
+                            const content = data.choices[0]?.delta?.content;
+                            if (content) {
+                                result += content; // 累加内容
+                                resultElement.textContent = result; // 实时更新页面
+                            }
+
+                        } catch (e) {
+                            console.error('解析单块数据失败:', e, '原始数据:', jsonStr);
+                        }
+                    }
+                }
+            }
+
+            resultElement.textContent = result; // 最终结果
+            var obj={
+              tit:prompt,
+              result:result
+            }
+            var len=resultList.length;
+            if(len>0){
+              resultList.unshift(obj);
+            }else{
+              resultList.push(obj);
+            }
+            this.resultList=JSON.parse(JSON.stringify(resultList))
+            if(resultList&&resultList.length){
+               Cookies.set("modelList", JSON.stringify(resultList), { expires: 30 })
+            }
+            this.firstfalg=false;
+            this.is_loading=false;
+        } catch (error) {
+            resultElement.innerHTML = `<span class="error">调用失败:${error.message}</span>`;
+            console.error('错误:', error);
+        }
+    },
     /** 提交按钮 */
     submitForm: function() {
       this.$refs["form"].validate(valid => {
@@ -272,6 +448,99 @@ export default {
 </script>
 
 <style lang="scss">
+.resbox{background: rgba(255, 255, 255, 0.6);margin-bottom: 20px;
+border-radius: 10px;
+  .resboxa{
+    display: flex;align-items: flex-start;padding: 20px 14px;
+    img{width: 20px;height: 20px;margin-right: 12px;flex: 0 0 auto;}
+    .restit{font-weight: 500;font-size: 14px;color: #3D455B;}
+  }
+}
+.hisbox{border-top: 1px solid #E6E6E6; display: flex;align-items: center;padding: 0 19px 0 13px;min-height: 48px;
+.flexc{display: flex;align-items: center;}
+  .hisboxa{font-weight: 800;font-size: 14px;color: #3D455B;flex: 1;cursor: pointer;
+    img{width: 22px;height: 20px;margin-right: 7px;}
+  }
+  .hisboxb{flex: 0 0 auto;font-weight: 500;font-size: 14px;color: #6BD2B0;cursor: pointer;
+    img{width: 17px;height: 16px;margin-right: 4px;}
+  }
+  .hisboxc{flex: 0 0 auto;font-weight: 500;font-size: 14px;color: #666666;margin-left: 28px;cursor: pointer;
+    img{width: 12px;height: 12px;margin-left: 8px;transition: all 0.3s;
+      &.act{transform: rotate(180deg);}
+    }
+  }
+}
+// max-height: 400px;overflow: auto;
+.histit{
+    padding:0 13px;
+    .histlist{font-weight: 500;font-size: 14px;color: #3D455B;line-height: 22px;border-bottom: 1px solid #E6E6E6;padding: 2px 0;
+      .tit{font-weight: 500;font-size: 12px;color: #666666;}
+      .result{font-weight: 500;font-size: 14px;color: #3D455B;}
+    }
+  }
+.fileupload{
+  width:18px;height: 18px;position: relative;margin-right: 10px;
+  .file-input{opacity: 0;width:18px;height: 18px;position: absolute;left: 0 ;top: 0;}
+}
+
+.files{display: flex;flex-direction: column;border-top: 1px solid #E6E6E6;padding: 4px 0;
+  .file{display: flex;align-items: center;padding: 0 28px;
+    .imga{width: 16px;height: 16px;margin-right: 12px;}
+    .imgb{width: 14px;height: 14px;margin-left: 20px;}
+    .tit{font-weight: 500;font-size: 14px;color: #3D455B;line-height: 28px;}
+  }
+}
+.ambox {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-flow: row nowrap;
+    height: 20px;width: 20px;margin-right:12px ;
+  .item {
+    background: linear-gradient(to bottom, #00b278, #00b278);
+    width: 4px;
+    height: 8px;
+    margin-right: 2px;
+  }
+
+  .item:nth-child(1) {
+    animation: love1 2s infinite;
+  }
+
+  .item:nth-child(2) {
+    animation: love2 2s infinite;
+    animation-delay: 0.1s;
+  }
+
+  .item:nth-child(3) {
+    animation: love2 2s infinite;
+    animation-delay: 0.3s;
+  }
+
+  .item:nth-child(4) {
+    animation: love1 2s infinite;
+    animation-delay: 0.4s;
+  }
+  }
+@keyframes love1 {
+    50% {
+      height: 16px;
+    }
+  	0%,
+    100% {
+      height: 8px;
+    }
+  }
+
+  @keyframes love2 {
+   50% {
+      height: 20px;
+    }
+  	0%,
+    100% {
+      height: 8px;
+    }
+  }
   .ungshin{
     .el-upload-dragger{
       border: none;
@@ -575,7 +844,9 @@ padding: 25px;
 	.app-container{
 		background-color: #f3f4f6;
 		padding-top: 10px;
-		height: 180vh;
+    padding: 0 20px 12px 0 !important;
+    box-sizing: border-box;
+		// height: 180vh;
 
 	}
 	.ntgs{
@@ -591,7 +862,10 @@ padding: 25px;
 	}
   .nhgytw{
     height: 100%;
-    background: url('../../../assets/images/pic_jsbg.png') no-repeat;
+    background: url('../../../assets/images/searchbg.png') no-repeat;
+    background-size: 100% 100%;
+    overflow-y: auto;
+    overflow-x: hidden;
   }
 	.nghfs{
 	position: relative;
@@ -611,10 +885,14 @@ padding: 25px;
 	   font-weight: bold;
 	   font-size: 16px;
    }
+   .acitwbox{
+     width: 70%;margin-left: 50%;transform: translateX(-50%);
+   }
    .acitw{
-     width: 70%; height: 130px;box-shadow: 0px 0px 3px 0px #D1E5E1;
+     width: 100%; min-height: 130px;box-shadow: 0px 0px 20px 0px #D1E5E1;
      border-radius: 10px; margin-top: 4px;
-     border: 1px solid #E6E6E6;background: #FFFFFF; margin-left: 50%;transform: translateX(-50%);
+     // border: 1px solid #E6E6E6;
+     background: #FFFFFF;
      position: relative; border-top-left-radius: 0;
    }
    .acitwis{

+ 10 - 1
ruoyi-ui/vue.config.js

@@ -10,9 +10,9 @@ const CompressionPlugin = require('compression-webpack-plugin')
 const name = process.env.VUE_APP_TITLE || 'AI-Hub智能处理平台' // 网页标题
 
 const baseUrl = defaultSettings.urls // 后端接口
+const modelurl=defaultSettings.modelurl // 模型接口
 
 const port = process.env.port || process.env.npm_config_port || 80 // 端口
-
 // vue.config.js 配置说明
 //官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
 // 这里只列一部分,具体配置参考文档
@@ -36,6 +36,14 @@ module.exports = {
     port: port,
     open: true,
     proxy: {
+      // 大模型数据
+      '/v1': {
+        target: modelurl,
+        changeOrigin: true,
+        pathRewrite: {
+          '^/v1': '/v1'
+        }
+      },
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
         target: baseUrl,
@@ -49,6 +57,7 @@ module.exports = {
         target: baseUrl,
         changeOrigin: true
       }
+
     },
     disableHostCheck: true
   },