src/api/AiRetrievalView.ts
New file @@ -0,0 +1,25 @@ import request from "@/scripts/httpRequest" export default {//æ·»å 对è¯è®°å½ createChat(data) { return request({ url: '/api-v1/v1/chat/addDetail', method: 'post', data }) }, getDetail(params) {//è·å对è¯è¯¦æ return request({ url: '/api-v1/v1/chat/detail', method: 'get', params }) }, getChatDetail(params) {//å¯¹è¯æ£ç´¢ return request({ url: '/api-v1/v1/record/chat', method: 'post', params }) }, } src/api/ChatHistoryView.ts
New file @@ -0,0 +1,20 @@ import request from "@/scripts/httpRequest" export default { getChats(data) { // return service.get('/chat/all', data) return request({ url: '/api-v1/v1/chat/all', method: 'get', data }) }, deleteChat(params) { // return service.delete('/chat/delete', { params }) return request({ url: '/api-v1/v1/chat/delete', method: 'delete', params }) }, } src/api/SurveyView.ts
New file @@ -0,0 +1,101 @@ import request from '@/scripts/httpRequest' export default { // getSurveys(params) { // return service.get('/record/all', { params }) // }, createFile(data) { // return service.post('/task/add', data) return request({ url: '/api-v1/v1/task/add', method: 'post', data }) }, updateFile(data) { // return service.put(`/task/update`, data) return request({ url: '/api-v1/v1/task/update', method: 'put', data }) }, deleteFile(params) { // return service.delete(`/task/delete`, { params }) return request({ url: '/api-v1/v1/task/delete', method: 'delete', params }) }, getTasks(params) { // return service.get('/task/all', { params }) return request({ url: '/api-v1/v1/task/all', method: 'get', params }) }, getCheckContents(params) { // return service.get('/checkContent/all', { params }) return request({ url: '/api-v1/v1/checkContent/all', method: 'get', params }) }, getWarnings(params) { // return service.get('/warningRule/all', { params }) return request({ url: '/api-v1/v1/warningRule/all', method: 'get', params }) }, getCameras(params) { // return service.get('/task/getVideoOption') return request({ url: '/api-v1/v1/task/getVideoOption', method: 'get', params }) }, getTimes(params) { // return service.get('/workTime/all') return request({ url: '/api-v1/v1/workTime/all', method: 'get', params }) }, getEvents(params) { // return service.get('/task/getDictType') return request({ url: '/api-v1/v1/task/getDictType', method: 'get', params }) }, getSurveys(data) { return request({ url: '/api-v1/v1/record/all', method: 'post', data }) }, insertModelTraining (params){ return request({ url: "/api-v1/v1/train/add ", method: "post", data: params }) }, downloadFiles(params){ return request({ url: '/api-v1/v1/knowledge/download', method: 'get', params }) }, } src/assets/img/AI-avatar.png
src/assets/img/UDPÅäÖÃ.png
src/assets/img/article-fill@1x copy.png
src/assets/img/conversation.png
src/assets/img/fold_up.png
src/assets/img/historical copy.png
src/assets/img/historical.png
src/assets/img/live-fill@1x.png
src/assets/img/time-fill@1x.png
src/pages/datapush/index/RightEvent.vue
@@ -15,9 +15,9 @@ <div> <span style="line-height: 38px;margin-right: 20px;">æ¨éæ¹å¼</span> <el-radio :disabled="urls.length > 0" v-model="pushType" label="1">UDP</el-radio> <el-radio :disabled="urls.length > 0" v-model="pushType" label="2">HTTP</el-radio> <el-radio disabled v-model="pushType" label="3">MQTT</el-radio> <el-radio :disabled="urls.length > 0" v-model="taskEditData.pushType" label="1">UDP</el-radio> <el-radio :disabled="urls.length > 0" v-model="taskEditData.pushType" label="2">HTTP</el-radio> <el-radio disabled v-model="taskEditData.pushType" label="3">MQTT</el-radio> </div> <span style="line-height: 38px">æ¨éæå¡å¨</span> <div class="icon-btn" v-if="urls.length < 1" @click="addUrl()"> @@ -28,13 +28,14 @@ <div> <el-checkbox v-model="item.enable"></el-checkbox> <span class="ml20">{{ "URL " }}</span> <el-input v-if="pushType === '1'" v-model="item.ip" style="width: 180px; margin-left: 0px;margin-right: 30px" size="small" <el-input v-if="taskEditData.pushType === '1'" v-model="item.server_ip" style="width: 180px; margin-left: 0px;margin-right: 30px" size="small" placeholder="192.168.1.100"></el-input> ç«¯å£ <el-input v-if="pushType === '1'" v-model="item.sort" style="width: 70px; margin-left: 10px" size="small" placeholder="8030"></el-input> <el-input v-if="pushType === '2'" v-model="item.url" style="width: 360px; margin-left: 0px" size="small" placeholder="http://10.10.10.10:8000/dataApi"></el-input> <!-- <el-input v-if="pushType === '3'" v-model="item.url" style="width: 360px; margin-left: 0px" size="small" ç«¯å£ <el-input v-if="taskEditData.pushType === '1'" v-model="item.port" style="width: 70px; margin-left: 10px" size="small" placeholder="8030"></el-input> <el-input v-if="taskEditData.pushType === '2'" v-model="item.url" style="width: 360px; margin-left: 0px" size="small" placeholder="http://10.10.10.10:8000/dataApi"></el-input> <!-- <el-input v-if="taskEditData.pushType === '3'" v-model="item.url" style="width: 360px; margin-left: 0px" size="small" placeholder="MQTT"></el-input> --> </div> <div class="server-add"> @@ -110,10 +111,15 @@ <el-input v-model="rule.rule_value" placeholder="请è¾å ¥å 容" size="small"></el-input> </div> <div v-else> <el-select v-model="rule.rule_values" multiple collapse-tags placeholder="è¯·éæ©" size="small" @change="selectValue(rule, $event)"> <el-select v-if="!isWarningSelect" v-model="rule.rule_values" multiple collapse-tags placeholder="è¯·éæ©" size="small" @change="selectValue(rule, $event)"> <el-option v-for="item in rule.ruleValueOptions" :key="item.id" :label="item.name" :disabled="item.disabled" :value="item.value"></el-option> </el-select> <el-select v-else v-model="rule.rule_values" collapse-tags placeholder="è¯·éæ©" size="small" @change="selectValue(rule, $event)"> <el-option v-for="item in rule.ruleValueOptions" :key="item.id" :label="item.name" :value="item.value"></el-option> </el-select> </div> </el-col> @@ -135,7 +141,8 @@ </div> <div class="config-item"> <b>æ¨éåæ®µ</b> <el-button v-if="pushType === '1'" type="primary" size="mini" @click="openPushImsDialog">æ¥ç</el-button> <el-button v-if="taskEditData.pushType === '1'" type="primary" size="mini" @click="openPushImsDialog">æ¥ç</el-button> <el-button v-else type="primary" size="mini" @click="openPushSetDialog">设置</el-button> </div> <div class="save-btn"> @@ -145,7 +152,8 @@ </div> <el-dialog :visible="pushImageDialog" :append-to-body="false" :close-on-click-modal="false" class="dialog-push-field" @close="pushImageDialog = false"> <el-image fit="fill" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"></el-image> <!-- <el-image fit="fill" src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"></el-image> --> <img src="@/assets/img/UDPé ç½®.png" style="width: 100%;"> </el-dialog> <el-dialog :visible="pushFieldDialog" :append-to-body="false" :close-on-click-modal="false" class="dialog-push-field" @close="pushFieldDialog = false"> @@ -203,7 +211,7 @@ }, data() { return { pushType: '1', isWarningSelect: true, taskEditData: {}, dataList: [], dictionary: [], @@ -226,7 +234,7 @@ ruleValueOptions: [], }, pushFieldDialog: false, pushImageDialog:false, pushImageDialog: false, tempPushSet: [], pushFields: [], allFieldChecked: false, @@ -252,7 +260,7 @@ this.taskEditData.lineWay = newVal.link_type; this.taskEditData.eventTxt = newVal.rule_text; this.taskEditData.radioValue = newVal.is_satisfy_all ? "1" : "2"; this.pushType = '2' this.taskEditData.pushType = newVal.push_type + "" if (!this.taskEditData.urls) { this.$set(this.taskEditData, "urls", []); @@ -426,6 +434,8 @@ checked: true, hash: Math.random().toString(36).substr(2), url: "", server_ip: "", port: "" }); }, delUrl(index) { @@ -434,6 +444,7 @@ }, // ä¿å async eventPushsSave() { console.log(this.taskEditData.urls) if (this.taskEditData.name.length < 1) { this.$notify({ type: "warning", @@ -450,7 +461,7 @@ return; } for (let i = 0; i < this.taskEditData.urls.length; i++) { if (this.taskEditData.urls[i].url.length < 1) { if (this.taskEditData.urls[i].url.length < 1 && this.taskEditData.urls[i].server_ip.length < 1) { this.$notify({ type: "warning", message: "æ¥å£URLå°åä¸å 许为空", @@ -511,11 +522,16 @@ rules: ruleList, time_start: this.taskEditData.time[0], time_end: this.taskEditData.time[1], urls: this.taskEditData.urls, urls: this.taskEditData.urls.map(item => { return { ...item, port: item.port ? Number(item.port) : 0 } }), is_satisfy_all: this.taskEditData.radioValue === "1", link_type: this.taskEditData.lineWay, push_set: this.taskEditData.push_set, pushType:this.pushType push_type: Number(this.taskEditData.pushType) }; let res = await eventPushsSave(json); @@ -536,6 +552,12 @@ this.baseRule.topicTypeOptions = this.dictionary.EVENTRULETOPIC; this.baseRule.operatorTypeOpionts = this.dictionary.EVENTTYPECOMPUTE; this.dictionary["alarmLevel"] = this.dictionary.ALARMLEVEL.map((el) => { return { name: el.name, value: el.name, }; }); this.dictionary["warning"] = this.dictionary.WARNING.map((el) => { return { name: el.name, value: el.name, @@ -578,6 +600,7 @@ // æ°å»ºé ç½® createSet() { this.dataList.push(JSON.parse(JSON.stringify(this.baseRule))); console.log(this.dataList, "dataList") }, cleanSet() { this.dataList.splice(0, this.dataList.length); @@ -602,6 +625,11 @@ } } }); if (rule.topic_type && rule.topic_type === 'warning') { this.isWarningSelect = true } else { this.isWarningSelect = false } }, selectArg(rule, resetNext = false) { let argInfo = rule.topicArgOptions.filter((arg) => { @@ -668,8 +696,12 @@ return; } } if (this.isWarningSelect) { rule.rule_value = val } else { rule.rule_value = val.join(","); } rule.rule_value = val.join(","); }, setOptionsDisable(rule) { console.log(rule); @@ -729,9 +761,9 @@ radioValue: "1", eventTxt: "", push_set: this.pushFields, pushType: '1' }; this.dataList = []; this.pushType ='1' }, onCancle() { this.$emit("onCancle"); src/pages/modelTuning/components/rightCardList.vue
@@ -47,7 +47,7 @@ <!-- éæ©æ¶æ®µ --> <el-form-item label="éæ©æ¶æ®µ"> <el-date-picker style="width: 256px;" v-model="filter.timeRange" type="daterange" value-format="yyyy-MM-dd hh:mm:ss" range-separator="è³" start-placeholder="å¼å§æ¥æ" value-format="yyyy-MM-dd" range-separator="è³" start-placeholder="å¼å§æ¥æ" end-placeholder="ç»ææ¥æ" /> </el-form-item> @@ -584,6 +584,7 @@ } .image-gallery { min-width: 1265px; background-color: #ffffff; padding: 20px; border-radius: 4px; src/pages/searchNew/components/AiRetrievalView.vue
New file @@ -0,0 +1,938 @@ <template> <div class="chat-container"> <div class="messages"> <div v-for="(msg, index) in messages" :key="index" :class="['message-bubble', msg.role, { 'with-think': msg.think }]"> <!-- æèå 容åºå --> <div v-if="msg.think" class="think-section"> <div class="think-header" @click="toggleThink(index)"> <span>æèè¿ç¨</span> <i :class="[ 'arrow', msg.isThinkExpanded ? 'el-icon-arrow-down' : 'el-icon-arrow-right', ]"></i> </div> <transition name="slide"> <div v-show="msg.isThinkExpanded" class="think-content"> {{ msg.think }} </div> </transition> </div> <!-- æ£å¸¸æ¶æ¯å 容 --> <div class="content"> <template v-if="msg.role === 'assistant'"> <span v-if="isGenerating && index === messages.length - 1"> <span v-if="!msg.think" class="generating-indicator">æ£å¨æè...</span> <!-- <span class="cursor"></span> --> </span> {{ msg.content }} </template> <template v-else> {{ msg.content }} </template> </div> </div> </div> <div class="preview-area" v-if="uploadedImages.length > 0"> <div v-for="(img, index) in uploadedImages" :key="index" class="image-preview"> <img :src="'data:image/png;base64,' + img.base64" class="thumbnail" /> <span class="image-name">{{ img.name }}</span> <span class="image-size">{{ formatSize(img.size) }}</span> <i class="el-icon-close remove-icon" @click="removeImage(index)"></i> </div> </div> <div class="input-area"> <div class="input-area2"> <textarea v-model="inputText" @keydown.enter.prevent="handleEnter" :disabled="isGenerating" placeholder="请è¾å ¥ä½ çé®é¢" class="input-textarea" rows="1" @input="autoResize"></textarea> </div> <div class="action-buttons"> <el-upload action="" :auto-upload="false" :show-file-list="false" :on-change="handleImageUpload" accept="image/*" class="action-img" :disabled="true"> <i class="el-icon-picture upload-icon"></i> </el-upload> <div class="action-img"> <i class="el-icon-microphone microphone-icon"></i> </div> <button @click="startStream" :disabled="isGenerating" class="send-button"> <i class="el-icon-position send-icon"></i> </button> </div> </div> <div class="tip-text">å 容ç±AI大模åçæï¼è¯·ä»ç»çå«</div> </div> </template> <script> import AiRetrieval from "@/api/AiRetrievalView"; export default { props: ["currentChatId"], watch: { currentChatId: { immediate: true, handler(newVal) { if (newVal) this.loadChat(); // çå¬chatIdåå }, }, }, name: "AiRetrieval", data() { return { chatUrl: "", severUrl:"", params: "", chatId: null, messages: [], inputText: "", isGenerating: false, controller: null, uploadedImages: [], }; }, mounted() { this.fetchCameraInfo(); }, methods: { async fetchCameraInfo() { const response = await fetch("/config.json"); if (!response.ok) throw new Error(`请æ±å¤±è´¥: ${response.status}`); const responseData = await response.json(); this.chatUrl = responseData.chatUrl; this.severUrl = responseData.severUrl; // console.info("chatUrl:"+this.chatUrl); }, toggleThink(index) { this.$set( this.messages[index], "isThinkExpanded", !this.messages[index].isThinkExpanded ); }, handleItemSend(params) { this.$emit("list-selected", params); // 触åèªå®ä¹äºä»¶ å·æ°å³ä¾§å 容åºå }, async loadChat() { if (!this.currentChatId) return; try { // console.info("æ°å¯¹è¯"+this.currentChatId); const res = await AiRetrieval.getDetail({ chatId: this.currentChatId, }); // console.info(res.data); this.messages = []; this.chatId = res.data[0].chatId; // console.info(+this.chatId ) for (let i = 0; i < res.data.length; i++) { this.messages.push({ role: "user", content: res.data[i].question, }); this.messages.push({ role: "assistant", content: res.data[i].content, think: res.data[i].think, }); } // this.messages = res.data.data; // å设æ¥å£è¿åæ¶æ¯å表 this.$nextTick(() => { const container = this.$el.querySelector(".messages"); container.scrollTop = container.scrollHeight; }); } catch (error) { console.error("å è½½è天失败:", error); } }, // async saveToDatabase(chatData) { // if (this.currentChatId) { // æ´æ°é»è¾ // await AiRetrieval.updateChat(this.currentChatId, chatData); // } else { // æ°å»ºé»è¾ // const res = await AiRetrieval.createChat(chatData); // this.currentChatId = res.data.chatId; // } // }, reset() { // console.info("æ¸ ç©ºå¯¹è¯è®°å½"); // æ¸ ç©ºå¯¹è¯è®°å½ this.messages = []; // éç½®è¾å ¥æ¡ this.inputMessage = ""; // 妿æå ¶ä»éè¦éç½®çç¶æ this.searchResults = []; this.currentPage = 1; // å¯ä»¥æ ¹æ®éè¦æ·»å å ¶ä»éç½®é»è¾ }, // æ°å¢èªå¨è°æ´é«åº¦æ¹æ³ autoResize(e) { const textarea = e.target; textarea.style.height = "auto"; // éç½®é«åº¦ const maxHeight = 6 * 24; // 6è¡*24pxè¡é« + 8px*2å è¾¹è· textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`; }, handleImageUpload(file) { const reader = new FileReader(); reader.onload = (e) => { const base64Data = e.target.result.split(",")[1]; this.uploadedImages.push({ name: file.name, size: file.size, base64: base64Data, }); }; reader.readAsDataURL(file.raw); }, removeImage(index) { this.uploadedImages.splice(index, 1); }, formatSize(bytes) { if (bytes === 0) return "0 B"; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }, handleEnter(e) { if (!e.shiftKey && !this.isGenerating) { this.startStream(); } }, async startStream() { if (!this.inputText.trim() || this.isGenerating) return; this.messages.push({ role: "user", content: this.inputText }); const userMessage = this.inputText; this.inputText = ""; this.messages.push({ role: "assistant", content: "", think: "", isThinkExpanded: true, }); // this.$nextTick(() => { // const container = this.$el.querySelector(".messages"); // container.scrollTop = container.scrollHeight; // }); this.isGenerating = true; try { this.controller = new AbortController(); let ids = []; let idsStr = ""; //çéæç»ç»æ // const response = await AiRetrieval.getChatDetail( // { // message:userMessage // } // ); const response = await fetch(this.severUrl +"/v1/record/chat", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ message: userMessage }), }); if (!response.ok) throw new Error(`请æ±å¤±è´¥: ${response.status}`); const reader = response.body.getReader(); const decoder = new TextDecoder(); let boo = true; // ä¿®æ¹åçfetchå¤çé»è¾ while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value, { stream: true }); const lines = chunk.split("\n").filter((line) => line.trim()); // ä¿åææ°æ¶æ¯çå¼ç¨ const lastMessage = this.messages[this.messages.length - 1]; for (const line of lines) { try { const json = JSON.parse(line.replace("data:", "").trim()); if (json.data === true) break; // ç´æ¥æ¿æ¢è䏿¯è¿½å if (json.data) { // ä½¿ç¨æ£å表达å¼å¹é æèå 容 const thoughtMatch = json.data.match( /<think>(.*?)(?=\n<\/think>|$)/s ); if (thoughtMatch) { // æ´æ°æ¶æ¯å 容 带think lastMessage.think = thoughtMatch[1].trim(); let con = json.data //第ä¸é¨åï¼ .match(/<\/think>([\s\S]*)/)[1] .trim(); // console.info('con='+con) const rescou = con .match(/第ä¸é¨åï¼(.*?)(?=\n第äºé¨åï¼|$)/s)[1] .trim(); // console.info('rescou='+rescou) if (rescou) { lastMessage.content = rescou; idsStr = con.match(/第äºé¨åï¼([\s\S]*)/)[1].trim(); // console.info("idsStr=" + idsStr); boo = false; } } else { // æ´æ°æ¶æ¯å 容 lastMessage.think = json.data .match(/<think>([\s\S]*)/)[1] .trim(); } } // console.info("boo:" + boo) } catch (e) { console.info("") } } } // console.info("boo:"+boo) if (boo) { this.messages[this.messages.length - 1].content = "å¹é å°çæ°æ®ä¸º0æ¡"; } else { ids = idsStr.split(","); } // this.handleItemSend(lists);//åéæ°æ®å±ç¤ºå°é¡µé¢ this.handleItemSend(ids); // // ä¿åå°æ°æ®åºçé»è¾ // console.info("ä¿åæ§æ°æ®"+this.chatId) if (this.messages.length >= 2) { // console.info(this.messages) const userMsg = this.messages[this.messages.length - 2]; const assistantMsg = this.messages[this.messages.length - 1]; this.saveToDatabase({ question: userMsg.content, content: assistantMsg.content, think: assistantMsg.think, chatId: this.chatId, // images: this.uploadedImages }); } } catch (error) { if (error.name !== "AbortError") { console.error("çæå¤±è´¥:", error); this.messages[this.messages.length - 1].content += "ï¼çæå¤±è´¥ï¼"; } } finally { this.isGenerating = false; this.controller = null; } }, // async startStream() { // if (!this.inputText.trim() || this.isGenerating) return; // this.messages.push({ role: "user", content: this.inputText }); // const userMessage = this.inputText; // this.inputText = ""; // this.messages.push({ // role: "assistant", // content: "", // think: "", // isThinkExpanded: true, // }); // // this.$nextTick(() => { // // const container = this.$el.querySelector(".messages"); // // container.scrollTop = container.scrollHeight; // // }); // this.isGenerating = true; // try { // this.controller = new AbortController(); // //ä¼åç¨æ·é®é¢ // const resstar = // "milvusæ°æ®åºä¸åå¨äºidãæåæºææå¾çãå¾ççææ¬æè¿°ï¼å¾çææ¬æè¿°æ¯éè¿qwen模åæå¾çä¸å å«çå 容形æäºææ¬å 容ãç¨æ·ä¼è¾å ¥ä»milvusæ°æ®åºä¸æ¥æ¾å¾ççéæ±ï¼è¯·å¸®ææç¨æ·çé®é¢æ¹åä¸ä¸ï¼ä»¥ä¾¿å¨milvusæ°æ®åºä¸æ¥æ¾å°æ´å åç¡®çä¸ç¬¦åç¨æ·ææçæ°æ®ãä¸è¦è¾åºæ¹å说æ /nothink"; // const startRes = await fetch(this.chatUrl + "/chat", { // method: "POST", // headers: { // "Content-Type": "application/json", // }, // body: JSON.stringify({ // prompt: resstar, // llm_name: "qwen3:8b",//qwen2.5vl // messages: [ // ...this.messages.slice(0, -1), // { role: "user", content: userMessage }, // ], // stream: false, // gen_conf: { // temperature: 0.1, // max_tokens: 1000 // } // // image:this.uploadedImages[0]?.base64 || '' // }), // }); // // console.info("chatå°åï¼"+"/api-AI" + "/chat") // if (!startRes.ok) throw new Error(`请æ±å¤±è´¥: ${startRes.status}`); // const responseData = await startRes.json(); // let res1 = responseData.data.split('\n\n') // let rescou = res1.length>1?res1[2]:"" // //let rescou = responseData.data.match(/ï¼(.*?)(?=\nï¼|$)/s)[1].trim(); // console.info(rescou); // if (!rescou) { // rescou = userMessage; // } // //æ£ç´¢åéæ°æ®åº // const res = await fetch(this.chatUrl + "/search", { // method: "POST", // headers: { // "Content-Type": "application/json", // }, // body: JSON.stringify({ // collection_name: "smartrag", // query_text: rescou, // search_mode: "hybrid", // limit: 20, // weight_dense: 0.7, // weight_sparse: 0.3, // filter_expr: "", // output_fields: [ // "id", // "image_path", // "create_timestamp", // "task_name", // "event_level_name", // "rtsp_address", // "video_point_id", // "zh_desc_class", // "video_path", // "detect_time", // ], // }), // }); // // console.info(res.json()) // const text = await res.text(); // const data = JSONbig({ storeAsString: true }).parse(text); // console.info(data); // let lists = []; // let ids = []; // let idsStr = ""; // for (let i = 0; i < data.results.length; i++) { // // console.info(data.results[i].entity.id) // this.params += // "ä¿¡æ¯idï¼" + // data.results[i].entity.id + // ",å 容ï¼" + // data.results[i].entity.video_path + // "æå头å¨" + // data.results[i].entity.detect_time + // "æ¶é´ææçå¾ç" + // data.results[i].entity.zh_desc_class + // "\n"; // lists.push(data.results[i].entity); // } // // const mes = // // "ç¨æ·è¯¢é®ã" + // // rescou + // // "ãã\n请å¨ä»¥ä¸ä¿¡æ¯ä¸è¿è¡å¹é ï¼ä¿¡æ¯ä¸º\n" + // // this.params + // // "ã\nåç:ãXXXä¿¡æ¯idã"; // const mes = // "ç¨æ·è¯¢é®ã" + // rescou + // "ãã\n请å¨ä»¥ä¸ä¿¡æ¯ä¸è¿è¡å¹é ï¼ä¿¡æ¯ä¸º\n" + // this.params + // "ã\nä¸è¿è¡æ¨çåè§£è¯ï¼å¿ é¡»è¿å两é¨åç»æï¼ä¸ç»æä¸å¿ 须带æç¬¬ä¸é¨åæç¬¬äºé¨åå个åï¼ç¬¬ä¸é¨å为æ»ç»å¹é å°å¤å°æ¡ï¼ç¬¬äºé¨åçæ ¼å¼å¿ é¡»æ¯ ç¬¬äºé¨åï¼XXX,XXXå ¶ä¸XXXä¸ºæ°æ®åºid"; // this.params = ""; // if (!res.ok) throw new Error(`请æ±å¤±è´¥: ${res.status}`); // let contentMes = mes.replace(/\s+/g, '') // //çéæç»ç»æ // const response = await fetch(this.chatUrl + "/chat", { // method: "POST", // headers: { // "Content-Type": "application/json", // }, // body: JSON.stringify({ // prompt: "", // llm_name: "qwen3:8b",//qwen2.5vl // messages: [ // ...this.messages.slice(0, -1), // { role: "user", content: contentMes }, // ], // stream: true, // gen_conf: { // temperature: 0.7, // max_tokens: 4000, // }, // // image:this.uploadedImages[0]?.base64 || '' // }), // }); // if (!response.ok) throw new Error(`请æ±å¤±è´¥: ${response.status}`); // const reader = response.body.getReader(); // const decoder = new TextDecoder(); // let boo = true; // // ä¿®æ¹åçfetchå¤çé»è¾ // while (true) { // const { done, value } = await reader.read(); // if (done) break; // const chunk = decoder.decode(value, { stream: true }); // const lines = chunk.split("\n").filter((line) => line.trim()); // // ä¿åææ°æ¶æ¯çå¼ç¨ // const lastMessage = this.messages[this.messages.length - 1]; // for (const line of lines) { // try { // const json = JSON.parse(line.replace("data:", "").trim()); // if (json.data === true) break; // // ç´æ¥æ¿æ¢è䏿¯è¿½å // if (json.data) { // // ä½¿ç¨æ£å表达å¼å¹é æèå 容 // const thoughtMatch = json.data.match( // /<think>(.*?)(?=\n<\/think>|$)/s // ); // if (thoughtMatch) { // // æ´æ°æ¶æ¯å 容 带think // lastMessage.think = thoughtMatch[1].trim(); // let con = json.data //第ä¸é¨åï¼ // .match(/<\/think>([\s\S]*)/)[1] // .trim(); // // console.info('con='+con) // const rescou = con // .match(/第ä¸é¨åï¼(.*?)(?=\n第äºé¨åï¼|$)/s)[1] // .trim(); // // console.info('rescou='+rescou) // if (rescou) { // lastMessage.content = rescou; // idsStr = con.match(/第äºé¨åï¼([\s\S]*)/)[1].trim(); // // console.info("idsStr=" + idsStr); // boo = false; // } // } else { // // æ´æ°æ¶æ¯å 容 // lastMessage.think = json.data // .match(/<think>([\s\S]*)/)[1] // .trim(); // } // } // } catch (e) { // // console.error("è§£æé误:", e); // } // } // // this.$nextTick(() => { // // const container = this.$el.querySelector(".messages"); // // container.scrollTop = container.scrollHeight; // // }); // } // if (boo) { // this.messages[this.messages.length - 1].content = "å¹é å°çæ°æ®ä¸º0æ¡"; // lists = []; // } else { // ids = idsStr.split(","); // } // // this.handleItemSend(lists);//åéæ°æ®å±ç¤ºå°é¡µé¢ // this.handleItemSend(ids); // // // ä¿åå°æ°æ®åºçé»è¾ // // console.info("ä¿åæ§æ°æ®"+this.chatId) // if (this.messages.length >= 2) { // // console.info(this.messages) // const userMsg = this.messages[this.messages.length - 2]; // const assistantMsg = this.messages[this.messages.length - 1]; // this.saveToDatabase({ // question: userMsg.content, // content: assistantMsg.content, // think: assistantMsg.think, // chatId: this.chatId, // // images: this.uploadedImages // }); // } // } catch (error) { // if (error.name !== "AbortError") { // console.error("çæå¤±è´¥:", error); // this.messages[this.messages.length - 1].content += "ï¼çæå¤±è´¥ï¼"; // } // } finally { // this.isGenerating = false; // this.controller = null; // } // }, async saveToDatabase(chatData) { // å®é è°ç¨APIæ¥å£ try { const response = await AiRetrieval.createChat(chatData); // console.info(response.data); this.chatId = response.data || null; } catch (error) { console.error("API Error:", error); } }, cancelGeneration() { if (this.controller) this.controller.abort(); }, }, }; </script> <style scoped> /* æ°å¢æèå å®¹æ ·å¼ */ .message-bubble.assistant.with-think { border-radius: 12px; } .think-section { background: #f8f9fa; padding: 12px; border-bottom: 1px solid #e9ecef; text-align: left; } .think-header { font-size: 12px; color: #6c757d; margin-bottom: 8px; display: flex; align-items: center; font-weight: 500; } .think-header::before { content: "ð¡"; margin-right: 6px; } .think-content { font-size: 10px; color: #495057; line-height: 1.6; white-space: pre-wrap; } .generating-indicator { color: #6c757d; font-size: 0.9em; margin-right: 8px; } /* è°æ´åææ ·å¼ */ .message-bubble.assistant .content { background: #f5f6f8; padding: 12px 15px; border-radius: 0 0 12px 12px; text-align: left; } /* æ°å¢å¾çç¸å ³æ ·å¼ */ .preview-area { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px; background: #f5f5f5; border-radius: 4px; margin-bottom: 8px; } .image-preview { position: relative; display: flex; align-items: center; gap: 4px; padding: 4px; background: white; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .thumbnail { width: 30px; height: 30px; object-fit: cover; border-radius: 2px; } .image-name { font-size: 12px; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .image-size { font-size: 10px; color: #666; } .remove-icon { position: absolute; top: -8px; right: -8px; font-size: 12px; background: white; border-radius: 50%; cursor: pointer; } .action-img { width: 20px; height: 20px; border-radius: 13px; padding: 6px; background-color: #f2f8ff; } .upload-icon { font-size: 20px; color: #7fade6; cursor: pointer; } .chat-container { position: relative; width: 100%; max-width: 500px; height: 600px; margin: 0 auto; /* background: #f1f1f1; */ border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; } .close-button { position: absolute; right: 15px; top: 15px; font-size: 20px; color: #999; cursor: pointer; z-index: 1; transition: color 0.2s; } .close-button:hover { color: #666; } .messages { flex: 1; padding: 20px; overflow-y: auto; display: flex; flex-direction: column; gap: 15px; } .message-bubble { max-width: 80%; position: relative; font-size: 14px; line-height: 1.5; } .message-bubble.user { align-self: flex-end; } .message-bubble.assistant { align-self: flex-start; } .content { padding: 12px 15px; border-radius: 5px; word-break: break-word; } .user .content { background: #cadcff; color: #2e2f31; border-radius: 12px 12px 0 12px; } .assistant .content { background: white; color: #2e2f31; border-radius: 12px 12px 12px 0; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .input-area { display: flex; flex-direction: column; /* æ¹ä¸ºåç´å¸å± */ background: #fff; border: 2px solid #1890ff; border-radius: 10px; padding-top: 8px; gap: 8px; /* æ·»å é´è· */ min-height: 80px; max-height: 200px; align-items: center; /* position: relative; */ } .input-area2 { width: 100%; display: flex; background: #fff; min-height: 60px; max-height: 180px; /* border: 2px solid #1890ff; */ } .input-textarea { /* background: #0c0c0c; */ width: 100%; border: none; resize: none; line-height: 24px; /* 精确è¡é« */ font-size: 14px; padding: 8px 15px; min-height: 24px !important; /* 强å¶åè¡é«åº¦ */ max-height: 152px !important; /* 强å¶6è¡é«åº¦ */ overflow-y: auto; box-sizing: content-box; /* 鲿¢paddingå½±åè®¡ç® */ } .input-textarea:focus { outline: none; box-shadow: none; } .wechat-input { flex: 1; min-height: 40px; max-height: 144px; /* 6è¡ x 24pxè¡é« */ padding: 8px 0; line-height: 1.5; border: 0; background: transparent; font-size: 16px; color: #2c3e50; resize: none; overflow-y: auto; } .wechat-input:focus { border-color: #07c160; background: white; box-shadow: 0 4px 12px rgba(7, 193, 96, 0.1); /* æ·»å èç¦é´å½± */ } .wechat-input::placeholder { color: #a8c5e6; /* æµ èè²å ä½ç¬¦ */ } .microphone-icon { font-size: 20px; color: #7fade6; cursor: pointer; } /* æä½æé®åºå */ .action-buttons { width: 100%; display: flex; justify-content: flex-end; /* å³å¯¹é½æé® */ gap: 8px; padding: 0 0 5px 0; background: transparent; /* border: 2px solid #1890ff; */ } /* åéæé®æ¹é */ .send-button { width: 45px; height: 25px; border-radius: 10px; background: #1890ff; border: none; padding: 0; display: flex; align-items: center; justify-content: center; transition: all 0.3s ease; margin: 2px 6px; } .send-icon { color: white; font-size: 16px; /* transform: rotate(45deg); */ } .send-button:disabled { background: #b6d7ff; cursor: not-allowed; } /* æç¤ºææ¬ */ .tip-text { text-align: center; font-size: 12px; color: #95a5c6; margin: 12px 0 10px 0; letter-spacing: 0.5px; } /* è¾å ¥æ¡ç¦ç¨ç¶æ */ .wechat-input:disabled { background: #f0f0f0; opacity: 0.8; } .send-button:not(:disabled):hover { background: #2482ff; } .cursor { display: inline-block; width: 8px; height: 16px; background: #666; margin-left: 2px; animation: blink 1s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } </style> src/pages/searchNew/components/ChatHistoryView.vue
New file @@ -0,0 +1,248 @@ <template> <div class="schedule-container"> <el-card class="schedule-card" shadow="never"> <div v-for="(item, index) in chatList" :key="index" class="chat-item" @mouseenter="showDelete = index" @mouseleave="showDelete = null"> <!-- æ°å¢å é¤æé® --> <i class="el-icon-delete delete-button" v-show="showDelete === index" @click.stop="handleDelete(item.chatId, $event)"></i> <div class="chat-header"></div> <div class="chat-header"> <!-- <span class="chat-time">{{ formatDate(item.createTime) }}</span> <span class="chat-id">#{{ item.chatId }}</span> --> </div> <div class="chat-content" @click="handleItemClick(item.chatId)"> {{ extractContent(item.title) }} </div> </div> </el-card> </div> </template> <script> import chatHistory from "@/api/ChatHistoryView" export default { name: 'ChatHistory', data() { return { showDelete: null, // å½åæ¾ç¤ºå 餿é®çç´¢å¼ chatData: { list: [], pagination: {} } } }, computed: { chatList() { return this.chatData.list || [] } }, methods: { // æ°å¢å é¤å¤çæ¹æ³ async handleDelete(chatId, index) { try { await this.$confirm('ç¡®å®è¦å 餿¤è天记å½åï¼', 'æç¤º', { confirmButtonText: 'ç¡®å®', cancelButtonText: 'åæ¶', type: 'warning' }) // è°ç¨å 餿¥å£ await chatHistory.deleteChat({ chatId:chatId }) // å端ç«å³æ´æ°å表 this.chatData.list.splice(index, 1) this.$message.success('å 餿å') } catch (error) { if (error !== 'cancel') { console.error('å é¤å¤±è´¥:', error) this.$message.error('å é¤å¤±è´¥') } } }, handleItemClick(chatId) { // console.info("ç¹å»"+chatId) this.$emit('item-selected', chatId); // 触åèªå®ä¹äºä»¶ }, async fetchChatHistory() { try { const tasks = await chatHistory.getChats({//æ£æµå¯¹è¯åå² // page: 1, // pageSize: 999 }); // console.info(tasks.data.list) this.chatData.list = tasks.data.list this.chatData.pagination = tasks.data.pagination } catch (error) { console.error('è·åçéé项失败:', error); this.$message.error('çéé项å 载失败'); } }, formatDate(dateString) { return dateString.split(' ')[0] // æåæ¥æé¨å }, extractContent(content) { // ç§»é¤<think>æ ç¾å å®¹ï¼æåå®é å 容 const cleanContent = content.replace(/<think>[\s\S]*?<\/think>\n*/g, '') return cleanContent.trim() } }, mounted() { // æ¨¡ææ¥æ¶åç«¯æ°æ® this.fetchChatHistory() // this.chatData = { // list: [ // { // chatId: 19, // title: `<think>æèè¿ç¨...</think>\n\nä½ å¥½ï¼æä»ä¹æå¯ä»¥å¸®ä½ çåï¼ð`, // createTime: "2025-05-27 09:30:33.1284251+00:00" // } // // å ¶ä»æ°æ®é¡¹... // ], // pagination: { // page: 1, // total: 6 // } // } } } </script> <style scoped> /* ä¼ååçæ ·å¼ */ .schedule-container { height: 600px; background: white; padding: 0; overflow-y: auto; } .schedule-card { padding: 0; border: none; min-height: 100%; box-sizing: border-box; display: flex; flex-direction: column; } .chat-item { padding: 10px 0; /* border-bottom: 1px solid #f0f0f0; */ cursor: pointer; transition: background-color 0.3s; position: relative; /* 为ç»å¯¹å®ä½æé®æä¾åè */ padding-right: 40px; /* 为æé®çåºç©ºé´ */ } .delete-button { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); cursor: pointer; color: #ff4d4f; padding: 5px; font-size: 16px; transition: all 0.3s; z-index: 2; } .delete-button:hover { background-color: rgba(255,77,79,0.1); border-radius: 50%; } /* ä¼åæ¬åææ */ .chat-item:hover .delete-button { display: block; } .chat-item:hover { background-color: #f8f9fa; } .chat-header { display: flex; justify-content: space-between; /* margin-bottom: 8px; */ font-size: 12px; color: #666; } .item-container { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; } .chat-content { flex: 1; cursor: pointer; color: #1a1a1a; font-size: 16px; line-height: 1.6; word-break: break-word; text-align: left; /* æ°å¢å·¦å¯¹é½ */ /* æ°å¢åè¡çç¥æ ·å¼ */ display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; /* ç¡®ä¿å®½åº¦éå¶ */ max-width: 100%; width: 100%; } /* æ¬åæ¶æ¾ç¤ºå®æ´å 容 */ /* .chat-item:hover .chat-content { white-space: normal; overflow: visible; text-overflow: initial; } */ .loading-container { padding: 40px; text-align: center; color: #666; font-size: 14px; } .empty-state { text-align: center; padding: 40px; color: #999; i { font-size: 48px; margin-bottom: 16px; } } .pagination { margin-top: auto; padding: 16px; justify-content: center; } /* æ»å¨æ¡ä¼å */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: #f1f1f1; } ::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #a8a8a8; } </style> src/pages/searchNew/components/SurveyView.vue
New file @@ -0,0 +1,1570 @@ <template> <div> <!-- æ°å¢å®æ¶çæ§å¼¹çª --> <el-dialog title="宿¶çæ§" :visible.sync="realTimeVisible" width="60%" custom-class="resizable-dialog" :modal="true" :lock-scroll="false" :close-on-click-modal="false"> <!-- <div class="dialog-content"> --> <camera-video @close="realTimeVisible = false" /> <!-- <p>å°è¯ææ½è¾¹ç¼æè§è½è°æ´å¤§å°</p> <div v-for="handle in handles" :key="handle.position" class="resize-handle" :class="handle.position" @mousedown.prevent="startResize(handle.position, $event)"> </div> </div> --> </el-dialog> <!-- 详æ å¼¹çª --> <!-- ä¿®æ¹åç详æ å¼¹çª --> <el-dialog :visible.sync="detailVisible" title="" width="660px" custom-class="detail-dialog right-aligned"> <!-- é¡¶é¨åæ¢æé® --> <div class="media-switch"> <el-radio-group v-model="currentMediaType"> <el-radio-button label="image" class="media-tab">大å¾</el-radio-button> <el-radio-button label="video" class="media-tab">è§é¢</el-radio-button> </el-radio-group> </div> <el-divider></el-divider> <!-- ä¸é¨åªä½åº --> <div class="media-container"> <div v-if="currentMediaType === 'image'" class="main-image"> <el-image :src="detailItem.image_path" alt="äºä»¶æªå¾" class="detail-img previewable-image" :preview-src-list="[detailItem.image_path]" /> </div> <div v-else class="video-section"> <div class="video-placeholder"> <!-- ä¿®æ¹videoæ ç¾é¨å --> <video :src="detailItem.video_path" controls style="width: 100%; height: 100%; object-fit: contain"></video> <!-- <wasm-player style="width: 100%; height: 100%; object-fit: contain"></wasm-player> --> </div> </div> </div> <!-- ä¸é¨ä¿¡æ¯åº --> <div class="info-container"> <!-- <el-descriptions :column="2" border> --> <el-descriptions :column="1"> <el-descriptions-item label="æ¶é´" class="info-item"> <span style="width: 28px"></span> <span class="info-value">{{ formatStartDateTime(detailItem.detect_time) }}</span> </el-descriptions-item> <el-descriptions-item label="è§é¢ç¹ä½" class="info-item"> <span class="info-value">{{ detailItem.video_name }}</span> </el-descriptions-item> <el-descriptions-item label="ä»»å¡åç§°" v-if="backendData"> <span class="multi-value"> {{ detailItem.task_names }} </span> </el-descriptions-item> <el-descriptions-item label="äºä»¶ç级" v-if="backendData"> <span style="background-color: #f70713; width: 25px; height: 15px"> </span><span style="width: 5px"></span>{{ detailItem.event_levels }} </el-descriptions-item> <el-descriptions-item label="鿣æè¿°" v-if="backendData"> <el-tooltip placement="top" :content="detailItem.risk_description" effect="light" popper-class="my-tooltip"> <span class="multi-value ellipsis">{{ detailItem.risk_description }}</span> </el-tooltip> </el-descriptions-item> <el-descriptions-item label="å¤ç建议" v-if="backendData"> <el-tooltip placement="top" :content="detailItem.suggestion" effect="light" popper-class="my-tooltip"> <span class="multi-value ellipsis">{{ detailItem.suggestion }}</span> </el-tooltip> </el-descriptions-item> <el-descriptions-item label="ç¸å ³ææ¡£" v-if="backendData"> <div v-for="(doc, index) in detailItem.knowledge_documents || []" :key="index" class="multi-value"> <!-- <a :href="getPreviewUrl(doc)" target="_blank" class="doc-link" style="text-decoration: none;"> <span>ã{{ doc.fileName }}ã</span> </a> --> <span class="doc-link" @click="getPreviewUrl2(doc.id)">ã{{ doc.fileName }}ã</span> <span v-if="index < detailItem.knowledge_documents.length - 1">, </span> </div> </el-descriptions-item> <el-descriptions-item label="å¾çå 容" style=""> <div class="desc_class"> <span class="multi-value2"> {{ detailItem.zh_desc_class }} </span> </div> </el-descriptions-item> </el-descriptions> </div> </el-dialog> <div class="statistics-container"> <!-- 左侧çéæ --> <div class="left-sidebar" :class="{ collapsed: isSidebarCollapsed }"> <div class="stats-header"> <div class="ai-avatar"> <img :src="require('@/assets/img/AI-avatar.png')" class="avatar-img" /> <div class="header-text" v-show="!isSidebarCollapsed"> <h3 class="content-title header-gradient2">ä½ å¥½ï¼ææ¯å°è´</h3> <div class="stats-numbers"> <span>å¾é«å ´è§å°ä½ ï¼</span> </div> </div> <div class="header-actions"> <img :src="require('@/assets/img/fold_up.png')" class="fold-icon" :class="{ reverse: isSidebarCollapsed }" @click="toggleSidebar" /> <img v-if="isSidebarCollapsed" :src="require('@/assets/img/conversation.png')" class="fold-icon" :class="{ reverse: isSidebarCollapsed }" @click="handleNewSession" /> <img v-if="isSidebarCollapsed" :src="require('@/assets/img/historical.png')" class="fold-icon" :class="{ reverse: isSidebarCollapsed }" @click="handleHistoryClick" /> </div> </div> </div> <div class="session-buttons" v-if="!isSidebarCollapsed"> <el-button @click="handleHistoryClick" type="primary" round class="history-btn" style="flex: 1; margin-right: 10px"> åå²ä¼è¯ </el-button> <el-button @click="handleNewSession" type="primary" round class="new-session-btn" style="flex: 1; position: relative"> å¼å¯æ°ä¼è¯ <i class="el-icon-arrow-right" style="margin-left: 8px; font-size: 14px"></i> </el-button> </div> <el-divider v-if="!isSidebarCollapsed"></el-divider> <!-- AIæ£ç´¢ç»ä»¶ --> <ai-retrieval @list-selected="handleAiRetrievalSelected" :current-chat-id="selectedChatId" ref="aiRetrieval" class="ai-retrieval-container" v-show="showAIRetrieval && !isSidebarCollapsed" /> <chat-history @item-selected="handleHistorySelected" class="ai-retrieval-container" v-show="!showAIRetrieval && !isSidebarCollapsed" /> </div> <!-- å³ä¾§ä¸»å 容 --> <div class="right-content"> <div class="right-header"> <h2 class="content-title" > <span class="header-gradient">AI</span><span style="margin-left: 15px;font-size: 30px">ææä¸ç©</span> </h2> <div class="header-actions-right"> <el-button type="primary" class="action-btn" @click="handleRealTimeMonitor"> 宿¶çæ§ </el-button> <el-button type="primary" class="action-btn" @click="resetList()"> å·æ°æ°æ® </el-button> </div> </div> <!-- æ£æµç»æå±ç¤º --> <div class="content-wrapper"> <!-- æ·»å æ°æ®å è½½ç¶ææç¤º --> <div v-if="isLoading" class="loading-container"> <el-icon class="is-loading" size="24"> <Loading /> </el-icon> <span>æ°æ®å è½½ä¸...</span> </div> <!-- æ éæ»å¨å®¹å¨ --> <div class="results-container" @scroll="handleScroll"> <el-empty v-if="results.length === 0" description="ææ æ°æ®" style="margin-top: 50px"></el-empty> <div v-else class="gallery-section"> <div class="image-grid-container"> <div class="image-grid" ref="imageGrid"> <div v-for="(item, index) in results" :key="index" class="image-card-wrapper" :style="{ width: cardWidth }"> <el-card class="result-card" :class="{ 'selected-card': selectedItemId === item.id }" @click.native.stop="handleCardClick(item)"> <div class="image-wrapper"> <el-image slot="error" :src="item.image_path" class="result-image" alt="æ£æµç»æ" /> <!-- <img slot="error" src="@/assets/01.png" class="result-image" alt="æ£æµç»æ" /> --> <div class="image-overlay" v-if="item.is_warning == 1"> <span class="check-item"> {{ item.task_names }} </span> <el-tag size="mini" class="level-tag"> {{ item.event_levels }} </el-tag> </div> </div> <div class="card-content"> <div class="meta-info"> <div class="time-info"> <img src="@/assets/img/time-fill@1x.png" alt="" class="time-icon"> <span class="detect-time">{{ formatStartDateTime(item.detect_time) }}</span> <el-popover placement="bottom" width="300" trigger="click" v-model="popoverVisible[index]" v-if="item.is_desc === 2" class="right-btn2"> <i class="el-icon-close" @click="closePopover(index)"></i> <span style="color: black; font-weight: 600">å¾çå 容</span> <div class="task-popover-content"> {{ item.zh_desc_class }} </div> <img src="@/assets/img/article-fill@1x.png" alt="" class="time-icon2" slot="reference" @click.stop="togglePopover(index)"> </el-popover> </div> <div class="device-info"> <div class="camera-info"> <img src="@/assets/img/live-fill@1x.png" alt="" class="time-icon"> <span>{{ item.video_name }}</span> <el-dropdown size="small" @command="handleCommand" class="right-btn"> <img src="@/assets/img/modelTraining.png" style="width: 16px;height: 16px;margin-left: 10px; vertical-align: middle"> <el-dropdown-menu slot="dropdown"> <el-dropdown-item :command="{ ruleName: item.rule_names ? item.rule_names[0].fileName : '', cameraId: item.video_point_id + '', cameraName: item.video_name, imagePath: item.image_path, status: 1 }">æ£ç¡®</el-dropdown-item> <el-dropdown-item :command="{ ruleName: item.rule_names ? item.rule_names[0].fileName : '', cameraId: item.video_point_id + '', cameraName: item.video_name, imagePath: item.image_path, status: 2 }">é误</el-dropdown-item> <el-dropdown-item :command="{ ruleName: item.rule_names ? item.rule_names[0].fileName : '', cameraId: item.video_point_id + '', cameraName: item.video_name, imagePath: item.image_path, status: 0 }">ä¸ç¡®å®</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </div> </div> </div> </div> </el-card> </div> </div> </div> </div> <!-- æ»å¨å è½½æç¤º --> <div v-if="isLoadingMore" class="loading-more"> <el-icon class="is-loading" size="16"> <Loading /> </el-icon> <span>å è½½æ´å¤...</span> </div> <div v-if="!hasMore && results.length > 0" class="no-more-data"> æ²¡ææ´å¤æ°æ®äº </div> </div> <!-- å页 --> <!-- <div class="pagination-wrapper" v-if="results.length > 0"> <el-pagination :current-page="currentPage" :page-size="16" :total="totalResults" layout="prev, pager, next" @current-change="handlePageChange"> </el-pagination> </div> --> </div> </div> </div> </div> </template> <script> import _ from 'lodash'; // ç¨äºé²æ import surey from "@/api/SurveyView"; import aiRetrieval from './AiRetrievalView'; import chatHistory from './ChatHistoryView'; import cameraVideo from './cameraVideo.vue'; export default { components: { aiRetrieval, chatHistory, cameraVideo }, data() { return { // æ°å¢å¡ç宽度计ç®ç¸å ³æ°æ® cardWidth: '300px', // é»è®¤å¡ç宽度 minCardWidth: 300, // å¡çæå°å®½åº¦ margin: 20, // å¡çé´è· idsList: [], pageSize: 30, // æ¯é¡µå è½½æ°æ®é hasMore: true, // æ¯å¦ææ´å¤æ°æ® isLoadingMore: false, // æ¯å¦æ£å¨å è½½æ´å¤ scrollTop: 0, // æ»å¨ä½ç½®è®°å½ docUrl: "", isLoading: false, // æ°å¢å è½½ç¶æåé backendData: true, popoverVisible: {}, // ç¨äºæ§å¶æ¯ä¸ªå¡ççæ°æ³¡æ¾ç¤ºç¶æ dialogVisible: true, handles: [ { position: "top" }, { position: "bottom" }, { position: "left" }, { position: "right" }, { position: "top-left" }, { position: "top-right" }, { position: "bottom-left" }, { position: "bottom-right" }, ], isResizing: false, startX: 0, startY: 0, startWidth: 0, startHeight: 0, startLeft: 0, startTop: 0, realTimeVisible: false, // æ°å¢å¼¹çªæ§å¶åé selectedChatId: null, showAIRetrieval: true, isSidebarCollapsed: false, selectedItemId: null, // æ°å¢éä¸é¡¹ID filterOptions: { tasks: [], levels: [], cameras: [], rules: [], }, currentMediaType: "image", // å½ååªä½ç±»å detailVisible: false, detailItem: {}, currentPage: 1, totalResults: 40, // æ»æ°æ®é filter: { keyword: "", task: "", timeRange: [], level: "", camera: "", rules: "", }, showWarningOnly: true, results: [ { // image_path: require("@/assets/01.png"), // video_path: require("@/assets/video.mp4"), risk_description: "鿣æè¿°", detect_time: "2021-12-09 20:23", video_name: "æåæºA", event_level: "1", is_warning: 1, event_levels: "ä¸çº§", task_names: "ç产任å¡ç®¡æ§,ç产任å¡ç®¡æ§,ç产任å¡ç®¡æ§,ç产任å¡ç®¡æ§,ç产任å¡ç®¡æ§", // æ¹ä¸ºæ°ç» suggestion: "å¤ç建议", // å¤ç建议 video_point_id: 1, rule_names: "", knowledge_documents: [ { ruleId: 1, fileName: "å®å ¨åå", file_url: "https://image.baidu.com/search/detail?z=0&word=%E5%9B%BE%E7%89%87&hs=0&pn=1&spn=0&di=7498023338351001601&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&lm=&cs=2629589424%2C2655723787&os=2900564191%2C785059641&simid=3974997321%2C325136453&adpicid=0&lpn=0&fr=click-pic&fm=&ic=&hd=&latest=©right=&isImgSet=&commodity=&hot=&imgratio=&imgformat=&sme=&width=0&height=0&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fww2.sinaimg.cn%2Fmw690%2F61d7678dgy1hvt194v9kqj20p00uuape.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fojtk5_z%26e3Bv54AzdH3F8m98cam0a8AzdH3FP81pPuN3N&gsm=1e&islist=&querylist=&lid=9dec68e500008991" }, { ruleId: 1, fileName: "å®å ¨åå", file_url: "http://192.168.1.232:7010/home/debian/GroundingDINO/txt/zs/AI-avatar.png" }, ], // é¢è¦è§åæ°ç» zh_desc_class: "å¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²çå¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²ç" }, { image_path: "", risk_description: "鿣æè¿°", detect_time: "2021-12-09 21:15", video_name: "æåæºB", is_desc: 2, zh_desc_class: "å¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²çå¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²çå¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²çå¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²çå¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²çå¾çæ¾ç¤ºçæ¯ä¸ä¸ªå®¤å åºæ¯ï¼æ¶é´æ³æ¾ç¤ºä¸º2025å¹´6æ27æ¥ææäº16:57:12ãç»é¢ä¸å¯ä»¥çå°ä¸ä¸ªèµ°å»è¿éçç¯å¢ï¼å³ä¾§æä¸æç»çé¨ï¼é¨ä¸è¦ççèè²çé²å°å¸ã左侧å¢ä¸æä¸ä¸ªé»æ¿ï¼ä¸é¢åæä¸äºä¸æåè±æçæåï¼å å®¹å æ¬âè«ä¼è¾¾âãâint4âãâAWQâãâ4Gâçã黿¿æè¾¹æä¸ä¸ªç½è²çæåï¼æå䏿¾çä¸äºç©åãå¢è§å¤è¿æä¸æ ªå°æ ãæ´ä¸ªæ¿é´çå¢å£æ¯ç½è²çï¼å¤©è±æ¿ä¹æ¯ç½è²ç" }, ], }; }, computed: { normalizedPath() { return (backendPath) => { // æ¿æ¢é»è¾ return require(backendPath .replace(/^[a-zA-Z]:/, "") // å»é¤ç符 .replace(/\/+/g, "/") // åå¹¶å¤ä½ææ .replace("/opt/smart", "@/assets")); // å ³é®è·¯å¾æ¿æ¢ }; }, visibleResults() { return this.results.slice( (this.currentPage - 1) * 8, this.currentPage * 8 ); }, boxHeight() { // æ ¹æ®åç«¯æ°æ®å³å®é«åº¦ // if (!this.backendData) return "380px"; return this.backendData ? "160px" : "320px"; }, }, mounted() { // this.fetchFilterOptions();//çéå表 this.handleSearch([]); this.fetchCameraInfo(); this.resetList(); this.calculateCardWidth(); // æ·»å 鲿çresizeçå¬ window.addEventListener('resize', _.debounce(this.calculateCardWidth, 100)); }, beforeDestroy() { window.removeEventListener('resize', this.calculateCardWidth); }, methods: { // æ°å¢å¡çå®½åº¦è®¡ç®æ¹æ³ calculateCardWidth() { if (this.$refs.imageGrid) { const containerWidth = this.$refs.imageGrid.clientWidth; // è®¡ç®æ¯è¡å¯ä»¥æ¾ç½®çå¡çæ°éï¼æå°3å const cardsPerRow = Math.max(3, Math.floor(containerWidth / (this.minCardWidth + this.margin))); // 设置å¡çå®½åº¦å ¬å¼ this.cardWidth = `calc(${100 / cardsPerRow}% - ${this.margin}px)`; } }, handleCommand(command) { console.log(JSON.stringify(command)) surey.insertModelTraining(JSON.stringify(command)).then((res) => { if (res && res.status === 200) { this.$notify({ type: "success", message: "æ·»å æå", }); } else { this.$notify({ type: "error", message: "æ·»å 失败ï¼", }); } }); }, // éç½®å表 resetList() { this.idsList = []; this.currentPage = 1; this.hasMore = true; this.results = []; this.handleSearch([]); }, // æ»å¨äºä»¶å¤ç handleScroll(event) { const container = event.target; // è®°å½æ»å¨ä½ç½® this.scrollTop = container.scrollTop; // æ£æ¥æ¯å¦æ»å¨å°åºé¨ const isBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 50; // 彿»å¨å°åºé¨ä¸ææ´å¤æ°æ®ï¼ä¸å½å没æå è½½ä¸ï¼åå è½½æ´å¤ if (isBottom && this.hasMore && !this.isLoading && !this.isLoadingMore) { this.loadMoreData(); } }, // å è½½æ´å¤æ°æ® async loadMoreData() { if (this.isLoadingMore || !this.hasMore) return; try { this.isLoadingMore = true; this.currentPage++; const params = { ids: [], page: this.currentPage, pageSize: this.pageSize }; const response = await surey.getSurveys(params); const lists = response.data.list || []; if (lists) { for (let i = 0; i < lists.length; i++) { console.log("333:" + lists[i].video_point_id) this.results.push({ task_names: lists[i].task_name, video_name: lists[i].video_name, image_path: "/api-img" + lists[i].image_path, video_path: "/api-img" + lists[i].video_path, detect_time: lists[i].detect_time, event_levels: lists[i].event_level_name, zh_desc_class: lists[i].zh_desc_class, is_warning: lists[i].is_warning, is_desc: lists[i].is_desc, video_point_id: lists[i].video_point_id, rule_names: lists[i].rule_names, knowledge_documents: lists[i].knowledge_documents.map(file => { return { ...file, file_url: "/api-img" + file.file_url, fileName: file.title } }) }); } } // åå¹¶ç»æ // this.results = [...this.results, ...lists]; // æ´æ°æ¯å¦ææ´å¤æ°æ®ç¶æ this.hasMore = lists.length >= this.pageSize; } catch (error) { console.error("å è½½æ´å¤å¤±è´¥:", error); } finally { this.isLoadingMore = false; } }, async fetchCameraInfo() { const response = await fetch("/config.json"); if (!response.ok) throw new Error(`请æ±å¤±è´¥: ${response.status}`); const responseData = await response.json(); this.docUrl = responseData.docUrl; console.info("docUrl:" + this.docUrl) }, closePopover(index) { this.$set(this.popoverVisible, index, false); }, startResize(position, e) { this.isResizing = true; const dialog = document.querySelector(".resizable-dialog"); this.startX = e.clientX; this.startY = e.clientY; this.startWidth = dialog.offsetWidth; this.startHeight = dialog.offsetHeight; this.startLeft = dialog.offsetLeft; this.startTop = dialog.offsetTop; document.addEventListener("mousemove", this.handleResize(position)); document.addEventListener("mouseup", this.stopResize); }, handleResize(position) { return (e) => { if (!this.isResizing) return; const dialog = document.querySelector(".resizable-dialog"); const deltaX = e.clientX - this.startX; const deltaY = e.clientY - this.startY; // éå¶æå°å°ºå¯¸ const minWidth = 400; const minHeight = 300; switch (position) { case "top": dialog.style.height = Math.max(minHeight, this.startHeight - deltaY) + "px"; dialog.style.top = `${this.startTop + deltaY}px`; break; case "bottom": dialog.style.height = Math.max(minHeight, this.startHeight + deltaY) + "px"; break; case "left": dialog.style.width = Math.max(minWidth, this.startWidth - deltaX) + "px"; dialog.style.left = `${this.startLeft + deltaX}px`; break; case "right": dialog.style.width = Math.max(minWidth, this.startWidth + deltaX) + "px"; break; case "top-left": dialog.style.width = Math.max(minWidth, this.startWidth - deltaX) + "px"; dialog.style.height = Math.max(minHeight, this.startHeight - deltaY) + "px"; dialog.style.left = `${this.startLeft + deltaX}px`; dialog.style.top = `${this.startTop + deltaY}px`; break; case "top-right": dialog.style.width = Math.max(minWidth, this.startWidth + deltaX) + "px"; dialog.style.height = Math.max(minHeight, this.startHeight - deltaY) + "px"; dialog.style.top = `${this.startTop + deltaY}px`; break; case "bottom-left": dialog.style.width = Math.max(minWidth, this.startWidth - deltaX) + "px"; dialog.style.height = Math.max(minHeight, this.startHeight + deltaY) + "px"; dialog.style.left = `${this.startLeft + deltaX}px`; break; case "bottom-right": dialog.style.width = Math.max(minWidth, this.startWidth + deltaX) + "px"; dialog.style.height = Math.max(minHeight, this.startHeight + deltaY) + "px"; break; } }; }, stopResize() { this.isResizing = false; document.removeEventListener("mousemove", this.handleResize); document.removeEventListener("mouseup", this.stopResize); }, // 宿¶çæ§æ¹æ³ handleRealTimeMonitor() { this.realTimeVisible = true; }, handleHistorySelected(chatId) { this.showAIRetrieval = true; // 忢ç»ä»¶ this.selectedChatId = chatId; // ä¿åchatId // this.$nextTick(() => { // this.$refs.aiRetrieval.loadChat(); // 触ååç»ä»¶å è½½ // }); }, handleAiRetrievalSelected(params) { console.info("params:" + params) //æ ¹æ®idæ¥è¯¢æ°æ® this.idsList = params; this.handleSearch() }, handleHistoryClick() { this.showAIRetrieval = false; this.isSidebarCollapsed = false; }, handleNewSession() { // è°ç¨AIæ£ç´¢ç»ä»¶çéç½®æ¹æ³ if ( this.$refs.aiRetrieval && typeof this.$refs.aiRetrieval.reset === "function" ) { this.$refs.aiRetrieval.reset(); } // ä¿æä¾§è¾¹æ å±å¼ç¶æ this.isSidebarCollapsed = false; this.showAIRetrieval = true; }, toggleSidebar() { this.isSidebarCollapsed = !this.isSidebarCollapsed; }, // ä¿®æ¹åçç¹å»å¤çæ¹æ³ handleCardClick(item) { this.selectedItemId = item.id; this.showDetail(item); }, // getLevelType(level) { // const typeMap = { // '1': 'danger', // '2': 'warning', // '3': 'primary', // '4': 'info', // '5': 'success' // } // return typeMap[level] || 'info' // }, // è·åç鿡件é项 async fetchFilterOptions() { try { const tasks = await surey.getTasks({ //æ£æµå 容 page: 1, pageSize: 999, }); this.filterOptions.tasks = tasks.data.list; const cameras = await surey.getCameras(); //è§é¢ç¹ä½ this.filterOptions.cameras = cameras.data; const warnings = await surey.getWarnings({ //é¢è¦è§å page: 1, pageSize: 999, }); this.filterOptions.rules = warnings.data.list; const events = await surey.getEvents(); //äºä»¶ç级 this.filterOptions.levels = events.data; // console.info(this.filterOptions.levels) } catch (error) { console.error("è·åçéé项失败:", error); this.$message.error("çéé项å 载失败"); } }, // æ ¼å¼åå¼å§æ¶é´ï¼ä¿æåæ¶é´ï¼ formatStartDateTime(date) { if (!date) return null; const d = new Date(date); const pad = (num) => num.toString().padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad( d.getDate() )} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; }, // æ ¼å¼åç»ææ¶é´ï¼åºå®ä¸º 23:59:59ï¼ formatEndDateTime(date) { if (!date) return null; const d = new Date(date); d.setHours(23, 59, 59); // 强å¶è®¾ç½®ä¸ºå½å¤©ç 23:59:59 const pad = (num) => num.toString().padStart(2, "0"); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad( d.getDate() )} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; }, // æ¥è¯¢æ¹æ³ async handleSearch() { console.info("this.idsList:" + this.idsList) this.isLoading = true; // å¼å§å è½½ // console.info(ids) try { this.currentPage = 1; const params = { ids: this.idsList, page: 1, pageSize: this.pageSize, }; const response = await surey.getSurveys(params); const lists = response.data.list || []; this.results = []; if (lists) { for (let i = 0; i < lists.length; i++) { this.results.push({ task_names: lists[i].task_name, video_name: lists[i].video_name, image_path: "/api-img" + lists[i].image_path, video_path: "/api-img" + lists[i].video_path, detect_time: lists[i].detect_time, event_levels: lists[i].event_level_name, zh_desc_class: lists[i].zh_desc_class, is_warning: lists[i].is_warning, is_desc: lists[i].is_desc, video_point_id: lists[i].video_point_id, rule_names: lists[i].rule_names, knowledge_documents: lists[i].knowledge_documents.map(file => { return { ...file, file_url: "/api-img" + file.file_url, fileName: file.title } }), risk_description: lists[i].risk_description, suggestion: lists[i].suggestion }); } } this.totalResults = response.data.pagination.total; // æ´æ°æ¯å¦ææ´å¤æ°æ®ç¶æ this.hasMore = lists.length >= this.pageSize; // console.info(response.data) } catch (error) { console.error("æ¥è¯¢å¤±è´¥:", error); this.$message.error("æ¥è¯¢å¤±è´¥ï¼è¯·ç¨åéè¯"); } finally { this.isLoading = false; // ç»æå è½½ } }, // æ°å¢è¯¦æ å±ç¤ºæ¹æ³ showDetail(item) { // console.info(item) this.backendData = item.is_warning == 1 ? true : false; // console.info(item.is_warning) this.currentMediaType = "image"; this.detailItem = { ...item, task: "ç产任å¡ç®¡æ§", // æ ¹æ®å¾çä¿¡æ¯æ·»å ä»»å¡åç§° }; // this.detailItem.image_path = 'http://192.168.1.232:7010/' + item.image_path // this.detailItem.video_path = 'http://192.168.1.232:7010/' + item.video_path this.detailItem.image_path = item.image_path; this.detailItem.video_path = item.video_path; this.detailItem.is_warning = item.is_warning; this.detailItem.suggestion = item.suggestion; this.detailItem.risk_description = item.risk_description; this.detailVisible = true; // console.info(item) }, // å页å¤ç handlePageChange(page) { this.currentPage = page; this.handleSearch([]); }, // éç½®ç鿡件 resetFilter() { this.filter = { keyword: "", task: "", timeRange: [], level: "", camera: "", }; this.currentPage = 1; this.handleSearch([]); }, // è·åææ¡£é¢è§URL getPreviewUrl(doc) { // è·åæä»¶æ©å±å const extension = this.getFileExtension(doc.fileName); // PDF使ç¨ç´æ¥è®¿é® if (extension === 'pdf') { return doc.file_url; } // Officeææ¡£ä½¿ç¨å¾®è½¯é¢è§æå¡ // if (['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(extension)) { // return `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(doc.file_url)}`; // } // å ¶ä»ç±»åç´æ¥è¿å return doc.file_url; }, getPreviewUrl2(id) { window.location.href = "/api/v1/knowledge/download?id=" + id // window.location.href="http://192.168.1.176:8088/v1/knowledge/download?id="+id }, // è·åæä»¶æ©å±å getFileExtension(filename) { const parts = filename.split('.'); return parts.length > 1 ? parts.pop().toLowerCase() : ''; }, }, watch: { detailVisible(newVal) { if (!newVal) { this.selectedItemId = null; // å ³éå¼¹çªæ¶æ¸ é¤éä¸ç¶æ } }, }, }; </script> <style> /* å ¨å±çæï¼å¯è¦ç tooltip */ .my-tooltip { max-width: 540px !important; color: #606266 !important; } </style> <style lang="scss" scoped> /* æ°å¢å¾çç½æ ¼å¸å±æ ·å¼ */ .image-grid-container { width: 100%; overflow: hidden; /* è¶ åºé¨åéè */ } .image-grid { display: flex; flex-wrap: wrap; margin: -10px; /* è´è¾¹è·æµæ¶å 裹å ç´ çè¾¹è· */ width: 100%; } .image-card-wrapper { margin: 10px; /* 设置å¡çé´è· */ // box-sizing: border-box; // transition: width 0.3s ease; /* æ·»å å¹³æ»è¿æ¸¡ææ */ min-width: 300px; /* å¡çæå°å®½åº¦ */ flex-shrink: 0; /* 鲿¢å¡çç¼©å° */ } .right-btn { position: absolute; right: 8%; top: 50%; transform: translateY(-50%); /* åç´å± ä¸ */ } .right-btn2 { position: absolute; right: 10.5%; top: 74.5%; transform: translateY(-50%); /* åç´å± ä¸ */ } .time-icon { width: 16px; height: 16px; margin-right: 20px; vertical-align: middle; } .time-icon2 { width: 16px; height: 16px; margin-right: 0px; vertical-align: middle; } /* å ³éæé®æ ·å¼ */ .el-icon-close { position: absolute; top: 10px; right: 5px; padding: 0; width: 24px; height: 24px; font-size: 14px; color: #909399; background: none; border: none; cursor: pointer; transition: all 0.2s; } /* æ°å¢æ°æ³¡å å®¹æ ·å¼ */ .task-popover-content { max-height: 100px; overflow-y: auto; padding: 20px 15px 15px 15px; /* å¢å é¡¶é¨ç©ºé´ç»å ³éæé® */ line-height: 1.5; word-break: break-word; white-space: pre-line; border-radius: 10px; /* èªå®ä¹æ»å¨æ¡æ ·å¼ */ &::-webkit-scrollbar { width: 4px; /* ç¼©å°æ»å¨æ¡å®½åº¦ */ background-color: transparent; } &::-webkit-scrollbar-track { background: transparent; /* éè轨é */ } &::-webkit-scrollbar-thumb { background-color: #c0c4cc; /* 设置æ»åé¢è² */ border-radius: 2px; } /* ç§»é¤æ»å¨æ¡ç®å¤´ */ &::-webkit-scrollbar-button { display: none; /* éèä¸ä¸ç®å¤´ */ } } .resizable-dialog { position: fixed !important; margin: 0 !important; padding-top: 0px; min-width: 200px; min-height: 300px; overflow: auto; top: 50% !important; left: 50% !important; transform: translate(-50%, -50%) !important; } .resize-handle { position: absolute; background: #409eff !important; z-index: 9999; opacity: 0; transition: opacity 0.2s; } .resize-handle:hover { opacity: 1; } /* 忹忿å®ä½ */ .top { top: 0; left: 0; right: 0; height: 4px; cursor: ns-resize; } .bottom { bottom: 0; left: 0; right: 0; height: 4px; cursor: ns-resize; } .left { top: 0; left: 0; width: 4px; bottom: 0; cursor: ew-resize; } .right { top: 0; right: 0; width: 4px; bottom: 0; cursor: ew-resize; } .top-left { top: 0; left: 0; width: 10px; height: 10px; cursor: nwse-resize; } .top-right { top: 0; right: 0; width: 10px; height: 10px; cursor: nesw-resize; } .bottom-left { bottom: 0; left: 0; width: 10px; height: 10px; cursor: nesw-resize; } .bottom-right { bottom: 0; right: 0; width: 10px; height: 10px; cursor: nwse-resize; } .el-dialog__body { height: calc(100% - 54px); overflow: auto; } .fold-icon { width: 24px; height: 24px; cursor: pointer; transition: transform 0.3s ease; margin: 8px; &:hover { opacity: 0.8; filter: drop-shadow(0 0 2px rgba(11, 113, 216, 0.5)); } &.reverse { transform: rotate(180deg); } } /* ai-retrieval容卿 ·å¼ */ .ai-retrieval-container { position: absolute; // left: 5px; width: 360px; // height: 1000px; z-index: 1000; background: rgb(245, 244, 244); // box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1); } /* æ°å¢å¤å¼æ¾ç¤ºæ ·å¼ */ .multi-value { display: inline-block; max-width: 540px; // white-space: nowrap; overflow: hidden; // text-overflow: ellipsis; } .desc_class { // max-height: 380px; overflow-y: auto; } .multi-value2 { display: inline-block; // max-height: 380px; } .stats-header { margin-bottom: 20px; .ai-avatar { display: flex; align-items: flex-start; // é¡¶é¨å¯¹é½ .header-text { margin-left: 10px; .content-title { text-align: left; margin: 5px 0 10px; color: #0e3eaa; } .stats-numbers { font-size: 12px; text-align: left; } } .header-actions { flex: 1; text-align: right; margin-top: 10px; } } } .results-container { padding: 0 15px; // min-height: 600px; overflow-x: hidden; /* éèæ¨ªåæ»å¨æ¡ */ overflow-y: auto; /* ä¿ççºµåæ»å¨ */ /* æ°å¢æ»å¨æ¡ */ height: 900px; .result-card { border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); border: none; position: relative; // 为ç»å¯¹å®ä½æä¾åºå overflow: visible; // 鲿¢æé®è¢«è£åª .image-wrapper { position: relative; // height: 200px; overflow: visible; border-radius: 6px 6px 0 0; .result-image { width: 100%; // height: 130px; object-fit: cover; transition: transform 0.3s; } .image-overlay { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.6); /* åéæé»è²èæ¯ */ padding: 5px 10px; display: flex; justify-content: space-between; align-items: center; z-index: 2; /* ç¡®ä¿å¨å¾ç䏿¹ */ } .check-item { color: white; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 70%; } .level-tag { z-index: 3; /* ç¡®ä¿å¨è¦çå±ä¸æ¹ */ transform: scale(0.9); margin: 2px; color: #fa0505; background-color: #f3e1e1; } } .card-content { padding: 15px; .meta-info { .time-info { display: flex; text-align: left; align-items: center; margin-bottom: 10px; color: #909399; width: 100%; margin-bottom: 10px; .el-icon-time { color: #409eff !important; margin-right: 20px; font-size: 16px; } span { font-size: 13px; margin-right: 20px; } .el-icon-document { color: #034914; } .detect-time { flex-shrink: 0; color: #909399; font-size: 13px; } } .device-info { // display: flex; justify-content: space-between; align-items: center; .camera-info { // display: flex; position: relative; text-align: left; align-items: center; i { color: #409eff !important; margin-right: 20px; font-size: 16px; } span { font-size: 14px; color: #303133; } } } } } } } .pagination-wrapper { // position: fixed; right: 30px; bottom: 30px; background: #fff; padding: 10px 20px; border-radius: 4px; // box-shadow: 0 2px 12px rgba(0,0,0,0.1); } // è°æ´å¸å±ç»æ .statistics-container { display: flex; height: 98vh; // æ°å¢ .left-sidebar { width: 320px; transition: all 0.3s ease; left: 0; // height: 100vh; z-index: 1000; background: #fff; box-shadow: 1px 0 10px rgba(0, 0, 0, 0.1); padding: 10px; border-right: 1px solid #ebeef5; position: relative; padding-bottom: 40px; /* 为åºé¨æé®çåºç©ºé´ */ /* 为ç»å¯¹å®ä½å建åç § */ .session-buttons { // position: absolute; bottom: 20px; left: 20px; right: 20px; display: flex; gap: 0; .el-button { height: 50px; border-radius: 5px; transition: all 0.3s ease; background-color: #e1ebff !important; color: #2482ff; border: 1px solid #e1ebff !important; font-size: 16px !important; padding: 0 12px; } ::v-deep .el-button { background-color: #e1ebff !important; } } &.collapsed { width: 40px; .ai-avatar { flex-direction: column; align-items: center; padding: 10px 0; .avatar-img { margin: 0; width: 51px; height: 35px; } } .header-actions i { transform: rotate(180deg); margin-top: 10px; } } .header-actions { i { transition: all 0.3s ease; cursor: pointer; font-size: 20px; color: #666; &:hover { color: #0b71d8; } &.reverse { transform: rotate(180deg); } } } .right-content { transition: margin 0.3s ease; padding: 20px; min-height: 100vh; background: #f5f6fa; } .ai-retrieval-container { width: 100%; height: calc(100% - 150px); /* æ ¹æ®å®é å¸å±è°æ´é«åº¦ */ position: relative; box-shadow: none; background: #fff; } .el-checkbox { margin-left: 15px; display: block; text-align: left; } .el-form-item__content { margin-left: 0 !important; } } .right-content { flex: 1; margin-left: 20px; position: relative; box-shadow: 1px 0 10px rgba(0, 0, 0, 0.1); padding: 20px; padding-top: 0px; border-left: 1px solid #ebeef5; .right-header { display: flex; margin-bottom: 20px; border-bottom: 1px solid #ebeef5; padding-bottom: 10px; justify-content: space-between; height: 70px; /* æ°å¢ï¼å·¦å³ä¸¤ç«¯å¯¹é½ */ .content-title{ text-align: left; margin: 15px 0 0 0; } .header-actions-right { margin-top: 20px; .action-btn { height: 40px; margin-bottom: 0px; padding: 10px 20px; border-radius: 6px; letter-spacing: 0.5px; } } } } } .form-actions { margin-top: 50px; display: flex; gap: 10px; // æé®é´è· .full-width-btn { flex: 1; // çåå©ä½ç©ºé´ margin-left: 0 !important; } // æ¸ é¤Elementé»è®¤è¾¹è· ::v-deep .el-button { margin-left: 0; margin-right: 0; } } // 详æ å¼¹çªæ ·å¼ .detail-dialog { .el-dialog__header { border-bottom: 1px solid #ebeef5; } .media-switch { margin: -50px 0 15px; text-align: left; .media-tab { &.is-active { background: #409eff !important; border-color: #409eff !important; color: white; } } } .media-container { height: 350px; border: 1px solid #ebeef5; border-radius: 4px; margin-bottom: 20px; position: relative; overflow: hidden; // æ·»å æº¢åºéè .main-image { height: 100%; padding: 0px; .detail-img { width: 100%; height: 100%; object-fit: contain; } } .video-section { height: 100%; width: 100%; .video-placeholder { width: 100%; height: 100%; position: relative; video { max-width: 100%; max-height: 100%; display: block; margin: 0 auto; } } } } .info-container { height: 400px; // overflow-y: auto; ::v-deep .el-descriptions__body { background: white; } .info-item { padding: 12px 10px; &>.el-descriptions-item__label { color: #606266; width: 90px; } } .red-text { color: #f56c6c !important; } .blue-text { color: #409eff !important; } .doc-link { padding-left: 10px; text-decoration: none; color: #1b50e4; // display: inline-block; // padding: 5px 8px; // border-radius: 4px; // transition: all 0.3s; // color: #165DFF; &:hover { cursor: pointer; } } ::v-deep .el-descriptions-item__label { color: black !important; font-weight: 600 !important; } } } /* å³ä¾§å¯¹é½æ ·å¼ */ .detail-dialog { position: fixed !important; top: 50% !important; left: auto !important; right: 20px !important; transform: translateY(-50%) !important; width: 660px !important; margin: 0 !important; z-index: 2000 !important; } ::v-deep .el-dialog { // margin-right: 0px; .el-divider--horizontal { margin: 12px 0; } } /* è¡¨æ ¼è¡¨å¤´æ ·å¼ */ .header-gradient { background: radial-gradient(circle at 20% 30%, #0e5397, #2482FF); -webkit-background-clip: text; background-clip: text; color: transparent; font-weight: bold; font-size: 35px; } .header-gradient2 { font-size: 18px; background: radial-gradient(circle at 20% 30%, #165DFF, #01040a); -webkit-background-clip: text; background-clip: text; } /* æ°å¢å è½½ç¶ææ ·å¼ */ .loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 300px; color: #606266; font-size: 16px; .el-icon { margin-bottom: 15px; animation: rotating 1.5s linear infinite; @keyframes rotating { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } } } .ellipsis { display: inline-block; /* æ block */ // max-width: 100%; /* æ ¹æ®å®é 容å¨å®½åº¦è°æ´ */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; /* å ³é®ï¼ç¦æ¢æ¢è¡ */ } </style> src/pages/searchNew/components/cameraVideo.vue
New file @@ -0,0 +1,232 @@ <template> <div class="container"> <!-- å¸å±æ§å¶åææ¾å°åéæ© --> <div class="layout-controls"> <el-select v-model="selectedUrl" placeholder="éæ©ææ¾å°å" class="left-control" style="width: 200px; margin-right: 20px" @change="handleUrlChange" :disabled="activeIndex === -1" > <el-option v-for="item in playbackOptions" :key="item.videoId" :label="item.deviceName" :value="item.videoId" /> </el-select> <el-button-group class="right-control"> <el-button :type="gridLayout === 1 ? 'primary' : ''" @click="changeLayout(1)" > åæ ¼ </el-button> <el-button :type="gridLayout === 4 ? 'primary' : ''" @click="changeLayout(4)" > åæ ¼ </el-button> <el-button :type="gridLayout === 9 ? 'primary' : ''" @click="changeLayout(9)" > 乿 ¼ </el-button> </el-button-group> </div> <!-- è§é¢ç½æ ¼å¸å± --> <el-row :gutter="5"> <el-col v-for="(item, index) in showVideos" :key="index" :span="colSpan" class="video-col" :class="{ active: activeIndex === index }" > <div class="video-container" @click="handleGridClick(index)"> <!-- 纯é»èæ¯å°é¢ --> <div class="video-cover" v-show="!item.playing" style="pointer-events: none" /> <!-- è§é¢ææ¾å¨ --> <iframe v-show="item.playing" class="video-iframe" :src="item.url" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="pointer-events: none" ></iframe> </div> </el-col> </el-row> </div> </template> <script> import camera from "@/api/SurveyView" export default { name: 'cameraVideo', data() { return { iframeUrl:'', gridLayout: 1, activeIndex: -1, selectedUrl: '', playbackOptions: [ { label: 'æå头1', value: 'rtsp://Admin:1234@192.168.1.209/h264' }, { label: 'æå头2', value: 'http://example.com/another-video-source' } ], allVideos: Array(9).fill(null).map((_, i) => ({ title: `è§é¢ ${i + 1}`, url: '', playing: false })) } }, computed: { showVideos() { return this.allVideos.slice(0, this.gridLayout) }, colSpan() { return 24 / Math.sqrt(this.gridLayout) } }, mounted() { this.fetchSelect(); this.fetchCameraInfo() }, methods: { async fetchCameraInfo() { const response = await fetch('/config.json'); if (!response.ok) throw new Error(`请æ±å¤±è´¥: ${response.status}`); const responseData = await response.json(); this.iframeUrl = responseData.iframeUrl; console.info(this.iframeUrl) }, async fetchSelect(){ const cameras = await camera.getCameras();//è§é¢ç¹ä½ this.playbackOptions = cameras.data }, changeLayout(num) { this.gridLayout = num this.activeIndex = -1 this.resetAllVideos() }, handleGridClick(index) { this.activeIndex = index console.info('ç¹å»äºä»¶çæ') }, handleUrlChange(val) { console.info(process.env.NODE_ENV) if (this.activeIndex === -1) return // console.info('ç¹å»éä¸çæ°æ®ï¼'+this.playbackOptions.find(item => item.videoId === val).rtspAddress) const currentVideo = this.allVideos[this.activeIndex] currentVideo.url = this.iframeUrl+`/view/cameraPlayer/index.html?rtspUrl=${encodeURIComponent(this.playbackOptions.find(item => item.videoId === val).rtspAddress)}` currentVideo.playing = true console.info("å°åï¼"+currentVideo.url) }, resetAllVideos() { this.allVideos.forEach((video, index) => { if (index >= this.gridLayout) { video.playing = false video.url = '' } }) } } } </script> <style scoped> /* ç®ååçæ ·å¼ */ .container { padding-top: 80px; position: relative; } .layout-controls { position: absolute; left: 60px; right: 60px; /* æ°å¢å³ä¾§å¯¹é½ */ top: 20px; z-index: 1000; display: flex; justify-content: space-between; /* å ³é®å±æ§ */ align-items: center; } /* ç§»é¤åæç margin-right */ .el-select { width: 200px; } /* å¯éï¼é²æ¢æé®ç»æ¢è¡ */ .el-button-group { flex-shrink: 0; } .video-col { position: relative; margin-bottom: 5px; transition: all 0.3s; min-height: 100px; } .video-col.active::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border: 3px solid #409EFF; pointer-events: none; z-index: 2; } .video-container { position: relative; width: 100%; height: 0; padding-top: 56.25%; cursor: pointer; background-color: #000; overflow: hidden; } .video-cover { pointer-events: none; /* å 许äºä»¶ç©¿é */ position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #000; /* 纯é»èæ¯ */ } .video-iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #000; pointer-events: none; } </style> src/pages/searchNew/index/App.vue
New file @@ -0,0 +1,131 @@ <template> <div class="column"> <div class="column-right"> <survey-view ref="cardlist"/> </div> </div> </template> <script> import { getUrlKey } from "@/api/utils"; import surveyView from "../components/SurveyView"; export default { name: "VideoManage", components: { surveyView }, computed: { app() { return getUrlKey("dataStack") !== null ? "DataStack" : "Camera"; }, }, data() { return { leftWith: 0, screenHeight: 0, }; }, mounted() { this.screenHeight = document.documentElement.clientHeight; window.onresize = () => { return (() => { this.screenHeight = document.documentElement.clientHeight; })(); }; this.leftWith = this.$refs["left"].offsetWidth; this.TreeDataPool.readonly = false; this.TreeDataPool.gbReadonly = false; this.DataStackPool.readonly = false; }, methods: { changeTrainId(trainId){ if (this.$refs.cardlist) { this.$refs.cardlist.changeTrainId(trainId); } } }, }; </script> <style lang="scss" scoped> .column { overflow: hidden; //min-width: 1399px; //min-width: 1920px; height: 100%; } .column-right { padding: 5px; height: 100vh; // background-color: #eee; box-sizing: border-box; overflow: scroll; } .heigher-index { position: absolute; top: 0; z-index: 10; width: 100%; height: 100%; } .resize-save { position: absolute; top: 0; right: 5px; bottom: 0; left: 0; padding: 16px; padding-top: 8px; overflow-x: hidden; overflow-y: auto; } .resize-bar { width: 338px; height: inherit; resize: horizontal; cursor: ew-resize; opacity: 0; overflow: scroll; max-width: 500px; //è®¾å®æå¤§æä¼¸é¿åº¦ min-width: 33px; //è®¾å®æå°å®½åº¦ } /* ææ½çº¿ */ .resize-line { position: absolute; right: 0; top: 0; bottom: 0; border-right: 2px solid #efefef; border-left: 1px solid #e0e0e0; pointer-events: none; } .resize-bar:hover ~ .resize-line, .resize-bar:active ~ .resize-line { border-left: 1px dashed skyblue; } .resize-bar::-webkit-scrollbar { width: 200px; height: inherit; } /* Firefoxåªæä¸é¢ä¸å°ååºåå¯ä»¥æä¼¸ */ @supports (-moz-user-select: none) { .resize-bar:hover ~ .resize-line, .resize-bar:active ~ .resize-line { border-left: 1px solid #bbb; } .resize-bar:hover ~ .resize-line::after, .resize-bar:active ~ .resize-line::after { content: ""; position: absolute; width: 16px; height: 16px; bottom: 0; right: -8px; // background: url(./resize.svg); background-size: 100% 100%; } } </style> src/pages/searchNew/index/main.ts
New file @@ -0,0 +1,30 @@ import Vue from "vue"; import App from './App.vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; // import "@/assets/css/element-variables.scss"; import ToggleButton from 'vue-js-toggle-button'; import VueAwesomeSwiper from "vue-awesome-swiper"; import "swiper/dist/css/swiper.css"; import * as VueWindow from "@hscmap/vue-window"; import moment from 'moment'; import Mixin from "./mixins"; Vue.prototype.$moment = moment; Vue.use(ElementUI); Vue.use(ToggleButton); Vue.use(VueAwesomeSwiper as any); Vue.use(VueWindow); Vue.filter('moment', function (value, formatString) { formatString = formatString || 'YYYY-MM-DD HH:mm:ss'; return moment(value).format(formatString); }); Vue.mixin(Mixin); new Vue({ el: '#app', render: h => h(App) }) src/pages/searchNew/index/mixins.ts
New file @@ -0,0 +1,25 @@ import TreeDataPool from "@/Pool/TreeData"; import DataStackPool from "@/Pool/dataStack" import DataPool from "@/Pool/PollData" import VideoManageData from "@/Pool/VideoManageData"; import TaskMange from '@/Pool/TaskMange' /* eslint-disable */ const onlyTreeDataPool = new TreeDataPool const onlyDataStack = new DataStackPool const onlyDataPool = new DataPool const onlyVideoManageData = new VideoManageData const onlyTaskMange = new TaskMange const mixin = { data() { return { TreeDataPool: onlyTreeDataPool, DataStackPool: onlyDataStack, VideoManageData: onlyVideoManageData, TaskMange: onlyTaskMange, PollData: onlyDataPool }; }, }; export default mixin;