fix: frontend build error
This commit is contained in:
@@ -23,7 +23,7 @@ export async function list(
|
||||
pageSize: number = 20,
|
||||
filters?: {
|
||||
type?: RedeemCodeType;
|
||||
status?: 'active' | 'used' | 'expired';
|
||||
status?: 'active' | 'used' | 'expired' | 'unused';
|
||||
search?: string;
|
||||
}
|
||||
): Promise<PaginatedResponse<RedeemCode>> {
|
||||
|
||||
@@ -506,7 +506,7 @@
|
||||
{{ t('common.back') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="oauthFlowRef?.inputMethod?.value === 'manual'"
|
||||
v-if="isManualInputMethod"
|
||||
type="button"
|
||||
:disabled="!canExchangeCode"
|
||||
class="btn btn-primary"
|
||||
@@ -533,14 +533,22 @@ import { ref, reactive, computed, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import { useAccountOAuth, type AddMethod } from '@/composables/useAccountOAuth'
|
||||
import { useAccountOAuth, type AddMethod, type AuthInputMethod } from '@/composables/useAccountOAuth'
|
||||
import type { Proxy, Group, AccountPlatform, AccountType } from '@/types'
|
||||
import Modal from '@/components/common/Modal.vue'
|
||||
import Select from '@/components/common/Select.vue'
|
||||
import ProxySelector from '@/components/common/ProxySelector.vue'
|
||||
import GroupSelector from '@/components/common/GroupSelector.vue'
|
||||
import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue'
|
||||
|
||||
// Type for exposed OAuthAuthorizationFlow component
|
||||
// Note: defineExpose automatically unwraps refs, so we use the unwrapped types
|
||||
interface OAuthFlowExposed {
|
||||
authCode: string
|
||||
sessionKey: string
|
||||
inputMethod: AuthInputMethod
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
interface Props {
|
||||
@@ -561,7 +569,7 @@ const appStore = useAppStore()
|
||||
const oauth = useAccountOAuth()
|
||||
|
||||
// Refs
|
||||
const oauthFlowRef = ref<InstanceType<typeof OAuthAuthorizationFlow> | null>(null)
|
||||
const oauthFlowRef = ref<OAuthFlowExposed | null>(null)
|
||||
|
||||
// Model mapping type
|
||||
interface ModelMapping {
|
||||
@@ -630,8 +638,12 @@ const form = reactive({
|
||||
// Helper to check if current type needs OAuth flow
|
||||
const isOAuthFlow = computed(() => accountCategory.value === 'oauth-based')
|
||||
|
||||
const isManualInputMethod = computed(() => {
|
||||
return oauthFlowRef.value?.inputMethod === 'manual'
|
||||
})
|
||||
|
||||
const canExchangeCode = computed(() => {
|
||||
const authCode = oauthFlowRef.value?.authCode?.value || ''
|
||||
const authCode = oauthFlowRef.value?.authCode || ''
|
||||
return authCode.trim() && oauth.sessionId.value && !oauth.loading.value
|
||||
})
|
||||
|
||||
@@ -815,7 +827,7 @@ const handleGenerateUrl = async () => {
|
||||
}
|
||||
|
||||
const handleExchangeCode = async () => {
|
||||
const authCode = oauthFlowRef.value?.authCode?.value || ''
|
||||
const authCode = oauthFlowRef.value?.authCode || ''
|
||||
if (!authCode.trim() || !oauth.sessionId.value) return
|
||||
|
||||
oauth.loading.value = true
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
{{ t('common.cancel') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="oauthFlowRef?.inputMethod?.value === 'manual'"
|
||||
v-if="isManualInputMethod"
|
||||
type="button"
|
||||
:disabled="!canExchangeCode"
|
||||
class="btn btn-primary"
|
||||
@@ -98,11 +98,20 @@ import { ref, computed, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import { useAccountOAuth, type AddMethod } from '@/composables/useAccountOAuth'
|
||||
import { useAccountOAuth, type AddMethod, type AuthInputMethod } from '@/composables/useAccountOAuth'
|
||||
import type { Account } from '@/types'
|
||||
import Modal from '@/components/common/Modal.vue'
|
||||
import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue'
|
||||
|
||||
// Type for exposed OAuthAuthorizationFlow component
|
||||
// Note: defineExpose automatically unwraps refs, so we use the unwrapped types
|
||||
interface OAuthFlowExposed {
|
||||
authCode: string
|
||||
sessionKey: string
|
||||
inputMethod: AuthInputMethod
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
account: Account | null
|
||||
@@ -121,14 +130,18 @@ const { t } = useI18n()
|
||||
const oauth = useAccountOAuth()
|
||||
|
||||
// Refs
|
||||
const oauthFlowRef = ref<InstanceType<typeof OAuthAuthorizationFlow> | null>(null)
|
||||
const oauthFlowRef = ref<OAuthFlowExposed | null>(null)
|
||||
|
||||
// State
|
||||
const addMethod = ref<AddMethod>('oauth')
|
||||
|
||||
// Computed
|
||||
const isManualInputMethod = computed(() => {
|
||||
return oauthFlowRef.value?.inputMethod === 'manual'
|
||||
})
|
||||
|
||||
const canExchangeCode = computed(() => {
|
||||
const authCode = oauthFlowRef.value?.authCode?.value || ''
|
||||
const authCode = oauthFlowRef.value?.authCode || ''
|
||||
return authCode.trim() && oauth.sessionId.value && !oauth.loading.value
|
||||
})
|
||||
|
||||
@@ -163,7 +176,7 @@ const handleGenerateUrl = async () => {
|
||||
const handleExchangeCode = async () => {
|
||||
if (!props.account) return
|
||||
|
||||
const authCode = oauthFlowRef.value?.authCode?.value || ''
|
||||
const authCode = oauthFlowRef.value?.authCode || ''
|
||||
if (!authCode.trim() || !oauth.sessionId.value) return
|
||||
|
||||
oauth.loading.value = true
|
||||
|
||||
@@ -47,7 +47,7 @@ interface Emits {
|
||||
(e: 'cancel'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
withDefaults(defineProps<Props>(), {
|
||||
confirmText: 'Confirm',
|
||||
cancelText: 'Cancel',
|
||||
danger: false
|
||||
|
||||
@@ -86,16 +86,10 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { Column } from './types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
export interface Column {
|
||||
key: string
|
||||
label: string
|
||||
sortable?: boolean
|
||||
formatter?: (value: any, row: any) => string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
columns: Column[]
|
||||
data: any[]
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
interface Props {
|
||||
icon?: Component | string
|
||||
@@ -81,7 +80,7 @@ interface Props {
|
||||
message?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
withDefaults(defineProps<Props>(), {
|
||||
title: 'No data found',
|
||||
description: '',
|
||||
actionIcon: true
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<div class="select-options">
|
||||
<div
|
||||
v-for="option in filteredOptions"
|
||||
:key="getOptionValue(option)"
|
||||
:key="getOptionValue(option) ?? undefined"
|
||||
@click="selectOption(option)"
|
||||
:class="[
|
||||
'select-option',
|
||||
@@ -136,9 +136,9 @@ const searchQuery = ref('')
|
||||
const containerRef = ref<HTMLElement | null>(null)
|
||||
const searchInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const getOptionValue = (option: SelectOption | Record<string, unknown>): string | number | null => {
|
||||
const getOptionValue = (option: SelectOption | Record<string, unknown>): string | number | null | undefined => {
|
||||
if (typeof option === 'object' && option !== null) {
|
||||
return option[props.valueKey] as string | number | null
|
||||
return option[props.valueKey] as string | number | null | undefined
|
||||
}
|
||||
return option as string | number | null
|
||||
}
|
||||
@@ -187,7 +187,7 @@ const toggle = () => {
|
||||
}
|
||||
|
||||
const selectOption = (option: SelectOption | Record<string, unknown>) => {
|
||||
const value = getOptionValue(option)
|
||||
const value = getOptionValue(option) ?? null
|
||||
emit('update:modelValue', value)
|
||||
emit('change', value, option as SelectOption)
|
||||
isOpen.value = false
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import subscriptionsAPI from '@/api/subscriptions';
|
||||
import type { UserSubscription } from '@/types';
|
||||
|
||||
@@ -10,4 +10,4 @@ export { default as EmptyState } from './EmptyState.vue'
|
||||
export { default as LocaleSwitcher } from './LocaleSwitcher.vue'
|
||||
|
||||
// Export types
|
||||
export type { Column } from './DataTable.vue'
|
||||
export type { Column } from './types'
|
||||
|
||||
10
frontend/src/components/common/types.ts
Normal file
10
frontend/src/components/common/types.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Common component types
|
||||
*/
|
||||
|
||||
export interface Column {
|
||||
key: string
|
||||
label: string
|
||||
sortable?: boolean
|
||||
formatter?: (value: any, row: any) => string
|
||||
}
|
||||
@@ -268,7 +268,7 @@ const routes: RouteRecordRaw[] = [
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
scrollBehavior(_to, _from, savedPosition) {
|
||||
// Scroll to saved position when using browser back/forward
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
@@ -283,7 +283,7 @@ const router = createRouter({
|
||||
*/
|
||||
let authInitialized = false;
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach((to, _from, next) => {
|
||||
const authStore = useAuthStore();
|
||||
|
||||
// Restore auth state from localStorage on first navigation (page refresh)
|
||||
|
||||
@@ -152,6 +152,12 @@ export interface UserStats {
|
||||
|
||||
// ==================== API Response Types ====================
|
||||
|
||||
export interface ApiResponse<T = unknown> {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface ApiError {
|
||||
detail: string;
|
||||
code?: string;
|
||||
@@ -357,6 +363,7 @@ export interface CreateAccountRequest {
|
||||
|
||||
export interface UpdateAccountRequest {
|
||||
name?: string;
|
||||
type?: AccountType;
|
||||
credentials?: Record<string, unknown>;
|
||||
extra?: Record<string, string>;
|
||||
proxy_id?: number | null;
|
||||
|
||||
@@ -265,7 +265,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { Account, Proxy, Group } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import DataTable from '@/components/common/DataTable.vue'
|
||||
import Pagination from '@/components/common/Pagination.vue'
|
||||
|
||||
@@ -398,7 +398,7 @@ const lineOptions = computed(() => ({
|
||||
font: {
|
||||
size: 10,
|
||||
},
|
||||
callback: (value: number) => formatTokens(value),
|
||||
callback: (value: string | number) => formatTokens(Number(value)),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -450,7 +450,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { Group, GroupPlatform, SubscriptionType } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import DataTable from '@/components/common/DataTable.vue'
|
||||
import Pagination from '@/components/common/Pagination.vue'
|
||||
@@ -570,7 +570,7 @@ const loadGroups = async () => {
|
||||
pagination.page,
|
||||
pagination.page_size,
|
||||
{
|
||||
platform: filters.platform || undefined,
|
||||
platform: (filters.platform as GroupPlatform) || undefined,
|
||||
status: filters.status as any,
|
||||
is_exclusive: filters.is_exclusive ? filters.is_exclusive === 'true' : undefined
|
||||
}
|
||||
|
||||
@@ -465,7 +465,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { Proxy, ProxyProtocol } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import DataTable from '@/components/common/DataTable.vue'
|
||||
import Pagination from '@/components/common/Pagination.vue'
|
||||
|
||||
@@ -349,7 +349,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { RedeemCode, RedeemCodeType, Group } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import DataTable from '@/components/common/DataTable.vue'
|
||||
import Pagination from '@/components/common/Pagination.vue'
|
||||
|
||||
@@ -303,7 +303,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { UserSubscription, Group, User } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import DataTable from '@/components/common/DataTable.vue'
|
||||
import Pagination from '@/components/common/Pagination.vue'
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { adminAPI } from '@/api/admin'
|
||||
@@ -303,7 +303,7 @@ import EmptyState from '@/components/common/EmptyState.vue'
|
||||
import Select from '@/components/common/Select.vue'
|
||||
import DateRangePicker from '@/components/common/DateRangePicker.vue'
|
||||
import type { UsageLog } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import type { SimpleUser, SimpleApiKey, AdminUsageStatsResponse, AdminUsageQueryParams } from '@/api/admin/usage'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -641,7 +641,7 @@ const { t } = useI18n()
|
||||
import { adminAPI } from '@/api/admin'
|
||||
import type { User, ApiKey, Group } from '@/types'
|
||||
import type { BatchUserUsageStats } from '@/api/admin/dashboard'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import DataTable from '@/components/common/DataTable.vue'
|
||||
import Pagination from '@/components/common/Pagination.vue'
|
||||
|
||||
@@ -163,7 +163,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { AuthLayout } from '@/components/layout';
|
||||
import TurnstileWidget from '@/components/TurnstileWidget.vue';
|
||||
|
||||
@@ -523,7 +523,7 @@ const lineOptions = computed(() => ({
|
||||
font: {
|
||||
size: 10,
|
||||
},
|
||||
callback: (value: number) => formatTokens(value),
|
||||
callback: (value: string | number) => formatTokens(Number(value)),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -217,17 +217,17 @@
|
||||
<template #selected="{ option }">
|
||||
<GroupBadge
|
||||
v-if="option"
|
||||
:name="option.label"
|
||||
:subscription-type="option.subscriptionType"
|
||||
:rate-multiplier="option.rate"
|
||||
:name="(option as unknown as GroupOption).label"
|
||||
:subscription-type="(option as unknown as GroupOption).subscriptionType"
|
||||
:rate-multiplier="(option as unknown as GroupOption).rate"
|
||||
/>
|
||||
<span v-else class="text-gray-400">{{ t('keys.selectGroup') }}</span>
|
||||
</template>
|
||||
<template #option="{ option }">
|
||||
<GroupBadge
|
||||
:name="option.label"
|
||||
:subscription-type="option.subscriptionType"
|
||||
:rate-multiplier="option.rate"
|
||||
:name="(option as unknown as GroupOption).label"
|
||||
:subscription-type="(option as unknown as GroupOption).subscriptionType"
|
||||
:rate-multiplier="(option as unknown as GroupOption).rate"
|
||||
/>
|
||||
</template>
|
||||
</Select>
|
||||
@@ -366,7 +366,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, type ComponentPublicInstance } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
@@ -382,9 +382,16 @@ import Select from '@/components/common/Select.vue'
|
||||
import UseKeyModal from '@/components/keys/UseKeyModal.vue'
|
||||
import GroupBadge from '@/components/common/GroupBadge.vue'
|
||||
import type { ApiKey, Group, PublicSettings, SubscriptionType } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
import type { BatchApiKeyUsageStats } from '@/api/usage'
|
||||
|
||||
interface GroupOption {
|
||||
value: number
|
||||
label: string
|
||||
rate: number
|
||||
subscriptionType: SubscriptionType
|
||||
}
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const columns = computed<Column[]>(() => [
|
||||
@@ -428,8 +435,8 @@ const selectedKeyForGroup = computed(() => {
|
||||
return apiKeys.value.find(k => k.id === groupSelectorKeyId.value) || null
|
||||
})
|
||||
|
||||
const setGroupButtonRef = (keyId: number, el: HTMLElement | null) => {
|
||||
if (el) {
|
||||
const setGroupButtonRef = (keyId: number, el: Element | ComponentPublicInstance | null) => {
|
||||
if (el instanceof HTMLElement) {
|
||||
groupButtonRefs.value.set(keyId, el)
|
||||
} else {
|
||||
groupButtonRefs.value.delete(keyId)
|
||||
@@ -562,7 +569,9 @@ const editKey = (key: ApiKey) => {
|
||||
formData.value = {
|
||||
name: key.name,
|
||||
group_id: key.group_id,
|
||||
status: key.status
|
||||
status: key.status,
|
||||
use_custom_key: false,
|
||||
custom_key: ''
|
||||
}
|
||||
showEditModal.value = true
|
||||
}
|
||||
|
||||
@@ -341,10 +341,6 @@ const isBalanceType = (type: string) => {
|
||||
return type === 'balance' || type === 'admin_balance'
|
||||
}
|
||||
|
||||
const isConcurrencyType = (type: string) => {
|
||||
return type === 'concurrency' || type === 'admin_concurrency'
|
||||
}
|
||||
|
||||
const isSubscriptionType = (type: string) => {
|
||||
return type === 'subscription'
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ import EmptyState from '@/components/common/EmptyState.vue'
|
||||
import Select from '@/components/common/Select.vue'
|
||||
import DateRangePicker from '@/components/common/DateRangePicker.vue'
|
||||
import type { UsageLog, ApiKey, UsageQueryParams, UsageStatsResponse } from '@/types'
|
||||
import type { Column } from '@/components/common/DataTable.vue'
|
||||
import type { Column } from '@/components/common/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const appStore = useAppStore()
|
||||
@@ -316,21 +316,6 @@ const pagination = ref({
|
||||
pages: 0
|
||||
})
|
||||
|
||||
const totalTokens = computed(() => {
|
||||
return usageLogs.value.reduce((sum, log) =>
|
||||
sum + log.input_tokens + log.output_tokens, 0
|
||||
)
|
||||
})
|
||||
|
||||
const totalCost = computed(() => {
|
||||
return usageLogs.value.reduce((sum, log) => sum + log.total_cost, 0)
|
||||
})
|
||||
|
||||
const avgDuration = computed(() => {
|
||||
if (usageLogs.value.length === 0) return 0
|
||||
return usageLogs.value.reduce((sum, log) => sum + log.duration_ms, 0) / usageLogs.value.length
|
||||
})
|
||||
|
||||
const formatDuration = (ms: number): string => {
|
||||
if (ms < 1000) return `${ms.toFixed(0)}ms`
|
||||
return `${(ms / 1000).toFixed(2)}s`
|
||||
|
||||
16
frontend/src/vite-env.d.ts
vendored
Normal file
16
frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_API_BASE_URL: string
|
||||
readonly BASE_URL: string
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
Reference in New Issue
Block a user