|
@@ -1,7 +1,7 @@
|
|
|
<template>
|
|
|
<el-dialog :title="title" v-model="dialogFormVisible" @closed="handleClosed">
|
|
|
- <el-form :model="form">
|
|
|
- <el-form-item label="任务名称" :label-width="formLabelWidth">
|
|
|
+ <el-form :model="form" ref="formRef">
|
|
|
+ <el-form-item label="任务名称" prop="name" :label-width="formLabelWidth" required>
|
|
|
<el-input v-model="form.name" autocomplete="off"></el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="任务状态" :label-width="formLabelWidth">
|
|
@@ -57,12 +57,12 @@
|
|
|
<el-input v-model="knowledgeBase.querySearch" size="large" placeholder="搜索"
|
|
|
@keydown.enter="debounceGetKBfileList" :prefix-icon="Search" />
|
|
|
</span>
|
|
|
- <span class="add-file" @click="handleImportFiles(toRaw(knowledgeBase.filesList))">
|
|
|
- <span class="text">全部导入</span>
|
|
|
+ <span class="add-file" @click="handleSelectedImport()">
|
|
|
+ <span class="text">批量导入</span>
|
|
|
</span>
|
|
|
</div>
|
|
|
<el-scrollbar class="management-content-middle">
|
|
|
- <el-table :data="knowledgeBase.filesList">
|
|
|
+ <el-table :data="knowledgeBase.filesList" ref="KBTableRef">
|
|
|
<el-table-column type="selection" width="30" />
|
|
|
<el-table-column label="#" prop="index" width="50" />
|
|
|
<el-table-column prop="file_name" min-width="150" label="标题">
|
|
@@ -123,14 +123,17 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, watch, toRaw, onMounted, computed } from 'vue'
|
|
|
+import { ref, watch, toRaw, onMounted, computed, getCurrentInstance } from 'vue'
|
|
|
import { Search } from '@element-plus/icons-vue'
|
|
|
+import CryptoJS from 'crypto-js';
|
|
|
+import encHex from 'crypto-js/enc-hex'
|
|
|
import { createJob, putQueueJob, getKnowledgeBase, getKnowledgeBaseFilesList } from '@/api/AgentApi'
|
|
|
import axios from 'axios'
|
|
|
import { genFileId, ElMessage } from 'element-plus'
|
|
|
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
|
|
|
import { throttle, debounce } from 'lodash'
|
|
|
-
|
|
|
+const KBTableRef = ref()
|
|
|
+const formRef = ref()
|
|
|
const dialogFormVisible = ref(false)
|
|
|
const formLabelWidth = ref('120px')
|
|
|
|
|
@@ -254,16 +257,17 @@ function fetchFile(fileName: string, fileUrl: string) {
|
|
|
const rawFile = new File([response.data], fileName, { type: response.data.type });
|
|
|
const file = rawFile as UploadRawFile
|
|
|
file.uid = genFileId()
|
|
|
- const fileHashHex = await calculateFileHash(file)
|
|
|
+ // const fileHashHex = await calculateFileHashForCryptoJS(file)
|
|
|
+ // const { md5, sha256 } = await hashFile(file)
|
|
|
let fileExist = false;
|
|
|
- await Promise.allSettled(fileList.value.map(async (it: any) => {
|
|
|
- const itFileHashHex = await calculateFileHash(it.raw);
|
|
|
- if (itFileHashHex === fileHashHex) {
|
|
|
- fileExist = true;
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }))
|
|
|
+ // await Promise.allSettled(fileList.value.map(async (it: any) => {
|
|
|
+ // const itFileHash = await hashFile(it.raw);
|
|
|
+ // if (sha256 === itFileHash.sha256) {
|
|
|
+ // fileExist = true;
|
|
|
+ // return true;
|
|
|
+ // }
|
|
|
+ // return false;
|
|
|
+ // }))
|
|
|
if (fileExist) {
|
|
|
ElMessage({
|
|
|
message: `文件“${fileName}”已存在上传列表中`,
|
|
@@ -288,15 +292,50 @@ function fetchFile(fileName: string, fileUrl: string) {
|
|
|
console.log(error);
|
|
|
});
|
|
|
}
|
|
|
-// 计算文件的hash值
|
|
|
-async function calculateFileHash(file: File) {
|
|
|
+function handleSelectedImport() {
|
|
|
+ const SelectionRows = KBTableRef.value.getSelectionRows()
|
|
|
+ handleImportFiles(toRaw(SelectionRows))
|
|
|
+ // console.log(SelectionRows)
|
|
|
+}
|
|
|
+// 使用原生方法计算文件的hash值,该方法兼容性较差
|
|
|
+async function calculateFileHashForNative(file: File) {
|
|
|
const arrayBuffer = await file.arrayBuffer(); // 将文件读取为 ArrayBuffer
|
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer); // 计算哈希值
|
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer)); // 将哈希转换为字节数组
|
|
|
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join(''); // 转换为十六进制字符串
|
|
|
+ // console.log("calculateFileHashForNative1:", hashHex)
|
|
|
return hashHex;
|
|
|
}
|
|
|
|
|
|
+// 使用crypto-js计算文件的hash值 ,该方法易发生卡顿
|
|
|
+async function calculateFileHashForCryptoJS(file: File) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ try {
|
|
|
+ const reader = new FileReader();
|
|
|
+ reader.readAsArrayBuffer(file)
|
|
|
+ reader.onload = function (e) {
|
|
|
+ const arrayBuffer = e.target?.result; // 读取的文件内容
|
|
|
+ // 将 ArrayBuffer 转换为 CryptoJS 可以处理的 WordArray
|
|
|
+ const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer);
|
|
|
+ // 计算文件的 SHA-256 哈希值并转为 Base64 编码字符串
|
|
|
+ const hashBase64 = CryptoJS.SHA256(wordArray).toString(CryptoJS.enc.Base64);
|
|
|
+ // 计算文件的 SHA-256 哈希值并转为 16 进制字符串
|
|
|
+ const hashHex = CryptoJS.SHA256(wordArray).toString(CryptoJS.enc.hex);
|
|
|
+ // console.log('calculateFileHashForCryptoJS:', hashHex);
|
|
|
+ resolve(hashHex)
|
|
|
+ };
|
|
|
+
|
|
|
+ reader.onerror = () => {
|
|
|
+ reject('')
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e)
|
|
|
+ reject('')
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
function handleImportFiles(filesList: any[]) {
|
|
|
for (let i = 0; i < filesList.length; i++) {
|
|
|
// if (i > 0) break;
|
|
@@ -304,6 +343,51 @@ function handleImportFiles(filesList: any[]) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 用于计算文件的hash值,包括sha256值和md5值
|
|
|
+ */
|
|
|
+function hashFile(file: any) {
|
|
|
+ /**
|
|
|
+ * 使用指定的算法计算hash值
|
|
|
+ */
|
|
|
+ function hashFileInternal(file: any, alog: any) {
|
|
|
+ // 指定块的大小,这里设置为20MB,可以根据实际情况进行配置
|
|
|
+ const chunkSize = 20 * 1024 * 1024
|
|
|
+ let promise: any = Promise.resolve()
|
|
|
+ // 使用promise来串联hash计算的顺序。因为FileReader是在事件中处理文件内容的,必须要通过某种机制来保证update的顺序是文件正确的顺序
|
|
|
+ for (let index = 0; index < file.size; index += chunkSize) {
|
|
|
+ promise = promise.then(() => hashBlob(file.slice(index, index + chunkSize)))
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新文件块的hash值
|
|
|
+ */
|
|
|
+ function hashBlob(blob: any) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const reader = new FileReader()
|
|
|
+ reader.onload = ({ target }) => {
|
|
|
+ const wordArray = CryptoJS.lib.WordArray.create(target?.result)
|
|
|
+ // 增量更新计算结果
|
|
|
+ alog.update(wordArray)
|
|
|
+ resolve(true)
|
|
|
+ }
|
|
|
+ reader.readAsArrayBuffer(blob)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用promise返回最终的计算结果
|
|
|
+ return promise.then(() => encHex.stringify(alog.finalize()))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 同时计算文件的sha256和md5,并使用promise返回
|
|
|
+ return Promise.all([hashFileInternal(file, CryptoJS.algo.SHA256.create()),
|
|
|
+ hashFileInternal(file, CryptoJS.algo.MD5.create())])
|
|
|
+ .then(([sha256, md5]) => ({
|
|
|
+ sha256,
|
|
|
+ md5
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
const handlePreview = (file: any) => {
|
|
|
console.log(file)
|
|
|
}
|
|
@@ -326,19 +410,23 @@ const handleSuccess = (response: any, file: any, fileList: any) => {
|
|
|
}
|
|
|
|
|
|
const handleConfirm = () => {
|
|
|
- // console.log('submit', props)
|
|
|
- form.value.job_category = props.queue_category + "_" + props.queue_name;
|
|
|
- form.value.job_name = form.value.name;
|
|
|
- form.value.queue_category = props.queue_category;
|
|
|
- form.value.queue_name = props.queue_name;
|
|
|
- form.value.job_details = props.job_details;
|
|
|
- form.value.job_creator = props.job_creator;
|
|
|
-
|
|
|
- // console.log('form values ', form.value)
|
|
|
- createJob(form.value).then((res: any) => {
|
|
|
- var job = res.records[0]
|
|
|
- form.value.job_id = job.id;
|
|
|
- upload.value.submit();
|
|
|
+ formRef.value.validate((valid: any) => {
|
|
|
+ if (valid) {
|
|
|
+ // console.log('submit', props)
|
|
|
+ form.value.job_category = props.queue_category + "_" + props.queue_name;
|
|
|
+ form.value.job_name = form.value.name;
|
|
|
+ form.value.queue_category = props.queue_category;
|
|
|
+ form.value.queue_name = props.queue_name;
|
|
|
+ form.value.job_details = props.job_details;
|
|
|
+ form.value.job_creator = props.job_creator;
|
|
|
+
|
|
|
+ // console.log('form values ', form.value)
|
|
|
+ createJob(form.value).then((res: any) => {
|
|
|
+ var job = res.records[0]
|
|
|
+ form.value.job_id = job.id;
|
|
|
+ upload.value.submit();
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
defineExpose({ showDialog })
|