Browse Source

AI智能标注/bug修复

master
xuhuajiao 4 months ago
parent
commit
7b05727888
  1. 439
      src/views/AIAssistant/AIKeywords/archivesList.vue
  2. 2
      src/views/AIAssistant/AIKeywords/index.vue
  3. 210
      src/views/AIAssistant/AIKeywords/module/editOcrContent.vue
  4. 256
      src/views/AIAssistant/AIKeywords/module/keywordMark.vue
  5. 6
      src/views/collectReorganizi/collectionLibrary/module/uploadOriginal/bigUpload.vue
  6. 2
      src/views/login.vue

439
src/views/AIAssistant/AIKeywords/archivesList.vue

@ -3,13 +3,33 @@
<div class="ai-keyword-archives">
<div class="collect-header">
<h4 class="is-juannei">{{ collectTitle }} </h4>
<div class="head-search" style="align-items: center;">
<treeselect
v-model="query.archive_ctg_no"
:options="classifyOptions"
style="width: 180px; margin-right: 10px;"
flat
:multiple="false"
:normalizer="normalizer"
placeholder="请选择档案分类"
@input="handleSearch(collectLevel)"
@select="handleSearch(collectLevel)"
>
<p
slot="option-label"
slot-scope="{node}"
style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis; width: 100%; padding-left: 10px;"
:title="node.label"
>
<template> {{ node.label }} </template>
</p>
</treeselect>
<el-input v-model="query.search" clearable size="small" placeholder="输入题名或档号" prefix-icon="el-icon-search" style="width: 200px;" class="filter-item" @keyup.enter.native="handleSearch(collectLevel)" @clear="handleSearch(collectLevel)" />
<el-button class="filter-item filter-search" size="mini" type="success" icon="el-icon-search" @click="handleSearch(collectLevel)">搜索</el-button>
<el-button class="filter-item filter-refresh" size="mini" type="warning" icon="el-icon-refresh-left" @click="resetQuery">重置</el-button>
</div>
</div>
<div class="collect-table">
<!-- @select-all="selectAll"
@selection-change="crud.selectionChangeHandler"
@row-click="clickRowHandler"
@cell-dblclick="tableDoubleClick"
@select="handleCurrentChange" -->
<el-table
ref="table"
v-loading="loading"
@ -21,7 +41,6 @@
:row-class-name="tableRowClassName"
:row-key="rowKey"
>
<!-- <el-table-column type="selection" :reserve-selection="true" width="55" align="center" /> -->
<el-table-column label="序号" width="55" align="center" show-overflow-tooltip>
<template slot-scope="scope">
<span>{{ (page.page === 1 ? 0 : (page.page === 0 ? 0 : page.page - 1)) * page.size + scope.$index + 1 }}</span>
@ -47,14 +66,12 @@
{{ scope.row[field.fieldName] }}
</template>
</el-table-column>
<!-- :fixed="parentsData.fixedStatusBar ? false : 'right' " -->
<el-table-column label="装盒" width="88" align="center">
<template slot-scope="scope">
<!-- 未装 / 已装 -->
<span :class="['row-state', 'row-packing', scope.row.case_no ? 'state-active' : '' ]">{{ scope.row.case_no ? '已装': '未装' }}</span>
</template>
</el-table-column>
<!-- :fixed="parentsData.fixedStatusBar ? false : 'right' " -->
<el-table-column label="审批锁定" width="88" align="center">
<template slot-scope="scope">
<span :class="['row-state', 'row-warehousing', scope.row.collect_formal===2 ? 'state-active' : '' ]">{{ scope.row.collect_formal===2 ? '归档': '空闲' }}</span>
@ -76,19 +93,14 @@
<div class="ai-keyword-file">
<div class="collect-header">
<h4 class="is-file">原文 </h4>
<el-button size="mini" type="warning"><i class="iconfont icon-wenjianjiexi" />批量解析</el-button>
<el-button style="margin-right: 20px;" class="filter-item filter-refresh" size="mini" type="warning" icon="el-icon-refresh-left">刷新</el-button>
<!-- archiveNo -->
<h4 class="is-file">原文 <span style="font-size:12px; margin-left: 10px;">所属文件YXK-2022-JJ-001-001</span></h4>
<el-button size="mini" type="warning" :disabled="fileSelections.length === 0" @click="batchAIOcrFile"><i class="iconfont icon-wenjianjiexi" />批量解析</el-button>
<el-button style="margin-right: 20px;" class="filter-item filter-refresh" size="mini" type="warning" icon="el-icon-refresh-left" @click="refreshFile">刷新</el-button>
</div>
<div class="collect-table">
<!--
@select-all="selectAll"
@selection-change="crud.selectionChangeHandler"
@row-click="clickRowHandler"
@cell-dblclick="tableDoubleClick"
@select="handleCurrentChange" -->
<el-table
ref="table"
ref="tableFile"
v-loading="fileLoading"
class="archives-table"
:data="fileData"
@ -97,6 +109,9 @@
height="calc(100vh - 690px)"
:row-class-name="tableRowClassName"
:row-key="rowKey"
@selection-change="selectionChangeHandlerFile"
@row-click="clickRowHandlerFile"
@cell-dblclick="tableDoubleClickFile"
>
<el-table-column type="selection" width="55" :reserve-selection="true" align="center" />
<el-table-column label="序号" width="55" align="center" show-overflow-tooltip>
@ -111,65 +126,37 @@
{{ getFileSize(scope.row.file_size) }}
</template>
</el-table-column>
<el-table-column
prop="file_type"
label="解析状态"
min-width="60"
align="center"
class-name="parse-column"
>
<!-- slot-scope="scope" -->
<template>
<!-- <span class="row-state row-warehousing state-active">解析中</span> -->
<!-- <span class="row-state row-binding state-active">已解析</span> -->
<span class="row-state">未解析</span>
<el-table-column prop="file_state" label="解析状态" min-width="60" align="center" class-name="parse-column">
<template slot-scope="scope">
<span v-if="scope.row.file_state===0" class="row-state">未解析</span>
<span v-if="scope.row.file_state===1" class="row-state row-warehousing state-active">解析中</span>
<span v-if="scope.row.file_state===2" class="row-state row-binding state-active">已解析</span>
</template>
</el-table-column>
<el-table-column
prop="file_type"
label="解析操作"
min-width="60"
align="center"
class-name="parse-column"
>
<!-- slot-scope="scope" -->
<template>
<!-- <el-button size="mini" class="check-btn" style="padding: 5px; margin: 0;">
<i class="iconfont icon-bianji" />
编辑
</el-button> -->
<el-button size="mini" class="check-btn" style="padding: 5px; margin: 0;">
<el-table-column prop="file_state" label="解析操作" min-width="60" align="center" class-name="parse-column">
<template slot-scope="scope">
<el-button v-if="scope.row.file_state===0" size="mini" class="check-btn" style="padding: 5px; margin: 0;" @click="handleCurrentAIOcr(scope.row)">
<i class="iconfont icon-wenjianjiexi" />
解析
</el-button>
<el-button v-if="scope.row.file_state===2" size="mini" class="check-btn" style="padding: 5px; margin: 0;" @click="handleEditAIOcr(scope.row)">
<i class="iconfont icon-bianji" />
编辑
</el-button>
</template>
</el-table-column>
<el-table-column
prop="file_type"
label="标注状态"
min-width="60"
align="center"
class-name="mark-column"
>
<!-- slot-scope="scope" -->
<template>
<!-- <span class="row-state row-binding state-active">已标注</span> -->
<span class="row-state">未标注</span>
<el-table-column prop="file_mark_state" label="标注状态" min-width="60" align="center" class-name="mark-column">
<template slot-scope="scope">
<span v-if="scope.row.file_mark_state===0" class="row-state">未标注</span>
<span v-if="scope.row.file_mark_state===1" class="row-state row-binding state-active">已标注</span>
</template>
</el-table-column>
<el-table-column
prop="file_type"
label="标注操作"
min-width="60"
align="center"
class-name="mark-column"
>
<!-- slot-scope="scope" -->
<template>
<!-- <el-button size="mini" class="check-btn" style="padding: 5px; margin: 0;">
<el-table-column prop="file_mark_state" label="标注操作" min-width="60" align="center" class-name="mark-column">
<template slot-scope="scope">
<el-button v-if="scope.row.file_state===2" size="mini" class="check-btn" style="padding: 5px; margin: 0;" @click="handleMarkKeyword(scope.row)">
<i class="iconfont icon-bianji" />
编辑
</el-button> -->
{{ scope.row.file_mark_state===1 ? '编辑' :'标注' }}
</el-button>
</template>
</el-table-column>
</el-table>
@ -186,18 +173,23 @@
/> -->
</div>
</div>
<!-- 档案详情 -->
<!-- <ArchivesInfo ref="archivesInfo" :selected-category="selectedCategory" :arc-id="arcId" :is-title-type="isTitleType" :is-collect="true" /> -->
<EditOcrContent ref="ocrRefs" />
<KeywordMark ref="markRefs" />
</div>
</template>
<script>
import { header, form } from '@crud/crud'
// FetchInitCategoryView
import { FetchInitCategoryViewTable } from '@/api/collect/collect'
import { FetchInitCategoryViewTable, FetchInitCategoryView } from '@/api/collect/collect'
import { FetchArchivesClassTree } from '@/api/system/archivesClass'
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import { mapGetters } from 'vuex'
import EditOcrContent from './module/editOcrContent'
import KeywordMark from './module/keywordMark'
export default {
name: 'Sorted',
components: { },
components: { EditOcrContent, KeywordMark, Treeselect },
mixins: [
header(),
form({})
@ -212,6 +204,12 @@ export default {
},
data() {
return {
query: {
search: null,
project_class: null,
archive_ctg_no: null
},
classifyOptions: [],
loading: false,
arrySort: null,
tableDisplayFields: [],
@ -222,20 +220,80 @@ export default {
total: 0
},
fileLoading: false,
fileData: [{}, {}],
fileData: [
{
'create_time': '2025-3-6 10:29:43',
'file_name': 'DAT 18-2022 档案著录规则_02.png',
'file_type': 'png',
'file_dpi': '1653px*2339px',
'id': '6D48990DF88B21EB7609EB',
'archive_id': 'EC7D3E9798DE5DD7A6DFC0',
'file_size': 436989,
'file_state': 0,
'file_mark_state': 0
},
{
'create_time': '2025-3-6 10:29:43',
'file_name': 'DAT 18-2022 档案著录规则_04.png',
'file_type': 'png',
'file_dpi': '1653px*2339px',
'id': '7A699E8D7FB01E3D7C1653',
'archive_id': 'EC7D3E9798DE5DD7A6DFC0',
'file_size': 512453,
'file_state': 1,
'file_mark_state': 0
},
{
'create_time': '2025-3-6 10:29:43',
'file_name': 'DAT 18-2022 档案著录规则_00.png',
'file_type': 'png',
'file_dpi': '1653px*2339px',
'id': '8A1F52E855304609020EB3',
'archive_id': 'EC7D3E9798DE5DD7A6DFC0',
'file_size': 289748,
'file_state': 2,
'file_mark_state': 0
},
{
'create_time': '2025-3-6 10:29:43',
'file_name': 'DAT 18-2022 档案著录规则_01.png',
'file_type': 'png',
'file_dpi': '1653px*2339px',
'id': 'A20895478FDC988B7EA6D5',
'archive_id': 'EC7D3E9798DE5DD7A6DFC0',
'file_size': 686235,
'file_state': 2,
'file_mark_state': 0
},
{
'create_time': '2025-3-6 10:29:43',
'file_name': 'DAT 18-2022 档案著录规则_03.png',
'file_type': 'png',
'file_dpi': '1653px*2339px',
'id': 'D594A10A3C78F3A5EB17B9',
'archive_id': 'EC7D3E9798DE5DD7A6DFC0',
'file_size': 466450,
'file_state': 2,
'file_mark_state': 1
}
],
filePage: {
page: 1,
size: 10,
total: 0
},
fileSelections: [],
arcId: '',
title: '',
selections: [],
collectLevel: 3
}
},
computed: {
...mapGetters([
'baseApi',
'user'
]),
collectTitle() {
if (this.selectedCategory.arrangeType === 1) {
return '文件'
@ -249,9 +307,45 @@ export default {
created() {
},
mounted() {
},
methods: {
getInitArchivesClass() {
this.classifyOptions = []
this.query = {
'search': null,
'project_class': null,
'archive_ctg_no': null
}
const params = {
'categoryId': this.selectedCategory.id
}
FetchArchivesClassTree(params).then((res) => {
this.classifyOptions = JSON.parse(JSON.stringify(res))
}).catch(err => {
console.log(err)
})
},
normalizer(node) {
if (node.childArchivesClass === null) {
delete node.childArchivesClass
}
return {
id: node.code,
label: `${node.name} - ${node.code}`,
children: node.childArchivesClass
}
},
resetQuery() {
this.query = {
'search': null,
'project_class': null,
'archive_ctg_no': null
}
this.handleSearch(this.collectLevel)
},
handleSearch(collectLevel) {
},
rowKey(row) {
return row.id
},
@ -265,35 +359,6 @@ export default {
})
return color
},
handleSizeChange(size) {
this.page.size = size
this.page.page = 1
// if (this.activeIndex === 1) {
// this.getViewTable(3, null)
// } else {
// if (this.selectedCategory.arrangeType === 3) {
// this.getViewTable(2, this.parentsData.parentsProjectId)
// } else if (this.selectedCategory.arrangeType === 1) {
// this.getViewTable(3, null)
// } else {
// this.getViewTable(2, null)
// }
// }
},
handleCurrentPage(val) {
this.page.page = val
// if (this.activeIndex === 1) {
// this.getViewTable(3, null)
// } else {
// if (this.selectedCategory.arrangeType === 3) {
// this.getViewTable(2, this.parentsData.parentsProjectId)
// } else if (this.selectedCategory.arrangeType === 1) {
// this.getViewTable(3, null)
// } else {
// this.getViewTable(2, null)
// }
// }
},
getViewTable() {
this.tableDisplayFields = []
FetchInitCategoryViewTable({ categoryId: this.selectedCategory.id, categoryLevel: 3 }).then((res) => {
@ -306,46 +371,116 @@ export default {
this.arrySort.push(item.fieldName + ',' + item.displayOrderBy)
}
})
// this.getAiFileList(this.selections[0].id)
this.$nextTick(() => {
this.getAiFileList()
})
}
})
},
// async getAiFileList(id) {
// const parentsId = id
async getAiFileList() {
this.loading = true
this.archivesTable = []
const params = {
'categoryId': this.selectedCategory.id,
'categoryLevel': 3,
'parentId': null,
'ignore': true,
'isdel': false,
'page': this.page.page === 0 ? 0 : this.page.page - 1,
'size': this.page.size,
'sort': this.arrySort,
'retention': null,
'security_class': null,
'search': this.query.search,
'medium_type': null,
'doc_type': null,
'archive_year': null,
'organ_or_function': null,
'fonds_no': this.selectedCategory.fondsId,
'project_class': this.query.project_class,
'archive_ctg_no': this.query.archive_ctg_no
}
const res = await FetchInitCategoryView(params)
this.archivesTable = res.list.content
this.page.total = res.list.totalElements
this.loading = false
},
handleSizeChange(size) {
this.page.size = size
this.page.page = 1
this.getAiFileList()
},
handleCurrentPage(val) {
this.page.page = val
this.getAiFileList()
},
//
refreshFile() {
},
// table -
tableDoubleClickFile(row) {
},
// table - row
clickRowHandlerFile(row) {
this.$refs.tableFile.toggleRowSelection(row)
},
selectionChangeHandlerFile(val) {
this.fileSelections = val
},
//
batchAIOcrFile() {
console.log('fileSelections', this.fileSelections)
this.$confirm('当前所选文件即将进行批量解析' + '<span>你是否还要继续?</span>', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
//
const filteredFiles = this.fileSelections.filter(file => file.file_state !== 2)
console.log(filteredFiles)
// const params = {
// 'categoryId': this.selectedCategory.id,
// 'categoryLevel': 3,
// 'parentId': parentsId,
// 'ignore': false,
// 'isdel': false,
// 'page': 0,
// 'size': 100,
// 'sort': this.arrySort,
// 'queryType': null,
// 'queryTitle': null,
// 'itemNo': null,
// 'archiveCtgNo': null,
// 'responsibleby': null,
// 'archiveNo': null,
// 'archiveYear': null,
// 'department': null,
// 'retention': null,
// 'securityClass': null,
// 'organizationMatter': null
// 'archivesIds': archivesIds
// }
// const data = await FetchInitCategoryView(params)
// if (data) {
// if (this.isPackingOrPartType === 1 && this.selectedCategory.arrangeType === 2) {
// this.archivesTable = data.list.content
// this.packingData = data.list.content
// FetchDisbandArchives(params).then((res) => {
// if (res.code !== 500) {
// this.$message({ message: '', type: 'success', offset: 8 })
// this.handleSearch(this.collectLevel)
// } else {
// data.list.content.forEach(item => {
// item.archivesParentsId = id
// this.$message({ message: '', type: 'error', offset: 8 })
// }
// }).catch(err => {
// console.log(err)
// })
// return data.list.content
}).catch(() => {
})
},
//
handleCurrentAIOcr(row) {
// const params = {
// 'categoryId': this.selectedCategory.id,
// 'archivesIds': archivesIds
// }
// FetchDisbandArchives(params).then((res) => {
// if (res.code !== 500) {
// this.$message({ message: '', type: 'success', offset: 8 })
// this.handleSearch(this.collectLevel)
// } else {
// this.$message({ message: '', type: 'error', offset: 8 })
// }
// },
// }).catch(err => {
// console.log(err)
// })
},
handleEditAIOcr(row) {
this.$refs.ocrRefs.ocrVisible = true
},
handleMarkKeyword(row) {
this.$refs.markRefs.markVisible = true
},
handleFileSizeChange(size) {
this.filePage.size = size
this.filePage.page = 1
@ -394,17 +529,43 @@ export default {
.ai-keyword-file{
height: calc(100% / 2);
}
// ---------------------------------------------------------
.el-pagination{
margin: 10px 0 !important;
}
::v-deep .parse-column {
color: red;
::v-deep .vue-treeselect__list-item{
width: 220px;
}
::v-deep .mark-column {
color: green;
::v-deep .vue-treeselect__menu {
padding: 20px 0;
}
::v-deep .vue-treeselect__option--highlight{
background: #f5f9fc !important;
color: #0348f3 !important;
}
::v-deep .vue-treeselect__option-arrow-container .vue-treeselect__option-arrow{
color: #1c1c1c !important;
}
::v-deep .vue-treeselect__placeholder{
font-size: 14px;
}
::v-deep .vue-treeselect__label-container{
font-size: 14px;
height: 32px;
line-height: 32px;
color: #1c1c1c;
}
::v-deep .vue-treeselect__option.vue-treeselect__option--disabled{
.vue-treeselect__label-container{
color: #545b65 !important;
}
}
::v-deep.vue-treeselect--has-value .vue-treeselect__single-value{
font-size: 14px;
}
::v-deep .el-table {
.el-table__header-wrapper th.el-table__cell.parse-column,
.el-table__header th.el-table__cell.parse-column,

2
src/views/AIAssistant/AIKeywords/index.vue

@ -126,7 +126,9 @@ export default {
localStorage.setItem('currentArchivesKey', JSON.stringify(val))
this.selectedCategory = val
this.$nextTick(() => {
this.$refs.archivesListRef.page.page = 1
this.$refs.archivesListRef.getViewTable()
this.$refs.archivesListRef.getInitArchivesClass()
})
}
},

210
src/views/AIAssistant/AIKeywords/module/editOcrContent.vue

@ -0,0 +1,210 @@
<template>
<!-- 编辑解析 -->
<el-dialog class="aiFile-dialog" title="编辑解析" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="ocrVisible" :before-close="handleCloseDialog">
<div class="setting-dialog">
<swiper
ref="mySwiper"
class="swiper-server"
:options="swiperOptionServer"
:auto-update="true"
:auto-destroy="true"
:delete-instance-on-destroy="true"
:cleanup-styles-on-destroy="true"
>
<swiper-slide v-for="(item, index) in swiperImg" :key="index" class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/4.png">
</div>
<div
class="aiText"
:class="{ editable: editingIndex === index }"
contenteditable="false"
@click="startEditing(index)"
@blur="saveEditing(index)"
v-html="item.textWithBr"
/>
</swiper-slide>
<div slot="pagination" class="swiper-pagination" />
</swiper>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="handleCloseDialog">取消</el-button>
<el-button type="primary" @click.native="handleComfiredMark">保存</el-button>
</div>
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
export default {
name: 'EditOcrContent',
components: { swiper, swiperSlide },
props: {},
data() {
return {
editingIndex: -1,
ocrLoading: false,
ocrVisible: false,
displayedText: '',
isTxtEditing: new Array(3).fill(false),
swiperImg: [
{
'file_path': '~@/assets/images/test/1.png',
'ocr_path': 'D:/ProjectsFTZN/Python/Project_AI_dangan/service_AI_archives_fileParsing/files\\img\\ocr_1.jpg',
'content': 'ICS 01.140.30\nA13\nGB\n中华人民共和国国家标准\nGB/T7408-2005/ISO8601:2000\n代替GB/T7408-1994\n数据元和交换格式\n信息交换\n日期和时间表示法\nDataelementsandinterchangeformats-\nInformation interchange-Representation of dates and times\n(ISO8601:2000,IDT)\n2005-03-28发布\n2005-10-01实施\n中华人民共和国国家质量监督检验检疫总局\n发布\n中国国家标准化管理委员会'
},
{
'file_path': '~@/assets/images/test/2.png',
'ocr_path': 'D:/ProjectsFTZN/Python/Project_AI_dangan/service_AI_archives_fileParsing/files\\img\\ocr_2.jpg',
'content': 'GB/T7408--2005/ISO8601:2000\n目\n次\n前言\n1范围\n2规范性引用文件\n3术语和定义\n4基本原则\n5表示法\n....\nX\n附录A(资料性附录)举例\n20\nA.1日期(见表A.1)\n20\nA.2\n日的时间(见表A.2)\n20\nA.3\n日期和该日的时间的组合(见表A.3)\n21\nA.4\n时间间隔(见表A.4)\n22\nA.5\n循环时间间隔(见表A.5)\n22'
},
{
'file_path': '~@/assets/images/test/3.png',
'ocr_path': 'D:/ProjectsFTZN/Python/Project_AI_dangan/service_AI_archives_fileParsing/files\\img\\ocr_3.jpg',
'content': 'GB/T7408-2005/ISO8601:2000\n前言\n本标准等同采用国际标准ISO8601一2000《数据元和交换格式信息交换日期和时间表示法》\n(英文版),并且代替GB/T7408一1994《数据元和交换格式信息交换日期和时间表示法》。\n本标准与GB/T7408-1994相比主要变化如下:\n增加日期和时间历法系统(本版的4.3);\n一一完善1994年版本的日期和时间的各种表示法(本版本的5.1,5.2,5.3,5.4);\n一增加时间间隔和循环时间间隔的表示法(本版本的5.5,5.6)。\n本标准的附录A为资料性附录。\n本标准由中国标准化研究院提出。\n本标准由全国电子业务标准化技术委员会归口。\n本标准起草单位:中国标准化研究院。\n本标准主要起草人:章建方、刘碧松、魏宏、孙文峰、刘颖。'
}
],
swiperOptionServer: {
autoplay: false,
allowTouchMove: false,
pagination: {
el: '.swiper-pagination',
clickable: true, //
bulletActiveClass: 'swiper-pagination-bullet-active-custom', //
bulletClass: 'swiper-pagination-bullet-custom',
renderBullet: function(index, className) {
return '<span class="' + className + '">' + (index + 1) + '</span>'
}
}
}
}
},
computed: {
...mapGetters([
'baseApi'
]),
swiper() {
return this.$refs.mySwiper.swiper
},
processedSwiperImg() {
return this.swiperImg.map(item => {
const textWithBr = item.content.replace(/\n/g, '<br>')
return { ...item, textWithBr }
})
}
},
watch: {
swiperImg: {
deep: true,
handler() {
this.processedSwiperImg
}
}
},
created() {
this.swiperImg = this.processedSwiperImg
},
mounted() {
},
methods: {
startEditing(index) {
this.editingIndex = index
const slide = this.$el.querySelectorAll('.swiper-slide-server')[index]
const editableDiv = slide.querySelector('div[contenteditable]')
editableDiv.setAttribute('contenteditable', 'true')
},
saveEditing(index) {
const slide = this.$el.querySelectorAll('.swiper-slide-server')[index]
const editableDiv = slide.querySelector('div[contenteditable]')
const editedHtml = editableDiv.innerHTML
// <br> \n
let editedText = editedHtml.replace(/<br>/g, '\n')
editedText = editedText.trim()
this.swiperImg[index].content = editedText
this.swiperImg[index].textWithBr = editedText.replace(/\n/g, '<br>')
this.editingIndex = -1
editableDiv.setAttribute('contenteditable', 'false')
},
handleCloseDialog() {
this.ocrVisible = false
},
handleComfiredMark() {
//
console.log('保存数据:', this.swiperImg)
}
}
}
</script>
<style>
.swiper-container{
padding-bottom: 40px;
}
.swiper-pagination-fraction,
.swiper-pagination-custom, .swiper-container-horizontal > .swiper-pagination-bullets{
bottom: 0;
}
.swiper-pagination-bullet-custom {
display: inline-block;
width: 20px;
height: 20px;
line-height: 20px;
background-color: rgba(0, 0, 0, 0.2);
opacity: 1;
margin: 0 5px;
border-radius: 50%;
color: #000;
cursor: pointer;
}
.swiper-pagination-bullet-active-custom {
background-color: #ff6600;
}
</style>
<style lang='scss' scoped>
.aiFile-dialog {
::v-deep .el-dialog {
width: 1400px !important;
.el-dialog__body{
padding: 10px 0 0 0 !important;
}
}
::v-deep .swiper-server {
.swiper-slide-server {
display: flex;
justify-content: space-between;
height: 700px;
.aiImg {
width: 800px;
overflow: hidden;
overflow-y: scroll;
margin-right: 20px;
img {
display: block;
width: 100%;
}
}
.aiText {
flex: 1;
padding: 0 10px;
white-space: pre-wrap;
word-wrap: break-word;
font-family: monospace;
background-color: #f4f4f4;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
height: calc(100vh - 245px);
overflow: hidden;
overflow-y: auto;
}
}
}
}
.dialog-footer{
margin-top: 0;
}
</style>

256
src/views/AIAssistant/AIKeywords/module/keywordMark.vue

@ -0,0 +1,256 @@
<template>
<!-- 编辑标注 -->
<el-dialog class="aiMark-dialog" title="编辑标注" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="markVisible" :before-close="handleCloseDialog">
<div class="setting-dialog">
<div style="display: flex; justify-content: space-between;">
<div v-loading="markLoading" style="width: calc(55%);">
<pre ref="typingContainer" v-highlightjs="displayedText">{{ displayedText }}</pre>
<div style="text-align: right; margin-top: 10px;">
<el-button type="primary" @click="handleAllPick"><i class="iconfont icon-huoqu" />AI智能提取</el-button>
</div>
</div>
<div class="mark-list" style="width: calc(45%); margin-left: 20px; ">
<div class="mark-ai-list">
<h3 class="item-title">AI提取标注</h3>
<el-button size="mini" class="check-btn btn-add-all" @click="handleAllAdd"><i class="iconfont icon-liuchengfaqi" />一键加入</el-button>
<div style="height:280px; padding: 0 10px; overflow: hidden; overflow-y: scroll; background-color: #f0f9eb;">
<div v-for="(item, index) in aiPickData" :key="index" class="keywords-list">
<div class="keywords-item ai-item-text">
<el-checkbox v-model="selectedItems[index]" @change="handleCheckboxChange(index)" />
<div class="keywords-text">
<el-tooltip class="item" effect="dark" :content="item" :enterable="false" placement="top">
<p>{{ item }}</p>
</el-tooltip>
</div>
<div class="keywords-handle">
<i class="el-icon-circle-plus" @click="handleSingleAdd(index)" />
</div>
</div>
</div>
</div>
</div>
<div class="manual-add">
<el-input v-model="manualVal" type="text" placeholder="可手动添加关键词" />
<el-button @click="handleManualAdd">手动加入</el-button>
</div>
<div class="marked-list">
<h3 class="item-title">已标注关键词</h3>
<div style="height: 260px; padding: 0 10px; overflow: hidden; overflow-y: scroll; background-color: #f0f9eb;">
<div v-for="(item, index) in keywords" :key="index" class="keywords-list">
<div class="keywords-item">
<div v-if="isEditing[index]" class="keywords-input">
<el-input v-model="keywords[index]" style="width: 320px;" @blur="handleBlur(index)" />
</div>
<div v-else class="keywords-text">
<el-tooltip class="item" effect="dark" :content="item" :enterable="false" placement="left">
<p>{{ item }}</p>
</el-tooltip>
</div>
</div>
<div class="keywords-handle">
<i class="iconfont icon-shanchu" @click="handleDelete(index)" />
<i class="iconfont icon-bianji" @click="handleEdit(index)" />
</div>
</div>
</div>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="handleCloseDialog">取消</el-button>
<el-button type="primary" @click.native="handleComfiredMark">保存</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'EditMark',
components: { },
mixins: [],
props: {
},
data() {
return {
markLoading: false,
markVisible: false,
displayedText: '',
keywords: ['永久', '武汉飞天', '2024'],
isEditing: [],
aiPickData: ['数字人', '多媒体技术在档案信息存贮与检索中的应用中的应用中的应用中的应用', '2024'],
selectedItems: [],
manualVal: ''
}
},
computed: {
...mapGetters([
'baseApi'
])
},
created() {
},
mounted() {
},
methods: {
handleAllPick() {
},
handleEdit(index) {
this.$set(this.isEditing, index, !this.isEditing[index])
},
handleBlur(index) {
this.$set(this.isEditing, index, false)
},
handleDelete(index) {
this.keywords.splice(index, 1)
this.isEditing.splice(index, 1)
},
handleCheckboxChange(index) {
const allSelectedItems = this.aiPickData.filter((_, i) => this.selectedItems[i])
console.log('所有选中项:', allSelectedItems)
},
handleComfiredMark() {
},
handleCloseDialog() {
this.markVisible = false
},
// AI
handleAllAdd() {
let itemsToAdd = []
//
if (this.selectedItems.some(item => item)) {
itemsToAdd = this.aiPickData.filter((_, i) => this.selectedItems[i])
} else {
itemsToAdd = this.aiPickData
}
// keywords
const newKeywords = itemsToAdd.filter(item => !this.keywords.includes(item))
this.keywords = [...this.keywords, ...newKeywords]
// aiPickData
this.aiPickData = this.aiPickData.filter(item => !newKeywords.includes(item))
//
this.selectedItems = this.selectedItems.map(() => false)
},
// AI
handleSingleAdd(index) {
const item = this.aiPickData[index]
if (this.keywords.includes(item)) {
this.$message({ message: '已标注关键词列表已有相关关键词,不可重复加入', type: 'warning', offset: 8 })
} else {
this.keywords.push(item)
this.aiPickData.splice(index, 1)
}
},
//
handleManualAdd() {
const keyword = this.manualVal.trim()
if (keyword) {
if (this.keywords.includes(keyword)) {
this.$message({ message: '已标注关键词列表已有相关关键词,不可重复加入', type: 'warning', offset: 8 })
} else {
this.keywords.push(keyword)
this.manualVal = ''
}
}
}
}
}
</script>
<style lang='scss' scoped>
.aiMark-dialog{
::v-deep .el-dialog{
width: 1100px !important;
}
}
.mark-list{
.mark-ai-list{
position: relative;
.btn-add-all{
position: absolute;
right: 0;
top: 12px;
}
}
.manual-add{
display:flex;
justify-content: space-between;
margin: 20px 0 0 0;
.el-button{
margin-left: 10px;
}
}
}
.item-title{
position: relative;
padding: 18px 0 18px 15px;
font-size: 16px;
color: #0C0E1E;
&::before{
position: absolute;
left: 0;
top: 50%;
content: "";
width: 3px;
height: 16px;
background-color: #0348F3;
transform: translateY(-50%);
}
}
.keywords-list{
display: flex;
justify-content: space-between;
width: 100%;
line-height: 30px;
border-bottom: 1px solid #e6e8ed;
.keywords-text {
flex: 1;
p{
// width: 400px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
}
.ai-item-text{
display: flex;
justify-content: space-between;
width: 100%;
p{
margin-left: 10px;
}
.keywords-handle{
font-size: 16px;
}
}
}
.keywords-item{
flex: 1;
}
pre {
background-color: #f4f4f4;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
white-space: pre-wrap;
word-wrap: break-word;
height: calc(100vh - 264px);
overflow: hidden;
overflow-y: auto;
}
.dialog-footer{
margin-top: 20px;
}
</style>

6
src/views/collectReorganizi/collectionLibrary/module/uploadOriginal/bigUpload.vue

@ -79,7 +79,7 @@ export default {
uploadBigVisible: false,
skip: false,
options: {
target: '/api/collect/upload',
target: null,
//
testChunks: true, //
singleFile: true, //
@ -128,6 +128,9 @@ export default {
'baseApi'
])
},
mounted() {
this.options.target = this.baseApi + '/api/collect/upload'
},
methods: {
fileSuccess(rootFile, file, response, chunk) {
this.chunkOffset = []
@ -199,6 +202,7 @@ export default {
if (item.completed && this.submitted) {
this.btnLoading = true
this.submitted = false
axios.post(this.baseApi + '/api/collect/merge', json, { headers: {
'Authorization': getToken()
}}).then((res) => {

2
src/views/login.vue

@ -86,7 +86,7 @@ export default {
codeUrl: '',
cookiePass: '',
loginForm: {
username: 'admin',
username: '',
password: '',
rememberMe: false,
code: '',

Loading…
Cancel
Save