feat: 支持用手机标注ner

This commit is contained in:
maxmon 2022-04-07 20:58:51 +08:00
parent ac1d2d41e4
commit 71435a451a
6 changed files with 74 additions and 10 deletions

View File

@ -1,4 +1,4 @@
# dw-tool-ner # whale-anno
> Datawhale's ner tool. > Datawhale's ner tool.

View File

@ -17,7 +17,7 @@ module.exports = {
}, },
// Various Dev Server settings // Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST host: '0.0.0.0', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false, autoOpenBrowser: false,
errorOverlay: true, errorOverlay: true,

2
fe/dist/index.html vendored
View File

@ -1,4 +1,4 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>dw-tool-ner</title><link href=/static/css/app.cb359e49664391241f41c3263ba7c710.css rel=stylesheet></head><body><div id=app></div><style>html, body { <!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>whale-anno</title><link href=/static/css/app.cb359e49664391241f41c3263ba7c710.css rel=stylesheet></head><body><div id=app></div><style>html, body {
display: inline-block; display: inline-block;
margin: 0; margin: 0;
width: 100%; width: 100%;

View File

@ -2,8 +2,8 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>dw-tool-ner</title> <title>whale-anno</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
@ -15,6 +15,9 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
button, input, span {
-webkit-tap-highlight-color: transparent;
}
</style> </style>
</body> </body>
</html> </html>

View File

@ -43,7 +43,24 @@
</span> </span>
</div> </div>
</div> </div>
<div id="ner-box" class="ner-box" @mouseup="setMode('')" @mouseleave="setMode('');setFocus('')" @keydown="setTypeByFastKey" @mouseover="setFocus('ner-box')"> <div id="ner-box" class="ner-box" @mouseup="setMode('')" @touchend="setMode('')" @mouseleave="setMode('');setFocus('')" @keydown="setTypeByFastKey" @mouseover="setFocus('ner-box')">
<!-- <svg style=" position: absolute; z-index: 99999999; overflow: unset; pointer-events: none">
<template class="rect" v-for="(word, idx) in nowNers" >
<template v-for="(w, i) in word.name">
<line
v-if="i === 0 || ((word.start%columnWordCount)+i) % columnWordCount === 0"
:key="`${word}${w}${i}`"
:x1="`${((word.start+i)%columnWordCount)*20 + (word.isSmall && i === 0 ? 2 : 0)}px`"
:y1="`${((word.start+i)/columnWordCount|0)*35 + (word.isSmall ? 0 : -2)}px`"
:x2="`${((Math.min(word.end-(word.start+i), columnWordCount, columnWordCount - (word.start+i)%columnWordCount))*20 - (word.isSmall && i === 0 ? 4 : 0)) + ((word.start+i)%columnWordCount)*20 + (word.isSmall && i === 0 ? 2 : 0)}px`"
:y2="`${word.isSmall?18:22+((word.start+i)/columnWordCount|0)*35 + (word.isSmall ? 0 : -2)}px`"
style="stroke:rgb(255,0,0);stroke-width:2"
></line>
</template>
</template>
</span>
<text x="201" y="10" fill="red">关系一</text>
</svg> -->
<div class="word-rect-area"> <div class="word-rect-area">
<span class="rect" v-for="(word, idx) in nowNers" :key="idx"> <span class="rect" v-for="(word, idx) in nowNers" :key="idx">
<span v-for="(w, i) in word.name" :key="`${word}${w}${i}`"> <span v-for="(w, i) in word.name" :key="`${word}${w}${i}`">
@ -77,7 +94,7 @@
</span> </span>
</div> </div>
<div v-if="projectType === '命名实体识别'"> <div v-if="projectType === '命名实体识别'">
<span class="word" v-for="(word, idx) in nowText" :key="idx" @contextmenu="stopPrev" @mousedown="startSelect(idx, $event)" @mousemove="pointWord(idx)" <span class="word" v-for="(word, idx) in nowText" :key="idx" :id="idx" @contextmenu="stopPrev" @mousedown="startSelect(idx, $event)" @touchstart="startSelect(idx, $event)" @mousemove="pointWord(idx)" @touchmove="pointWordByTouch($event)"
> >
{{ word }} {{ word }}
</span> </span>
@ -90,8 +107,8 @@
</div> </div>
</div> </div>
<div class="page-btn-box"> <div class="page-btn-box">
<button class="page-btn" @click="changeIdx(-1)" @mouseover="setFocus('page-up')" @mouseleave="setFocus('')">上一个 {{ fastTypeKey['page-up'] ? `${fastTypeKey['page-up']}` : '' }}</button> <button class="page-btn" @click="changeIdx(-1, $event)" @mouseover="setFocus('page-up')" @mouseleave="setFocus('')">上一个 {{ fastTypeKey['page-up'] ? `${fastTypeKey['page-up']}` : '' }}</button>
<button class="page-btn" @click="changeIdx(+1)" @mouseover="setFocus('page-down')" @mouseleave="setFocus('')">下一个 {{ fastTypeKey['page-down'] ? `${fastTypeKey['page-down']}` : '' }}</button> <button class="page-btn" @click="changeIdx(+1, $event)" @mouseover="setFocus('page-down')" @mouseleave="setFocus('')">下一个 {{ fastTypeKey['page-down'] ? `${fastTypeKey['page-down']}` : '' }}</button>
</div> </div>
</div> </div>
<div class="result-box"> <div class="result-box">
@ -316,7 +333,10 @@ export default {
}) })
} }
}, },
changeIdx: function (d) { changeIdx: function (d, event) {
if (event) {
event.preventDefault() //
}
const nowIdx = this.files.indexOf(this.nowFile) const nowIdx = this.files.indexOf(this.nowFile)
let newIdx = nowIdx + d let newIdx = nowIdx + d
if (newIdx < 0) newIdx = 0 if (newIdx < 0) newIdx = 0
@ -328,6 +348,7 @@ export default {
* @param idx 点击文字的定位 * @param idx 点击文字的定位
*/ */
pointWord: function (idx, config = {}) { pointWord: function (idx, config = {}) {
console.log(this.mode, idx, config)
if (this.mode === 'select') { if (this.mode === 'select') {
if (!this.nowType) { if (!this.nowType) {
alert('请先选择标签') alert('请先选择标签')
@ -342,6 +363,14 @@ export default {
return idx return idx
} }
}, },
pointWordByTouch: function (event) {
event.preventDefault() //
let {pageX, pageY} = event.touches[0]
let dom = document.elementFromPoint(pageX, pageY)
if (dom && dom.id) {
this.pointWord(dom.id)
}
},
/** /**
* 配置标注类型 * 配置标注类型
* @param 类型 * @param 类型
@ -457,7 +486,19 @@ export default {
}, },
startSelect: function (idx, event) { startSelect: function (idx, event) {
if (this.projectType !== '命名实体识别') return if (this.projectType !== '命名实体识别') return
let isNeedDel = false
if (event.touches) {
//
if (this.startSelectTouchTs && Date.now() - this.startSelectTouchTs < 300) {
isNeedDel = true
}
this.startSelectTouchTs = Date.now()
}
if (event.which === 3) { if (event.which === 3) {
//
isNeedDel = true
}
if (isNeedDel) {
event.preventDefault() event.preventDefault()
// //
const ners = this.ners const ners = this.ners
@ -494,6 +535,7 @@ export default {
} }
const isRepeat = this.checkIsRepeat(this.nowNer) const isRepeat = this.checkIsRepeat(this.nowNer)
if (!isRepeat && this.nowNer.isMove) { if (!isRepeat && this.nowNer.isMove) {
console.log(this.nowNer)
delete this.nowNer.isMove delete this.nowNer.isMove
this.ners.push(this.nowNer) this.ners.push(this.nowNer)
this.$set(this, 'ners', this.ners.sort((a, b) => { this.$set(this, 'ners', this.ners.sort((a, b) => {
@ -963,4 +1005,22 @@ export default {
top: -9px; top: -9px;
right: -9px; right: -9px;
} }
@media only screen and (max-width: 800px) {
.out-title {
text-align: right !important;
}
}
@media only screen and (max-width: 600px) {
.out-title {
font-size: 0;
}
}
@media only screen and (max-width: 800px) {
.result-box {
display: none;
}
.left {
display: none;
}
}
</style> </style>

View File

@ -290,6 +290,7 @@ export default {
display: flex; display: flex;
flex: 1 1; flex: 1 1;
flex-direction: row; flex-direction: row;
margin: 0 10px;
/* height: calc(100% - 92px); */ /* height: calc(100% - 92px); */
} }
.button { .button {