Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-05-05 18:59:06 +08:00
co-authored by Copilot
parent 44c9df9b20
commit 01b648a3e7
52 changed files with 2491 additions and 89 deletions
+12
View File
@@ -0,0 +1,12 @@
import type { IConfig } from '@/stores'
import { defineRequest } from '../common'
import type { IResult } from './type'
export default defineRequest((request) => {
return {
async calc(config: IConfig) {
const res = await request.post<IResult>('/calc', config)
return res.data
},
}
})
// Compare this snippet from frontend/src/api/common/index.ts:
+56
View File
@@ -0,0 +1,56 @@
export interface IResult {
uuid: string
skills: IResultSkill[]
info: IResultUserInfo[]
suits: {
suit?: IResultSuit
oath?: IResultSuit
correction?: number
}
buffer: boolean
}
export interface IResultSkill {
name: string
icon: string
lv: number
data: number
ratio: number
cd: number
damage: number
mode: string
buff: number[]
learnLv: number
type: string
}
export interface IResultUserInfo {
id: number
name: string
value: string | number
}
export interface IResultSuit {
id: string
count: number
imageUrl: string
level: number
name: string
point: number
rarity: string
value: string
suitId: string
skills: {
id: string
skillId: string
name: string
imageUrl: string
enable: boolean
}[]
}
export interface IResultSkillCount {
count: number
mode: string
name: string
}
+119
View File
@@ -0,0 +1,119 @@
import type { AxiosInstance, AxiosResponse } from "axios";
import axios from "axios";
import { h } from "vue";
// import { gzip, ungzip } from 'pako'
import { useInfoStore } from "@/stores";
// import { useDialog } from '@/components/hooks/dialog'
// import { useAppStore, useConfigStore } from '@/store'
// import { useDialog } from '@/components/hooks/dialog'
export interface HttpResponse<T = unknown> extends AxiosResponse {
code?: number;
message: string;
data: T;
state?: number;
}
let instance: AxiosInstance | null = null;
let baseURL = "/api";
// 避免远程开发的时候冲突
if (import.meta.env.MODE == "development") {
baseURL = "http://127.0.0.1:47173/api";
}
if (import.meta.env.MODE == "web" || import.meta.env.MODE == "show") {
baseURL = "https://api.dnftools.com/";
}
if (import.meta.env.MODE == "test") {
baseURL = "https://testapi.dnftools.com/";
}
// 解压
function unzip(key: string) {
// 将二进制字符串转换为字符数组
const charData = key.split("").map((x) => {
return x?.charCodeAt(0);
});
// 将数字数组转换成字节数组
const binData = new Uint8Array(charData);
// 解压
const data = ungzip(binData);
const decoder = new TextDecoder();
const str = decoder.decode(data);
// unescape(str) --->解压后解码,防止中午乱码
return str;
}
export function defineRequest<T>(fn: (ax: AxiosInstance) => T) {
if (!instance) {
instance = axios.create({
baseURL,
});
instance.interceptors.request.use(
(request) => {
// const configStore = useConfigStore()
// const token = configStore.token
// const uid = useAppStore().uid
const token = useInfoStore().alter_token;
if (token) {
request.headers["Alter-Token"] = token;
}
return request;
},
(error) => {
// do something
return Promise.reject(error);
},
);
instance.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
// instance.defaults.headers["Accept-Encoding"] = "gzip"
// instance.defaults.headers["Content-Encoding"] = "gzip"
// add response interceptors
instance.interceptors.response.use(
async (response: AxiosResponse<HttpResponse>) => {
if (response.data.code === 500) {
// const { alert } = useDialog()
// await alert({
// content: () => {
// return h(
// 'div',
// {
// class: 'justify-center text-#d4d6b6 text-center',
// style: 'white-space:pre-wrap;width:250px;margin:10px',
// },
// `出现错误,可加群反馈:183439472\n${response.data.message}`,
// )
// },
// })
console.error(response.data.message);
}
// 解密数据
if (typeof response.data.data == "string") {
try {
const data = unzip(atob(response.data.data as string));
response.data.data = JSON.parse(data);
} catch (e) {}
}
return response.data;
},
async (error: Error) => {
return Promise.reject(error);
},
);
}
return fn(Object.create(instance));
}
+4
View File
@@ -0,0 +1,4 @@
import info from './info'
import calc from './calc'
export default { ...info, ...calc }
+39
View File
@@ -0,0 +1,39 @@
import { defineRequest } from "../common";
import type { IAdventureInfo, ICharacterInfo, IHelperInfo, ISkillInfo } from "./type";
export default defineRequest((request) => {
return {
async adventures() {
const res = await request.get<IAdventureInfo[]>("/adventure");
return res.data;
},
async createToken(alter: string, equVersion?: string) {
const res = await request.get<string>(`/token/get/${alter}`, { params: { equVersion } });
return res.data;
},
async characterInfo() {
const res = await request.get<ICharacterInfo>("/character");
return res.data;
},
async skillDetail(skillId: string, level: number) {
const res = await request.get<ISkillInfo>(`/skill/${skillId}/${level}`);
return res.data;
},
async getConfigFromHelper(uid: string) {
const res = await request.get(`/dnfhelper/${uid}/`);
return res.data;
},
async getSkillFromCode(code: string) {
const res = await request.post(`/skillTree/`, { code });
return res.data;
},
async getOathInfo(suitId: string, skillId: string, rarity: string, lv: number) {
const res = await request.get(`/info/oath/${suitId}/${skillId}`, { params: { rarity, lv } });
return res.data;
},
async getHelperList(id: string) {
const res = await request.get<IHelperInfo>(`/dnfhelper/${id}/list`);
return res.data;
},
};
});
+227
View File
@@ -0,0 +1,227 @@
export interface IAdventureInfo {
title: string;
name: string;
children: IAlterInfo[];
}
export interface IAlterOption {
name: string;
title: string;
value?: string;
}
export interface IAlterInfo {
name: string;
url?: string;
title: string;
default_value: string;
options?: IAlterOption[];
open?: boolean;
comment?: string;
value?: string;
}
export interface ICharacterInfo {
role: string;
alter: string;
name: string;
buffer: boolean;
equVersion: string;
equips: IEquipment[];
properties: string[];
stones: IEquipment[];
enchants: IEnchantingInfo[];
emblems: IEmblemInfo[];
oaths: IEquipment[];
weapons: string[];
subweapons: string[];
skills: ISkill[];
suits: ISuit[];
avatar: Record<string, IAvatarInfo[]>;
jades: IJade[];
sundry: Record<string, ISundry>;
options: { id: number; name: string; options: string[] }[];
oaths_skills: Record<
string,
{
suitInfo: {
name: string;
rarity: string;
SkillAttack: number;
Buffer: number;
Attack: number;
}[];
skills: { skillId: string; name: string; icon: string }[];
}
>;
}
export interface ISundry {
id: number;
options: {
name: string;
value: number;
}[];
}
export interface IEquipment {
id: string;
imageUrl: string;
itemType: string;
itemDetailType: string;
name: string;
categorize: string;
suit: string[];
rarity: string;
max_adaptation: number;
STR: number[];
INT: number[];
Vitality: number[];
Spirit: number[];
AtkP: number[];
AtkM: number[];
AtkI: number[];
SkillAttack: number[];
Attack: number[];
Buffer: number[];
detail: string;
bufferDetail: string;
position: string[];
wearUrl: string[];
oathPos: string;
displayUrl?: string;
}
export interface ISkill {
icon: string;
name: string;
id: number;
learnLv: number;
position: number;
line: number;
type: string;
maxLearnLv: number;
maxLv: number;
bind: boolean;
hasVP: boolean;
hasUP: boolean;
vps: {
name: string;
desc: string;
}[];
uuid: string;
upType: "damage" | "heal";
}
export interface ISuit {
id: number;
name: string;
imageUrl: string;
suitId: number;
suitName: string;
point: number;
rarity: string;
level: number;
SkillAttack: number;
Buffer: number;
value: string;
bufferValue: string;
}
export interface IEnchantingInfo {
id: string | number;
fame: number | undefined;
position: string[];
detail: string;
categorize: string[];
rarity?: string;
type?: string;
}
export interface IEmblemInfo {
fame: number;
id: string;
rarity: string;
type: string;
value: string;
}
export interface IAvatarInfo {
id: number;
options: string[];
part: string;
rarity: string;
suit?: string;
name: string;
}
export interface IJade {
name: string;
id: number;
max: number;
min: number;
pre: number;
unit: string;
}
export interface ISkillInfo {
name: string;
type: string;
desc: string;
descDetail: string;
consumeItem: ConsumeItem;
descSpecial: string[];
maxLevel: number;
requiredLevel: number;
requiredLevelRange: number;
preRequiredSkill: any;
jobId: string;
jobName: string;
jobGrowLevel: any[];
evolution: Evolution[];
enhancement: Enhancement[];
attribute: Attribute;
}
export interface ConsumeItem {
itemId: string;
itemName: string;
value: number;
}
export interface Evolution {
type: number;
name: string;
desc: string;
descDetail: string;
skills?: string[];
}
export interface Enhancement {
type: number;
status: Status[];
}
export interface Status {
name: string;
value: string;
}
export interface Attribute {
optionDesc: string;
level: number;
consumeMp: number;
coolTime: number;
castingTime?: number;
optionValue: Record<`value${string}`, string>;
detail: string;
}
export interface IHelperInfo {
id: string;
avatar: string;
roleList: {
roleId: string;
roleName: string;
avatarLink: string;
}[];
}
+6
View File
@@ -0,0 +1,6 @@
export interface IDataType<T = any> {
code?: number
message: string
data: T
state?: number
}
+4 -33
View File
@@ -11,12 +11,7 @@ import {
FieldLabel,
FieldSeparator,
} from "@/components/ui/field";
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { Input } from "@/components/ui/input";
const props = defineProps<{
class?: HTMLAttributes["class"];
@@ -35,35 +30,11 @@ const props = defineProps<{
<span class="sr-only">DNF计算器</span>
</a>
<!-- <h1 class="text-xl font-bold">输入助手ID</h1> -->
<FieldDescription>请输入助手的社区ID </FieldDescription>
<!-- <FieldDescription>请输入助手的社区ID </FieldDescription> -->
</div>
<Field class="flex flex-col items-center">
<FieldLabel for="otp" class="sr-only"> 助手ID </FieldLabel>
<InputOTP id="otp" :maxlength="9" required container-class="gap-4" class="justify-center">
<InputOTPGroup
class="gap-2.5 *:data-[slot=input-otp-slot]:h-16 *:data-[slot=input-otp-slot]:w-12 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:text-xl"
>
<InputOTPSlot :index="0" />
<InputOTPSlot :index="1" />
<InputOTPSlot :index="2" />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup
class="gap-2.5 *:data-[slot=input-otp-slot]:h-16 *:data-[slot=input-otp-slot]:w-12 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:text-xl"
>
<InputOTPSlot :index="3" />
<InputOTPSlot :index="4" />
<InputOTPSlot :index="5" />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup
class="gap-2.5 *:data-[slot=input-otp-slot]:h-16 *:data-[slot=input-otp-slot]:w-12 *:data-[slot=input-otp-slot]:rounded-md *:data-[slot=input-otp-slot]:border *:data-[slot=input-otp-slot]:text-xl"
>
<InputOTPSlot :index="6" />
<InputOTPSlot :index="7" />
<InputOTPSlot :index="8" />
</InputOTPGroup>
</InputOTP>
<FieldLabel for="helperId" class="sr-only"> 助手ID </FieldLabel>
<Input id="helperId" type="number" placeholder="助手社区ID" required />
<!-- <FieldDescription class="text-center">
Didn't receive the code? <a href="#">Resend</a>
</FieldDescription> -->
@@ -0,0 +1,29 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue";
import { cn } from "@/lib/utils";
const props = withDefaults(
defineProps<{
class?: HTMLAttributes["class"];
size?: "default" | "sm";
}>(),
{
size: "default",
},
);
</script>
<template>
<div
data-slot="card"
:data-size="size"
:class="
cn(
'ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col transition-all duration-200 hover:shadow-lg hover:ring-foreground/20',
props.class,
)
"
>
<slot />
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="card-action"
:class="cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', props.class)"
>
<slot />
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="card-content"
:class="cn('px-4 group-data-[size=sm]/card:px-3', props.class)"
>
<slot />
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="card-description"
:class="cn('text-muted-foreground text-sm', props.class)"
>
<slot />
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="card-footer"
:class="cn('bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center', props.class)"
>
<slot />
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="card-header"
:class="cn('gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]', props.class)"
>
<slot />
</div>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="card-title"
:class="cn('text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading', props.class)"
>
<slot />
</div>
</template>
@@ -0,0 +1,7 @@
export { default as Card } from './Card.vue'
export { default as CardAction } from './CardAction.vue'
export { default as CardContent } from './CardContent.vue'
export { default as CardDescription } from './CardDescription.vue'
export { default as CardFooter } from './CardFooter.vue'
export { default as CardHeader } from './CardHeader.vue'
export { default as CardTitle } from './CardTitle.vue'
@@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<div
data-slot="input-group"
role="group"
:class="cn(
'border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-disabled:bg-input/50 dark:has-disabled:bg-input/80 h-8 rounded-lg border transition-colors in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 group/input-group relative flex w-full min-w-0 items-center outline-none has-[>textarea]:h-auto',
props.class,
)"
>
<slot />
</div>
</template>
@@ -0,0 +1,36 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import type { InputGroupVariants } from '.'
import { cn } from '@/lib/utils'
import { inputGroupAddonVariants } from '.'
const props = withDefaults(defineProps<{
align?: InputGroupVariants['align']
class?: HTMLAttributes['class']
}>(), {
align: 'inline-start',
})
function handleInputGroupAddonClick(e: MouseEvent) {
const currentTarget = e.currentTarget as HTMLElement | null
const target = e.target as HTMLElement | null
if (target && target.closest('button')) {
return
}
if (currentTarget && currentTarget?.parentElement) {
currentTarget.parentElement?.querySelector('input')?.focus()
}
}
</script>
<template>
<div
role="group"
data-slot="input-group-addon"
:data-align="props.align"
:class="cn(inputGroupAddonVariants({ align: props.align }), props.class)"
@click="handleInputGroupAddonClick"
>
<slot />
</div>
</template>
@@ -0,0 +1,22 @@
<script setup lang="ts">
import type { InputGroupButtonProps } from '.'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { inputGroupButtonVariants } from '.'
const props = withDefaults(defineProps<InputGroupButtonProps>(), {
size: 'xs',
variant: 'ghost',
})
</script>
<template>
<Button
type="button"
:data-size="props.size"
:variant="props.variant"
:class="cn(inputGroupButtonVariants({ size: props.size }), props.class)"
>
<slot />
</Button>
</template>
@@ -0,0 +1,19 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
import { Input } from '@/components/ui/input'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<Input
data-slot="input-group-control"
:class="cn(
'rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent flex-1',
props.class,
)"
/>
</template>
@@ -0,0 +1,19 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<span
:class="cn(
'text-muted-foreground gap-2 text-sm [&_svg:not([class*=size-])]:size-4 flex items-center [&_svg]:pointer-events-none',
props.class,
)"
>
<slot />
</span>
</template>
@@ -0,0 +1,19 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
import { Textarea } from '@/components/ui/textarea'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<Textarea
data-slot="input-group-control"
:class="cn(
'rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent flex-1 resize-none',
props.class,
)"
/>
</template>
@@ -0,0 +1,55 @@
import type { VariantProps } from 'class-variance-authority'
import type { HTMLAttributes } from 'vue'
import type { ButtonVariants } from '@/components/ui/button'
import { cva } from 'class-variance-authority'
export { default as InputGroup } from './InputGroup.vue'
export { default as InputGroupAddon } from './InputGroupAddon.vue'
export { default as InputGroupButton } from './InputGroupButton.vue'
export { default as InputGroupInput } from './InputGroupInput.vue'
export { default as InputGroupText } from './InputGroupText.vue'
export { default as InputGroupTextarea } from './InputGroupTextarea.vue'
export const inputGroupAddonVariants = cva(
'text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*=size-])]:size-4 flex cursor-text items-center justify-center select-none',
{
variants: {
align: {
'inline-start': 'pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem] order-first',
'inline-end': 'pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem] order-last',
'block-start': 'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',
'block-end': 'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',
},
},
defaultVariants: {
align: 'inline-start',
},
},
)
export type InputGroupVariants = VariantProps<typeof inputGroupAddonVariants>
export const inputGroupButtonVariants = cva(
'gap-2 text-sm flex items-center shadow-none',
{
variants: {
size: {
'xs': 'h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*=size-])]:size-3.5',
'sm': '',
'icon-xs': 'size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0',
'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
},
},
defaultVariants: {
size: 'xs',
},
},
)
export type InputGroupButtonVariants = VariantProps<typeof inputGroupButtonVariants>
export interface InputGroupButtonProps {
variant?: ButtonVariants['variant']
size?: InputGroupButtonVariants['size']
class?: HTMLAttributes['class']
}
+17
View File
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<kbd
data-slot="kbd"
:class="cn('bg-muted text-muted-foreground in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10 h-5 w-fit min-w-5 gap-1 rounded-sm px-1 font-sans text-xs font-medium [&_svg:not([class*=size-])]:size-3 pointer-events-none inline-flex items-center justify-center select-none', props.class)"
>
<slot />
</kbd>
</template>
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>
<template>
<kbd
data-slot="kbd-group"
:class="cn('gap-1 inline-flex items-center', props.class)"
>
<slot />
</kbd>
</template>
@@ -0,0 +1,2 @@
export { default as Kbd } from './Kbd.vue'
export { default as KbdGroup } from './KbdGroup.vue'
@@ -0,0 +1,28 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { useVModel } from '@vueuse/core'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
defaultValue?: string | number
modelValue?: string | number
}>()
const emits = defineEmits<{
(e: 'update:modelValue', payload: string | number): void
}>()
const modelValue = useVModel(props, 'modelValue', emits, {
passive: true,
defaultValue: props.defaultValue,
})
</script>
<template>
<textarea
v-model="modelValue"
data-slot="textarea"
:class="cn('border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors focus-visible:ring-3 aria-invalid:ring-3 md:text-sm flex field-sizing-content min-h-16 w-full outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)"
/>
</template>
@@ -0,0 +1 @@
export { default as Textarea } from './Textarea.vue'
+13 -9
View File
@@ -1,13 +1,17 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createApp } from "vue";
import { pinia } from "@/stores";
import App from './App.vue'
import router from './router'
import './style.css'
import router from "./router";
import App from "@/pages/index.vue";
import "./style.css";
const app = createApp(App)
const appContainer = document.createElement("div");
appContainer.setAttribute("style", "text-rendering: geometricPrecision;letter-spacing: -0.1px;");
document.body.appendChild(appContainer);
app.use(createPinia())
app.use(router)
const app = createApp(App);
app.mount('#app')
app.use(pinia);
app.use(router);
app.mount(appContainer);
@@ -0,0 +1,76 @@
<script setup lang="ts">
import type { HTMLAttributes } from "vue";
import { GalleryVerticalEnd, Shuffle } from "lucide-vue-next";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
FieldSeparator,
} from "@/components/ui/field";
import { InputGroup, InputGroupInput, InputGroupAddon } from "@/components/ui/input-group";
import { Kbd } from "@/components/ui/kbd";
import { useDnfHelperStore } from "@/stores/dnfhelper";
const props = defineProps<{
class?: HTMLAttributes["class"];
}>();
const helperId = ref("");
const onSubmit = async (e: Event) => {
e.preventDefault();
await useDnfHelperStore().refleshHelperInfo(helperId.value);
};
</script>
<template>
<div :class="cn('flex flex-col gap-6', props.class)">
<form @submit="onSubmit">
<FieldGroup>
<div class="flex flex-col items-center gap-2 text-center">
<a href="#" class="flex flex-col items-center gap-2 font-medium">
<div class="flex size-8 items-center justify-center rounded-md">
<GalleryVerticalEnd class="size-6" />
</div>
<span class="sr-only">DNF计算器</span>
</a>
<!-- <h1 class="text-xl font-bold">输入助手ID</h1> -->
<!-- <FieldDescription>请输入助手的社区ID </FieldDescription> -->
</div>
<Field class="flex flex-col items-center">
<FieldLabel for="helperId" class="sr-only"> 助手ID </FieldLabel>
<InputGroup>
<InputGroupInput
id="helperId"
type="number"
placeholder="助手社区ID"
required
v-model="helperId"
/>
<InputGroupAddon>
<Shuffle />
</InputGroupAddon>
<InputGroupAddon align="inline-end">
<Kbd>Enter</Kbd>
</InputGroupAddon>
</InputGroup>
<!-- <FieldDescription class="text-center">
Didn't receive the code? <a href="#">Resend</a>
</FieldDescription> -->
</Field>
<Field>
<Button class="self-center w-sm!" type="submit"> 确认 </Button>
</Field>
</FieldGroup>
</form>
<FieldSeparator class="w-sm self-center">没有助手ID</FieldSeparator>
<FieldDescription class="px-6 text-center">
<Button variant="outline"> 选择职业 </Button>
</FieldDescription>
</div>
</template>
@@ -0,0 +1,18 @@
<script setup lang="ts">
import { useDnfHelperStore } from "@/stores/dnfhelper";
import Bind from "./bind.vue";
import List from "./list.vue";
const helperStore = useDnfHelperStore();
const listInfo = computed(() => helperStore.helperInfo?.roleList ?? []);
</script>
<template>
<template v-if="listInfo.length == 0">
<Bind class="self-center justify-center w-fit !max-w-sm h-full" />
</template>
<template v-else>
<List :list="listInfo" />
</template>
</template>
@@ -0,0 +1,41 @@
<script setup lang="ts">
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
const props = defineProps<{
list: {
roleId: string;
roleName: string;
roleJob: string;
avatarLink: string;
fame: number;
}[];
}>();
const { list } = toRefs(props);
</script>
<template>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 p-5 box-border">
<template v-for="item in list" :key="item.roleId">
<Card class="w-full">
<CardHeader>
<CardTitle>{{ item.roleJob }}</CardTitle>
</CardHeader>
<CardContent class="flex items-center justify-center h-sm">
<img :src="item.avatarLink" alt="avatar" class="w-auto h-sm rounded-full object-cover" />
</CardContent>
<CardFooter class="flex justify-between">
<span>{{ item.roleName }}</span>
<span>{{ item.fame }} </span>
</CardFooter>
</Card>
</template>
</div>
</template>
@@ -1,7 +1,6 @@
<script setup lang="ts">
import AppSidebar from "@/components/AppSidebar.vue";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import OTPForm from "@/components/OTPForm.vue";
</script>
<template>
@@ -9,13 +8,7 @@ import OTPForm from "@/components/OTPForm.vue";
<div class="flex w-full">
<AppSidebar />
<SidebarInset>
<div
class="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10"
>
<div class="w-full max-w-xl">
<OTPForm />
</div>
</div>
<router-view />
<!-- router-view -->
</SidebarInset>
</div>
+13 -2
View File
@@ -1,12 +1,23 @@
import { createRouter, createWebHistory } from "vue-router";
import App from "../App.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
component: App,
name: "home",
component: () => import("@/pages/index.vue"),
},
{
path: "/dcalc",
name: "dcalc",
children: [
{
path: "/dcalc/dnfhelper",
name: "dnfhelper",
component: () => import("@/pages/Dcalc/DnfHelper/index.vue"),
},
],
},
],
});
+293
View File
@@ -0,0 +1,293 @@
import { defineStore } from "pinia";
import { useInfoStore } from "./info";
import api from "@/api";
import type { IEquipment } from "@/api/info/type";
import type { IResultSkillCount } from "@/api/calc/type";
// | '上衣'
// | '头肩'
// | '下装'
// | '腰带'
// | '鞋'
// | '手镯'
// | '项链'
// | '戒指'
// | '耳环'
// | '辅助装备'
// | '魔法石'
// | '武器',
export interface IConfigEquip {
id: string;
reinforce: number;
reinforceType: number;
enchant: string;
emblem_0: string;
emblem_1: string;
upgrade: number;
refine: number;
adaptation: number;
fusion: string;
precision: number;
weaponFusion: number;
}
export interface IConfigOath {
id: string;
adaptation: number;
}
export interface IConfigEmblem {
lv: number;
items: string[];
}
export interface IConfig {
skills: Record<string, { lv: number; vp: number; up: number }>;
equips: Record<string, IConfigEquip>;
oaths: Record<string, IConfigOath>;
oathSkill: string;
jades: Record<
string,
{
id: number;
value: number;
}
>;
avatar: Record<
string,
{
id: string;
enchant: string;
emblem_0: string;
emblem_1: string;
option: string;
}
>;
sundry: Record<string, unknown>;
bindAwake: number;
/** 系统奶 */
DSB: boolean;
/** 奶 */
BUFF: boolean;
options: Record<string, number>;
avatarLink: string;
emblems: Record<string, IConfigEmblem>;
}
const defaultEqusConfig = {
id: "",
reinforce: 0,
reinforceType: 0,
enchant: "",
emblem_0: "",
emblem_1: "",
upgrade: 0,
refine: 0,
adaptation: 0,
fusion: "",
precision: 0,
weaponFusion: 0,
};
const defaultAvatarConfig = {
id: "",
enchant: "",
emblem_0: "",
emblem_1: "",
option: "0",
};
const defaultOathConfig = {
id: "",
adaptation: 0,
};
const defaultEmblemConfig = {
lv: 0,
items: ["", "", "", "", "", ""],
};
export const useConfigStore = defineStore("configStore", () => {
const config = ref<IConfig>({
skills: {},
equips: {},
oaths: {},
oathSkill: "1",
jades: {},
avatar: {},
sundry: {},
bindAwake: 50,
DSB: false,
BUFF: false,
options: {},
avatarLink: "",
emblems: {},
});
const skillCountConfig = ref<IResultSkillCount[]>([]);
const loadConfig = () => {
const infoStore = useInfoStore();
const alter = infoStore.infos?.alter;
if (alter) {
const localConfig = localStorage.getItem(`dcalc/${alter}/config`);
const localSkillConfig = localStorage.getItem(`dcalc/${alter}/config/skillCount`);
if (localConfig) {
config.value = JSON.parse(localConfig);
}
if (localSkillConfig) {
skillCountConfig.value = JSON.parse(localSkillConfig);
}
}
if (!config.value) {
config.value = {
skills: {},
equips: {},
oaths: {},
oathSkill: "1",
jades: {},
avatar: {},
sundry: {},
bindAwake: 50,
DSB: false,
BUFF: false,
options: {},
avatarLink: "",
emblems: {},
};
}
if (!config.value?.equips) config.value.equips = {};
if (!config.value?.skills) config.value.skills = {};
if (!config.value?.jades) config.value.jades = {};
if (!config.value?.avatar) config.value.avatar = {};
if (!config.value?.sundry) config.value.sundry = {};
if (!config.value?.bindAwake) config.value.bindAwake = 50;
if (!config.value?.options) config.value.options = {};
if (!config.value?.oaths) config.value.oaths = {};
if (!config.value?.emblems) config.value.emblems = {};
const part = [...infoStore.parts, "宠物", "副武器"].filter(
(a) => !config.value?.equips.hasOwnProperty(a),
);
const avatarPart = infoStore.avatarParts
.filter((a) => a != "宠物")
.filter((a) => !config.value?.avatar.hasOwnProperty(a));
const skillIDs = infoStore.skills
.map((skill) => skill.id.toString())
.filter((a) => !config.value?.skills.hasOwnProperty(a));
part.forEach((p) => {
// oxlint-disable-next-line no-unused-expressions
config.value && (config.value.equips[p] = { ...defaultEqusConfig });
});
avatarPart.forEach((p) => {
// oxlint-disable-next-line no-unused-expressions
config.value && (config.value.avatar[p] = { ...defaultAvatarConfig });
});
infoStore.oathParts.forEach((p) => {
// oxlint-disable-next-line no-unused-expressions
config.value &&
!config.value?.oaths.hasOwnProperty(p) &&
(config.value.oaths[p.toString()] = { ...defaultOathConfig });
});
infoStore.options.forEach((option) => {
if (Object.keys(config.value.options).includes(option.id.toString())) return;
config.value.options[option.id.toString()] = 0;
});
skillIDs.forEach((id) => {
// oxlint-disable-next-line no-unused-expressions
config.value &&
(config.value.skills[id] = {
lv: infoStore.skills.find((skill) => skill.id.toString() === id)?.maxLearnLv ?? 0,
vp: 0,
up: 0,
});
});
(
["0", "1", "2", "3"].filter((key) => !Object.keys(config.value.jades).includes(key)) ?? []
).forEach((key) => {
config.value.jades[key] = {
id: -1,
value: 0,
};
});
};
const saveConfig = () => {
const infoStore = useInfoStore();
const alter = infoStore.infos?.alter;
if (alter) {
localStorage.setItem(`dcalc/${alter}/config`, JSON.stringify(config.value));
localStorage.setItem(
`dcalc/${alter}/config/skillCount`,
JSON.stringify(skillCountConfig.value),
);
}
};
const calc = async () => {
if (!useInfoStore().infos?.alter) return undefined;
const res = await api.calc(config.value);
if (res.suits.oath?.suitId != res.suits.suit?.suitId) {
config.value.oathSkill = "1";
}
return res;
};
const chooseEqu = (
equip: IEquipment | undefined,
isFusion: boolean = false,
isSubWeapon: boolean = false,
) => {
if (!equip) return;
const part = isSubWeapon
? "副武器"
: equip?.itemType == "武器"
? "武器"
: equip?.itemDetailType;
if (equip && config.value.equips[part]) {
const key = isFusion ? "fusion" : "id";
if (config.value.equips[part][key] == equip.id) {
config.value.equips[part][key] = "";
} else {
config.value.equips[part][key] = equip.id;
}
}
};
const result = useAsyncState(
() => {
return calc();
},
{
uuid: "",
skills: [],
info: [],
suits: {},
buffer: false,
},
{ resetOnExecute: false },
);
return {
config,
loadConfig,
saveConfig,
calc,
result,
chooseEqu,
skillCountConfig,
defaultEqusConfig,
defaultAvatarConfig,
defaultOathConfig,
defaultEmblemConfig,
};
});
-12
View File
@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
+31
View File
@@ -0,0 +1,31 @@
import { defineStore } from "pinia";
import api from "@/api";
import type { IHelperInfo } from "@/api/info/type";
export const useDnfHelperStore = defineStore("dnfHelperStore", () => {
const helperInfo = useLocalStorage<IHelperInfo | null>("dnfHelperInfo", null, {
serializer: {
read: (v) => (v ? JSON.parse(v) : null),
write: (v) => JSON.stringify(v),
},
});
const refleshHelperInfo = async (uid: string) => {
const data = await api.getHelperList(uid);
console.log("helperInfo", data);
if (data) {
helperInfo.value = data;
console.log(helperInfo.value);
}
};
const logout = () => {
helperInfo.value = null;
};
return {
helperInfo,
refleshHelperInfo,
logout,
};
});
+6
View File
@@ -0,0 +1,6 @@
import { createPinia } from 'pinia'
export * from './info'
export * from './config'
export const pinia = createPinia()
+194
View File
@@ -0,0 +1,194 @@
import api from '@/api'
import type { ICharacterInfo, IEquipment, ISuit } from '@/api/info/type'
import { defineStore } from 'pinia'
import { useConfigStore } from './config'
import type { IResult, IResultSkill, IResultSkillCount } from '@/api/calc/type'
export const useInfoStore = defineStore('infoStore', () => {
const alter_token = ref<string>('')
const infos = ref<ICharacterInfo | null>(null)
const oathCache = ref<
{
suitId: string
skillId: string
rarity: string
lv: number
data: ISuit
}[]
>([])
const skills = computed(() => infos.value?.skills ?? [])
const equips = computed(() => infos.value?.equips ?? [])
const suits = computed(() => infos.value?.suits ?? [])
const enchants = computed(() => infos.value?.enchants ?? [])
const emblems = computed(() => infos.value?.emblems ?? [])
// const stones = computed(() => infos.value?.stones ?? [])
const avatars = computed(() => infos.value?.avatar ?? {})
const jades = computed(() => infos.value?.jades ?? [])
const sundries = computed(() => infos.value?.sundry ?? {})
const options = computed(() => infos.value?.options ?? [])
const oaths = computed(() => {
const origin = infos.value?.oaths ?? []
const result: IEquipment[] = []
origin.forEach((o) => {
o.position.forEach((p, i) => {
result.push({
...o,
oathPos: p,
displayUrl: o.wearUrl?.[parseInt(p)]
? `/equipment/icon/oathprimer/primer_equip/${o.wearUrl?.[parseInt(p)]}`
: o.imageUrl,
})
})
})
return result
})
const getOathInfo = async (suitId: string, skillId: string, rarity: string, lv: number) => {
const res = oathCache.value.find(
(o) => o.suitId === suitId && o.skillId === skillId && o.rarity === rarity && o.lv === lv,
)
if (res) {
return {
...res.data,
}
}
const data: ISuit = await api.getOathInfo(suitId, skillId, rarity, lv)
oathCache.value.push({ suitId, skillId, rarity, lv, data })
return {
...data,
}
}
const oathSkills = computed(() => {
const origin = infos.value?.oaths_skills
if (!origin) return origin
const skillOrder = ['2', '1', '3']
return Object.fromEntries(
Object.entries(origin).map(([key, value]) => [
key,
{
...value,
skills: [...value.skills].sort((left, right) => {
const leftIndex = skillOrder.indexOf(left.skillId)
const rightIndex = skillOrder.indexOf(right.skillId)
const normalizedLeftIndex = leftIndex === -1 ? skillOrder.length : leftIndex
const normalizedRightIndex = rightIndex === -1 ? skillOrder.length : rightIndex
return normalizedLeftIndex - normalizedRightIndex
}),
},
]),
)
})
const standardUUID = ref<string>()
const setStandard = (
result?: IResult,
skillCount?: {
count: number
mode: string
name: string
}[],
) => {
if (!result) {
if (!!standardUUID.value) {
sessionStorage.removeItem(standardUUID.value)
}
standardUUID.value = undefined
} else {
standardUUID.value = result.uuid
sessionStorage.setItem(result.uuid, JSON.stringify({ skills: result.skills, skillCount }))
}
}
const standard = computed(() => {
const value = sessionStorage.getItem(standardUUID.value ?? '')
if (!value) return undefined
else return JSON.parse(value) as { skills: IResultSkill[]; skillCount: IResultSkillCount[] }
})
const parts = [
'头肩',
'上衣',
'下装',
'腰带',
'鞋',
'武器',
'称号',
'手镯',
'项链',
'辅助装备',
'戒指',
'耳环',
'魔法石',
'宠物',
]
const avatarParts = [
'武器装扮',
'头部',
'帽子',
'脸部',
'光环',
'胸部',
'上衣',
'皮肤',
'快捷栏装备',
'腰带',
'下装',
'鞋',
'宠物',
'宠物装备-红',
'宠物装备-绿',
'宠物装备-蓝',
]
const oathParts = Array.from({ length: 12 }, (_, i) => i.toString())
const emblemParts = ['彩色', '红色', '蓝色', '绿色', '黄色', '白金']
const createCharacter = async (alter: string, equVersion?: string) => {
const time = new Date().getTime()
alter_token.value = btoa(JSON.stringify({ alter, equVersion, time }))
infos.value = await api.characterInfo()
useConfigStore().loadConfig()
}
return {
alter_token,
infos,
skills,
equips,
suits,
createCharacter,
enchants,
emblems,
parts,
oaths,
avatarParts,
oathParts,
emblemParts,
avatars,
jades,
setStandard,
standard,
sundries,
options,
oathSkills,
getOathInfo,
}
})
+306
View File
@@ -0,0 +1,306 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue').EffectScope
const asyncComputed: typeof import('@vueuse/core').asyncComputed
const autoResetRef: typeof import('@vueuse/core').autoResetRef
const computed: typeof import('vue').computed
const computedAsync: typeof import('@vueuse/core').computedAsync
const computedEager: typeof import('@vueuse/core').computedEager
const computedInject: typeof import('@vueuse/core').computedInject
const computedWithControl: typeof import('@vueuse/core').computedWithControl
const controlledComputed: typeof import('@vueuse/core').controlledComputed
const controlledRef: typeof import('@vueuse/core').controlledRef
const createApp: typeof import('vue').createApp
const createDisposableDirective: typeof import('@vueuse/core').createDisposableDirective
const createEventHook: typeof import('@vueuse/core').createEventHook
const createGlobalState: typeof import('@vueuse/core').createGlobalState
const createInjectionState: typeof import('@vueuse/core').createInjectionState
const createReactiveFn: typeof import('@vueuse/core').createReactiveFn
const createRef: typeof import('@vueuse/core').createRef
const createReusableTemplate: typeof import('@vueuse/core').createReusableTemplate
const createSharedComposable: typeof import('@vueuse/core').createSharedComposable
const createTemplatePromise: typeof import('@vueuse/core').createTemplatePromise
const createUnrefFn: typeof import('@vueuse/core').createUnrefFn
const customRef: typeof import('vue').customRef
const debouncedRef: typeof import('@vueuse/core').debouncedRef
const debouncedWatch: typeof import('@vueuse/core').debouncedWatch
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineComponent: typeof import('vue').defineComponent
const eagerComputed: typeof import('@vueuse/core').eagerComputed
const effectScope: typeof import('vue').effectScope
const extendRef: typeof import('@vueuse/core').extendRef
const getCurrentInstance: typeof import('vue').getCurrentInstance
const getCurrentScope: typeof import('vue').getCurrentScope
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const h: typeof import('vue').h
const ignorableWatch: typeof import('@vueuse/core').ignorableWatch
const inject: typeof import('vue').inject
const injectLocal: typeof import('@vueuse/core').injectLocal
const isDefined: typeof import('@vueuse/core').isDefined
const isProxy: typeof import('vue').isProxy
const isReactive: typeof import('vue').isReactive
const isReadonly: typeof import('vue').isReadonly
const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow
const makeDestructurable: typeof import('@vueuse/core').makeDestructurable
const markRaw: typeof import('vue').markRaw
const nextTick: typeof import('vue').nextTick
const onActivated: typeof import('vue').onActivated
const onBeforeMount: typeof import('vue').onBeforeMount
const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave
const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onClickOutside: typeof import('@vueuse/core').onClickOutside
const onDeactivated: typeof import('vue').onDeactivated
const onElementRemoval: typeof import('@vueuse/core').onElementRemoval
const onErrorCaptured: typeof import('vue').onErrorCaptured
const onKeyStroke: typeof import('@vueuse/core').onKeyStroke
const onLongPress: typeof import('@vueuse/core').onLongPress
const onMounted: typeof import('vue').onMounted
const onRenderTracked: typeof import('vue').onRenderTracked
const onRenderTriggered: typeof import('vue').onRenderTriggered
const onScopeDispose: typeof import('vue').onScopeDispose
const onServerPrefetch: typeof import('vue').onServerPrefetch
const onStartTyping: typeof import('@vueuse/core').onStartTyping
const onUnmounted: typeof import('vue').onUnmounted
const onUpdated: typeof import('vue').onUpdated
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const pausableWatch: typeof import('@vueuse/core').pausableWatch
const provide: typeof import('vue').provide
const provideLocal: typeof import('@vueuse/core').provideLocal
const reactify: typeof import('@vueuse/core').reactify
const reactifyObject: typeof import('@vueuse/core').reactifyObject
const reactive: typeof import('vue').reactive
const reactiveComputed: typeof import('@vueuse/core').reactiveComputed
const reactiveOmit: typeof import('@vueuse/core').reactiveOmit
const reactivePick: typeof import('@vueuse/core').reactivePick
const readonly: typeof import('vue').readonly
const ref: typeof import('vue').ref
const refAutoReset: typeof import('@vueuse/core').refAutoReset
const refDebounced: typeof import('@vueuse/core').refDebounced
const refDefault: typeof import('@vueuse/core').refDefault
const refManualReset: typeof import('@vueuse/core').refManualReset
const refThrottled: typeof import('@vueuse/core').refThrottled
const refWithControl: typeof import('@vueuse/core').refWithControl
const resolveComponent: typeof import('vue').resolveComponent
const shallowReactive: typeof import('vue').shallowReactive
const shallowReadonly: typeof import('vue').shallowReadonly
const shallowRef: typeof import('vue').shallowRef
const syncRef: typeof import('@vueuse/core').syncRef
const syncRefs: typeof import('@vueuse/core').syncRefs
const templateRef: typeof import('@vueuse/core').templateRef
const throttledRef: typeof import('@vueuse/core').throttledRef
const throttledWatch: typeof import('@vueuse/core').throttledWatch
const toRaw: typeof import('vue').toRaw
const toReactive: typeof import('@vueuse/core').toReactive
const toRef: typeof import('vue').toRef
const toRefs: typeof import('vue').toRefs
const toValue: typeof import('vue').toValue
const triggerRef: typeof import('vue').triggerRef
const tryOnBeforeMount: typeof import('@vueuse/core').tryOnBeforeMount
const tryOnBeforeUnmount: typeof import('@vueuse/core').tryOnBeforeUnmount
const tryOnMounted: typeof import('@vueuse/core').tryOnMounted
const tryOnScopeDispose: typeof import('@vueuse/core').tryOnScopeDispose
const tryOnUnmounted: typeof import('@vueuse/core').tryOnUnmounted
const unref: typeof import('vue').unref
const unrefElement: typeof import('@vueuse/core').unrefElement
const until: typeof import('@vueuse/core').until
const useActiveElement: typeof import('@vueuse/core').useActiveElement
const useAnimate: typeof import('@vueuse/core').useAnimate
const useArrayDifference: typeof import('@vueuse/core').useArrayDifference
const useArrayEvery: typeof import('@vueuse/core').useArrayEvery
const useArrayFilter: typeof import('@vueuse/core').useArrayFilter
const useArrayFind: typeof import('@vueuse/core').useArrayFind
const useArrayFindIndex: typeof import('@vueuse/core').useArrayFindIndex
const useArrayFindLast: typeof import('@vueuse/core').useArrayFindLast
const useArrayIncludes: typeof import('@vueuse/core').useArrayIncludes
const useArrayJoin: typeof import('@vueuse/core').useArrayJoin
const useArrayMap: typeof import('@vueuse/core').useArrayMap
const useArrayReduce: typeof import('@vueuse/core').useArrayReduce
const useArraySome: typeof import('@vueuse/core').useArraySome
const useArrayUnique: typeof import('@vueuse/core').useArrayUnique
const useAsyncQueue: typeof import('@vueuse/core').useAsyncQueue
const useAsyncState: typeof import('@vueuse/core').useAsyncState
const useAttrs: typeof import('vue').useAttrs
const useBase64: typeof import('@vueuse/core').useBase64
const useBattery: typeof import('@vueuse/core').useBattery
const useBluetooth: typeof import('@vueuse/core').useBluetooth
const useBreakpoints: typeof import('@vueuse/core').useBreakpoints
const useBroadcastChannel: typeof import('@vueuse/core').useBroadcastChannel
const useBrowserLocation: typeof import('@vueuse/core').useBrowserLocation
const useCached: typeof import('@vueuse/core').useCached
const useClipboard: typeof import('@vueuse/core').useClipboard
const useClipboardItems: typeof import('@vueuse/core').useClipboardItems
const useCloned: typeof import('@vueuse/core').useCloned
const useColorMode: typeof import('@vueuse/core').useColorMode
const useConfirmDialog: typeof import('@vueuse/core').useConfirmDialog
const useCountdown: typeof import('@vueuse/core').useCountdown
const useCounter: typeof import('@vueuse/core').useCounter
const useCssModule: typeof import('vue').useCssModule
const useCssSupports: typeof import('@vueuse/core').useCssSupports
const useCssVar: typeof import('@vueuse/core').useCssVar
const useCssVars: typeof import('vue').useCssVars
const useCurrentElement: typeof import('@vueuse/core').useCurrentElement
const useCycleList: typeof import('@vueuse/core').useCycleList
const useDark: typeof import('@vueuse/core').useDark
const useDateFormat: typeof import('@vueuse/core').useDateFormat
const useDebounce: typeof import('@vueuse/core').useDebounce
const useDebounceFn: typeof import('@vueuse/core').useDebounceFn
const useDebouncedRefHistory: typeof import('@vueuse/core').useDebouncedRefHistory
const useDeviceMotion: typeof import('@vueuse/core').useDeviceMotion
const useDeviceOrientation: typeof import('@vueuse/core').useDeviceOrientation
const useDevicePixelRatio: typeof import('@vueuse/core').useDevicePixelRatio
const useDevicesList: typeof import('@vueuse/core').useDevicesList
const useDisplayMedia: typeof import('@vueuse/core').useDisplayMedia
const useDocumentVisibility: typeof import('@vueuse/core').useDocumentVisibility
const useDraggable: typeof import('@vueuse/core').useDraggable
const useDropZone: typeof import('@vueuse/core').useDropZone
const useElementBounding: typeof import('@vueuse/core').useElementBounding
const useElementByPoint: typeof import('@vueuse/core').useElementByPoint
const useElementHover: typeof import('@vueuse/core').useElementHover
const useElementSize: typeof import('@vueuse/core').useElementSize
const useElementVisibility: typeof import('@vueuse/core').useElementVisibility
const useEventBus: typeof import('@vueuse/core').useEventBus
const useEventListener: typeof import('@vueuse/core').useEventListener
const useEventSource: typeof import('@vueuse/core').useEventSource
const useEyeDropper: typeof import('@vueuse/core').useEyeDropper
const useFavicon: typeof import('@vueuse/core').useFavicon
const useFetch: typeof import('@vueuse/core').useFetch
const useFileDialog: typeof import('@vueuse/core').useFileDialog
const useFileSystemAccess: typeof import('@vueuse/core').useFileSystemAccess
const useFocus: typeof import('@vueuse/core').useFocus
const useFocusWithin: typeof import('@vueuse/core').useFocusWithin
const useFps: typeof import('@vueuse/core').useFps
const useFullscreen: typeof import('@vueuse/core').useFullscreen
const useGamepad: typeof import('@vueuse/core').useGamepad
const useGeolocation: typeof import('@vueuse/core').useGeolocation
const useId: typeof import('vue').useId
const useIdle: typeof import('@vueuse/core').useIdle
const useImage: typeof import('@vueuse/core').useImage
const useInfiniteScroll: typeof import('@vueuse/core').useInfiniteScroll
const useIntersectionObserver: typeof import('@vueuse/core').useIntersectionObserver
const useInterval: typeof import('@vueuse/core').useInterval
const useIntervalFn: typeof import('@vueuse/core').useIntervalFn
const useKeyModifier: typeof import('@vueuse/core').useKeyModifier
const useLastChanged: typeof import('@vueuse/core').useLastChanged
const useLink: typeof import('vue-router').useLink
const useLocalStorage: typeof import('@vueuse/core').useLocalStorage
const useMagicKeys: typeof import('@vueuse/core').useMagicKeys
const useManualRefHistory: typeof import('@vueuse/core').useManualRefHistory
const useMediaControls: typeof import('@vueuse/core').useMediaControls
const useMediaQuery: typeof import('@vueuse/core').useMediaQuery
const useMemoize: typeof import('@vueuse/core').useMemoize
const useMemory: typeof import('@vueuse/core').useMemory
const useModel: typeof import('vue').useModel
const useMounted: typeof import('@vueuse/core').useMounted
const useMouse: typeof import('@vueuse/core').useMouse
const useMouseInElement: typeof import('@vueuse/core').useMouseInElement
const useMousePressed: typeof import('@vueuse/core').useMousePressed
const useMutationObserver: typeof import('@vueuse/core').useMutationObserver
const useNavigatorLanguage: typeof import('@vueuse/core').useNavigatorLanguage
const useNetwork: typeof import('@vueuse/core').useNetwork
const useNow: typeof import('@vueuse/core').useNow
const useObjectUrl: typeof import('@vueuse/core').useObjectUrl
const useOffsetPagination: typeof import('@vueuse/core').useOffsetPagination
const useOnline: typeof import('@vueuse/core').useOnline
const usePageLeave: typeof import('@vueuse/core').usePageLeave
const useParallax: typeof import('@vueuse/core').useParallax
const useParentElement: typeof import('@vueuse/core').useParentElement
const usePerformanceObserver: typeof import('@vueuse/core').usePerformanceObserver
const usePermission: typeof import('@vueuse/core').usePermission
const usePointer: typeof import('@vueuse/core').usePointer
const usePointerLock: typeof import('@vueuse/core').usePointerLock
const usePointerSwipe: typeof import('@vueuse/core').usePointerSwipe
const usePreferredColorScheme: typeof import('@vueuse/core').usePreferredColorScheme
const usePreferredContrast: typeof import('@vueuse/core').usePreferredContrast
const usePreferredDark: typeof import('@vueuse/core').usePreferredDark
const usePreferredLanguages: typeof import('@vueuse/core').usePreferredLanguages
const usePreferredReducedMotion: typeof import('@vueuse/core').usePreferredReducedMotion
const usePreferredReducedTransparency: typeof import('@vueuse/core').usePreferredReducedTransparency
const usePrevious: typeof import('@vueuse/core').usePrevious
const useRafFn: typeof import('@vueuse/core').useRafFn
const useRefHistory: typeof import('@vueuse/core').useRefHistory
const useResizeObserver: typeof import('@vueuse/core').useResizeObserver
const useRoute: typeof import('vue-router').useRoute
const useRouter: typeof import('vue-router').useRouter
const useSSRWidth: typeof import('@vueuse/core').useSSRWidth
const useScreenOrientation: typeof import('@vueuse/core').useScreenOrientation
const useScreenSafeArea: typeof import('@vueuse/core').useScreenSafeArea
const useScriptTag: typeof import('@vueuse/core').useScriptTag
const useScroll: typeof import('@vueuse/core').useScroll
const useScrollLock: typeof import('@vueuse/core').useScrollLock
const useSessionStorage: typeof import('@vueuse/core').useSessionStorage
const useShare: typeof import('@vueuse/core').useShare
const useSlots: typeof import('vue').useSlots
const useSorted: typeof import('@vueuse/core').useSorted
const useSpeechRecognition: typeof import('@vueuse/core').useSpeechRecognition
const useSpeechSynthesis: typeof import('@vueuse/core').useSpeechSynthesis
const useStepper: typeof import('@vueuse/core').useStepper
const useStorage: typeof import('@vueuse/core').useStorage
const useStorageAsync: typeof import('@vueuse/core').useStorageAsync
const useStyleTag: typeof import('@vueuse/core').useStyleTag
const useSupported: typeof import('@vueuse/core').useSupported
const useSwipe: typeof import('@vueuse/core').useSwipe
const useTemplateRef: typeof import('vue').useTemplateRef
const useTemplateRefsList: typeof import('@vueuse/core').useTemplateRefsList
const useTextDirection: typeof import('@vueuse/core').useTextDirection
const useTextSelection: typeof import('@vueuse/core').useTextSelection
const useTextareaAutosize: typeof import('@vueuse/core').useTextareaAutosize
const useThrottle: typeof import('@vueuse/core').useThrottle
const useThrottleFn: typeof import('@vueuse/core').useThrottleFn
const useThrottledRefHistory: typeof import('@vueuse/core').useThrottledRefHistory
const useTimeAgo: typeof import('@vueuse/core').useTimeAgo
const useTimeAgoIntl: typeof import('@vueuse/core').useTimeAgoIntl
const useTimeout: typeof import('@vueuse/core').useTimeout
const useTimeoutFn: typeof import('@vueuse/core').useTimeoutFn
const useTimeoutPoll: typeof import('@vueuse/core').useTimeoutPoll
const useTimestamp: typeof import('@vueuse/core').useTimestamp
const useTitle: typeof import('@vueuse/core').useTitle
const useToNumber: typeof import('@vueuse/core').useToNumber
const useToString: typeof import('@vueuse/core').useToString
const useToggle: typeof import('@vueuse/core').useToggle
const useTransition: typeof import('@vueuse/core').useTransition
const useUrlSearchParams: typeof import('@vueuse/core').useUrlSearchParams
const useUserMedia: typeof import('@vueuse/core').useUserMedia
const useVModel: typeof import('@vueuse/core').useVModel
const useVModels: typeof import('@vueuse/core').useVModels
const useVibrate: typeof import('@vueuse/core').useVibrate
const useVirtualList: typeof import('@vueuse/core').useVirtualList
const useWakeLock: typeof import('@vueuse/core').useWakeLock
const useWebNotification: typeof import('@vueuse/core').useWebNotification
const useWebSocket: typeof import('@vueuse/core').useWebSocket
const useWebWorker: typeof import('@vueuse/core').useWebWorker
const useWebWorkerFn: typeof import('@vueuse/core').useWebWorkerFn
const useWindowFocus: typeof import('@vueuse/core').useWindowFocus
const useWindowScroll: typeof import('@vueuse/core').useWindowScroll
const useWindowSize: typeof import('@vueuse/core').useWindowSize
const watch: typeof import('vue').watch
const watchArray: typeof import('@vueuse/core').watchArray
const watchAtMost: typeof import('@vueuse/core').watchAtMost
const watchDebounced: typeof import('@vueuse/core').watchDebounced
const watchDeep: typeof import('@vueuse/core').watchDeep
const watchEffect: typeof import('vue').watchEffect
const watchIgnorable: typeof import('@vueuse/core').watchIgnorable
const watchImmediate: typeof import('@vueuse/core').watchImmediate
const watchOnce: typeof import('@vueuse/core').watchOnce
const watchPausable: typeof import('@vueuse/core').watchPausable
const watchPostEffect: typeof import('vue').watchPostEffect
const watchSyncEffect: typeof import('vue').watchSyncEffect
const watchThrottled: typeof import('@vueuse/core').watchThrottled
const watchTriggerable: typeof import('@vueuse/core').watchTriggerable
const watchWithFilter: typeof import('@vueuse/core').watchWithFilter
const whenever: typeof import('@vueuse/core').whenever
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}