mirror of https://github.com/doccano/doccano.git
Hironsan
2 years ago
19 changed files with 115 additions and 642 deletions
Split View
Diff Options
-
33frontend/components/links/ActionMenu.vue
-
95frontend/components/links/FormCreate.vue
-
28frontend/components/links/FormDelete.vue
-
91frontend/components/links/LinksList.vue
-
48frontend/domain/models/links/link.ts
-
13frontend/domain/models/links/linkRepository.ts
-
11frontend/domain/models/links/linkTypesRepository.ts
-
24frontend/domain/models/tasks/relation.ts
-
13frontend/domain/models/tasks/relationRepository.ts
-
162frontend/pages/projects/_id/links/index.vue
-
10frontend/pages/projects/_id/sequence-labeling/index.vue
-
4frontend/plugins/services.ts
-
38frontend/repositories/links/apiLinkRepository.ts
-
41frontend/repositories/links/apiLinkTypesRepository.ts
-
38frontend/repositories/tasks/sequenceLabeling/apiRelationRepository.ts
-
27frontend/services/application/links/linkData.ts
-
30frontend/services/application/links/linkTypesApplicationService.ts
-
15frontend/services/application/tasks/sequenceLabeling/relationData.ts
-
36frontend/services/application/tasks/sequenceLabeling/sequenceLabelingApplicationService.ts
@ -1,33 +0,0 @@ |
|||
<template> |
|||
<action-menu |
|||
:items="items" |
|||
:text="$t('dataset.actions')" |
|||
@create="$emit('create')" |
|||
@upload="$emit('upload')" |
|||
@download="$emit('download')" |
|||
/> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue from 'vue' |
|||
import { mdiPencil } from '@mdi/js' |
|||
import ActionMenu from '~/components/utils/ActionMenu.vue' |
|||
|
|||
export default Vue.extend({ |
|||
components: { |
|||
ActionMenu |
|||
}, |
|||
|
|||
computed: { |
|||
items() { |
|||
return [ |
|||
{ |
|||
title: this.$t('links.createLink'), |
|||
icon: mdiPencil, |
|||
event: 'create' |
|||
} |
|||
] |
|||
} |
|||
} |
|||
}) |
|||
</script> |
@ -1,95 +0,0 @@ |
|||
<template> |
|||
<base-card |
|||
:disabled="!valid" |
|||
:title="$t('labels.createLink')" |
|||
:agree-text="$t('generic.save')" |
|||
:cancel-text="$t('generic.cancel')" |
|||
@agree="$emit('save')" |
|||
@cancel="$emit('cancel')" |
|||
> |
|||
<template #content> |
|||
<v-form v-model="valid"> |
|||
<v-text-field |
|||
:value="name" |
|||
:label="$t('labels.linkName')" |
|||
:rules="[rules.required, rules.counter, rules.nameDuplicated]" |
|||
:prepend-icon="mdiLabel" |
|||
single-line |
|||
counter |
|||
autofocus |
|||
@input="updateValue('name', $event)" |
|||
/> |
|||
<v-color-picker |
|||
:value="color" |
|||
:rules="[rules.required]" |
|||
show-swatches |
|||
hide-mode-switch |
|||
width="800" |
|||
@input="updateValue('color', $event)" |
|||
/> |
|||
</v-form> |
|||
</template> |
|||
</base-card> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue from 'vue' |
|||
import { mdiLabel } from '@mdi/js' |
|||
import BaseCard from '@/components/utils/BaseCard.vue' |
|||
|
|||
export default Vue.extend({ |
|||
components: { |
|||
BaseCard |
|||
}, |
|||
|
|||
props: { |
|||
name: { |
|||
type: String, |
|||
default: '', |
|||
required: true |
|||
}, |
|||
suffixKey: { |
|||
type: String, |
|||
default: null, |
|||
}, |
|||
color: { |
|||
type: String, |
|||
default: '#ffffff', |
|||
required: true |
|||
}, |
|||
usedNames: { |
|||
type: Array, |
|||
default: () => [], |
|||
required: true |
|||
} |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
valid: false, |
|||
rules: { |
|||
required: (v: string) => !!v || 'Required', |
|||
// @ts-ignore |
|||
counter: (v: string) => (v && v.length <= 100) || this.$t('rules.labelNameRules').labelLessThan100Chars, |
|||
// @ts-ignore |
|||
nameDuplicated: (v: string) => (!this.usedNames.includes(v)) || this.$t('rules.labelNameRules').duplicated, |
|||
// @ts-ignore |
|||
keyDuplicated: (v: string) => !this.usedKeys.includes(v) || this.$t('rules.keyNameRules').duplicated, |
|||
}, |
|||
mdiLabel |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
shortkeys() { |
|||
return '0123456789abcdefghijklmnopqrstuvwxyz'.split('') |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
updateValue(key: string, value: string) { |
|||
this.$emit(`update:${key}`, value); |
|||
} |
|||
} |
|||
}) |
|||
</script> |
@ -1,28 +0,0 @@ |
|||
<template> |
|||
<confirm-form |
|||
:items="selected" |
|||
title="Delete Label" |
|||
:message="$t('labels.deleteMessage')" |
|||
item-key="text" |
|||
@ok="$emit('remove')" |
|||
@cancel="$emit('cancel')" |
|||
/> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue from 'vue' |
|||
import ConfirmForm from '@/components/utils/ConfirmForm.vue' |
|||
|
|||
export default Vue.extend({ |
|||
components: { |
|||
ConfirmForm |
|||
}, |
|||
|
|||
props: { |
|||
selected: { |
|||
type: Array, |
|||
default: () => [] |
|||
} |
|||
} |
|||
}) |
|||
</script> |
@ -1,91 +0,0 @@ |
|||
<template> |
|||
<v-data-table |
|||
:value="value" |
|||
:headers="headers" |
|||
:items="items" |
|||
:search="search" |
|||
:loading="isLoading" |
|||
:loading-text="$t('generic.loading')" |
|||
:no-data-text="$t('vuetify.noDataAvailable')" |
|||
:footer-props="{ |
|||
'showFirstLastPage': true, |
|||
'items-per-page-options': [5, 10, 15, $t('generic.all')], |
|||
'items-per-page-text': $t('vuetify.itemsPerPageText'), |
|||
'page-text': $t('dataset.pageText') |
|||
}" |
|||
item-key="id" |
|||
show-select |
|||
@input="$emit('input', $event)" |
|||
> |
|||
<template #top> |
|||
<v-text-field |
|||
v-model="search" |
|||
:prepend-inner-icon="mdiMagnify" |
|||
:label="$t('generic.search')" |
|||
single-line |
|||
hide-details |
|||
filled |
|||
/> |
|||
</template> |
|||
<template #[`item.color`]="props"> |
|||
<v-chip |
|||
:color="props.item.color" |
|||
:text-color="$contrastColor(props.item.color)" |
|||
> |
|||
{{ props.item.color }} |
|||
</v-chip> |
|||
</template> |
|||
<template #[`item.actions`]="{ item }"> |
|||
<v-icon |
|||
small |
|||
@click="$emit('edit', item)" |
|||
> |
|||
{{ mdiPencil }} |
|||
</v-icon> |
|||
</template> |
|||
</v-data-table> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue, { PropType } from 'vue' |
|||
import { mdiPencil, mdiMagnify } from '@mdi/js' |
|||
import { LinkTypeDTO } from '~/services/application/links/linkData' |
|||
|
|||
export default Vue.extend({ |
|||
props: { |
|||
isLoading: { |
|||
type: Boolean, |
|||
default: false, |
|||
required: true |
|||
}, |
|||
items: { |
|||
type: Array as PropType<LinkTypeDTO[]>, |
|||
default: () => [], |
|||
required: true |
|||
}, |
|||
value: { |
|||
type: Array as PropType<LinkTypeDTO[]>, |
|||
default: () => [], |
|||
required: true |
|||
} |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
search: '', |
|||
mdiPencil, |
|||
mdiMagnify |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
headers() { |
|||
return [ |
|||
{ text: this.$t('generic.name'), value: 'name' }, |
|||
{ text: this.$t('labels.color'), value: 'color' }, |
|||
{ text: 'Actions', value: 'actions', sortable: false }, |
|||
] |
|||
} |
|||
} |
|||
}) |
|||
</script> |
@ -1,48 +0,0 @@ |
|||
export class LinkTypeItem { |
|||
constructor( |
|||
public id: number, |
|||
public name: string, |
|||
public color: string = '#1f1f1f' |
|||
) { |
|||
} |
|||
|
|||
static valueOf( |
|||
{id, name, color}: |
|||
{ id: number, name: string, color: string } |
|||
): LinkTypeItem { |
|||
return new LinkTypeItem(id, name, color) |
|||
} |
|||
|
|||
toObject(): Object { |
|||
return { |
|||
id: this.id, |
|||
name: this.name, |
|||
color: this.color |
|||
} |
|||
} |
|||
} |
|||
|
|||
export class LinkItem { |
|||
constructor( |
|||
public id: number, |
|||
public fromId: number, |
|||
public toId: number, |
|||
public type: number, |
|||
) { |
|||
} |
|||
|
|||
static valueOf( |
|||
{id, from_id, to_id, type}: { id: number, from_id: number, to_id: number, type: number } |
|||
): LinkItem { |
|||
return new LinkItem(id, from_id, to_id, type) |
|||
} |
|||
|
|||
toObject(): Object { |
|||
return { |
|||
id: this.id, |
|||
from_id: this.fromId, |
|||
to_id: this.toId, |
|||
type: this.type, |
|||
} |
|||
} |
|||
} |
@ -1,13 +0,0 @@ |
|||
import {LinkItem} from '~/domain/models/links/link' |
|||
|
|||
export interface LinkRepository { |
|||
list(projectId: string, exampleId: number): Promise<LinkItem[]> |
|||
|
|||
create(projectId: string, exampleId: number, link: LinkItem): Promise<LinkItem> |
|||
|
|||
update(projectId: string, exampleId: number, linkId: number, linkType: number): Promise<LinkItem> |
|||
|
|||
delete(projectId: string, exampleId: number, linkId: number): Promise<void> |
|||
|
|||
bulkDelete(projectId: string, exampleId: number, linkIds: number[]): Promise<void> |
|||
} |
@ -1,11 +0,0 @@ |
|||
import { LinkTypeItem } from '~/domain/models/links/link' |
|||
|
|||
export interface LinkTypesRepository { |
|||
list(projectId: string): Promise<LinkTypeItem[]> |
|||
|
|||
create(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem> |
|||
|
|||
update(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem> |
|||
|
|||
bulkDelete(projectId: string, linkIds: number[]): Promise<void> |
|||
} |
@ -0,0 +1,24 @@ |
|||
export class RelationItem { |
|||
constructor( |
|||
public id: number, |
|||
public fromId: number, |
|||
public toId: number, |
|||
public type: number, |
|||
) { |
|||
} |
|||
|
|||
static valueOf( |
|||
{id, from_id, to_id, type}: { id: number, from_id: number, to_id: number, type: number } |
|||
): RelationItem { |
|||
return new RelationItem(id, from_id, to_id, type) |
|||
} |
|||
|
|||
toObject(): Object { |
|||
return { |
|||
id: this.id, |
|||
from_id: this.fromId, |
|||
to_id: this.toId, |
|||
type: this.type, |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
import { RelationItem } from '~/domain/models/tasks/relation' |
|||
|
|||
export interface RelationRepository { |
|||
list(projectId: string, exampleId: number): Promise<RelationItem[]> |
|||
|
|||
create(projectId: string, exampleId: number, relation: RelationItem): Promise<RelationItem> |
|||
|
|||
update(projectId: string, exampleId: number, relationId: number, relationType: number): Promise<RelationItem> |
|||
|
|||
delete(projectId: string, exampleId: number, relationId: number): Promise<void> |
|||
|
|||
bulkDelete(projectId: string, exampleId: number, relationIds: number[]): Promise<void> |
|||
} |
@ -1,162 +0,0 @@ |
|||
<template> |
|||
<v-card> |
|||
<v-card-title> |
|||
<action-menu |
|||
@create="dialogCreate=true" |
|||
@upload="dialogUpload=true" |
|||
/> |
|||
<v-btn |
|||
class="text-capitalize ms-2" |
|||
:disabled="!canDelete" |
|||
outlined |
|||
@click.stop="dialogDelete=true" |
|||
> |
|||
{{ $t('generic.delete') }} |
|||
</v-btn> |
|||
<v-dialog v-model="dialogCreate"> |
|||
<form-create |
|||
v-bind.sync="editedItem" |
|||
:used-names="usedNames" |
|||
@cancel="close" |
|||
@save="save" |
|||
/> |
|||
</v-dialog> |
|||
<v-dialog v-model="dialogDelete"> |
|||
<form-delete |
|||
:selected="selected" |
|||
@cancel="dialogDelete=false" |
|||
@remove="remove" |
|||
/> |
|||
</v-dialog> |
|||
</v-card-title> |
|||
|
|||
<links-list |
|||
v-model="selected" |
|||
:items="items" |
|||
:is-loading="isLoading" |
|||
@edit="editItem" |
|||
/> |
|||
</v-card> |
|||
</template> |
|||
|
|||
<script lang="ts"> |
|||
import Vue from 'vue' |
|||
import ActionMenu from '@/components/links/ActionMenu.vue' |
|||
import FormCreate from '@/components/links/FormCreate.vue' |
|||
import FormDelete from '@/components/links/FormDelete.vue' |
|||
import LinksList from '~/components/links/LinksList.vue' |
|||
import { LinkTypeDTO } from '~/services/application/links/linkData' |
|||
import { ProjectDTO } from '~/services/application/project/projectData' |
|||
|
|||
export default Vue.extend({ |
|||
|
|||
components: { |
|||
ActionMenu, |
|||
FormCreate, |
|||
FormDelete, |
|||
LinksList |
|||
}, |
|||
layout: 'project', |
|||
|
|||
validate({ params, app }) { |
|||
if (/^\d+$/.test(params.id)) { |
|||
return app.$services.project.findById(params.id) |
|||
.then((res:ProjectDTO) => { |
|||
return res.canDefineRelation |
|||
}) |
|||
} |
|||
return false |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
dialogCreate: false, |
|||
dialogDelete: false, |
|||
dialogUpload: false, |
|||
editedIndex: -1, |
|||
editedItem: { |
|||
text: '', |
|||
color: '#ffffff' |
|||
} as LinkTypeDTO, |
|||
defaultItem: { |
|||
text: '', |
|||
color: '#ffffff' |
|||
} as LinkTypeDTO, |
|||
items: [] as LinkTypeDTO[], |
|||
selected: [] as LinkTypeDTO[], |
|||
isLoading: false, |
|||
errorMessage: '' |
|||
} |
|||
}, |
|||
|
|||
async fetch() { |
|||
this.isLoading = true |
|||
this.items = await this.$services.linkTypes.list(this.projectId) |
|||
this.isLoading = false |
|||
}, |
|||
|
|||
computed: { |
|||
canDelete(): boolean { |
|||
return this.selected.length > 0 |
|||
}, |
|||
projectId(): string { |
|||
return this.$route.params.id |
|||
}, |
|||
usedNames(): string[] { |
|||
const item = this.items[this.editedIndex] // to remove myself |
|||
return this.items.filter(_ => _ !== item).map(item => item.text) |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
async create() { |
|||
await this.$services.linkTypes.create(this.projectId, this.editedItem) |
|||
}, |
|||
|
|||
async update() { |
|||
await this.$services.linkTypes.update(this.projectId, this.editedItem) |
|||
}, |
|||
|
|||
save() { |
|||
if (this.editedIndex > -1) { |
|||
this.update() |
|||
} else { |
|||
this.create() |
|||
} |
|||
this.$fetch() |
|||
this.close() |
|||
}, |
|||
|
|||
close() { |
|||
this.dialogCreate = false |
|||
this.$nextTick(() => { |
|||
this.editedItem = Object.assign({}, this.defaultItem) |
|||
this.editedIndex = -1 |
|||
}) |
|||
}, |
|||
|
|||
async remove() { |
|||
await this.$services.linkTypes.bulkDelete(this.projectId, this.selected) |
|||
this.$fetch() |
|||
this.dialogDelete = false |
|||
this.selected = [] |
|||
}, |
|||
|
|||
clearErrorMessage() { |
|||
this.errorMessage = '' |
|||
}, |
|||
|
|||
editItem(item: LinkTypeDTO) { |
|||
this.editedIndex = this.items.indexOf(item) |
|||
this.editedItem = Object.assign({}, item) |
|||
this.dialogCreate = true |
|||
} |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<style scoped> |
|||
::v-deep .v-dialog { |
|||
width: 800px; |
|||
} |
|||
</style> |
@ -1,38 +0,0 @@ |
|||
import ApiService from '@/services/api.service' |
|||
import {LinkRepository} from "~/domain/models/links/linkRepository"; |
|||
import {LinkItem} from "~/domain/models/links/link"; |
|||
|
|||
export class ApiLinkRepository implements LinkRepository { |
|||
constructor( |
|||
private readonly request = ApiService |
|||
) { |
|||
} |
|||
|
|||
async list(projectId: string, exampleId: number): Promise<LinkItem[]> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations` |
|||
const response = await this.request.get(url) |
|||
return response.data.map((link: any) => LinkItem.valueOf(link)) |
|||
} |
|||
|
|||
async create(projectId: string, exampleId: number, item: LinkItem): Promise<LinkItem> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations` |
|||
const response = await this.request.post(url, item.toObject()) |
|||
return LinkItem.valueOf(response.data) |
|||
} |
|||
|
|||
async update(projectId: string, exampleId: number, linkId: number, linkType: number): Promise<LinkItem> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations/${linkId}` |
|||
const response = await this.request.patch(url, {type: linkType}) |
|||
return LinkItem.valueOf(response.data) |
|||
} |
|||
|
|||
async delete(projectId: string, exampleId: number, linkId: number): Promise<void> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations/${linkId}` |
|||
const response = await this.request.delete(url) |
|||
} |
|||
|
|||
async bulkDelete(projectId: string, exampleId: number, linkIds: number[]): Promise<void> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations` |
|||
await this.request.delete(url, {ids: linkIds}) |
|||
} |
|||
} |
@ -1,41 +0,0 @@ |
|||
import ApiService from '@/services/api.service' |
|||
import { LinkTypesRepository } from "~/domain/models/links/linkTypesRepository"; |
|||
import { LinkTypeItem } from "~/domain/models/links/link"; |
|||
|
|||
export interface LinkTypeResponse { |
|||
id: number, |
|||
name: string, |
|||
color: string |
|||
} |
|||
|
|||
export class ApiLinkTypesRepository implements LinkTypesRepository { |
|||
constructor( |
|||
private readonly request = ApiService |
|||
) {} |
|||
|
|||
async list(projectId: string): Promise<LinkTypeItem[]> { |
|||
const url = `/projects/${projectId}/relation_types` |
|||
const response = await this.request.get(url) |
|||
const responseItems: LinkTypeResponse[] = response.data |
|||
return responseItems.map(item => LinkTypeItem.valueOf(item)) |
|||
} |
|||
|
|||
async create(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem> { |
|||
const url = `/projects/${projectId}/relation_types` |
|||
const response = await this.request.post(url, item.toObject()) |
|||
const responseItem: LinkTypeResponse = response.data |
|||
return LinkTypeItem.valueOf(responseItem) |
|||
} |
|||
|
|||
async update(projectId: string, item: LinkTypeItem): Promise<LinkTypeItem> { |
|||
const url = `/projects/${projectId}/relation_types/${item.id}` |
|||
const response = await this.request.patch(url, item.toObject()) |
|||
const responseItem: LinkTypeResponse = response.data |
|||
return LinkTypeItem.valueOf(responseItem) |
|||
} |
|||
|
|||
async bulkDelete(projectId: string, linkIds: number[]): Promise<void> { |
|||
const url = `/projects/${projectId}/relation_types` |
|||
await this.request.delete(url, { ids: linkIds }) |
|||
} |
|||
} |
@ -0,0 +1,38 @@ |
|||
import ApiService from '@/services/api.service' |
|||
import { RelationRepository } from "~/domain/models/tasks/relationRepository" |
|||
import { RelationItem } from "~/domain/models/tasks/relation" |
|||
|
|||
export class ApiRelationRepository implements RelationRepository { |
|||
constructor( |
|||
private readonly request = ApiService |
|||
) { |
|||
} |
|||
|
|||
async list(projectId: string, exampleId: number): Promise<RelationItem[]> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations` |
|||
const response = await this.request.get(url) |
|||
return response.data.map((relation: any) => RelationItem.valueOf(relation)) |
|||
} |
|||
|
|||
async create(projectId: string, exampleId: number, item: RelationItem): Promise<RelationItem> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations` |
|||
const response = await this.request.post(url, item.toObject()) |
|||
return RelationItem.valueOf(response.data) |
|||
} |
|||
|
|||
async update(projectId: string, exampleId: number, relationId: number, relationType: number): Promise<RelationItem> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations/${relationId}` |
|||
const response = await this.request.patch(url, {type: relationType}) |
|||
return RelationItem.valueOf(response.data) |
|||
} |
|||
|
|||
async delete(projectId: string, exampleId: number, relationId: number): Promise<void> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations/${relationId}` |
|||
const response = await this.request.delete(url) |
|||
} |
|||
|
|||
async bulkDelete(projectId: string, exampleId: number, relationIds: number[]): Promise<void> { |
|||
const url = `/projects/${projectId}/examples/${exampleId}/relations` |
|||
await this.request.delete(url, {ids: relationIds}) |
|||
} |
|||
} |
@ -1,27 +0,0 @@ |
|||
import { LinkTypeItem, LinkItem } from '~/domain/models/links/link' |
|||
|
|||
export class LinkTypeDTO { |
|||
id: number |
|||
text: string |
|||
color: string |
|||
|
|||
constructor(item: LinkTypeItem) { |
|||
this.id = item.id |
|||
this.text = item.name |
|||
this.color = item.color |
|||
} |
|||
} |
|||
|
|||
export class LinkDTO { |
|||
id: number |
|||
fromId: number |
|||
toId: number |
|||
labelId: number |
|||
|
|||
constructor(item: LinkItem) { |
|||
this.id = item.id |
|||
this.fromId = item.fromId |
|||
this.toId = item.toId |
|||
this.labelId = item.type |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
import { LinkTypeDTO } from './linkData'; |
|||
import { LinkTypesRepository } from '~/domain/models/links/linkTypesRepository'; |
|||
import { LinkTypeItem } from '~/domain/models/links/link'; |
|||
|
|||
|
|||
export class LinkTypesApplicationService { |
|||
constructor( |
|||
private readonly repository: LinkTypesRepository |
|||
) {} |
|||
|
|||
public async list(id: string): Promise<LinkTypeDTO[]> { |
|||
const items = await this.repository.list(id) |
|||
return items.map(item => new LinkTypeDTO(item)) |
|||
} |
|||
|
|||
public create(projectId: string, item: LinkTypeDTO): void { |
|||
const label = new LinkTypeItem(0, item.text, item.color) |
|||
this.repository.create(projectId, label) |
|||
} |
|||
|
|||
public update(projectId: string, item: LinkTypeDTO): void { |
|||
const label = new LinkTypeItem(item.id, item.text, item.color) |
|||
this.repository.update(projectId, label) |
|||
} |
|||
|
|||
public bulkDelete(projectId: string, items: LinkTypeDTO[]): Promise<void> { |
|||
const ids = items.map(item => item.id) |
|||
return this.repository.bulkDelete(projectId, ids) |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
import { RelationItem } from '~/domain/models/tasks/relation' |
|||
|
|||
export class RelationDTO { |
|||
id: number |
|||
fromId: number |
|||
toId: number |
|||
labelId: number |
|||
|
|||
constructor(item: RelationItem) { |
|||
this.id = item.id |
|||
this.fromId = item.fromId |
|||
this.toId = item.toId |
|||
this.labelId = item.type |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save