This commit is contained in:
polv committed 2021-10-24 22:44:24 +07:00
1 parent d3fdba1139
commit 70066f3086
5 files changed
+81 -77

No files matched your search

+8 -4
View File
@@ -6,7 +6,7 @@ export interface IEntry {
id: string id: string
entry: string[] entry: string[]
reading: string[] reading: string[]
english: string[] translation: string[]
type: IType type: IType
description: string description: string
tag: string[] tag: string[]
@@ -148,7 +148,7 @@ class Api {
} }
async extra_delete(params: { id: string }) { async extra_delete(params: { id: string }) {
return await this.axios.delete('/api/extra/delete', { return await this.axios.delete('/api/extra', {
params, params,
}) })
} }
@@ -167,7 +167,9 @@ class Api {
) )
} }
async character_getVocabulary(params: ILookup<{ entry: string }>) { async character_getVocabulary(
params: ILookup<{ entry: string; distinct?: 'id' }>
) {
return await this.axios.get('/api/character/vocabulary', { params }).then( return await this.axios.get('/api/character/vocabulary', { params }).then(
(r) => (r) =>
r.data as ILookupResult<{ r.data as ILookupResult<{
@@ -176,7 +178,9 @@ class Api {
) )
} }
async character_getSentence(params: ILookup<{ entry: string }>) { async character_getSentence(
params: ILookup<{ entry: string; distinct?: 'id' }>
) {
return await this.axios.get('/api/character/sentence', { params }).then( return await this.axios.get('/api/character/sentence', { params }).then(
(r) => (r) =>
r.data as ILookupResult<{ r.data as ILookupResult<{
+4
View File
@@ -74,3 +74,7 @@ export function capitalize(s: string): string {
if (!s) return s if (!s) return s
return s[0].toLocaleUpperCase() + s.substr(1) return s[0].toLocaleUpperCase() + s.substr(1)
} }
export function jsonClone<T>(o: T): T {
return JSON.parse(JSON.stringify(o))
}
+21 -42
View File
@@ -40,42 +40,39 @@
Remove from quiz Remove from quiz
</a> </a>
</li> </li>
<li v-if="entries.length === 1" class="separator"> <li v-if="entry" class="separator">
<a></a> <a></a>
</li> </li>
<li v-if="entries.length === 1 && type === 'vocabulary'"> <li v-if="entry && type === 'vocabulary'">
<a <a role="button" @click="openInNewTab('Vocabulary', { entry })">
role="button"
@click="openInNewTab('Vocabulary', { entry: entries[0] })"
>
Show details Show details
</a> </a>
</li> </li>
<li v-if="entries.length === 1"> <li v-if="entry">
<a role="button" @click="openInNewTab('Character', { q: entries[0] })"> <a role="button" @click="openInNewTab('Character', { q: entry })">
{{ type === 'character' ? 'Show details' : 'Search for Hanzi' }} {{ type === 'character' ? 'Show details' : 'Search for Hanzi' }}
</a> </a>
</li> </li>
<li v-if="entries.length === 1 && type !== 'character'"> <li v-if="entry && type !== 'character'">
<a role="button" @click="openInNewTab('Vocabulary', { q: entries[0] })"> <a role="button" @click="openInNewTab('Vocabulary', { q: entry })">
Search for vocabs Search for vocabs
</a> </a>
</li> </li>
<li v-if="entries.length === 1"> <li v-if="entry">
<a <a
role="button" role="button"
:href="`https://jisho.org/word/${encodeURIComponent(entries[0])}`" :href="`https://jisho.org/word/${encodeURIComponent(entry)}`"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
Open in Jisho.org Open in Jisho.org
</a> </a>
</li> </li>
<li v-if="entries.length === 1 && type !== 'sentence'"> <li v-if="entry && type !== 'sentence'">
<a <a
role="button" role="button"
:href="`https://dictionary.goo.ne.jp/srch/all/${encodeURIComponent( :href="`https://dictionary.goo.ne.jp/srch/all/${encodeURIComponent(
entries[0] entry
)}/m0u`" )}/m0u`"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@@ -98,19 +95,18 @@
<script lang="ts"> <script lang="ts">
import { Vue, Component, Prop, Ref } from 'nuxt-property-decorator' import { Vue, Component, Prop, Ref } from 'nuxt-property-decorator'
import { speak } from '@/assets/speak' import { speak } from '~/assets/speak'
import { api } from '~/assets/api' import { api, IType } from '~/assets/api'
@Component @Component
export default class ContextMenu extends Vue { export default class ContextMenu extends Vue {
@Prop() id?: string @Prop() id?: string
@Prop() entry?: string | string[] @Prop() entry?: string
@Prop() type?: string @Prop() list?: string[]
@Prop() type?: IType
@Prop() source?: string @Prop() source?: string
@Prop() direction?: string @Prop() direction?: string
@Prop() description?: string @Prop() description?: string
@Prop({ default: () => ({}) }) pinyin!: Record<string, string>
@Prop({ default: () => ({}) }) english!: Record<string, string>
@Prop({ default: () => [] }) additional!: { @Prop({ default: () => [] }) additional!: {
name: string name: string
@@ -134,15 +130,11 @@ export default class ContextMenu extends Vue {
} }
get entries() { get entries() {
return Array.isArray(this.entry) return this.list || (this.entry ? [this.entry] : [])
? this.entry
: this.entry
? [this.entry]
: []
} }
async setQuiz() { async setQuiz() {
if (this.entry && this.type) { if (this.entries.length && this.type) {
const { result } = await api.quiz_getMany({ const { result } = await api.quiz_getMany({
entries: this.entries, entries: this.entries,
select: ['id', 'entry'], select: ['id', 'entry'],
@@ -168,21 +160,8 @@ export default class ContextMenu extends Vue {
} }
open(evt: MouseEvent) { open(evt: MouseEvent) {
this.$nextTick(async () => { this.$nextTick(() => {
if (this.entry && this.type) {
if (
!(
this.quiz.of &&
this.quiz.of.type !== this.type &&
this.quiz.of.entries.length === this.entries.length &&
this.quiz.of.entries.every((el, i) => this.entries[i] === el)
)
) {
await this.setQuiz()
}
this.contextmenu.open(evt) this.contextmenu.open(evt)
}
}) })
} }
@@ -197,8 +176,8 @@ export default class ContextMenu extends Vue {
} }
async doSpeak() { async doSpeak() {
if (this.entries.length === 1) { if (this.entry) {
await speak(this.entries[0]) await speak(this.entry)
} }
} }
+30 -19
View File
@@ -67,8 +67,8 @@
</span> </span>
</b-table-column> </b-table-column>
<b-table-column <b-table-column
field="english" field="translation"
label="English" label="Translation"
v-slot="props" v-slot="props"
width="40vw" width="40vw"
> >
@@ -77,7 +77,7 @@
@click="(ev) => onTableContextmenu(props.row, ev)" @click="(ev) => onTableContextmenu(props.row, ev)"
@contextmenu.prevent="(ev) => onTableContextmenu(props.row, ev)" @contextmenu.prevent="(ev) => onTableContextmenu(props.row, ev)"
> >
<div v-for="it in props.row.english" :key="it"> <div v-for="it in props.row.translation" :key="it">
{{ it }} {{ it }}
</div> </div>
</span> </span>
@@ -122,16 +122,16 @@
placeholder="Must not be empty" placeholder="Must not be empty"
></b-input> ></b-input>
</b-field> </b-field>
<b-field label="English"> <b-field label="Translation">
<template slot="label"> <template slot="label">
English Translation
<b-tooltip type="is-dark" label="New-line separated"> <b-tooltip type="is-dark" label="New-line separated">
<b-icon size="is-small" icon="info-circle"></b-icon> <b-icon size="is-small" icon="info-circle"></b-icon>
</b-tooltip> </b-tooltip>
</template> </template>
<b-input <b-input
:value="selected.english.join('\n')" :value="selected.translation.join('\n')"
@input="(ev) => (selected.english = ev.trim().split('\n'))" @input="(ev) => (selected.translation = ev.trim().split('\n'))"
type="textarea" type="textarea"
placeholder="Must not be empty" placeholder="Must not be empty"
></b-input> ></b-input>
@@ -179,7 +179,8 @@
<ContextMenu <ContextMenu
:id="selected.id" :id="selected.id"
ref="context" ref="context"
:entry="selected.entry[0]" :entry="selected.entry"
:list="selected.list"
:type="selected.type || 'vocabulary'" :type="selected.type || 'vocabulary'"
:description="selected.description" :description="selected.description"
source="extra" source="extra"
@@ -193,6 +194,7 @@
import { Component, Ref, Vue, Watch } from 'nuxt-property-decorator' import { Component, Ref, Vue, Watch } from 'nuxt-property-decorator'
import ContextMenu from '@/components/ContextMenu.vue' import ContextMenu from '@/components/ContextMenu.vue'
import { api, IEntry } from '~/assets/api' import { api, IEntry } from '~/assets/api'
import { jsonClone } from '~/assets/util'
@Component<BrowseTab>({ @Component<BrowseTab>({
components: { components: {
@@ -220,11 +222,15 @@ export default class BrowseTab extends Vue {
type: 'desc', type: 'desc',
} }
selected: IEntry = { selected: Omit<IEntry, 'entry'> & {
entry: string
list: string[]
} = {
id: '', id: '',
entry: [], entry: '',
list: [],
reading: [], reading: [],
english: [], translation: [],
type: 'vocabulary', type: 'vocabulary',
description: '', description: '',
tag: [], tag: [],
@@ -257,8 +263,8 @@ export default class BrowseTab extends Vue {
] ]
} }
if (!this.selected.english.length) { if (!this.selected.translation.length) {
this.selected.english = await api this.selected.translation = await api
.all_getMeaning({ .all_getMeaning({
entry: this.selected.entry[0], entry: this.selected.entry[0],
}) })
@@ -292,9 +298,8 @@ export default class BrowseTab extends Vue {
async doCreate() { async doCreate() {
const { id } = await api.extra_create({ const { id } = await api.extra_create({
...this.selected, ...this.selected,
description: this.selected.description || '', entry: this.selected.list,
}) })
this.selected.id = id this.selected.id = id
await this.context.addToQuiz() await this.context.addToQuiz()
@@ -309,7 +314,7 @@ export default class BrowseTab extends Vue {
if (id) { if (id) {
await api.extra_update({ await api.extra_update({
...this.selected, ...this.selected,
description: this.selected.description || '', entry: this.selected.list,
id, id,
}) })
@@ -337,9 +342,10 @@ export default class BrowseTab extends Vue {
onNewItem() { onNewItem() {
this.selected = { this.selected = {
id: '', id: '',
entry: [], entry: '',
list: [],
reading: [], reading: [],
english: [], translation: [],
type: 'vocabulary', type: 'vocabulary',
description: '', description: '',
tag: [], tag: [],
@@ -351,7 +357,12 @@ export default class BrowseTab extends Vue {
async onTableContextmenu(row: IEntry, evt: MouseEvent) { async onTableContextmenu(row: IEntry, evt: MouseEvent) {
evt.preventDefault() evt.preventDefault()
this.selected = JSON.parse(JSON.stringify(row)) const o = jsonClone(row)
this.selected = {
...o,
entry: o.entry[0],
list: o.entry,
}
this.context.open(evt) this.context.open(evt)
} }
+18 -12
View File
@@ -95,21 +95,25 @@
</div> </div>
</b-collapse> </b-collapse>
<b-collapse class="card" animation="slide" :open="!!english.length"> <b-collapse
class="card"
animation="slide"
:open="!!translation.length"
>
<div <div
slot="trigger" slot="trigger"
slot-scope="props" slot-scope="props"
class="card-header" class="card-header"
role="button" role="button"
> >
<h2 class="card-header-title">English</h2> <h2 class="card-header-title">Translation</h2>
<a role="button" class="card-header-icon"> <a role="button" class="card-header-icon">
<fontawesome :icon="props.open ? 'caret-down' : 'caret-up'" /> <fontawesome :icon="props.open ? 'caret-down' : 'caret-up'" />
</a> </a>
</div> </div>
<div class="card-content"> <div class="card-content">
{{ english.join(' / ') }} {{ translation.join(' / ') }}
</div> </div>
</b-collapse> </b-collapse>
@@ -222,7 +226,7 @@
<span class="pinyin">[{{ v.reading.join(' / ') }}]</span> <span class="pinyin">[{{ v.reading.join(' / ') }}]</span>
<span>{{ v.english.join(' / ') }}</span> <span>{{ v.translation.join(' / ') }}</span>
</div> </div>
</div> </div>
</b-collapse> </b-collapse>
@@ -244,15 +248,15 @@
<div v-for="(s, i) in sentences" :key="i" class="long-item"> <div v-for="(s, i) in sentences" :key="i" class="long-item">
<span <span
class="clickable" class="clickable"
@click="(evt) => openContext(evt, s.entry, 'sentence')" @click="(evt) => openContext(evt, s.entry[0], 'sentence')"
@contextmenu.prevent=" @contextmenu.prevent="
(evt) => openContext(evt, s.entry, 'sentence') (evt) => openContext(evt, s.entry[0], 'sentence')
" "
> >
{{ s.entry }} {{ s.entry[0] }}
</span> </span>
<span>{{ s.english }}</span> <span>{{ s.translation[0] }}</span>
</div> </div>
</div> </div>
</b-collapse> </b-collapse>
@@ -304,7 +308,7 @@ export default class CharacterTab extends Vue {
visual: string[] = [] visual: string[] = []
reading: string[] = [] reading: string[] = []
english: string[] = [] translation: string[] = []
tag: string[] = [] tag: string[] = []
level = 0 level = 0
@@ -387,7 +391,7 @@ export default class CharacterTab extends Vue {
this.visual = [] this.visual = []
this.reading = [] this.reading = []
this.english = [] this.translation = []
this.tag = [] this.tag = []
this.level = 0 this.level = 0
this.vocabulary_lookup = [] this.vocabulary_lookup = []
@@ -402,7 +406,7 @@ export default class CharacterTab extends Vue {
this.variant = [] this.variant = []
this.visual = [] this.visual = []
this.english = [] this.translation = []
this.tag = [] this.tag = []
this.level = 0 this.level = 0
return return
@@ -428,7 +432,7 @@ export default class CharacterTab extends Vue {
}) })
this.reading = r?.reading || [] this.reading = r?.reading || []
this.english = r?.english || [] this.translation = r?.translation || []
this.tag = r?.tag || [] this.tag = r?.tag || []
this.level = r?.level || 0 this.level = r?.level || 0
})(), })(),
@@ -443,6 +447,7 @@ export default class CharacterTab extends Vue {
const { result } = await api.character_getVocabulary({ const { result } = await api.character_getVocabulary({
entry: this.current, entry: this.current,
distinct: 'id',
}) })
this.vocabulary_lookup = await Promise.all( this.vocabulary_lookup = await Promise.all(
@@ -465,6 +470,7 @@ export default class CharacterTab extends Vue {
const { result } = await api.character_getSentence({ const { result } = await api.character_getSentence({
entry: this.current, entry: this.current,
distinct: 'id',
}) })
this.vocabulary_lookup = await Promise.all( this.vocabulary_lookup = await Promise.all(