1、+ 默认选中第一个标签;
2、+ 已标注的标签展示首2字,防止过长;
3、+ 标注过长的,按...展示;
v1.2.6
1、+ 按服务器返回显示是否标注;
2、+ 限制没加载完不能切换修改;
3、^ 修复没有实体类型默认值,置空;
v1.2.5
1、+ 快捷键配置本地缓存;
2、+ 增加整体标注进度条;
3、+ Eacape可删除按钮快捷键;
4、^ 已标注的文件增加checked图标;
5、^ 实体类型按钮文本居中;
6、^ 实体类型修改实时生效改为提交时生效;
This commit is contained in:
maxmon 2021-05-10 00:02:23 +08:00
parent fd45194e18
commit 69fcd22ffd
3 changed files with 89 additions and 33 deletions

View File

@ -54,7 +54,7 @@ module.exports = {
// Paths // Paths
assetsRoot: path.resolve(__dirname, '../dist'), assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static', assetsSubDirectory: 'static',
assetsPublicPath: '/dw-tool-ner/v1.2.4/', assetsPublicPath: '/dw-tool-ner/v1.2.7/',
/** /**
* Source Maps * Source Maps

View File

@ -6,10 +6,12 @@
<div class="left"> <div class="left">
<div class="file-list"> <div class="file-list">
<div v-for="file in files" :key="file" @click="setNowText(file)" <div v-for="file in files" :key="file" @click="setNowText(file)"
:class="['file', nowFile===file?'selected':'', nersCache[file]&&nersCache[file].length?'checked':'', ].join(' ')" :class="['file', nowFile===file?'selected':'', isAnnoDic[`${projectName}_${file}`]||(nersCache[file]&&nersCache[file].length)?'checked':'', ].join(' ')"
> >
{{ file }} {{ file }}
<svg v-if="isAnnoDic[`${projectName}_${file}`]||(nersCache[file]&&nersCache[file].length)" t="1619449859327" class="checked-icon" viewBox="0 0 1152 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1159" width="16" height="16"><path d="M4.266667 576l238.933333-187.733333 204.8 192c0 0 379.733333-328.533333 699.733333-512l0 123.733333C704 580.266667 426.666667 989.866667 426.666667 989.866667L4.266667 576 4.266667 576zM4.266667 576" p-id="1160" fill="#35e558"></path></svg>
</div> </div>
<div class="process-bar" :style="{width: `${processRate*100}%`}"></div>
</div> </div>
<div class="page-ctl"> <div class="page-ctl">
<span class="page-ctl-last" :style="{opacity: pageNumber === 1 ? 0 : 1}" @click="lastPage">上页</span> <span class="page-ctl-last" :style="{opacity: pageNumber === 1 ? 0 : 1}" @click="lastPage">上页</span>
@ -30,7 +32,7 @@
@mouseleave="setFocus('')" @mouseleave="setFocus('')"
> >
<svg v-if="configCanCtlType" t="1618942541356" @click.stop="clickColor(type, $event)" class="color-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1686" width="18" height="18"><path d="M204.4 524.9c-14.5 1.5-26.2 13.2-27.7 27.7-2.1 19.9 14.6 36.7 34.6 34.6 14.5-1.5 26.2-13.2 27.8-27.8 2-19.9-14.8-36.6-34.7-34.5zM265.4 473.7c21.8-1.9 39.4-19.5 41.4-41.4 2.5-28.5-21.2-52.3-49.7-49.7-21.8 1.9-39.4 19.5-41.4 41.4-2.6 28.4 21.2 52.2 49.7 49.7zM415.8 266.9c-28.5 1.8-51.6 24.9-53.4 53.4-2.2 34.5 26.4 63.1 60.9 60.9 28.5-1.8 51.6-24.9 53.4-53.4 2.1-34.6-26.4-63.1-60.9-60.9zM621.9 253.8c-35.1 2.2-63.4 30.6-65.6 65.6-2.7 42.4 32.4 77.6 74.8 74.8 35.1-2.2 63.4-30.6 65.6-65.6 2.8-42.4-32.3-77.5-74.8-74.8zM966.5 276.4c-0.5-7.6-4-14.6-9.8-19.6l-0.7-0.6c-5.2-4.5-11.9-7-18.8-7-8.3 0-16.2 3.6-21.6 9.9L574 652.4l-43.5 85.5 1.1 0.9-4.9 11.3 11.1-5.9 1.5 1.3 78-54.3 342.3-394c5-5.8 7.4-13.2 6.9-20.8z" p-id="1687" fill="#2c3e50"></path><path d="M897.8 476.3c-13.8-1.4-26.7 7.4-30.4 20.7-6.9 24.6-19.3 64.5-35.1 97.8C809.5 643 767.4 710.1 696.7 756c-72.2 46.9-142.7 56.7-189.2 56.7-37 0-72.2-6.1-101.7-17.7-26.9-10.5-46.4-24.6-54.9-39.7-3.4-6.1-7.2-12.9-11.2-20.2-17.2-31.1-36.6-66.5-49.7-77.4-15.9-13.2-39.1-15-59.8-15-8.1 0-40.8 1.3-48.5 1.3-33.1 0-49.4-6.5-56.1-22.4-17.8-42.3-7.3-114.3 26.8-183.4C205.2 331.4 300 253.3 412.6 224c40-10.6 81.2-18.9 121.3-18.9 85.6 0 187.8 32.8 252.5 77.2 11.4 7.8 26.9 5.8 35.7-4.9 10.4-12.6 7.1-31.4-6.8-39.8-23.3-14-57.9-34-86.3-47.1-60.3-27.9-123.7-41.9-189.2-41.9-68.1 0-148.8 16.4-217.2 47.2-78.1 35-135.2 85-179.4 147.5-36.4 51.4-67.8 111.1-80.1 168.7-7.5 35.1-6.8 57.4-2.4 87.8 4.2 29.2 13.4 52.5 26.9 67.5 22.4 25.1 51.5 37.4 89 37.4 13.9 0 56.3-5 63.1-5 7.4 0 12.2 1.2 14.4 3.8 6.4 7.4 14.4 22.4 23.7 39.9 7.5 14.1 15.9 30.1 25.4 45.3 12.1 19.5 36.9 40.4 66.5 55.9 27 14.1 71.9 31 132.2 31 72 0 148.3-23.6 226.7-70.1 74.9-44.4 123-118.9 150.2-173.6 19-38.3 34.7-87.2 43.8-119.1 4.8-17.3-7-34.7-24.8-36.5z" p-id="1688" fill="#2c3e50"></path></svg> <svg v-if="configCanCtlType" t="1618942541356" @click.stop="clickColor(type, $event)" class="color-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1686" width="18" height="18"><path d="M204.4 524.9c-14.5 1.5-26.2 13.2-27.7 27.7-2.1 19.9 14.6 36.7 34.6 34.6 14.5-1.5 26.2-13.2 27.8-27.8 2-19.9-14.8-36.6-34.7-34.5zM265.4 473.7c21.8-1.9 39.4-19.5 41.4-41.4 2.5-28.5-21.2-52.3-49.7-49.7-21.8 1.9-39.4 19.5-41.4 41.4-2.6 28.4 21.2 52.2 49.7 49.7zM415.8 266.9c-28.5 1.8-51.6 24.9-53.4 53.4-2.2 34.5 26.4 63.1 60.9 60.9 28.5-1.8 51.6-24.9 53.4-53.4 2.1-34.6-26.4-63.1-60.9-60.9zM621.9 253.8c-35.1 2.2-63.4 30.6-65.6 65.6-2.7 42.4 32.4 77.6 74.8 74.8 35.1-2.2 63.4-30.6 65.6-65.6 2.8-42.4-32.3-77.5-74.8-74.8zM966.5 276.4c-0.5-7.6-4-14.6-9.8-19.6l-0.7-0.6c-5.2-4.5-11.9-7-18.8-7-8.3 0-16.2 3.6-21.6 9.9L574 652.4l-43.5 85.5 1.1 0.9-4.9 11.3 11.1-5.9 1.5 1.3 78-54.3 342.3-394c5-5.8 7.4-13.2 6.9-20.8z" p-id="1687" fill="#2c3e50"></path><path d="M897.8 476.3c-13.8-1.4-26.7 7.4-30.4 20.7-6.9 24.6-19.3 64.5-35.1 97.8C809.5 643 767.4 710.1 696.7 756c-72.2 46.9-142.7 56.7-189.2 56.7-37 0-72.2-6.1-101.7-17.7-26.9-10.5-46.4-24.6-54.9-39.7-3.4-6.1-7.2-12.9-11.2-20.2-17.2-31.1-36.6-66.5-49.7-77.4-15.9-13.2-39.1-15-59.8-15-8.1 0-40.8 1.3-48.5 1.3-33.1 0-49.4-6.5-56.1-22.4-17.8-42.3-7.3-114.3 26.8-183.4C205.2 331.4 300 253.3 412.6 224c40-10.6 81.2-18.9 121.3-18.9 85.6 0 187.8 32.8 252.5 77.2 11.4 7.8 26.9 5.8 35.7-4.9 10.4-12.6 7.1-31.4-6.8-39.8-23.3-14-57.9-34-86.3-47.1-60.3-27.9-123.7-41.9-189.2-41.9-68.1 0-148.8 16.4-217.2 47.2-78.1 35-135.2 85-179.4 147.5-36.4 51.4-67.8 111.1-80.1 168.7-7.5 35.1-6.8 57.4-2.4 87.8 4.2 29.2 13.4 52.5 26.9 67.5 22.4 25.1 51.5 37.4 89 37.4 13.9 0 56.3-5 63.1-5 7.4 0 12.2 1.2 14.4 3.8 6.4 7.4 14.4 22.4 23.7 39.9 7.5 14.1 15.9 30.1 25.4 45.3 12.1 19.5 36.9 40.4 66.5 55.9 27 14.1 71.9 31 132.2 31 72 0 148.3-23.6 226.7-70.1 74.9-44.4 123-118.9 150.2-173.6 19-38.3 34.7-87.2 43.8-119.1 4.8-17.3-7-34.7-24.8-36.5z" p-id="1688" fill="#2c3e50"></path></svg>
<input :id="type" class="color-input" :value="types[type].color" type="color" @change="changeColor(type, $event)" @click.stop/> <input v-if="configCanCtlType" :id="type" class="color-input" :value="types[type].color" type="color" @change="changeColor(type, $event)" @click.stop/>
{{ type }} {{ fastTypeKey[type] ? `${fastTypeKey[type]}` : '' }} {{ type }} {{ fastTypeKey[type] ? `${fastTypeKey[type]}` : '' }}
<svg v-if="configCanCtlType" t="1618943942999" class="close-icon" @click="checkDelType(type, $event)" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3793" width="18" height="18"><path d="M512 421.504l274.752-274.752 90.496 90.496L602.496 512l274.752 274.752-90.496 90.496L512 602.496l-274.752 274.752-90.496-90.496L421.504 512 146.752 237.248l90.496-90.496z" p-id="3794" fill="#ff0000"></path></svg> <svg v-if="configCanCtlType" t="1618943942999" class="close-icon" @click="checkDelType(type, $event)" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3793" width="18" height="18"><path d="M512 421.504l274.752-274.752 90.496 90.496L602.496 512l274.752 274.752-90.496 90.496L512 602.496l-274.752 274.752-90.496-90.496L421.504 512 146.752 237.248l90.496-90.496z" p-id="3794" fill="#ff0000"></path></svg>
</span> </span>
@ -90,7 +92,7 @@
backgroundColor: types[ner.type] ? types[ner.type].color : `#fff`, backgroundColor: types[ner.type] ? types[ner.type].color : `#fff`,
}" }"
> >
<span class="result-type">{{ ner.type }}</span> <span class="result-type">{{ ner.type.substr(0,2) }}</span>
<span class="result-name">{{ ner.name }}</span> <span class="result-name">{{ ner.name }}</span>
</div> </div>
</div> </div>
@ -130,9 +132,6 @@ function query (method, url, data = '', cb) {
} }
xhr.send(JSON.stringify(data)) xhr.send(JSON.stringify(data))
} }
get('/v1/index', function (data) {
console.log('data', data)
})
function updateType2Server (projectName, typeList, types) { function updateType2Server (projectName, typeList, types) {
const entityTypes = typeList.map((type) => { const entityTypes = typeList.map((type) => {
return { return {
@ -167,11 +166,12 @@ export default {
nowText: '', // nowText: '', //
ners: [], // ners: [], //
nowNer: {}, // nowNer: {}, //
nowType: 'person', // nowType: '', //
typeList: ['person', 'location', 'organiztion'], // typeList: ['person', 'location', 'organiztion'], //
isAnnoDic: {}, //
nowFocus: '', // // nowFocus: '', // //
fastKeyType: {}, // fastKeyType: localStorage.fastKeyType ? JSON.parse(localStorage.fastKeyType) : {}, //
fastTypeKey: {}, // fastTypeKey: localStorage.fastTypeKey ? JSON.parse(localStorage.fastTypeKey) : {}, //
types: { // types: { //
'person': { 'person': {
color: '#e61490' color: '#e61490'
@ -200,6 +200,21 @@ export default {
return [...this.ners, this.nowNer] return [...this.ners, this.nowNer]
} }
return this.ners return this.ners
},
processRate: function () {
const files = this.files || []
if (!files.length) return 0
const nersCache = this.nersCache || {}
const projectName = this.projectName || ''
const isAnnoDic = this.isAnnoDic || {}
let cnt = 0
for (let i = 0; i < files.length; i += 1) {
const file = files[i]
if (isAnnoDic[`${projectName}_${file}`] || (nersCache[file] && nersCache[file].length)) {
cnt += 1
}
}
return cnt / files.length
} }
}, },
methods: { methods: {
@ -208,7 +223,11 @@ export default {
if (that.pageNumber > 1) { if (that.pageNumber > 1) {
that.pageNumber = that.pageNumber - 1 that.pageNumber = that.pageNumber - 1
get(`/v1/files/query?projectName=${that.projectName}&pageNumber=${that.pageNumber}&pageSize=${that.pageSize}`, function (info) { get(`/v1/files/query?projectName=${that.projectName}&pageNumber=${that.pageNumber}&pageSize=${that.pageSize}`, function (info) {
that.$set(that, 'files', info) that.$set(that, 'files', info.map((item) => {
if (typeof item === 'string') return item
that.isAnnoDic[`${that.projectName}_${item.fileName}`] = item.isAnno
return item.fileName
}))
}) })
} }
}, },
@ -217,7 +236,11 @@ export default {
if (that.files.length === that.pageSize) { if (that.files.length === that.pageSize) {
that.pageNumber = that.pageNumber + 1 that.pageNumber = that.pageNumber + 1
get(`/v1/files/query?projectName=${that.projectName}&pageNumber=${that.pageNumber}&pageSize=${that.pageSize}`, function (info) { get(`/v1/files/query?projectName=${that.projectName}&pageNumber=${that.pageNumber}&pageSize=${that.pageSize}`, function (info) {
that.$set(that, 'files', info) that.$set(that, 'files', info.map((item) => {
if (typeof item === 'string') return item
that.isAnnoDic[`${that.projectName}_${item.fileName}`] = item.isAnno
return item.fileName
}))
}) })
} }
}, },
@ -225,6 +248,14 @@ export default {
this.$router.push({path: '/'}) this.$router.push({path: '/'})
}, },
save: function () { save: function () {
if (window.isLoadingNowText) {
if (Date.now() - window.isLoadingNowText < 30 * 1000) {
alert('请等待文件内容加载')
return false
} else {
delete window.isLoadingNowText
}
}
const that = this const that = this
// //
post('/v1/anno/create', { post('/v1/anno/create', {
@ -233,6 +264,7 @@ export default {
annoDetails: JSON.stringify(that.ners) annoDetails: JSON.stringify(that.ners)
}) })
that.$set(that.nersCache, that.nowFile, [...that.ners]) that.$set(that.nersCache, that.nowFile, [...that.ners])
return true
}, },
/** /**
* 选中目标文件 * 选中目标文件
@ -240,13 +272,16 @@ export default {
*/ */
setNowText: function (newFile) { setNowText: function (newFile) {
const that = this const that = this
that.save() const isSaved = that.save()
if (!isSaved) return false
window.isLoadingNowText = Date.now()
that.nowFile = newFile that.nowFile = newFile
that.nowText = that.textDic[newFile] that.nowText = that.textDic[newFile]
const hasCache = !!that.nersCache[newFile] const hasCache = !!that.nersCache[newFile]
that.$set(that, 'ners', that.nersCache[newFile] ? [...that.nersCache[newFile]] : []) that.$set(that, 'ners', that.nersCache[newFile] ? [...that.nersCache[newFile]] : [])
that.flushWordsType() that.flushWordsType()
get(`/v1/anno/query?projectName=${that.projectName}&fileName=${newFile}`, function (info) { get(`/v1/anno/query?projectName=${that.projectName}&fileName=${newFile}`, function (info) {
delete window.isLoadingNowText
// 线 // 线
if (!hasCache && info.annoDetails) { if (!hasCache && info.annoDetails) {
that.$set(that, 'ners', JSON.parse(info.annoDetails)) that.$set(that, 'ners', JSON.parse(info.annoDetails))
@ -271,6 +306,10 @@ export default {
*/ */
pointWord: function (idx, config = {}) { pointWord: function (idx, config = {}) {
if (this.mode === 'select') { if (this.mode === 'select') {
if (!this.nowType) {
alert('请先选择标签')
return false
}
this.endIdx = idx this.endIdx = idx
const type = this.nowType const type = this.nowType
const start = Math.min(this.startIdx, this.endIdx) const start = Math.min(this.startIdx, this.endIdx)
@ -408,6 +447,9 @@ export default {
return false return false
}, },
endSelect: function () { endSelect: function () {
if (!this.nowType) {
return false
}
const isRepeat = this.checkIsRepeat(this.nowNer) const isRepeat = this.checkIsRepeat(this.nowNer)
if (!isRepeat && this.nowNer.isMove) { if (!isRepeat && this.nowNer.isMove) {
delete this.nowNer.isMove delete this.nowNer.isMove
@ -516,7 +558,6 @@ export default {
}, },
mounted () { mounted () {
const that = this const that = this
console.log(that.$route)
const { projectName, projectType, entityTypes } = that.$route.query const { projectName, projectType, entityTypes } = that.$route.query
if (projectName) { if (projectName) {
that.projectName = projectName that.projectName = projectName
@ -531,8 +572,14 @@ export default {
return entityType.type return entityType.type
}) })
that.types = types that.types = types
//
if (that.typeList && that.typeList[0]) that.nowType = that.typeList[0]
get(`/v1/files/query?projectName=${projectName}&pageNumber=${that.pageNumber}&pageSize=${that.pageSize}`, function (info) { get(`/v1/files/query?projectName=${projectName}&pageNumber=${that.pageNumber}&pageSize=${that.pageSize}`, function (info) {
that.$set(that, 'files', info) that.$set(that, 'files', info.map((item) => {
if (typeof item === 'string') return item
that.isAnnoDic[`${that.projectName}_${item.fileName}`] = item.isAnno
return item.fileName
}))
}) })
} }
function calcColumnWordCount () { function calcColumnWordCount () {
@ -570,8 +617,8 @@ export default {
if (oldKey) { if (oldKey) {
that.$set(that.fastKeyType, oldKey, undefined) that.$set(that.fastKeyType, oldKey, undefined)
} }
// // Eacape
that.$set(that.fastKeyType, key, that.nowFocus) if (key !== 'Escape') that.$set(that.fastKeyType, key, that.nowFocus)
// -> // ->
const typeKey = {} const typeKey = {}
for (const key in that.fastKeyType) { for (const key in that.fastKeyType) {
@ -579,6 +626,8 @@ export default {
typeKey[type] = key typeKey[type] = key
} }
that.$set(that, 'fastTypeKey', typeKey) that.$set(that, 'fastTypeKey', typeKey)
localStorage.fastKeyType = JSON.stringify(that.fastKeyType)
localStorage.fastTypeKey = JSON.stringify(that.fastTypeKey)
} }
} }
} }
@ -638,8 +687,15 @@ export default {
cursor: pointer; cursor: pointer;
} }
.left .file.checked { .left .file.checked {
position: relative;
color: #ccc; color: #ccc;
} }
.left .file .checked-icon {
position: absolute;
right: 2px;
top: 0;
bottom: 0;
}
.left .file.selected { .left .file.selected {
background: #033271; background: #033271;
z-index: 5; z-index: 5;
@ -648,6 +704,12 @@ export default {
overflow: auto; overflow: auto;
height: calc(100% - 82px); height: calc(100% - 82px);
} }
.left .file-list .process-bar {
position: absolute;
top: 0;
border-top: 2px solid #35e558;
transition: 0.3s width linear;
}
.left .out-btn, .left .out-btn,
.left .page-ctl { .left .page-ctl {
position: absolute; position: absolute;
@ -720,9 +782,9 @@ export default {
.type { .type {
position: relative; position: relative;
box-sizing: content-box; box-sizing: content-box;
/* box-shadow: 2px 2px 0px #ccc; */
border: 1px solid #ccc; border: 1px solid #ccc;
border-bottom: 3px solid #ccc; border-bottom: 3px solid #ccc;
text-align: center;
} }
.type.selected, .type.selected,
.result.selected { .result.selected {
@ -781,6 +843,15 @@ export default {
height: 22px; height: 22px;
text-align: left; text-align: left;
} }
.result-type {
text-align: center;
}
.result-name {
overflow: hidden;
height: 100%;
text-overflow: ellipsis;
word-break: keep-all;
}
.result-type, .result-type,
.result-name { .result-name {
width: 30%; width: 30%;

View File

@ -69,18 +69,6 @@ function getColor () {
function get (url, cb) { function get (url, cb) {
query('GET', url, '', cb) query('GET', url, '', cb)
} }
function updateType2Server (projectName, typeList, types) {
const entityTypes = typeList.map((type) => {
return {
type,
color: types[type].color
}
})
post('/v1/project/update_entity_types', {
projectName: projectName,
entityTypes: JSON.stringify(entityTypes)
})
}
function post (url, data, cb) { function post (url, data, cb) {
query('POST', url, data, cb) query('POST', url, data, cb)
} }
@ -191,7 +179,6 @@ export default {
}) })
that.typeList.push(newType) that.typeList.push(newType)
console.log(that.typeList) console.log(that.typeList)
updateType2Server(that.projectName, that.typeList, that.types)
}, },
delType: function (type, ev) { delType: function (type, ev) {
const that = this const that = this
@ -209,7 +196,6 @@ export default {
} }
that.typeList = newTypeList that.typeList = newTypeList
that.types = newTypes that.types = newTypes
updateType2Server(that.projectName, that.typeList, that.types)
}, },
checkDelType: function (type, ev) { checkDelType: function (type, ev) {
ev && ev.stopPropagation() ev && ev.stopPropagation()
@ -232,7 +218,6 @@ export default {
const that = this const that = this
console.log(tarType, ev.target.value) console.log(tarType, ev.target.value)
that.$set(that.types[tarType], 'color', ev.target.value) that.$set(that.types[tarType], 'color', ev.target.value)
updateType2Server(that.projectName, that.typeList, that.types)
} }
}, },
mounted () { mounted () {