feat:修改类型结构
This commit is contained in:
224427
js_node/save/normalList.json
Normal file
224427
js_node/save/normalList.json
Normal file
File diff suppressed because it is too large
Load Diff
63692
js_node/save/unnormalList-fetch.json
Normal file
63692
js_node/save/unnormalList-fetch.json
Normal file
File diff suppressed because it is too large
Load Diff
179158
js_node/save/unnormalList.json
Normal file
179158
js_node/save/unnormalList.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,8 @@ async function crawlWord(val, page,) {
|
||||
try {
|
||||
await page.goto(url, {waitUntil: 'networkidle', timeout: 15000});
|
||||
|
||||
const titleEl = await page.locator('.title').first();
|
||||
data.word = await titleEl.evaluate(el => el.firstChild?.nodeValue || '');
|
||||
// const titleEl = await page.locator('.title').first();
|
||||
// data.word = await titleEl.evaluate(el => el.firstChild?.nodeValue || '');
|
||||
|
||||
const phones = await page.$$('.per-phone .phonetic');
|
||||
if (phones[0]) data.phonetic0 = (await phones[0].textContent())?.trim() || '';
|
||||
@@ -142,15 +142,20 @@ async function crawlWord(val, page,) {
|
||||
let removeList = raw.slice()
|
||||
const resultMap = new Map();
|
||||
let newFileName = file.replaceAll('.json', '-fetch.json')
|
||||
const newRaw = JSON.parse(fs.readFileSync(newFileName, 'utf-8'));
|
||||
newRaw.map(word => {
|
||||
resultMap.set(word.word, word);
|
||||
})
|
||||
try {
|
||||
const newRaw = JSON.parse(fs.readFileSync(newFileName, 'utf-8'));
|
||||
console.log('已保存:', newRaw.length);
|
||||
newRaw.map(word => {
|
||||
resultMap.set(word.word, word);
|
||||
})
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
let word = raw[i];
|
||||
console.log(`爬取:${file},${word.word},进度:${resultMap.size} / ${raw.length};时间:${dayjs().format('YYYY-MM-DD HH:mm:ss')}`)
|
||||
console.log(`爬取:${file},${word.word},进度:${i} / ${raw.length};时间:${dayjs().format('YYYY-MM-DD HH:mm:ss')}`)
|
||||
const result = await crawlWord(word, page);
|
||||
if (result) {
|
||||
resultMap.set(word.word, result);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {DictResource, DictType} from "@/types.ts"
|
||||
import {DictResource, DictType} from "@/types/types.ts"
|
||||
|
||||
// 中国考试
|
||||
const chinaExam = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {DictResource, DictType} from "@/types.ts"
|
||||
import {DictResource, DictType} from "@/types/types.ts"
|
||||
|
||||
// 国际考试
|
||||
const internationalExam: DictResource[] = [
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
slideTouchMove,
|
||||
slideTouchStart
|
||||
} from "./common";
|
||||
import {SlideType} from "@/types.ts";
|
||||
import {SlideType} from "@/types/types.ts";
|
||||
|
||||
const props = defineProps({
|
||||
index: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {emitter as bus} from "@/utils/eventBus.ts";
|
||||
import Utils from '@/utils/gm.js'
|
||||
import {SlideType} from "@/types.ts";
|
||||
import {SlideType} from "@/types/types.ts";
|
||||
import GM from "@/utils/gm.js";
|
||||
|
||||
export function slideInit(el, state, type) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Article, ArticleWord, getDefaultArticleWord, Sentence} from "@/types.ts";
|
||||
import {Article, ArticleWord, getDefaultArticleWord, Sentence} from "@/types/types.ts";
|
||||
import {cloneDeep} from "@/utils";
|
||||
import nlp from "compromise/one";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Article, getDefaultArticle, Word} from "@/types.ts";
|
||||
import {Article, getDefaultArticle, Word} from "@/types/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {nanoid} from "nanoid";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {onMounted, watch, watchEffect} from "vue"
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {PronunciationApi} from "@/types.ts";
|
||||
import {PronunciationApi} from "@/types/types.ts";
|
||||
import beep from "@/assets/sound/beep.wav";
|
||||
import correct from "@/assets/sound/correct.wav";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Article, Sentence, TranslateEngine} from "@/types.ts";
|
||||
import {Article, Sentence, TranslateEngine} from "@/types/types.ts";
|
||||
import Baidu from "@opentranslate/baidu";
|
||||
import {Translator} from "@opentranslate/translator/src/translator.ts";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {getAudioFileUrl, useChangeAllSound, usePlayAudio, useWatchAllSound} from "@/hooks/sound.ts";
|
||||
import {getShortcutKey, useDisableEventListener, useEventListener} from "@/hooks/event.ts";
|
||||
import {cloneDeep} from "@/utils";
|
||||
import {DefaultShortcutKeyMap, ShortcutKey} from "@/types.ts";
|
||||
import {DefaultShortcutKeyMap, ShortcutKey} from "@/types/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {APP_NAME, EXPORT_DATA_KEY, SAVE_DICT_KEY, SAVE_SETTING_KEY, SoundFileOptions} from "@/utils/const.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
|
||||
@@ -5,7 +5,7 @@ import "vue-activity-calendar/style.css";
|
||||
import {useRouter} from "vue-router";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {_getDictDataByUrl, useNav} from "@/utils";
|
||||
import {DictResource, DictType, getDefaultDict} from "@/types.ts";
|
||||
import {DictResource, DictType, getDefaultDict} from "@/types/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Book from "@/pages/pc/components/Book.vue";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import {Article, getDefaultArticle} from "@/types/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {cloneDeep} from "@/utils";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
|
||||
@@ -5,7 +5,7 @@ import BackIcon from "@/pages/pc/components/BackIcon.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import ArticleList from "@/pages/pc/components/list/ArticleList.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {Article, DictId, DictType, getDefaultArticle, getDefaultDict} from "@/types.ts";
|
||||
import {Article, DictId, DictType, getDefaultArticle, getDefaultDict} from "@/types/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import "vue-activity-calendar/style.css";
|
||||
import {useNav} from "@/utils";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {DictResource, getDefaultDict} from "@/types.ts";
|
||||
import {DictResource, getDefaultDict} from "@/types/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
|
||||
@@ -8,7 +8,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import PracticeArticle from "@/pages/pc/article/practice-article/index.vue";
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import {ShortcutKey} from "@/types/types.ts";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Article, getDefaultArticle, Sentence, TranslateEngine} from "@/types.ts";
|
||||
import {Article, getDefaultArticle, Sentence, TranslateEngine} from "@/types/types.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import EditAbleText from "@/pages/pc/components/EditAbleText.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Dict, DictType, getDefaultDict} from "@/types.ts";
|
||||
import {Dict, DictType, getDefaultDict} from "@/types/types.ts";
|
||||
import {cloneDeep} from "@/utils";
|
||||
|
||||
import {ElForm, ElFormItem, ElInput, ElSelect, ElOption, FormInstance, FormRules, ElMessage} from "element-plus";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Article, getDefaultArticle} from "@/types.ts";
|
||||
import {Article, getDefaultArticle} from "@/types/types.ts";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import EditArticle2 from "@/pages/pc/article/components/EditArticle2.vue";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, onUnmounted, watch} from "vue"
|
||||
import {Article, ArticleWord, getDefaultArticle, Sentence, Word} from "@/types.ts";
|
||||
import {Article, ArticleWord, getDefaultArticle, Sentence, Word} from "@/types/types.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import TypingArticle from "./TypingArticle.vue";
|
||||
import {Article, ArticleItem, ArticleWord, DisplayStatistics, getDefaultArticle, ShortcutKey, Word} from "@/types.ts";
|
||||
import {Article, ArticleItem, ArticleWord, DisplayStatistics, getDefaultArticle, ShortcutKey, Word} from "@/types/types.ts";
|
||||
import {cloneDeep} from "@/utils";
|
||||
import Panel from "../../components/Panel.vue";
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="tsx">
|
||||
|
||||
import {nextTick, useSlots} from "vue";
|
||||
import {Sort} from "@/types.ts";
|
||||
import {Sort} from "@/types/types.ts";
|
||||
import MiniDialog from "@/pages/pc/components/dialog/MiniDialog.vue";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {Dict, DictResource} from "@/types.ts";
|
||||
import {Dict, DictResource} from "@/types/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {ElProgress, ElCheckbox} from 'element-plus';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, provide} from "vue"
|
||||
import {ShortcutKey} from "@/types.ts"
|
||||
import {ShortcutKey} from "@/types/types.ts"
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import Close from "@/components/icon/Close.vue";
|
||||
import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Word} from "@/types.ts";
|
||||
import {Word} from "@/types/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {ElPopover} from 'element-plus'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Input from "@/pages/pc/components/Input.vue";
|
||||
import {Article} from "@/types.ts";
|
||||
import {Article} from "@/types/types.ts";
|
||||
import BaseList from "@/pages/pc/components/list/BaseList.vue";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import {watch} from "vue";
|
||||
import {DictResource} from "@/types.ts";
|
||||
import {DictResource} from "@/types/types.ts";
|
||||
import DictList from "@/pages/pc/components/list/DictList.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {Dict} from "@/types.ts";
|
||||
import {Dict} from "@/types/types.ts";
|
||||
import Book from "@/pages/pc/components/Book.vue";
|
||||
|
||||
defineProps<{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Input from "@/pages/pc/components/Input.vue";
|
||||
import {cloneDeep, throttle} from "@/utils";
|
||||
import {Article} from "@/types.ts";
|
||||
import {Article} from "@/types/types.ts";
|
||||
|
||||
interface IProps {
|
||||
list: T[]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {Word} from "@/types.ts";
|
||||
import {Word} from "@/types/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import BaseList from "@/pages/pc/components/list/BaseList.vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {ShortcutKey} from "@/types.ts";
|
||||
import {ShortcutKey} from "@/types/types.ts";
|
||||
import Logo from "@/pages/pc/components/Logo.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="tsx">
|
||||
import type {Word} from "@/types";
|
||||
import {DictId, getDefaultDict} from "@/types";
|
||||
import {DictId, getDefaultDict, Word} from "@/types/types.ts";
|
||||
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {computed, onMounted, reactive, shallowReactive} from "vue";
|
||||
@@ -64,8 +63,7 @@ function syncDictInMyStudyList(study = false) {
|
||||
_nextTick(() => {
|
||||
let rIndex = base.word.bookList.findIndex(v => v.id === runtimeStore.editDict.id)
|
||||
let temp = cloneDeep(runtimeStore.editDict);
|
||||
console.log(temp)
|
||||
if (!temp.custom) {
|
||||
if (!temp.custom && ![DictId.wordKnown, DictId.wordWrong, DictId.wordCollect].includes(temp.id)) {
|
||||
temp.custom = true
|
||||
temp.id += '_custom'
|
||||
}
|
||||
@@ -206,7 +204,7 @@ defineRender(() => {
|
||||
<div class="flex justify-between items-center relative">
|
||||
<BackIcon class="z-2" onClick={() => router.back()}/>
|
||||
<div class="absolute page-title text-align-center w-full">{runtimeStore.editDict.name}</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="flex">
|
||||
<BaseButton type="info" onClick={() => isEdit = true}>编辑</BaseButton>
|
||||
<BaseButton loading={studyLoading} onClick={addMyStudyList}>学习</BaseButton>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import "vue-activity-calendar/style.css";
|
||||
import {useNav} from "@/utils";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {DictResource, getDefaultDict} from "@/types.ts";
|
||||
import {DictResource, getDefaultDict} from "@/types/types.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {ShortcutKey, Statistics} from "@/types.ts";
|
||||
import {ShortcutKey, Statistics} from "@/types/types.ts";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {Icon} from '@iconify/vue';
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
|
||||
@@ -6,7 +6,7 @@ import Statistics from "@/pages/pc/word/Statistics.vue";
|
||||
import {emitter, EventKey, useEvents} from "@/utils/eventBus.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {getDefaultWord, ShortcutKey, StudyData, Word} from "@/types.ts";
|
||||
import {getDefaultWord, ShortcutKey, StudyData, Word} from "@/types/types.ts";
|
||||
import {useOnKeyboardEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import useTheme from "@/hooks/theme.ts";
|
||||
import {getCurrentStudyWord, useWordOptions} from "@/hooks/dict.ts";
|
||||
|
||||
@@ -8,7 +8,7 @@ import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Dialog from "@/pages/pc/components/dialog/Dialog.vue";
|
||||
import {_dateFormat, _getAccomplishDate, _getAccomplishDays, _getDictDataByUrl, useNav} from "@/utils";
|
||||
import BasePage from "@/pages/pc/components/BasePage.vue";
|
||||
import {DictResource, getDefaultDict} from "@/types.ts";
|
||||
import {DictResource, getDefaultDict} from "@/types/types.ts";
|
||||
import {onMounted, watch} from "vue";
|
||||
import {getCurrentStudyWord} from "@/hooks/dict.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import {inject, onMounted, onUnmounted} from "vue"
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {ShortcutKey, StudyData} from "@/types.ts";
|
||||
import {ShortcutKey, StudyData} from "@/types/types.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import Tooltip from "@/pages/pc/components/Tooltip.vue";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {getDefaultWord, ShortcutKey, Word} from "@/types.ts";
|
||||
import {getDefaultWord, ShortcutKey, Word} from "@/types/types.ts";
|
||||
import VolumeIcon from "@/components/icon/VolumeIcon.vue";
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import {usePlayBeep, usePlayCorrect, usePlayKeyboardAudio, usePlayWordAudio, useTTsPlayAudio} from "@/hooks/sound.ts";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import {Dict, DictId, getDefaultDict, Word} from "../types.ts"
|
||||
import {Dict, DictId, getDefaultDict, Word} from "../types/types.ts"
|
||||
import {cloneDeep} from "@/utils";
|
||||
import * as localforage from "localforage";
|
||||
import {nanoid} from "nanoid";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {defineStore} from "pinia"
|
||||
import {Dict, getDefaultDict} from "@/types.ts";
|
||||
import {Dict, getDefaultDict} from "@/types/types.ts";
|
||||
|
||||
export interface RuntimeState {
|
||||
disableEventListener: boolean,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {defineStore} from "pinia"
|
||||
import {checkAndUpgradeSaveSetting, cloneDeep} from "@/utils";
|
||||
import {DefaultShortcutKeyMap} from "@/types.ts";
|
||||
import {DefaultShortcutKeyMap} from "@/types/types.ts";
|
||||
import {SAVE_SETTING_KEY} from "@/utils/const.ts";
|
||||
|
||||
export interface SettingState {
|
||||
|
||||
0
src/types/func.ts
Normal file
0
src/types/func.ts
Normal file
@@ -1,4 +1,4 @@
|
||||
import {Dict, DictResource, getDefaultDict} from "@/types.ts";
|
||||
import {Dict, DictResource, getDefaultDict} from "@/types/types.ts";
|
||||
import {getDictFile} from "@/utils/index.ts";
|
||||
import {cloneDeep} from "@/utils";
|
||||
import {nanoid} from "nanoid";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {SAVE_DICT_KEY, SAVE_SETTING_KEY} from "@/utils/const.ts";
|
||||
import {BaseState, DefaultBaseState} from "@/stores/base.ts";
|
||||
import {getDefaultSettingState} from "@/stores/setting.ts";
|
||||
import {Dict, DictResource, DictType, getDefaultArticle, getDefaultDict, getDefaultWord} from "@/types.ts";
|
||||
import {Dict, DictResource, DictType, getDefaultArticle, getDefaultDict, getDefaultWord} from "@/types/types.ts";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {nanoid} from "nanoid";
|
||||
|
||||
Reference in New Issue
Block a user