Browse Source

AI辅助著录最终联调/AI智能标注页面制作/bug修复

master
xuhuajiao 4 months ago
parent
commit
0d109ae7fa
  1. 1
      .env.development
  2. 4
      .env.production
  3. 4
      public/static/config.js
  4. 10
      src/assets/iconfonts/light/iconfont.css
  5. 2
      src/assets/iconfonts/light/iconfont.js
  6. 7
      src/assets/iconfonts/light/iconfont.json
  7. BIN
      src/assets/iconfonts/light/iconfont.ttf
  8. BIN
      src/assets/iconfonts/light/iconfont.woff
  9. BIN
      src/assets/iconfonts/light/iconfont.woff2
  10. 53
      src/views/AIAssistant/AICataloging/running/index.vue
  11. 442
      src/views/AIAssistant/AIKeywords/archivesList.vue
  12. 768
      src/views/AIAssistant/AIKeywords/index copy.vue
  13. 559
      src/views/AIAssistant/AIKeywords/index.vue
  14. 6
      src/views/collectReorganizi/collectionLibrary/module/blukEditing/index.vue
  15. 8
      src/views/collectReorganizi/collectionLibrary/module/bulkImport/index.vue
  16. 6
      src/views/collectReorganizi/collectionLibrary/module/collectHeader.vue
  17. 2
      src/views/collectReorganizi/collectionLibrary/module/handleInfo/index.vue
  18. 17
      src/views/components/category/PreviewForm.vue

1
.env.development

@ -5,6 +5,7 @@ ENV = 'development'
# 许镇-本地服地址
VUE_APP_BASE_API = 'http://192.168.99.72:11100'
VUE_APP_AIDEEPSEEK_API = 'http://192.168.99.86:11434'
# VUE_APP_BASE_API = 'http://192.168.99.71:11110'
# VUE_APP_BASE_API = 'http://192.168.99.107:11100'

4
.env.production

@ -2,11 +2,11 @@ ENV = 'production'
# 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置
# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http
VUE_APP_BASE_API = 'http://192.168.99.107:11100'
# VUE_APP_BASE_API = 'http://192.168.99.107:11100'
VUE_APP_AIDEEPSEEK_API = 'http://192.168.99.86:11434'
# VUE_APP_BASE_API = 'http://27.19.215.77:11100'
# VUE_APP_BASE_API = 'http://27.16.212.58:11100'
# VUE_APP_BASE_API = 'http://192.168.99.71:11110'
VUE_APP_BASE_API = 'http://192.168.99.71:11110'
# 如果接口是 http 形式, wss 需要改为 ws
VUE_APP_WS_API = 'ws://27.16.212.58:11110'
VUE_APP_CAMERA_API = '192.168.99.107:3000'

4
public/static/config.js

@ -1,8 +1,8 @@
window.g = {
AXIOS_TIMEOUT: 10000,
// ApiUrl: 'http://27.16.212.58:11100', // 配置服务器地址,
// ApiUrl: 'http://192.168.99.71:11110',
ApiUrl: 'http://192.168.99.107:11100',
ApiUrl: 'http://192.168.99.71:11110',
// ApiUrl: 'http://192.168.99.107:11100',
AIDeepSeekUrl:'http://192.168.99.86:11434',
// ParentPage: { // 后续看需求配置
// CrossDomainProxyUrl: '/Home/CrossDomainProxy',

10
src/assets/iconfonts/light/iconfont.css

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3966148 */
src: url('iconfont.woff2?t=1741163523495') format('woff2'),
url('iconfont.woff?t=1741163523495') format('woff'),
url('iconfont.ttf?t=1741163523495') format('truetype');
src: url('iconfont.woff2?t=1741250796308') format('woff2'),
url('iconfont.woff?t=1741250796308') format('woff'),
url('iconfont.ttf?t=1741250796308') format('truetype');
}
.iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-wenjianjiexi:before {
content: "\e82f";
}
.icon-shendusikao:before {
content: "\e876";
}

2
src/assets/iconfonts/light/iconfont.js
File diff suppressed because it is too large
View File

7
src/assets/iconfonts/light/iconfont.json

@ -5,6 +5,13 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "40591561",
"name": "文件解析",
"font_class": "wenjianjiexi",
"unicode": "e82f",
"unicode_decimal": 59439
},
{
"icon_id": "43375318",
"name": "深度思考",

BIN
src/assets/iconfonts/light/iconfont.ttf

BIN
src/assets/iconfonts/light/iconfont.woff

BIN
src/assets/iconfonts/light/iconfont.woff2

53
src/views/AIAssistant/AICataloging/running/index.vue

@ -293,7 +293,8 @@ export default {
},
computed: {
...mapGetters([
'baseApi'
'baseApi',
'user'
])
// collectLevel() {
// if (this.isTitleType === 2) {
@ -477,6 +478,7 @@ export default {
fileDefault,
JSON.stringify(arrayUpload)
).then(res => {
console.log('res.data.data', res.data.data)
if (res.data.data !== null) {
this.$message({ message: '著录附件传输成功', type: 'success', offset: 8 })
this.crud.toQuery()
@ -484,6 +486,9 @@ export default {
this.$message({ message: '著录附件传输失败', type: 'error', offset: 8 })
}
this.handleClose()
}).catch(() => {
this.$message({ message: '著录附件传输失败', type: 'error', offset: 8 })
this.handleClose()
})
})
.catch((error) => {
@ -636,16 +641,20 @@ export default {
}
FetchDoHandleEnterAnalysis(params).then(data => {
console.log(data)
const inputMessage = data.query + '需要提取得内容部分是' + data.context
this.sendMessage(inputMessage)
// const inputMessage = data.query + '' + data.context
this.sendMessage(data.query, data.context)
})
},
// deepseek
async sendMessage(inputMessage) {
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: {
@ -654,7 +663,7 @@ export default {
body: JSON.stringify({
model: 'deepseek-r1:14b',
// model: 'qwen:7b',
prompt: inputMessage,
prompt: messages[0].content,
stream: true
})
})
@ -703,11 +712,20 @@ export default {
this.aiResultCaLoading = false
if (!this.isDialogClosed) {
console.log('this.displayedText.', this.displayedText)
// <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 = this.displayedText.indexOf('{')
const endIndex = this.displayedText.lastIndexOf('}')
const startIndex = lastContent.indexOf('{')
const endIndex = lastContent.lastIndexOf('}')
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
const jsonStr = this.displayedText.slice(startIndex, endIndex + 1)
const jsonStr = lastContent.slice(startIndex, endIndex + 1)
console.log('jsonStr', jsonStr)
const jsonData = JSON.parse(jsonStr)
console.log('提取并过滤后的 JSON 数据:', jsonData)
@ -721,13 +739,29 @@ export default {
this.reader = null //
}
},
replaceSeparators(str) {
return str.replace(/[,,、]/g, ' ')
},
//
handlerArchivesSubmit() {
console.log(this.$refs.previewForm.addOrUpdateForm)
// this.$refs.previewForm.submitForm('addOrUpdateForm', this.selectedCategory.id, this.quickPaperArcId)
// const newFormData = {}
// for (const key in this.$refs.previewForm.addOrUpdateForm) {
// if (this.$refs.previewForm.addOrUpdateForm.hasOwnProperty(key)) {
// const value = this.$refs.previewForm.addOrUpdateForm[key]
// if (typeof value === 'string' && (value.includes(',') || value.includes('') || value.includes(''))) {
// newFormData[key] = this.replaceSeparators(value)
// } else {
// newFormData[key] = value
// }
// }
// }
// console.log('newFormData', newFormData)
this.$refs.previewForm.$refs.addOrUpdateForm.validate((valid) => {
if (valid) {
const params = {
'fondsAffiliation': this.user.fonds.id.toString(),
'categoryId': this.aiAddArchiveCategory.id,
'assistEnterId': this.currentAnId.id,
'jsonString': JSON.stringify(this.$refs.previewForm.addOrUpdateForm)
@ -736,6 +770,7 @@ export default {
console.log('data', data)
this.$message({ message: data, type: 'success', offset: 8 })
this.crud.toQuery()
this.handleClose()
})
} else {
console.log('error submit!!')

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

@ -0,0 +1,442 @@
<template>
<div style="display: flex; justify-content: flex-start; flex-direction: column;">
<div class="ai-keyword-archives">
<div class="collect-header">
<h4 class="is-juannei">{{ collectTitle }} </h4>
</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"
class="archives-table"
:data="archivesTable"
highlight-current-row
style="width: 100%;"
height="calc(100vh - 600px)"
: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>
</template>
</el-table-column>
<el-table-column :label="selectedCategory.arrangeType === 1 ? '原文':'卷内'" prop="child" width="55" align="center">
<template slot-scope="scope">
{{ scope.row.child === '' ? 0 : scope.row.child }}
</template>
</el-table-column>
<el-table-column v-for="field in tableDisplayFields" :key="field.id" :label="field.fieldCnName" :align="field.displayformatType" :width="field.displayLength" show-overflow-tooltip>
<template slot="header">
<el-tooltip
class="item"
effect="dark"
:content="field.fieldCnName"
placement="top-start"
>
<span>{{ field.fieldCnName }}</span>
</el-tooltip>
</template>
<template slot-scope="scope">
{{ 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>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<el-pagination
:current-page="page.page"
:total="page.total"
:page-size="page.size"
:pager-count="5"
layout="total, prev, pager, next, sizes"
@size-change="handleSizeChange"
@current-change="handleCurrentPage"
/>
</div>
</div>
<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>
</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="fileLoading"
class="archives-table"
:data="fileData"
highlight-current-row
style="width: 100%;"
height="calc(100vh - 690px)"
:row-class-name="tableRowClassName"
:row-key="rowKey"
>
<el-table-column type="selection" width="55" :reserve-selection="true" 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>
</template>
</el-table-column>
<el-table-column prop="file_name" label="原文名称" show-overflow-tooltip min-width="140" />
<el-table-column prop="file_type" label="格式" min-width="60" align="center" />
<el-table-column prop="file_size" label="大小" min-width="85" align="center">
<template slot-scope="scope">
{{ 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>
</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;">
<i class="iconfont icon-wenjianjiexi" />
解析
</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>
</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;">
<i class="iconfont icon-bianji" />
编辑
</el-button> -->
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<!-- <el-pagination
v-if="fileData.length !== 0"
:current-page="filePage.page"
:total="filePage.total"
:page-size="filePage.size"
:pager-count="5"
layout="total, prev, pager, next, sizes"
@size-change="handleFileSizeChange"
@current-change="handleFileCurrentPage"
/> -->
</div>
</div>
<!-- 档案详情 -->
<!-- <ArchivesInfo ref="archivesInfo" :selected-category="selectedCategory" :arc-id="arcId" :is-title-type="isTitleType" :is-collect="true" /> -->
</div>
</template>
<script>
import { header, form } from '@crud/crud'
// FetchInitCategoryView
import { FetchInitCategoryViewTable } from '@/api/collect/collect'
export default {
name: 'Sorted',
components: { },
mixins: [
header(),
form({})
],
props: {
selectedCategory: {
type: Object,
default: function() {
return {}
}
}
},
data() {
return {
loading: false,
arrySort: null,
tableDisplayFields: [],
archivesTable: [],
page: {
page: 1,
size: 10,
total: 0
},
fileLoading: false,
fileData: [{}, {}],
filePage: {
page: 1,
size: 10,
total: 0
},
arcId: '',
title: '',
selections: [],
collectLevel: 3
}
},
computed: {
collectTitle() {
if (this.selectedCategory.arrangeType === 1) {
return '文件'
} else {
return '卷内'
}
}
},
watch: {
},
created() {
},
mounted() {
},
methods: {
rowKey(row) {
return row.id
},
// table
tableRowClassName({ row, rowIndex }) {
let color = ''
this.selections.forEach(item => {
if (item.id === row.id) {
color = 'rowStyle'
}
})
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) => {
if (res) {
this.arrySort = []
this.tableDisplayFields = res
const orderSortArry = this.tableDisplayFields.filter(item => item.queue).sort((a, b) => a.queue - b.queue)
orderSortArry.forEach(item => {
if (item.displayOrderBy) {
this.arrySort.push(item.fieldName + ',' + item.displayOrderBy)
}
})
// this.getAiFileList(this.selections[0].id)
}
})
},
// async getAiFileList(id) {
// const parentsId = id
// 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
// }
// 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
// } else {
// data.list.content.forEach(item => {
// item.archivesParentsId = id
// })
// return data.list.content
// }
// }
// },
handleFileSizeChange(size) {
this.filePage.size = size
this.filePage.page = 1
},
handleFileCurrentPage(val) {
this.filePage.page = val
},
getFileSize(fileSize) {
if (fileSize) {
const fileSizeInKB = (fileSize / 1024).toFixed(2) + 'kB'
const fileSizeInB = fileSize + 'B'
return (fileSize / 1024) <= 0.01 ? fileSizeInB : fileSizeInKB
} else {
return '-'
}
}
}
}
</script>
<style lang='scss' scoped>
@import "~@/assets/styles/collect-reorganizi.scss";
@mixin management-fixed-style{
[data-theme="dark"] & {
background-color: #031435 !important;
-webkit-box-shadow: -5px 5px 10px 1px rgba(15,164,222,.16);
box-shadow: -5px 5px 10px 1px rgba(15,164,222,.16);
}
[data-theme="light"] & {
background-color: #fff;
}
}
.el-table {
::v-deep .el-table__fixed-right {
@include management-fixed-style;
}
}
.collect-header{
padding: 0 10px 0 20px;
border-top: none;
align-items: center;
}
.ai-keyword-archives,
.ai-keyword-file{
height: calc(100% / 2);
}
.el-pagination{
margin: 10px 0 !important;
}
::v-deep .parse-column {
color: red;
}
::v-deep .mark-column {
color: green;
}
::v-deep .el-table {
.el-table__header-wrapper th.el-table__cell.parse-column,
.el-table__header th.el-table__cell.parse-column,
.el-table__body-wrapper td.el-table__cell.parse-column,
.el-table__fixed-right td.el-table__cell.parse-column{
background-color: oldlace !important;
}
.el-table__header-wrapper th.el-table__cell.mark-column,
.el-table__header th.el-table__cell.mark-column,
.el-table__body-wrapper td.el-table__cell.mark-column,
.el-table__fixed-right td.el-table__cell.mark-column{
background-color: #f0f9eb !important;
}
.el-table__body tr.el-table__row:hover>td.el-table__cell.parse-column,
.el-table__body tr.el-table__row:focus>td.el-table__cell.parse-column,
.el-table__body tr.current-row>td.el-table__cell.parse-column,
.el-table__body tr.current-row:hover>td.el-table__cell.parse-column,
.el-table__body tr.current-row:focus>td.el-table__cell.parse-column{
background-color: #eaf3fb !important;
color: #545b65!important;
}
.el-table__body tr.el-table__row:hover>td.el-table__cell.mark-column,
.el-table__body tr.el-table__row:focus>td.el-table__cell.mark-column,
.el-table__body tr.current-row>td.el-table__cell.mark-column,
.el-table__body tr.current-row:hover>td.el-table__cell.mark-column,
.el-table__body tr.current-row:focus>td.el-table__cell.mark-column{
background-color: #eaf3fb !important;
color: #545b65!important;
}
}
</style>

768
src/views/AIAssistant/AIKeywords/index copy.vue

@ -0,0 +1,768 @@
<template>
<div class="app-container category-container" style="height: calc(100vh - 140px);">
<!-- 门类列表 -->
<div class="container-main">
<div class="elect-cont-left">
<div class="container-left">
<span class="right-top-line" />
<span class="left-bottom-line" />
<!--门类树状结构-->
<div class="tree-scroll">
<el-scrollbar style="height: calc(100vh - 230px);">
<el-tree ref="categroyTree" v-loading="crud.loading" class="arc-tree arc-tree-01" :data="crud.data" :props="defaultProps" node-key="id" :expand-on-click-node="false" highlight-current @node-click="handleNodeClick">
<span slot-scope="{ node, data }" class="custom-tree-node">
<el-tooltip :content="node.label" placement="left" :enterable="false" effect="dark">
<span v-if="data.isType === 0">
{{ data.label }}
</span>
<span v-if="data.isType === 1" class="iconFolder tree-text">
{{ data.label }}
</span>
<span v-if="data.isType === 2" class="iconArch tree-text">
{{ data.label }}
</span>
<span v-if="data.isType === 3" class="iconFile tree-text">
{{ data.label }}
</span>
</el-tooltip>
</span>
</el-tree>
</el-scrollbar>
</div>
</div>
</div>
<div class="elect-cont-right">
<div v-if="selectedCategory.isType === 2" class="container-right">
<span class="right-top-line" />
<span class="left-bottom-line" />
<div class="ai-file-pickUp">
<div class="ai-file-all">
<div class="des-title">
<p>原文列表</p>
</div>
<el-table
ref="table"
:data="tableData"
style="min-width: 100%"
class="archives-table"
height="calc(100vh - 220px)"
@row-click="clickRowHandler"
>
<el-table-column type="selection" align="center" width="55" />
<!-- <el-table-column type="index" label="序号" width="55" align="center" /> -->
<el-table-column prop="file_name" label="文件名称" show-overflow-tooltip min-width="120" />
<el-table-column prop="file_type" label="格式" min-width="60" align="center">
<template slot-scope="scope">
<div v-if="scope.row.file_type === 'jpg' || scope.row.file_type === 'jpeg' || scope.row.file_type === 'png' || scope.row.file_type === 'bmp'|| scope.row.file_type === 'gif'">
<i class="fileIcon icon-image" />
</div>
<div v-else-if="scope.row.file_type === 'xlsx' || scope.row.file_type === 'xls'">
<i class="fileIcon icon-excel" />
</div>
<div v-else-if="scope.row.file_type === 'docx' || scope.row.file_type === 'doc'">
<i class="fileIcon icon-word" />
</div>
<div v-else-if="scope.row.file_type === 'pdf'">
<i class="fileIcon icon-pdf" />
</div>
<div v-else-if="scope.row.file_type === 'ppt' || scope.row.file_type === 'pptx'">
<i class="fileIcon icon-ppt" />
</div>
<div v-else-if="scope.row.file_type === 'zip' || scope.row.file_type === 'rar'">
<i class="fileIcon icon-zip" />
</div>
<div v-else-if="scope.row.file_type === 'txt'">
<i class="fileIcon icon-txt" />
</div>
<div v-else>
<i class="fileIcon icon-other" />
</div>
</template>
</el-table-column>
<el-table-column prop="file_status" label="是否解析" width="100" align="center">
<template slot-scope="scope">
<span v-if="scope.row.file_status === 0" class="row-state row-physical ">未解析</span>
<span v-else class="row-state row-binding state-active">已解析</span>
</template>
</el-table-column>
<el-table-column prop="file_status" label="是否标注" width="100" align="center">
<template slot-scope="scope">
<span v-if="scope.row.file_status === 0" class="row-state row-physical ">未标注</span>
<span v-else class="row-state row-binding state-active">已标注</span>
</template>
</el-table-column>
<!-- <el-table-column prop="file_status" label="操作" align="center" width="80">
<template slot-scope="scope">
<div class="handle-btn">
<el-button class="iconfont icon-huoqu" @click.stop="handlePickUp(scope.row)" />
</div>
</template>
</el-table-column> -->
</el-table>
</div>
<div class="pickUp-right">
<div class="pickUp-right-item">
<div class="des-title">
<p>已标注关键词</p>
</div>
<div v-if="currentRow && currentRow.file_status===1" style="height: calc(100% - 70px); overflow-y: auto;">
<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">
<!-- <p>{{ item }}</p> -->
<el-tooltip class="item" effect="dark" :content="item" :enterable="false" placement="top">
<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>
<el-empty v-else description="暂无标注的数据" />
</div>
<div :key="reloadKey" class="pickUp-Ai">
<div class="des-title">
<p>AI关键词标注</p>
<el-button @click="batchAiText">批量标注</el-button>
</div>
<div v-if="isPickUp " v-loading="aiLoadingPick" style="height: calc(100% - 70px); overflow-y: auto;">
<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>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<el-dialog class="aiFile-dialog" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :before-close="handleClose" :visible="aiFileVisible" title="文件内容">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<!-- <div class="ai-file-content" /> -->
<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 class="swiper-slide-server">
<div class="aiImg">
<img src="@/assets/images/test/1.png">
</div>
<div class="aiText">
<div v-if="isTxtEditing">
<textarea ref="textareaRef" v-model="text" style="height: 620px; width: calc(100% - 20px); padding: 10px; border: 1px solid #545b65; line-height: 26px; outline: none; resize: none;" @blur="toggleEdit(false)" />
</div>
<div v-else>
<p style=" height: 640px; overflow-y: scroll; line-height: 26px;" @click="toggleEdit(true)">{{ text }}</p>
</div>
</div>
</swiper-slide>
<swiper-slide class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/2.png">
</div>
<div class="aiText">
dddd
</div>
</swiper-slide>
<swiper-slide class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/3.png">
</div>
<div class="aiText">
dddd<br>
</div>
</swiper-slide>
<swiper-slide class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/4.png">
</div>
<div class="aiText">
444
</div>
</swiper-slide>
<div slot="button-prev" class="swiper-button-prev" />
<div slot="button-next" class="swiper-button-next" />
</swiper>
<div slot="footer" class="dialog-footer">
<!-- :loading="crud.status.cu === 2" -->
<el-button type="text" @click="aiFileVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditAi">确定</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import crudCategory from '@/api/system/category/category'
import CRUD, { presenter, header } from '@crud/crud'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
const data = [
{
'file_path': '/category//09A6ECD967BCC772DABB2F.txt',
'sequence': '',
'create_time': '2024-02-05 11:43:26',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '09A6ECD967BCC772DABB2F',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 1
},
{
'file_path': '/category//0EFEF1CB577453482ADE5F.txt',
'sequence': '',
'create_time': '2024-02-05 11:43:26',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000202.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '0EFEF1CB577453482ADE5F',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category/AE8B188F0C0314F9BE31B8/82FBCAE96CBC9F50809838/eb824e27-13eb-4f33-adc8-39ca5926d244.txt',
'sequence': '',
'create_time': '2024-3-20 16:16:32',
'file_name': '123.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': '',
'id': '3D49BD844334621851DF24',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 37679,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category/AE8B188F0C0314F9BE31B8/82FBCAE96CBC9F50809838/4B457990A874D55714FF8C.pdf',
'sequence': '',
'create_time': '2024-3-20 11:36:24',
'file_name': 'DAT 48-2009 基于XML的电子文件封装规范.pdf',
'file_type': 'pdf',
'document_file_id': '',
'file_dpi': '',
'id': '4B457990A874D55714FF8C',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 92308284,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//6867E751998B41C3B89FA3.txt',
'sequence': '',
'create_time': '2024-01-25 10:26:44',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '6867E751998B41C3B89FA3',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//694FD852367CD7465F7142.txt',
'sequence': '',
'create_time': '2024-02-05 13:21:39',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000202.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '694FD852367CD7465F7142',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//6E39B6C1428217A5AAA835.txt',
'sequence': '',
'create_time': '2024-02-05 11:52:33',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '6E39B6C1428217A5AAA835',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//761753E47A37F7F1C141BB.txt',
'sequence': '',
'create_time': '2024-01-25 10:25:25',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000202.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '761753E47A37F7F1C141BB',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 1
},
{
'file_path': '/category/AE8B188F0C0314F9BE31B8/82FBCAE96CBC9F50809838/95ec71e9-4745-4210-ba28-77c71b95bf06.ofd',
'sequence': '',
'create_time': '2024-3-20 14:20:57',
'file_name': '999.ofd',
'file_type': 'ofd',
'document_file_id': '',
'file_dpi': '',
'id': '7F846C5F6E3D5E50950384',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 29752,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//831817367E0C3D2E739787.txt',
'sequence': '',
'create_time': '2024-01-25 10:25:25',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '831817367E0C3D2E739787',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 1
}
]
export default {
name: 'AIKeywords',
components: { swiper, swiperSlide },
cruds() {
return [
CRUD({
title: 'AI关键词提取', url: 'api/category/fondMenu',
crudMethod: { ...crudCategory },
optShow: {
add: false,
edit: false,
del: false,
download: false,
group: false
}
})
]
},
mixins: [presenter(), header()],
data() {
return {
defaultProps: {
children: 'children',
label: 'label'
},
selectedCategory: {},
tableData: data,
selections: [],
keywords: [],
isEditing: [],
aiPickData: [],
isPickUp: false,
reloadKey: 0,
aiLoadingPick: false,
currentRow: {},
selectedItems: [],
aiFileVisible: false,
swiperOptionServer: {
autoplay: false,
allowTouchMove: false,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}
},
text: '# 这是可编辑的多行文本\n第二行文本',
isTxtEditing: false
}
},
computed: {
swiper() {
return this.$refs.mySwiper.swiper
}
},
created() {
this.isEditing = this.keywords.map(() => false)
},
mounted() {
},
methods: {
toggleEdit(editing) {
this.isTxtEditing = editing
if (editing) {
//
this.$nextTick(() => {
this.$refs.textareaRef.focus()
})
}
},
//
handleEdit(index) {
this.$set(this.isEditing, index, true)
},
//
handleBlur(index) {
this.$set(this.isEditing, index, false)
},
//
handleDelete(index) {
// keywords
this.keywords.splice(index, 1)
// isEditing
this.isEditing.splice(index, 1)
},
filterData(data) {
return data.filter(node => {
if (node.children && node.children.length > 0) {
node.children = this.filterData(node.children) //
}
return node.isType !== 3 // isType3
})
},
//
findNode(tree, func) {
for (const node of tree) {
if (func(node)) return node
if (node.children) {
const res = this.findNode(node.children, func)
if (res) return res
}
}
return null
},
//
expandAllChildren(node, targetElement) {
node.expanded = true
//
if (node.childNodes && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
if (node.childNodes[i].data.id === targetElement.id) {
this.$refs.categroyTree.setCurrentKey(node.childNodes[i])
}
this.expandAllChildren(node.childNodes[i], targetElement)
}
}
},
// el-tree
transformData(rawData) {
return rawData.map(item => {
return {
label: item.fondName,
isType: 0,
id: item.fondsId,
fondsNo: item.fondsNo,
children: item.categoryList.map(category => {
return {
label: category.cnName,
cnName: category.cnName,
id: category.id,
arrangeType: category.arrangeType,
isType: category.isType,
fondsId: item.fondsId,
fondName: item.fondName,
fondsNo: item.fondsNo,
children: this.transformChildren(category.children, item.fondsId, item.fondName, item.fondsNo)
}
})
}
})
},
//
transformChildren(children, fondsId, fondName, fondsNo) {
return children.map(child => {
return {
label: child.cnName,
cnName: child.cnName,
id: child.id,
isType: child.isType,
pid: child.pid,
code: child.code,
arrangeType: child.arrangeType,
fondsId: fondsId,
fondName: fondName,
fondsNo: fondsNo,
children: child.children.length ? this.transformChildren(child.children, fondsId, fondName, fondsNo) : []
}
})
},
//
findTopLevelNode(data, fondsId) {
for (let i = 0; i < data.length; i++) {
if (data[i].id === fondsId) {
return data[i]
}
}
return null
},
[CRUD.HOOK.afterRefresh]() {
this.crud.data = this.filterData(this.transformData(this.crud.data))
this.$nextTick(() => {
let currentKey
if (localStorage.getItem('currentArchivesKey') !== null) {
currentKey = JSON.parse(localStorage.getItem('currentArchivesKey'))
//
if (this.$refs.categroyTree.getCurrentKey(currentKey.id) == null) {
localStorage.removeItem('currentArchivesKey')
}
this.topLevelNode = this.findTopLevelNode(this.crud.data, currentKey.fondsId)
//
if (this.topLevelNode) {
if (currentKey) {
//
if (currentKey.isType === 1) {
currentKey = this.findNode(this.crud.data[0].children, (node) => {
return node.isType !== 1
})
}
this.expandAllChildren(this.$refs.categroyTree.getNode(this.topLevelNode), currentKey)
} else {
this.defaultSetting(currentKey)
}
} else {
this.defaultSetting(currentKey)
}
} else {
this.defaultSetting(currentKey)
}
if (currentKey && currentKey.id) {
this.$nextTick(() => {
//
this.handleNodeClick(currentKey)
})
}
})
},
defaultSetting(currentKey) {
if (this.crud.data[0].isType === 0) {
currentKey = this.findNode(this.crud.data[0].children, (node) => {
return node.isType !== 1
})
this.expandAllChildren(this.$refs.categroyTree.getNode(this.crud.data[0]), currentKey)
} else {
currentKey = this.crud.data[0]
this.expandAllChildren(this.$refs.categroyTree.getNode(this.crud.data[0]), currentKey)
}
},
//
handleNodeClick(val) {
if (val) {
localStorage.setItem('currentArchivesKey', JSON.stringify(val))
this.selectedCategory = val
this.$nextTick(() => {
})
}
},
// table
clickRowHandler(row) {
this.aiFileVisible = true
this.isPickUp = false
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row)
this.selections = [row]
this.currentRow = row
if (this.currentRow.file_status === 1) {
this.keywords = [
'永久', '武汉飞天', '2024'
]
} else {
this.keywords = []
}
},
handlePickUp() {
// this.currentRow = row
this.aiPickData = []
this.isPickUp = true
this.aiLoadingPick = true
setTimeout(() => {
this.aiPickData = ['数字人', '多媒体技术在档案信息存贮与检索中的应用中的应用中的应用中的应用', '2023', '30年']
this.aiLoadingPick = false
}, 3000)
},
//
handleCheckboxChange(index) {
// console.log(` ${index} : ${this.selectedItems[index]}`)
const allSelectedItems = this.aiPickData.filter((_, i) => this.selectedItems[i])
console.log('所有选中项:', allSelectedItems)
},
batchAiText() {
const selected = this.aiPickData.filter((_, index) => this.selectedItems[index])
if (selected.length === 0) {
this.$message({ message: '请选择要提取的关键词', type: 'warning', offset: 8 })
return
}
this.keywords = this.keywords.concat(selected)
this.isEditing = this.keywords.map(() => false)
//
this.aiPickData = this.aiPickData.filter((_, index) => !this.selectedItems[index])
this.selectedItems = this.aiPickData.map(() => false)
// file_status
if (this.currentRow) {
this.currentRow.file_status = 1
// tableData
const rowIndex = this.tableData.findIndex(row => row.id === this.currentRow.id)
if (rowIndex !== -1) {
this.$set(this.tableData, rowIndex, { ...this.tableData[rowIndex], file_status: 1 })
}
}
},
submitEditAi() {
this.aiFileVisible = false
this.handlePickUp()
},
handleClose() {
this.aiFileVisible = false
}
}
}
</script>
<style lang="scss" scoped>
.elect-cont-right .container-right {
// min-height: calc(100vh - 188px);
}
.category-container {
.elect-cont-left{
width: 296px !important;
}
}
.openSidebar .category-container .elect-cont-right{
width: calc(100vw - 614px) !important;
}
.hideSidebar .category-container .elect-cont-right {
width: calc(100vw - 412px) !important;
}
.tree-scroll{
font-size: 14px;
}
.ai-file-pickUp{
display: flex;
justify-content: space-between;
}
.ai-file-all{
flex: 1;
}
.pickUp-right{
width: 400px;
margin-left: 20px;
.pickUp-right-item,
.pickUp-Ai{
height: calc(100% / 2);
}
}
.keywords-text p{
width: 340px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}
.keywords-list{
display: flex;
justify-content: space-between;
line-height: 40px;
border-bottom: 1px solid #e6e8ed;
// padding: 0 10px;
}
.keywords-handle i{
cursor: pointer;
}
.ai-item-text{
display: flex;
justify-content: flex-start;
p{
margin-left: 10px;
}
}
.des-title{
position: relative;
.el-button{
position: absolute;
right: 0;
top: -5px;
}
}
.aiFile-dialog{
::v-deep .el-dialog{
width: 1400px !important;
}
::v-deep .swiper-server{
.swiper-slide-server{
display: flex;
justify-content: space-between;
height: 650px;
.aiImg{
width: 800px;
overflow: hidden;
overflow-y: scroll;
// border: 1px solid #000;
img{
display: block;
width: 100%;
// height: 100%;
}
}
.aiText{
flex: 1;
padding:0 10px;
// overflow: hidden;
// overflow-y: scroll;
// padding-left: 10px;
// border-left: 1px solid #000;
}
}
}
}
</style>

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

@ -9,7 +9,7 @@
<!--门类树状结构-->
<div class="tree-scroll">
<el-scrollbar style="height: calc(100vh - 230px);">
<el-tree ref="categroyTree" v-loading="crud.loading" class="arc-tree arc-tree-01" :data="crud.data" :props="defaultProps" node-key="id" :expand-on-click-node="false" highlight-current @node-click="handleNodeClick">
<el-tree ref="categroyTree" v-loading="crud.loading" class="arc-tree arc-tree-01" :data="crud.data" :props="defaultProps" node-key="id" :expand-on-click-node="false" :default-expand-all="true" highlight-current @node-click="handleNodeClick">
<span slot-scope="{ node, data }" class="custom-tree-node">
<el-tooltip :content="node.label" placement="left" :enterable="false" effect="dark">
<span v-if="data.isType === 0">
@ -35,348 +35,21 @@
<div v-if="selectedCategory.isType === 2" class="container-right">
<span class="right-top-line" />
<span class="left-bottom-line" />
<div class="ai-file-pickUp">
<div class="ai-file-all">
<div class="des-title">
<p>原文列表</p>
</div>
<el-table
ref="table"
:data="tableData"
style="min-width: 100%"
class="archives-table"
height="calc(100vh - 220px)"
@row-click="clickRowHandler"
>
<el-table-column type="selection" align="center" width="55" />
<!-- <el-table-column type="index" label="序号" width="55" align="center" /> -->
<el-table-column prop="file_name" label="文件名称" show-overflow-tooltip min-width="120" />
<el-table-column prop="file_type" label="格式" min-width="60" align="center">
<template slot-scope="scope">
<div v-if="scope.row.file_type === 'jpg' || scope.row.file_type === 'jpeg' || scope.row.file_type === 'png' || scope.row.file_type === 'bmp'|| scope.row.file_type === 'gif'">
<i class="fileIcon icon-image" />
</div>
<div v-else-if="scope.row.file_type === 'xlsx' || scope.row.file_type === 'xls'">
<i class="fileIcon icon-excel" />
</div>
<div v-else-if="scope.row.file_type === 'docx' || scope.row.file_type === 'doc'">
<i class="fileIcon icon-word" />
</div>
<div v-else-if="scope.row.file_type === 'pdf'">
<i class="fileIcon icon-pdf" />
</div>
<div v-else-if="scope.row.file_type === 'ppt' || scope.row.file_type === 'pptx'">
<i class="fileIcon icon-ppt" />
</div>
<div v-else-if="scope.row.file_type === 'zip' || scope.row.file_type === 'rar'">
<i class="fileIcon icon-zip" />
</div>
<div v-else-if="scope.row.file_type === 'txt'">
<i class="fileIcon icon-txt" />
</div>
<div v-else>
<i class="fileIcon icon-other" />
</div>
</template>
</el-table-column>
<el-table-column prop="file_status" label="是否解析" width="100" align="center">
<template slot-scope="scope">
<span v-if="scope.row.file_status === 0" class="row-state row-physical ">未解析</span>
<span v-else class="row-state row-binding state-active">已解析</span>
</template>
</el-table-column>
<el-table-column prop="file_status" label="是否标注" width="100" align="center">
<template slot-scope="scope">
<span v-if="scope.row.file_status === 0" class="row-state row-physical ">未标注</span>
<span v-else class="row-state row-binding state-active">已标注</span>
</template>
</el-table-column>
<!-- <el-table-column prop="file_status" label="操作" align="center" width="80">
<template slot-scope="scope">
<div class="handle-btn">
<el-button class="iconfont icon-huoqu" @click.stop="handlePickUp(scope.row)" />
</div>
</template>
</el-table-column> -->
</el-table>
</div>
<div class="pickUp-right">
<div class="pickUp-right-item">
<div class="des-title">
<p>已标注关键词</p>
</div>
<div v-if="currentRow && currentRow.file_status===1" style="height: calc(100% - 70px); overflow-y: auto;">
<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">
<!-- <p>{{ item }}</p> -->
<el-tooltip class="item" effect="dark" :content="item" :enterable="false" placement="top">
<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>
<el-empty v-else description="暂无标注的数据" />
</div>
<div :key="reloadKey" class="pickUp-Ai">
<div class="des-title">
<p>AI关键词标注</p>
<el-button @click="batchAiText">批量标注</el-button>
</div>
<div v-if="isPickUp " v-loading="aiLoadingPick" style="height: calc(100% - 70px); overflow-y: auto;">
<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>
</div>
</div>
</div>
</div>
</div>
<ArchivesList ref="archivesListRef" :selected-category="selectedCategory" />
</div>
</div>
</div>
<el-dialog class="aiFile-dialog" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :before-close="handleClose" :visible="aiFileVisible" title="文件内容">
<span class="dialog-right-top" />
<span class="dialog-left-bottom" />
<div class="setting-dialog">
<!-- <div class="ai-file-content" /> -->
<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 class="swiper-slide-server">
<div class="aiImg">
<img src="@/assets/images/test/1.png">
</div>
<div class="aiText">
<div v-if="isTxtEditing">
<textarea ref="textareaRef" v-model="text" style="height: 620px; width: calc(100% - 20px); padding: 10px; border: 1px solid #545b65; line-height: 26px; outline: none; resize: none;" @blur="toggleEdit(false)" />
</div>
<div v-else>
<p style=" height: 640px; overflow-y: scroll; line-height: 26px;" @click="toggleEdit(true)">{{ text }}</p>
</div>
</div>
</swiper-slide>
<swiper-slide class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/2.png">
</div>
<div class="aiText">
dddd
</div>
</swiper-slide>
<swiper-slide class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/3.png">
</div>
<div class="aiText">
dddd<br>
</div>
</swiper-slide>
<swiper-slide class="swiper-slide-server">
<div class="aiImg">
<img src="~@/assets/images/test/4.png">
</div>
<div class="aiText">
444
</div>
</swiper-slide>
<div slot="button-prev" class="swiper-button-prev" />
<div slot="button-next" class="swiper-button-next" />
</swiper>
<div slot="footer" class="dialog-footer">
<!-- :loading="crud.status.cu === 2" -->
<el-button type="text" @click="aiFileVisible = false">取消</el-button>
<el-button type="primary" @click="submitEditAi">确定</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import crudCategory from '@/api/system/category/category'
import CRUD, { presenter, header } from '@crud/crud'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'
const data = [
{
'file_path': '/category//09A6ECD967BCC772DABB2F.txt',
'sequence': '',
'create_time': '2024-02-05 11:43:26',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '09A6ECD967BCC772DABB2F',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 1
},
{
'file_path': '/category//0EFEF1CB577453482ADE5F.txt',
'sequence': '',
'create_time': '2024-02-05 11:43:26',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000202.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '0EFEF1CB577453482ADE5F',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category/AE8B188F0C0314F9BE31B8/82FBCAE96CBC9F50809838/eb824e27-13eb-4f33-adc8-39ca5926d244.txt',
'sequence': '',
'create_time': '2024-3-20 16:16:32',
'file_name': '123.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': '',
'id': '3D49BD844334621851DF24',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 37679,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category/AE8B188F0C0314F9BE31B8/82FBCAE96CBC9F50809838/4B457990A874D55714FF8C.pdf',
'sequence': '',
'create_time': '2024-3-20 11:36:24',
'file_name': 'DAT 48-2009 基于XML的电子文件封装规范.pdf',
'file_type': 'pdf',
'document_file_id': '',
'file_dpi': '',
'id': '4B457990A874D55714FF8C',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 92308284,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//6867E751998B41C3B89FA3.txt',
'sequence': '',
'create_time': '2024-01-25 10:26:44',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '6867E751998B41C3B89FA3',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//694FD852367CD7465F7142.txt',
'sequence': '',
'create_time': '2024-02-05 13:21:39',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000202.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '694FD852367CD7465F7142',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//6E39B6C1428217A5AAA835.txt',
'sequence': '',
'create_time': '2024-02-05 11:52:33',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '6E39B6C1428217A5AAA835',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//761753E47A37F7F1C141BB.txt',
'sequence': '',
'create_time': '2024-01-25 10:25:25',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000202.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '761753E47A37F7F1C141BB',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 1
},
{
'file_path': '/category/AE8B188F0C0314F9BE31B8/82FBCAE96CBC9F50809838/95ec71e9-4745-4210-ba28-77c71b95bf06.ofd',
'sequence': '',
'create_time': '2024-3-20 14:20:57',
'file_name': '999.ofd',
'file_type': 'ofd',
'document_file_id': '',
'file_dpi': '',
'id': '7F846C5F6E3D5E50950384',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 29752,
'file_thumbnail': '',
'file_status': 0
},
{
'file_path': '/category//831817367E0C3D2E739787.txt',
'sequence': '',
'create_time': '2024-01-25 10:25:25',
'file_name': 'A002-WSWJ·XZGL·2024·Y-000201.txt',
'file_type': 'txt',
'document_file_id': '',
'file_dpi': 'null',
'id': '831817367E0C3D2E739787',
'archive_id': '0FBCB1FCB6341BFF9709B6',
'document_id': '',
'file_size': 0,
'file_thumbnail': '',
'file_status': 1
}
]
import ArchivesList from './archivesList'
export default {
name: 'AIKeywords',
components: { swiper, swiperSlide },
components: { ArchivesList },
cruds() {
return [
CRUD({
@ -399,64 +72,74 @@ export default {
children: 'children',
label: 'label'
},
selectedCategory: {},
tableData: data,
selections: [],
keywords: [],
isEditing: [],
aiPickData: [],
isPickUp: false,
reloadKey: 0,
aiLoadingPick: false,
currentRow: {},
selectedItems: [],
aiFileVisible: false,
swiperOptionServer: {
autoplay: false,
allowTouchMove: false,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}
},
text: '# 这是可编辑的多行文本\n第二行文本',
isTxtEditing: false
selectedCategory: {}
}
},
computed: {
swiper() {
return this.$refs.mySwiper.swiper
}
},
created() {
this.isEditing = this.keywords.map(() => false)
},
mounted() {
},
methods: {
toggleEdit(editing) {
this.isTxtEditing = editing
if (editing) {
//
[CRUD.HOOK.afterRefresh]() {
this.crud.data = this.filterData(this.transformData(this.crud.data))
this.$nextTick(() => {
let currentKey
if (localStorage.getItem('currentArchivesKey') !== null) {
currentKey = JSON.parse(localStorage.getItem('currentArchivesKey'))
//
if (this.$refs.categroyTree.getCurrentKey(currentKey.id) == null) {
localStorage.removeItem('currentArchivesKey')
}
this.topLevelNode = this.findTopLevelNode(this.crud.data, currentKey.fondsId)
//
if (this.topLevelNode) {
if (currentKey) {
//
if (currentKey.isType === 1) {
currentKey = this.findNode(this.crud.data[0].children, (node) => {
return node.isType !== 1
})
}
this.expandAllChildren(this.$refs.categroyTree.getNode(this.topLevelNode), currentKey)
} else {
this.defaultSetting(currentKey)
}
} else {
this.defaultSetting(currentKey)
}
} else {
this.defaultSetting(currentKey)
}
if (currentKey && currentKey.id) {
this.$nextTick(() => {
//
this.handleNodeClick(currentKey)
})
}
})
},
//
handleNodeClick(val) {
if (val) {
localStorage.setItem('currentArchivesKey', JSON.stringify(val))
this.selectedCategory = val
this.$nextTick(() => {
this.$refs.textareaRef.focus()
this.$refs.archivesListRef.getViewTable()
})
}
},
//
handleEdit(index) {
this.$set(this.isEditing, index, true)
},
//
handleBlur(index) {
this.$set(this.isEditing, index, false)
},
//
handleDelete(index) {
// keywords
this.keywords.splice(index, 1)
// isEditing
this.isEditing.splice(index, 1)
defaultSetting(currentKey) {
if (this.crud.data[0].isType === 0) {
currentKey = this.findNode(this.crud.data[0].children, (node) => {
return node.isType !== 1
})
this.expandAllChildren(this.$refs.categroyTree.getNode(this.crud.data[0]), currentKey)
} else {
currentKey = this.crud.data[0]
this.expandAllChildren(this.$refs.categroyTree.getNode(this.crud.data[0]), currentKey)
}
},
filterData(data) {
return data.filter(node => {
@ -540,135 +223,13 @@ export default {
}
}
return null
},
[CRUD.HOOK.afterRefresh]() {
this.crud.data = this.filterData(this.transformData(this.crud.data))
this.$nextTick(() => {
let currentKey
if (localStorage.getItem('currentArchivesKey') !== null) {
currentKey = JSON.parse(localStorage.getItem('currentArchivesKey'))
//
if (this.$refs.categroyTree.getCurrentKey(currentKey.id) == null) {
localStorage.removeItem('currentArchivesKey')
}
this.topLevelNode = this.findTopLevelNode(this.crud.data, currentKey.fondsId)
//
if (this.topLevelNode) {
if (currentKey) {
//
if (currentKey.isType === 1) {
currentKey = this.findNode(this.crud.data[0].children, (node) => {
return node.isType !== 1
})
}
this.expandAllChildren(this.$refs.categroyTree.getNode(this.topLevelNode), currentKey)
} else {
this.defaultSetting(currentKey)
}
} else {
this.defaultSetting(currentKey)
}
} else {
this.defaultSetting(currentKey)
}
if (currentKey && currentKey.id) {
this.$nextTick(() => {
//
this.handleNodeClick(currentKey)
})
}
})
},
defaultSetting(currentKey) {
if (this.crud.data[0].isType === 0) {
currentKey = this.findNode(this.crud.data[0].children, (node) => {
return node.isType !== 1
})
this.expandAllChildren(this.$refs.categroyTree.getNode(this.crud.data[0]), currentKey)
} else {
currentKey = this.crud.data[0]
this.expandAllChildren(this.$refs.categroyTree.getNode(this.crud.data[0]), currentKey)
}
},
//
handleNodeClick(val) {
if (val) {
localStorage.setItem('currentArchivesKey', JSON.stringify(val))
this.selectedCategory = val
this.$nextTick(() => {
})
}
},
// table
clickRowHandler(row) {
this.aiFileVisible = true
this.isPickUp = false
this.$refs.table.clearSelection()
this.$refs.table.toggleRowSelection(row)
this.selections = [row]
this.currentRow = row
if (this.currentRow.file_status === 1) {
this.keywords = [
'永久', '武汉飞天', '2024'
]
} else {
this.keywords = []
}
},
handlePickUp() {
// this.currentRow = row
this.aiPickData = []
this.isPickUp = true
this.aiLoadingPick = true
setTimeout(() => {
this.aiPickData = ['数字人', '多媒体技术在档案信息存贮与检索中的应用中的应用中的应用中的应用', '2023', '30年']
this.aiLoadingPick = false
}, 3000)
},
//
handleCheckboxChange(index) {
// console.log(` ${index} : ${this.selectedItems[index]}`)
const allSelectedItems = this.aiPickData.filter((_, i) => this.selectedItems[i])
console.log('所有选中项:', allSelectedItems)
},
batchAiText() {
const selected = this.aiPickData.filter((_, index) => this.selectedItems[index])
if (selected.length === 0) {
this.$message({ message: '请选择要提取的关键词', type: 'warning', offset: 8 })
return
}
this.keywords = this.keywords.concat(selected)
this.isEditing = this.keywords.map(() => false)
//
this.aiPickData = this.aiPickData.filter((_, index) => !this.selectedItems[index])
this.selectedItems = this.aiPickData.map(() => false)
// file_status
if (this.currentRow) {
this.currentRow.file_status = 1
// tableData
const rowIndex = this.tableData.findIndex(row => row.id === this.currentRow.id)
if (rowIndex !== -1) {
this.$set(this.tableData, rowIndex, { ...this.tableData[rowIndex], file_status: 1 })
}
}
},
submitEditAi() {
this.aiFileVisible = false
this.handlePickUp()
},
handleClose() {
this.aiFileVisible = false
}
}
}
</script>
<style lang="scss" scoped>
.elect-cont-right .container-right {
// min-height: calc(100vh - 188px);
}
.category-container {
.elect-cont-left{
width: 296px !important;

6
src/views/collectReorganizi/collectionLibrary/module/blukEditing/index.vue

@ -218,9 +218,9 @@ export default {
}
},
mounted() {
this.$nextTick(() => {
this.getInitCategoryInputFieldByPid()
})
// this.$nextTick(() => {
// this.getInitCategoryInputFieldByPid()
// })
},
methods: {
// vue-treeSelectunknown

8
src/views/collectReorganizi/collectionLibrary/module/bulkImport/index.vue

@ -134,10 +134,10 @@ export default {
created() {
},
mounted() {
console.log('selectedCategory', this.selectedCategory)
this.$nextTick(() => {
this.getInitCategoryInputFieldByPid()
})
// console.log('selectedCategory', this.selectedCategory)
// this.$nextTick(() => {
// this.getInitCategoryInputFieldByPid()
// })
},
methods: {
getInitCategoryInputFieldByPid() {

6
src/views/collectReorganizi/collectionLibrary/module/collectHeader.vue

@ -777,7 +777,10 @@ export default {
this.$message({ message: '当前档案处于归档流程中,不可操作批量导入,请先确认!', offset: 8 })
return false
}
this.$refs.blukImportRef.bulkImportVisible = true
this.$nextTick(() => {
this.$refs.blukImportRef.bulkImportVisible = true
this.$refs.blukImportRef.getInitCategoryInputFieldByPid()
})
},
//
handleBlukEditing() {
@ -792,6 +795,7 @@ export default {
}
this.$nextTick(() => {
this.$refs.blukEditingRef.bulkEditingVisible = true
this.$refs.blukEditingRef.getInitCategoryInputFieldByPid()
})
},
// //

2
src/views/collectReorganizi/collectionLibrary/module/handleInfo/index.vue

@ -14,7 +14,7 @@
<div>{{ scope.row.update_time | parseTime }}</div>
</template>
</el-table-column>
<el-table-column prop="remarks" label="备注" min-width="60" />
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
</el-table>
<!--分页组件-->
<el-pagination

17
src/views/components/category/PreviewForm.vue

@ -757,6 +757,23 @@ export default {
rule.message = `输入内容最多为${item.isColumnLength}个字符的${item.fieldCnName}`
}
//
const fieldValue = this.addOrUpdateForm[item.fieldName]
// isDataType
if (item.isDataType && fieldValue) {
switch (item.isDataType) {
case 1:
//
break
case 2:
rule.type = 'number'
rule.message = `请输入有效的数字类型的${item.fieldCnName}`
break
default:
break
}
}
this.$set(this.rules, item.fieldName, [rule])
})
},

Loading…
Cancel
Save