|
|
|
@ -157,34 +157,69 @@ |
|
|
|
:collect-level="collectLevel" |
|
|
|
:category-menu="categoryMenu" |
|
|
|
@close-dialog="closeDialog" |
|
|
|
@handleForm="handleForm" |
|
|
|
@formLoadingShow="formLoadingShow" |
|
|
|
/> |
|
|
|
<div v-if="isAiAutoCategory" style="flex: 1; margin-left: 10px; "> |
|
|
|
<!-- AI辅助著录内容 --> |
|
|
|
<!-- style="display: flex; justify-content: flex-start;" --> |
|
|
|
<!-- <div> --> |
|
|
|
<div class="upload-btn"> |
|
|
|
<input id="upFile" type="file" name="upFile" multiple @change="changeAiFile($event)"> |
|
|
|
<el-button :loading="aiLoading" size="small" type="primary"><i :class="['iconfont', aiLoading ? 'icon-huoqu' : 'icon-shangchuan']" />{{ aiLoading ? 'AI辅助著录识别中' : '选择文件' }}</el-button> |
|
|
|
</div> |
|
|
|
<!-- margin-left: 10px; line-height: 34px; height: 106px; overflow: hidden; overflow-y: scroll; --> |
|
|
|
<div style="flex: 1; padding: 10px 0; font-size: 12px; "> |
|
|
|
<div v-for="item in fileList" :key="item.name" class="file-list"> |
|
|
|
<i class="iconfont icon-xiaowenjian" style="font-size: 14px;" /> |
|
|
|
{{ item.name }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<!-- </div> --> |
|
|
|
<pre v-if="aiJsonData" ref="typingContainer" v-highlightjs="displayedText">{{ displayedText }}</pre> |
|
|
|
<div v-if="isAiAutoCategory" v-loading="aiResultCaLoading" style="flex: 1; margin-left: 10px; "> |
|
|
|
<pre ref="typingContainer" v-highlightjs="displayedText">{{ displayedText }}</pre> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div slot="footer" class="dialog-footer" style="margin-top: 85px !important;"> |
|
|
|
<el-button type="text" style="width: 140px; border-color: #0348f3; color: #0348f3;">保存并新增下一条</el-button> |
|
|
|
<el-button v-if="(isTitleType === 3 && selectedCategory.arrangeType === 1) || isTitleType === 4 || isTitleType === 6 || (isTitleType === 3 && activeIndex === 1)" type="text" style="width: 84px; border-color: #0348f3; color: #0348f3; " @click="handleAiCategory">AI辅助著录</el-button> |
|
|
|
<el-button type="primary" @click="handlerArchivesSubmit">保存</el-button> |
|
|
|
<el-button v-if="formIsAddOrEdit==='add' && ((isTitleType === 3 && selectedCategory.arrangeType === 1) || isTitleType === 4 || isTitleType === 6 || (isTitleType === 3 && activeIndex === 1))" type="text" style="width: 84px; border-color: #0348f3; color: #0348f3; " @click="handleAiCategory">AI辅助著录</el-button> |
|
|
|
<el-button v-if="formIsAddOrEdit==='add'" type="primary" style="width: 140px; " @click="handlerArchivesSubmit(1)">保存并新增下一条</el-button> |
|
|
|
<el-button :loading="archivesBtnLoading" type="primary" @click="handlerArchivesSubmit(0)">保存</el-button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!-- AI辅助著录未处理已解析的文件 --> |
|
|
|
<el-dialog class="aiAssist-dialog" title="AI已解析文件" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="aIAssistEnterVisible"> |
|
|
|
<div class="setting-dialog"> |
|
|
|
<!-- @select="crud.selectChange" |
|
|
|
@select-all="crud.selectAllChange" |
|
|
|
@cell-dblclick="tableDoubleClick" |
|
|
|
@selection-change="crud.selectionChangeHandler" --> |
|
|
|
<el-table |
|
|
|
ref="table" |
|
|
|
v-loading="aiCategoryloading" |
|
|
|
class="archives-table" |
|
|
|
:data="aiCategoryData" |
|
|
|
row-key="id" |
|
|
|
> |
|
|
|
<el-table-column type="selection" width="55" align="center" /> |
|
|
|
<el-table-column label="任务编号" prop="id" /> |
|
|
|
<el-table-column label="文件数量" prop="fileNum" /> |
|
|
|
<el-table-column label="创建人" prop="create_by" /> |
|
|
|
<el-table-column label="创建时间" prop="update_time" align="center" width="160"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<div v-if="scope.row.update_time">{{ scope.row.update_time | parseTime }}</div> |
|
|
|
<div v-else>-</div> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column label="解析完成时间" prop="analysisTime" align="center" width="160"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<div v-if="scope.row.analysisTime">{{ scope.row.analysisTime | parseTime }}</div> |
|
|
|
<div v-else>-</div> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column label="状态" prop="status" align="center" width="140"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<span v-if="!scope.row.isAnalysis" class="row-state row-warehousing state-active">解析中</span> |
|
|
|
<span v-else class="row-state row-binding state-active">已解析</span> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
<el-table-column label="操作" prop="status" align="center" width="140"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<el-button v-if="scope.row.isAnalysis" size="mini" class="check-btn" style="padding: 5px;" @click="getDoHandleEnterAnalysis(scope.row)"> |
|
|
|
<i class="iconfont icon-tianjiawenjian" /> |
|
|
|
新增档案 |
|
|
|
</el-button> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
</div> |
|
|
|
</el-dialog> |
|
|
|
|
|
|
|
<!--卷内移出 组件--> |
|
|
|
<el-dialog class="tip-dialog" title="提示" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="moveVisible"> |
|
|
|
<div class="setting-dialog"> |
|
|
|
@ -229,7 +264,7 @@ |
|
|
|
<QuickPaper ref="quickPaperRef" :selected-category="selectedCategory" :arc-id="arcId" :collect-level="collectLevel" @close-dialog="closeDialog" /> |
|
|
|
|
|
|
|
<!-- 归档 --> |
|
|
|
<ArchivesFilling ref="archivesFillingRef" :selected-category="selectedCategory" :collect-level="collectLevel" :selections="selections" @close-dialog="closeDialog" /> |
|
|
|
<ArchivesFilling ref="archivesFillingRef" :selected-category="selectedCategory" :collect-level="collectLevel" :selections="selections" @close-dialog="closeDialog" @handleFillingToFourTest="handleFillingToFourTest" /> |
|
|
|
|
|
|
|
<!-- 装盒 / 分卷装盒 --> |
|
|
|
<PackingBox ref="packingBox" :selected-category="selectedCategory" :selections="selections" :total-sum-all="totalSumAll" @close-dialog="closeDialog" /> |
|
|
|
@ -247,7 +282,8 @@ |
|
|
|
import CRUD, { crud } from '@crud/crud' |
|
|
|
import { collectionLibraryCrud } from '../mixins/index' |
|
|
|
import { FetchInitCategoryInputFieldByPid, FetchCategoryMenu } from '@/api/system/category/category' |
|
|
|
import { FetchDetailsById, collectDel, FetchRemoveArchivesSingle, FetchDeleteArchivesFile, FetchUpdateArchivesNo, FetchDisbandArchives, FetchReturnReDocument, FetchCompleteDelArchives, FetchRestoreArchives, FetchAIResultZhulu } from '@/api/collect/collect' |
|
|
|
import { FetchDetailsById, collectDel, FetchRemoveArchivesSingle, FetchDeleteArchivesFile, FetchUpdateArchivesNo, FetchDisbandArchives, FetchReturnReDocument, FetchCompleteDelArchives, FetchRestoreArchives } from '@/api/collect/collect' |
|
|
|
import { FetchInitAssistEnter, FetchDoHandleEnterAnalysis } from '@/api/ai/ai' |
|
|
|
import { FetchArchivesClassTree } from '@/api/system/archivesClass' |
|
|
|
import Treeselect from '@riophae/vue-treeselect' |
|
|
|
import '@riophae/vue-treeselect/dist/vue-treeselect.css' |
|
|
|
@ -266,8 +302,7 @@ import QuickPaper from './quickPaper/index' |
|
|
|
import PackingBox from './packingBox/index' |
|
|
|
import ArchivesFilling from './archivesFilling/index' |
|
|
|
import qs from 'qs' |
|
|
|
import { archivesUpload } from '@/utils/upload' |
|
|
|
import { downloadFile, exportFile, getCurrentTime } from '@/utils/index' |
|
|
|
import { downloadFile, exportFile } from '@/utils/index' |
|
|
|
import { mapGetters } from 'vuex' |
|
|
|
|
|
|
|
export default { |
|
|
|
@ -309,6 +344,7 @@ export default { |
|
|
|
inject: ['parentsData'], |
|
|
|
data() { |
|
|
|
return { |
|
|
|
archivesBtnLoading: false, |
|
|
|
activeMenuIndex: '1', |
|
|
|
formVisible: false, |
|
|
|
formTitle: '项目', |
|
|
|
@ -324,15 +360,14 @@ export default { |
|
|
|
totalSumAll: 0, |
|
|
|
categoryMenu: [], |
|
|
|
isAiAutoCategory: false, |
|
|
|
aiLoading: false, |
|
|
|
nowDate: '', |
|
|
|
fileList: [], |
|
|
|
aiJsonData: null, |
|
|
|
displayedText: '', |
|
|
|
typingInterval: null, |
|
|
|
typingFinished: false, |
|
|
|
currentLineIndex: 0, |
|
|
|
shouldContinueFetching: true |
|
|
|
formIsAddOrEdit: '', |
|
|
|
aIAssistEnterVisible: false, |
|
|
|
aiCategoryData: [], |
|
|
|
aiCategoryloading: false, |
|
|
|
aiResultCaLoading: true, // ai分析得内容结果前得loading |
|
|
|
isDialogClosed: false, // 新增标志位,用于控制是否继续处理流式响应 |
|
|
|
reader: null // 用于存储响应体的读取器 |
|
|
|
} |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
@ -489,6 +524,7 @@ export default { |
|
|
|
}, |
|
|
|
// 著录界面-form/详情-api |
|
|
|
handleForm(type, isPaper) { |
|
|
|
this.formIsAddOrEdit = type |
|
|
|
if (type === 'add') { |
|
|
|
if (this.parentsData.parentsProjectId && this.isTitleType === 3) { |
|
|
|
console.log('项目下的案卷') |
|
|
|
@ -530,6 +566,14 @@ export default { |
|
|
|
}) |
|
|
|
}, |
|
|
|
getFormInfo(type) { |
|
|
|
const currentPageSize = localStorage.getItem('currentPageSize') |
|
|
|
if (currentPageSize) { |
|
|
|
this.page.size = parseInt(currentPageSize) |
|
|
|
} else { |
|
|
|
this.page.size = 10 |
|
|
|
} |
|
|
|
console.log('this.page.size', this.page.size) |
|
|
|
console.log('this.page.page', this.page.page) |
|
|
|
if (type === 'edit') { |
|
|
|
const params = { |
|
|
|
'categoryId': this.selectedCategory.id, |
|
|
|
@ -557,38 +601,76 @@ export default { |
|
|
|
FetchInitCategoryInputFieldByPid(params).then(data => { |
|
|
|
this.formPreviewData = data |
|
|
|
this.isDesFormType = 'arcives' |
|
|
|
|
|
|
|
this.$nextTick(() => { |
|
|
|
this.$refs.previewForm.archivesType = 'add' |
|
|
|
this.$refs.previewForm.activeIndex = this.activeIndex |
|
|
|
const savePrevFromData = JSON.parse(localStorage.getItem('savePrevFromData')) |
|
|
|
console.log('savePrevFromData', savePrevFromData) |
|
|
|
if (savePrevFromData) { |
|
|
|
if ('record_no' in savePrevFromData && 'item_no' in savePrevFromData) { |
|
|
|
if (savePrevFromData.item_no && !isNaN(Number(savePrevFromData.item_no))) { |
|
|
|
savePrevFromData.item_no = (parseInt(savePrevFromData.item_no) + 1).toString() |
|
|
|
} |
|
|
|
} else if ('record_no' in savePrevFromData) { |
|
|
|
if (savePrevFromData.record_no && !isNaN(Number(savePrevFromData.record_no))) { |
|
|
|
savePrevFromData.record_no = (parseInt(savePrevFromData.record_no) + 1).toString() |
|
|
|
} |
|
|
|
} else if ('item_no' in savePrevFromData) { |
|
|
|
if (savePrevFromData.item_no && !isNaN(Number(savePrevFromData.item_no))) { |
|
|
|
savePrevFromData.item_no = (parseInt(savePrevFromData.item_no) + 1).toString() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 查找 item_no 或 record_no 字段并进行补零操作 |
|
|
|
const findAndPadField = (fieldName) => { |
|
|
|
const field = this.formPreviewData.find(item => item.fieldName === fieldName) |
|
|
|
if (field && field.isFilling) { |
|
|
|
const fillingDigit = field.fillingDigit |
|
|
|
if (savePrevFromData[fieldName]) { |
|
|
|
savePrevFromData[fieldName] = savePrevFromData[fieldName].toString().padStart(fillingDigit, '0') |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
findAndPadField('item_no') |
|
|
|
findAndPadField('record_no') |
|
|
|
|
|
|
|
this.$refs.previewForm.addOrUpdateForm = savePrevFromData |
|
|
|
} |
|
|
|
this.$refs.previewForm.FetchNoFormatField(this.selectedCategory.id) |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
}, |
|
|
|
// form - submit |
|
|
|
handlerArchivesSubmit() { |
|
|
|
handlerArchivesSubmit(type) { |
|
|
|
this.$refs.previewForm.submitForm('addOrUpdateForm', this.selectedCategory.id, this.quickPaperArcId, type) |
|
|
|
}, |
|
|
|
handlerArchivesSubmitAndAdd() { |
|
|
|
this.$refs.previewForm.submitForm('addOrUpdateForm', this.selectedCategory.id, this.quickPaperArcId) |
|
|
|
}, |
|
|
|
formLoadingShow(loadingType) { |
|
|
|
this.archivesBtnLoading = loadingType |
|
|
|
}, |
|
|
|
// 关闭 |
|
|
|
handleClose(done) { |
|
|
|
this.shouldContinueFetching = false |
|
|
|
this.formVisible = false |
|
|
|
this.quickPaper = false |
|
|
|
|
|
|
|
localStorage.removeItem('savePrevFromData') |
|
|
|
this.aiCategoryData = [] |
|
|
|
this.isAiAutoCategory = false |
|
|
|
this.aiJsonData = null |
|
|
|
this.currentLineIndex = 0 |
|
|
|
this.displayedText = '' |
|
|
|
this.typingFinished = false |
|
|
|
if (this.typingInterval) { |
|
|
|
clearInterval(this.typingInterval) |
|
|
|
this.displayedText = '' // 清空 displayedText |
|
|
|
this.isDialogClosed = true // 设置标志位为 true |
|
|
|
if (this.reader) { |
|
|
|
this.reader.cancel() // 取消读取器,终止流式响应 |
|
|
|
} |
|
|
|
this.fileList = [] |
|
|
|
this.aiLoading = false |
|
|
|
done() |
|
|
|
}, |
|
|
|
// 删除 |
|
|
|
toDelete() { |
|
|
|
console.log('this.page.page333', this.page.page) |
|
|
|
console.log('this.page.total33333', this.page.total) |
|
|
|
if (this.selections.length === 0) { |
|
|
|
this.$message({ message: '您还未勾选需要操作的条目,请先确认!', offset: 8 }) |
|
|
|
return false |
|
|
|
@ -629,6 +711,8 @@ export default { |
|
|
|
FetchCompleteDelArchives(params).then((res) => { |
|
|
|
if (res.code !== 500) { |
|
|
|
this.$message({ message: res, type: 'success', offset: 8 }) |
|
|
|
localStorage.removeItem('currentPageSize') |
|
|
|
localStorage.removeItem('currentPage') |
|
|
|
this.handleSearch(this.collectLevel) |
|
|
|
} else { |
|
|
|
this.$message({ message: '删除所选档案失败', type: 'error', offset: 8 }) |
|
|
|
@ -669,6 +753,8 @@ export default { |
|
|
|
}) |
|
|
|
FetchDeleteArchivesFile(params).then((res) => { |
|
|
|
if (res === 'SUCCESS') { |
|
|
|
localStorage.removeItem('currentPageSize') |
|
|
|
localStorage.removeItem('currentPage') |
|
|
|
this.$message({ message: '删除成功', type: 'success', offset: 8 }) |
|
|
|
this.handleSearch(this.collectLevel) |
|
|
|
} else { |
|
|
|
@ -689,6 +775,8 @@ export default { |
|
|
|
} |
|
|
|
collectDel(params).then((res) => { |
|
|
|
if (res.includes('成功')) { |
|
|
|
localStorage.removeItem('currentPageSize') |
|
|
|
localStorage.removeItem('currentPage') |
|
|
|
this.$message({ message: '删除成功', type: 'success', offset: 8 }) |
|
|
|
this.handleSearch(this.collectLevel) |
|
|
|
} else { |
|
|
|
@ -713,6 +801,8 @@ export default { |
|
|
|
} |
|
|
|
FetchRemoveArchivesSingle(params).then((res) => { |
|
|
|
if (res === true) { |
|
|
|
localStorage.removeItem('currentPageSize') |
|
|
|
localStorage.removeItem('currentPage') |
|
|
|
this.$message({ message: '移出成功', type: 'success', offset: 8 }) |
|
|
|
this.handleSearch(this.collectLevel) |
|
|
|
} else { |
|
|
|
@ -996,6 +1086,12 @@ export default { |
|
|
|
} |
|
|
|
this.$refs.archivesFillingRef.archivesFillingVisible = true |
|
|
|
}, |
|
|
|
handleFillingToFourTest(resultList) { |
|
|
|
this.$refs.fourTestRef.fourTestVisible = true |
|
|
|
this.$refs.fourTestRef.tableData = this.selections |
|
|
|
this.$refs.fourTestRef.updateTableData(resultList, this.selections) |
|
|
|
this.$refs.fourTestRef.isCheck = true |
|
|
|
}, |
|
|
|
// 退回预归档库 |
|
|
|
handleReturn() { |
|
|
|
if (this.selections.length === 0) { |
|
|
|
@ -1170,6 +1266,8 @@ export default { |
|
|
|
// this.$message('所选条目中存在已装盒档案,请勿重复操作!') |
|
|
|
// return false |
|
|
|
// } |
|
|
|
console.log('this.parentsData', this.parentsData) |
|
|
|
console.log('this.parentsData.listCategory', this.parentsData.listCategory.id) |
|
|
|
this.$refs.packingBox.packingVisible = true |
|
|
|
this.$refs.packingBox.isPackingOrPartType = type |
|
|
|
this.$refs.packingBox.packFileCategory = this.parentsData.listCategory |
|
|
|
@ -1226,6 +1324,8 @@ export default { |
|
|
|
} |
|
|
|
FetchRestoreArchives(params).then((res) => { |
|
|
|
if (res.code !== 500) { |
|
|
|
localStorage.removeItem('currentPageSize') |
|
|
|
localStorage.removeItem('currentPage') |
|
|
|
if (res.includes('成功')) { |
|
|
|
this.$message({ message: res, type: 'success', offset: 8 }) |
|
|
|
} else { |
|
|
|
@ -1283,232 +1383,132 @@ export default { |
|
|
|
}) |
|
|
|
}, |
|
|
|
handleAiCategory() { |
|
|
|
this.isAiAutoCategory = !this.isAiAutoCategory |
|
|
|
if (!this.isAiAutoCategory) { |
|
|
|
this.fileList = [] |
|
|
|
this.aIAssistEnterVisible = true |
|
|
|
this.aiCategoryloading = true |
|
|
|
const params = { |
|
|
|
'page': 0, |
|
|
|
'size': 10, |
|
|
|
'isHandle': 0 |
|
|
|
} |
|
|
|
FetchInitAssistEnter(params).then(data => { |
|
|
|
this.aiCategoryData = data |
|
|
|
this.aiCategoryloading = false |
|
|
|
setTimeout(() => { |
|
|
|
this.getDoHandleEnterAnalysis() |
|
|
|
}, 1000) |
|
|
|
}) |
|
|
|
}, |
|
|
|
async changeAiFile(e) { |
|
|
|
// 替换文件时清空 aiJsonData |
|
|
|
this.aiJsonData = null |
|
|
|
this.currentLineIndex = 0 |
|
|
|
this.displayedText = '' |
|
|
|
this.typingFinished = false |
|
|
|
this.typingInterval = null |
|
|
|
// if (this.typingInterval) { |
|
|
|
// clearInterval(this.typingInterval); |
|
|
|
// } |
|
|
|
|
|
|
|
const selectedFiles = Array.from(e.target.files) |
|
|
|
const imageFiles = selectedFiles.filter(file => file.type.startsWith('image/')) |
|
|
|
const nonImageFiles = selectedFiles.filter(file => !file.type.startsWith('image/')) |
|
|
|
|
|
|
|
// 不允许同时选择图片和非图片文件 |
|
|
|
if (imageFiles.length > 0 && nonImageFiles.length > 0) { |
|
|
|
this.$message.error('不能同时选择图片和其他类型文件,请重新选择') |
|
|
|
return |
|
|
|
// 获取自动分析内容和提问 |
|
|
|
getDoHandleEnterAnalysis(row) { |
|
|
|
this.isAiAutoCategory = true |
|
|
|
this.aIAssistEnterVisible = false |
|
|
|
const params = { |
|
|
|
'categoryId': this.selectedCategory.id, |
|
|
|
'anId': null |
|
|
|
} |
|
|
|
FetchDoHandleEnterAnalysis(params).then(data => { |
|
|
|
console.log(data) |
|
|
|
// const inputMessage = data.query + '需要提取得内容部分是' + data.context |
|
|
|
this.sendMessage(data.query, data.context) |
|
|
|
}) |
|
|
|
}, |
|
|
|
// 向deepseek发出提问和分析内容 |
|
|
|
async sendMessage(prompt, context) { |
|
|
|
const linkSrc = process.env.NODE_ENV === 'production' ? window.g.AIDeepSeekUrl : process.env.VUE_APP_AIDEEPSEEK_API |
|
|
|
this.displayedText = '' |
|
|
|
this.isDialogClosed = false // 重置标志位 |
|
|
|
try { |
|
|
|
// { 'role': 'system', 'content': '你是一个数据分析助手' }, |
|
|
|
const messages = [ |
|
|
|
{ 'role': 'user', 'content': `${context}\n\n${prompt}` } |
|
|
|
] |
|
|
|
const response = await fetch(linkSrc + '/api/generate', { |
|
|
|
method: 'POST', |
|
|
|
headers: { |
|
|
|
'Content-Type': 'application/json' |
|
|
|
}, |
|
|
|
body: JSON.stringify({ |
|
|
|
model: 'deepseek-r1:14b', |
|
|
|
// model: 'qwen:7b', |
|
|
|
prompt: messages[0].content, |
|
|
|
stream: true |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
const existingImageFiles = this.fileList.filter(item => item.formatType === 'image') |
|
|
|
const existingNonImageFiles = this.fileList.filter(item => item.formatType !== 'image') |
|
|
|
|
|
|
|
if (imageFiles.length > 0) { |
|
|
|
// if (existingImageFiles.length > 0) { |
|
|
|
// if (existingImageFiles.length + imageFiles.length > 3) { |
|
|
|
// // 若加入新图片会超过 3 张,清空已有图片 |
|
|
|
// this.fileList = this.fileList.filter(item => item.formatType !== 'image') |
|
|
|
// } |
|
|
|
// } else if (existingNonImageFiles.length > 0) { |
|
|
|
// // 若已有非图片文件,清空已有非图片文件 |
|
|
|
// this.fileList = this.fileList.filter(item => item.formatType === 'image') |
|
|
|
// } |
|
|
|
|
|
|
|
// // 检查图片文件数量 |
|
|
|
// if (imageFiles.length > 3) { |
|
|
|
// this.$message.error('图片文件最多只能选择 3 个,请重新选择') |
|
|
|
// return |
|
|
|
// } |
|
|
|
if (existingNonImageFiles.length > 0) { |
|
|
|
// 若已有非图片文件,清空已有非图片文件 |
|
|
|
this.fileList = this.fileList.filter(item => item.formatType === 'image') |
|
|
|
} |
|
|
|
|
|
|
|
for (const file of imageFiles) { |
|
|
|
// 检查文件是否已存在 |
|
|
|
if (this.fileList.some(item => item.name === file.name)) { |
|
|
|
this.$message.warning(`文件 ${file.name} 已存在,请勿重复上传`) |
|
|
|
continue |
|
|
|
} |
|
|
|
|
|
|
|
const fileInfo = { |
|
|
|
file: file, |
|
|
|
size: file.size, |
|
|
|
formatType: file.type.substring(0, file.type.indexOf('/')), |
|
|
|
name: file.name, |
|
|
|
postfix: file.name.substring( |
|
|
|
file.name.lastIndexOf('.') + 1, |
|
|
|
file.name.length |
|
|
|
), |
|
|
|
px: '' |
|
|
|
} |
|
|
|
|
|
|
|
const fileBase64 = await this.getBase64(file) |
|
|
|
const res = await this.getImgPx(fileBase64) |
|
|
|
fileInfo.px = res.width + 'px*' + res.height + 'px' |
|
|
|
|
|
|
|
this.fileList.push(fileInfo) |
|
|
|
} |
|
|
|
this.FetchAiFileUplaod(this.fileList) |
|
|
|
} else if (nonImageFiles.length > 0) { |
|
|
|
if (existingNonImageFiles.length > 0) { |
|
|
|
// 若已有非图片文件,直接替换 |
|
|
|
this.fileList = this.fileList.filter(item => item.formatType === 'image') |
|
|
|
} else if (existingImageFiles.length > 0) { |
|
|
|
// 若已有图片文件,清空已有图片文件 |
|
|
|
this.fileList = this.fileList.filter(item => item.formatType !== 'image') |
|
|
|
if (!response.ok) { |
|
|
|
throw new Error(`HTTP error! status: ${response.status}`) |
|
|
|
} |
|
|
|
|
|
|
|
// 检查非图片文件数量 |
|
|
|
if (nonImageFiles.length > 1) { |
|
|
|
this.$message.error('非图片文件最多只能选择 1 个,请重新选择') |
|
|
|
return |
|
|
|
} |
|
|
|
this.reader = response.body.getReader() // 存储读取器 |
|
|
|
const decoder = new TextDecoder('utf-8') |
|
|
|
let done = false |
|
|
|
|
|
|
|
for (const file of nonImageFiles) { |
|
|
|
// 检查文件是否已存在 |
|
|
|
if (this.fileList.some(item => item.name === file.name)) { |
|
|
|
this.$message.warning(`文件 ${file.name} 已存在,请勿重复上传`) |
|
|
|
continue |
|
|
|
} |
|
|
|
while (!done && !this.isDialogClosed) { // 检查标志位 |
|
|
|
this.aiResultCaLoading = false |
|
|
|
const { done: isDone, value } = await this.reader.read() |
|
|
|
done = isDone |
|
|
|
if (done) break |
|
|
|
|
|
|
|
const fileInfo = { |
|
|
|
file: file, |
|
|
|
size: file.size, |
|
|
|
formatType: file.type.substring(0, file.type.indexOf('/')), |
|
|
|
name: file.name, |
|
|
|
postfix: file.name.substring( |
|
|
|
file.name.lastIndexOf('.') + 1, |
|
|
|
file.name.length |
|
|
|
), |
|
|
|
px: '' |
|
|
|
} |
|
|
|
this.fileList.push(fileInfo) |
|
|
|
} |
|
|
|
this.FetchAiFileUplaod(this.fileList) |
|
|
|
} |
|
|
|
}, |
|
|
|
FetchAiFileUplaod(files) { |
|
|
|
this.aiLoading = true |
|
|
|
this.nowDate = getCurrentTime() |
|
|
|
const promiseArray = files.map(async(item, index) => { |
|
|
|
const json = {} |
|
|
|
json.file_name = item.name |
|
|
|
json.file_size = item.size |
|
|
|
json.file_type = item.postfix |
|
|
|
json.file_path = '' |
|
|
|
json.archive_id = this.arcId |
|
|
|
json.file_dpi = item.px |
|
|
|
json.file_thumbnail = '' |
|
|
|
json.create_time = this.nowDate |
|
|
|
json.id = null |
|
|
|
json.last_modified = item.file.lastModified |
|
|
|
return json |
|
|
|
}) |
|
|
|
console.log('promiseArray', promiseArray) |
|
|
|
const fileDefault = files.map(item => item.file) |
|
|
|
const chunk = decoder.decode(value) |
|
|
|
const lines = chunk.split('\n') |
|
|
|
|
|
|
|
Promise.all(promiseArray) |
|
|
|
.then((arrayUpload) => { |
|
|
|
archivesUpload(this.baseApi + '/api/collect/uploadAssistEnterFiles', |
|
|
|
fileDefault, |
|
|
|
this.selectedCategory.id, |
|
|
|
this.arcId, |
|
|
|
JSON.stringify(arrayUpload) |
|
|
|
).then(res => { |
|
|
|
if (res.data.data !== null) { |
|
|
|
const params = { |
|
|
|
'code': res.data.data.code, |
|
|
|
'id': res.data.data.id |
|
|
|
lines.forEach(line => { |
|
|
|
if (line.trim() !== '') { |
|
|
|
try { |
|
|
|
const data = JSON.parse(line) |
|
|
|
if (data.response && !this.isDialogClosed) { // 再次检查标志位 |
|
|
|
this.displayedText += data.response |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('解析JSON数据出错:', error) |
|
|
|
} |
|
|
|
const fetchAiZhuluResult = () => { |
|
|
|
// 检查是否应该继续请求 |
|
|
|
if (!this.shouldContinueFetching) return |
|
|
|
|
|
|
|
FetchAIResultZhulu(params).then((res) => { |
|
|
|
const data = JSON.parse(res) |
|
|
|
console.log('data', data) |
|
|
|
if (data.result !== null && data.status === 'success') { |
|
|
|
this.$message({ message: '解析著录附件成功', type: 'success', offset: 8 }) |
|
|
|
this.aiJsonData = data.result |
|
|
|
this.startTypingEffect() |
|
|
|
} else { |
|
|
|
setTimeout(fetchAiZhuluResult, 3000) |
|
|
|
} |
|
|
|
}).catch(err => { |
|
|
|
console.log(err) |
|
|
|
}) |
|
|
|
} |
|
|
|
fetchAiZhuluResult() |
|
|
|
} else { |
|
|
|
this.$message({ message: '著录附件传输失败', type: 'error', offset: 8 }) |
|
|
|
} |
|
|
|
}) |
|
|
|
}) |
|
|
|
.catch((error) => { |
|
|
|
console.error(error) |
|
|
|
}) |
|
|
|
}, |
|
|
|
startTypingEffect() { |
|
|
|
const lines = this.aiJsonData.split('\n') |
|
|
|
this.currentLineIndex = 0 |
|
|
|
this.displayedText = '' |
|
|
|
this.typingFinished = false |
|
|
|
|
|
|
|
this.typingInterval = setInterval(() => { |
|
|
|
if (this.currentLineIndex < lines.length) { |
|
|
|
this.displayedText += lines[this.currentLineIndex] + '\n' |
|
|
|
this.currentLineIndex++ |
|
|
|
} else { |
|
|
|
clearInterval(this.typingInterval) |
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
this.typingFinished = true |
|
|
|
if (this.aiJsonData) { |
|
|
|
// this.$refs.previewForm.archivesType = 'add' |
|
|
|
// this.$refs.previewForm.addOrUpdateForm = JSON.parse(this.aiJsonData) |
|
|
|
this.aiLoading = false |
|
|
|
} |
|
|
|
}, 1000) |
|
|
|
// 滚动条始终保持在底部 |
|
|
|
const container = this.$refs.typingContainer |
|
|
|
if (container) { |
|
|
|
container.scrollTop = container.scrollHeight |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('请求出错:', error) |
|
|
|
this.displayedText = '请求出错,请稍后再试。' |
|
|
|
this.aiResultCaLoading = false |
|
|
|
} finally { |
|
|
|
this.aiResultCaLoading = false |
|
|
|
if (!this.isDialogClosed) { |
|
|
|
console.log('this.displayedText.', this.displayedText) |
|
|
|
|
|
|
|
// 滚动条始终保持在底部 |
|
|
|
const container = this.$refs.typingContainer |
|
|
|
if (container) { |
|
|
|
container.scrollTop = container.scrollHeight |
|
|
|
} |
|
|
|
}, 200) |
|
|
|
}, |
|
|
|
// 将上传的图片转为base64 |
|
|
|
getBase64(file) { |
|
|
|
const reader = new FileReader() |
|
|
|
reader.readAsDataURL(file) |
|
|
|
return new Promise((resolve) => { |
|
|
|
reader.onload = () => { |
|
|
|
resolve(reader.result) |
|
|
|
// 去除 <think> 和 </think> 之间的内容 |
|
|
|
const thinkStartIndex = this.displayedText.indexOf('<think>') |
|
|
|
const thinkEndIndex = this.displayedText.indexOf('</think>') |
|
|
|
let lastContent = this.displayedText |
|
|
|
if (thinkStartIndex !== -1 && thinkEndIndex !== -1) { |
|
|
|
lastContent = lastContent.slice(0, thinkStartIndex) + lastContent.slice(thinkEndIndex + '</think>'.length) |
|
|
|
} |
|
|
|
console.log('lastContent', lastContent) |
|
|
|
// 提取 JSON 部分 |
|
|
|
const startIndex = lastContent.indexOf('{') |
|
|
|
const endIndex = lastContent.lastIndexOf('}') |
|
|
|
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) { |
|
|
|
const jsonStr = lastContent.slice(startIndex, endIndex + 1) |
|
|
|
console.log('jsonStr', jsonStr) |
|
|
|
const jsonData = JSON.parse(jsonStr) |
|
|
|
console.log('提取并过滤后的 JSON 数据:', jsonData) |
|
|
|
console.log('this.selectedCategory', this.selectedCategory) |
|
|
|
jsonData.fonds_no = this.selectedCategory.fondsNo |
|
|
|
jsonData.archival_category_code = this.selectedCategory.code |
|
|
|
jsonData.is_entity = 1 |
|
|
|
console.log('jsonData', jsonData) |
|
|
|
this.$refs.previewForm.addOrUpdateForm = jsonData |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
this.reader = null // 清空读取器 |
|
|
|
} |
|
|
|
}, |
|
|
|
// 获取图片的分辨率 |
|
|
|
getImgPx(img) { |
|
|
|
const image = new Image() |
|
|
|
image.src = img |
|
|
|
return new Promise((resolve) => { |
|
|
|
image.onload = () => { |
|
|
|
const width = image.width |
|
|
|
const height = image.height |
|
|
|
resolve({ width, height }) |
|
|
|
} |
|
|
|
}) |
|
|
|
replaceSeparators(str) { |
|
|
|
return str.replace(/[,,、]/g, ' ') |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -1553,6 +1553,11 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
.aiAssist-dialog{ |
|
|
|
::v-deep .el-dialog{ |
|
|
|
width: 1000px !important; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pre { |
|
|
|
background-color: #f4f4f4; |
|
|
|
@ -1561,7 +1566,7 @@ pre { |
|
|
|
border-radius: 4px; |
|
|
|
white-space: pre-wrap; |
|
|
|
word-wrap: break-word; |
|
|
|
height: calc(100vh - 430px); |
|
|
|
height: calc(100vh - 330px); |
|
|
|
overflow: hidden; |
|
|
|
overflow-y: auto; |
|
|
|
} |
|
|
|
|