@vesperjs/nuxt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/app/composables/backend/api/create-fetch.ts +3 -0
- package/app/composables/backend/api/create-request-fetch.ts +5 -0
- package/app/composables/backend/api/index.ts +7 -0
- package/app/composables/backend/api/use-api-constants.ts +21 -0
- package/app/composables/backend/api/use-http-headers.ts +18 -0
- package/app/composables/backend/api/use-mutation-api.ts +64 -0
- package/app/composables/backend/api/use-ofetch.ts +24 -0
- package/app/composables/backend/api/use-query-api.ts +93 -0
- package/app/composables/backend/error/index.ts +2 -0
- package/app/composables/backend/error/use-backend-error-info.ts +15 -0
- package/app/composables/backend/error/use-external-errors.ts +42 -0
- package/app/composables/backend/index.ts +8 -0
- package/app/composables/backend/use-alert.ts +94 -0
- package/app/composables/index.ts +8 -0
- package/app/composables/use-datetime-local.ts +35 -0
- package/app/composables/use-entity.ts +13 -0
- package/app/composables/use-flash.ts +15 -0
- package/app/composables/use-locale.ts +23 -0
- package/app/composables/use-more-scroll.ts +70 -0
- package/app/composables/use-referer.ts +9 -0
- package/app/composables/use-time-zone.ts +86 -0
- package/app/composables/util/index.ts +1 -0
- package/app/composables/util/use-date.ts +7 -0
- package/app.config.ts +7 -0
- package/interfaces/error/backend-error-info.ts +4 -0
- package/interfaces/error/index.ts +1 -0
- package/interfaces/flash.ts +4 -0
- package/interfaces/index.ts +5 -0
- package/interfaces/resource/beckend-error-resource.ts +5 -0
- package/interfaces/resource/errors-resource.ts +3 -0
- package/interfaces/resource/index.ts +2 -0
- package/nuxt.config.ts +6 -0
- package/package.json +33 -0
- package/types/error-messages.ts +1 -0
- package/types/index.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { QueryAPIOptions } from './use-query-api'
|
|
2
|
+
|
|
3
|
+
export { createFetch } from './create-fetch'
|
|
4
|
+
export { createRequestFetch } from './create-request-fetch'
|
|
5
|
+
export { useOFetch } from './use-ofetch'
|
|
6
|
+
export { useQueryApi } from './use-query-api'
|
|
7
|
+
export { useMutationApi } from './use-mutation-api'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useRuntimeConfig } from 'nuxt/app'
|
|
2
|
+
|
|
3
|
+
import { computed } from '@vue/reactivity'
|
|
4
|
+
|
|
5
|
+
export const useApiConstants = () => {
|
|
6
|
+
const runtimeConfig = useRuntimeConfig()
|
|
7
|
+
|
|
8
|
+
const baseOrigin = computed<string | undefined>(() => {
|
|
9
|
+
if (import.meta.client) {
|
|
10
|
+
return runtimeConfig.public.backendApiOrigin as string
|
|
11
|
+
} else if (import.meta.server) {
|
|
12
|
+
return runtimeConfig.backendApiOrigin as string
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const basePath = runtimeConfig.public.backendApiPath as string
|
|
17
|
+
|
|
18
|
+
const baseURL = computed<string>(() => `${baseOrigin.value}${basePath}`)
|
|
19
|
+
|
|
20
|
+
return { baseURL }
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { computed, type Ref } from '@vue/reactivity'
|
|
2
|
+
|
|
3
|
+
import { useLocale } from '../../use-locale'
|
|
4
|
+
import { useTimeZone } from '../../use-time-zone'
|
|
5
|
+
|
|
6
|
+
export const useHttpHeaders = () => {
|
|
7
|
+
const { locale } = useLocale()
|
|
8
|
+
const { timeZone } = useTimeZone()
|
|
9
|
+
|
|
10
|
+
const commonHeaders: Ref<Record<string, string>> = computed(() => ({
|
|
11
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
12
|
+
Accept: 'application/json',
|
|
13
|
+
'Accept-Language': locale.value,
|
|
14
|
+
'Time-Zone': timeZone.value.client,
|
|
15
|
+
}))
|
|
16
|
+
|
|
17
|
+
return { commonHeaders }
|
|
18
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { FetchOptions, FetchError, FetchResponse } from 'ofetch'
|
|
2
|
+
|
|
3
|
+
import { useHttpHeaders } from './use-http-headers'
|
|
4
|
+
import { useApiConstants } from './use-api-constants'
|
|
5
|
+
import { useOFetch } from './use-ofetch'
|
|
6
|
+
|
|
7
|
+
interface MutationAPIOptions {
|
|
8
|
+
method: 'post' | 'put' | 'delete'
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
body?: Record<string, any> | FormData
|
|
11
|
+
token?: string | null
|
|
12
|
+
onRequestError?: ({ error }: { error: Error }) => void
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
onResponseError?: ({ response }: { response: FetchResponse<any> }) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line
|
|
18
|
+
export const useMutationApi = async <T = unknown, E = any>(
|
|
19
|
+
url: string,
|
|
20
|
+
{ method, body = {}, token = null, onRequestError, onResponseError }: MutationAPIOptions,
|
|
21
|
+
): Promise<{
|
|
22
|
+
token: string | undefined
|
|
23
|
+
data: T | undefined
|
|
24
|
+
error: FetchError<E> | undefined
|
|
25
|
+
pending: boolean
|
|
26
|
+
}> => {
|
|
27
|
+
const { commonHeaders } = useHttpHeaders()
|
|
28
|
+
const { baseURL } = useApiConstants()
|
|
29
|
+
|
|
30
|
+
const headers: Record<string, string> = commonHeaders.value
|
|
31
|
+
|
|
32
|
+
const tokenRef = ref<string>()
|
|
33
|
+
|
|
34
|
+
if (token) {
|
|
35
|
+
headers.Authorization = `Bearer ${token}`
|
|
36
|
+
tokenRef.value = token
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const options: FetchOptions<'json'> = {
|
|
40
|
+
baseURL: baseURL.value,
|
|
41
|
+
headers,
|
|
42
|
+
method,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (onRequestError) {
|
|
46
|
+
options.onRequestError = onRequestError
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (onResponseError) {
|
|
50
|
+
options.onResponseError = onResponseError
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (method == 'post' || method == 'put') {
|
|
54
|
+
options.body = body
|
|
55
|
+
options.onResponse = ({ response }: { response: FetchResponse<T> }) => {
|
|
56
|
+
if ((method == 'post' && !tokenRef.value) || method == 'put')
|
|
57
|
+
tokenRef.value = response.headers.get('Authorization')?.split(' ')[1]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const { data, error, pending } = await useOFetch<T, E>(url, options)
|
|
62
|
+
|
|
63
|
+
return { token: tokenRef.value, data, error, pending }
|
|
64
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { $Fetch, FetchOptions, FetchError } from 'ofetch'
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line
|
|
4
|
+
export const useOFetch = async <T = unknown, E = any>(
|
|
5
|
+
url: string,
|
|
6
|
+
options?: FetchOptions<'json'>,
|
|
7
|
+
) => {
|
|
8
|
+
const { $api } = useNuxtApp()
|
|
9
|
+
|
|
10
|
+
const pending = ref<boolean>(true)
|
|
11
|
+
|
|
12
|
+
const data = ref<T>()
|
|
13
|
+
const error = ref<FetchError<E>>()
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
data.value = await ($api as $Fetch)<T>(url, options)
|
|
17
|
+
} catch (err: unknown) {
|
|
18
|
+
error.value = err as FetchError<E>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pending.value = false
|
|
22
|
+
|
|
23
|
+
return { data: data.value, error: error.value, pending: pending.value }
|
|
24
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useAsyncData, useNuxtApp } from 'nuxt/app'
|
|
2
|
+
|
|
3
|
+
import type { FetchOptions, FetchResponse } from 'ofetch'
|
|
4
|
+
|
|
5
|
+
import { ref } from '@vue/reactivity'
|
|
6
|
+
|
|
7
|
+
import { useHttpHeaders } from './use-http-headers'
|
|
8
|
+
import { useApiConstants } from './use-api-constants'
|
|
9
|
+
import { useOFetch } from './use-ofetch'
|
|
10
|
+
|
|
11
|
+
interface SearchParams {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
[key: string]: any
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type QueryAPIOptions = {
|
|
17
|
+
key?: MaybeRefOrGetter<string>
|
|
18
|
+
query?: SearchParams
|
|
19
|
+
token?: string | null
|
|
20
|
+
signal?: AbortSignal
|
|
21
|
+
onRequestError?: ({ error }: { error: Error }) => void
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
onResponseError?: ({ response }: { response: FetchResponse<any> }) => void
|
|
24
|
+
fresh?: boolean
|
|
25
|
+
cache?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
export const useQueryApi = async <T = unknown, E = any>(url: string, options?: QueryAPIOptions) => {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
const { $api } = useNuxtApp() as any
|
|
32
|
+
const { commonHeaders } = useHttpHeaders()
|
|
33
|
+
const { baseURL } = useApiConstants()
|
|
34
|
+
|
|
35
|
+
const key = options?.key ?? url
|
|
36
|
+
|
|
37
|
+
const tokenRef = ref<string>()
|
|
38
|
+
|
|
39
|
+
const headers: Record<string, string> = commonHeaders.value
|
|
40
|
+
|
|
41
|
+
const fresh: boolean = options?.fresh ?? false
|
|
42
|
+
const cache: boolean = options?.cache ?? true
|
|
43
|
+
|
|
44
|
+
if (options?.token) {
|
|
45
|
+
headers.Authorization = `Bearer ${options.token}`
|
|
46
|
+
tokenRef.value = options.token
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
const getOptions: FetchOptions<'json', any> = {
|
|
51
|
+
baseURL: baseURL.value,
|
|
52
|
+
method: 'get',
|
|
53
|
+
query: options?.query ?? {},
|
|
54
|
+
headers,
|
|
55
|
+
onResponse({ response }: { response: FetchResponse<T> }) {
|
|
56
|
+
if (!tokenRef.value) tokenRef.value = response.headers.get('Authorization')?.split(' ')[1]
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (options?.signal) {
|
|
61
|
+
getOptions.signal = options.signal
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (options?.onRequestError) {
|
|
65
|
+
getOptions.onRequestError = options.onRequestError
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options?.onResponseError) {
|
|
69
|
+
getOptions.onResponseError = options.onResponseError
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (cache) {
|
|
73
|
+
const { data, error, refresh, pending } = await useAsyncData<T, E>(key, () =>
|
|
74
|
+
$api(url, getOptions),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if (fresh) {
|
|
78
|
+
await refresh()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
token: tokenRef.value,
|
|
83
|
+
data: data.value,
|
|
84
|
+
error: error.value,
|
|
85
|
+
refresh,
|
|
86
|
+
pending: pending.value,
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
const { data, error, pending } = await useOFetch<T, E>(url, getOptions)
|
|
90
|
+
|
|
91
|
+
return { token: tokenRef.value, data, error, pending }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BackendErrorInfo } from '../../../../interfaces'
|
|
2
|
+
|
|
3
|
+
export function useBackendErrorInfo<R extends object>() {
|
|
4
|
+
const info = ref<BackendErrorInfo<R>>({})
|
|
5
|
+
|
|
6
|
+
const backendErrorInfo = computed<BackendErrorInfo<R>>(() => {
|
|
7
|
+
return info.value as BackendErrorInfo<R>
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const clearBackendErrorInfo = (): void => {
|
|
11
|
+
info.value = {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return { backendErrorInfo, clearBackendErrorInfo }
|
|
15
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Flash } from '../../../../interfaces'
|
|
2
|
+
import type { ErrorMessages } from '../../../../types'
|
|
3
|
+
|
|
4
|
+
export function useExternalErrors<P extends string>({ flash }: { flash: Ref<Flash> }) {
|
|
5
|
+
const errors = ref<ErrorMessages<P>>({})
|
|
6
|
+
|
|
7
|
+
const externalErrors = computed<ErrorMessages<P>>({
|
|
8
|
+
get() {
|
|
9
|
+
return errors.value as ErrorMessages<string>
|
|
10
|
+
},
|
|
11
|
+
set(value: ErrorMessages<P>) {
|
|
12
|
+
if (errors.value) {
|
|
13
|
+
for (const key in value) {
|
|
14
|
+
;(errors.value as ErrorMessages<P>)[key] = value[key] ?? []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const clearExternalErrors = (): void => {
|
|
21
|
+
externalErrors.value = {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const isSuccess = (): boolean => {
|
|
25
|
+
let result = true
|
|
26
|
+
|
|
27
|
+
for (const key in errors.value) {
|
|
28
|
+
const error = errors.value as ErrorMessages<string>
|
|
29
|
+
if ((error[key] as string[]).length > 0) result = false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (flash.value.alert) result = false
|
|
33
|
+
|
|
34
|
+
return result
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
externalErrors,
|
|
39
|
+
clearExternalErrors,
|
|
40
|
+
isSuccess,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { QueryAPIOptions } from './api'
|
|
2
|
+
|
|
3
|
+
export { createFetch, createRequestFetch, useOFetch, useMutationApi, useQueryApi } from './api'
|
|
4
|
+
|
|
5
|
+
export { useAlert } from './use-alert'
|
|
6
|
+
export { useExternalErrors } from './error'
|
|
7
|
+
|
|
8
|
+
export type { UseAlertType } from './use-alert'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useNuxtApp } from 'nuxt/app'
|
|
2
|
+
|
|
3
|
+
import type { FetchError } from 'ofetch'
|
|
4
|
+
import type { NuxtError } from 'nuxt/app'
|
|
5
|
+
|
|
6
|
+
import type { Ref } from '@vue/reactivity'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
ErrorsResource,
|
|
10
|
+
BackendErrorInfo,
|
|
11
|
+
BackendErrorResource,
|
|
12
|
+
Flash,
|
|
13
|
+
} from '../../../interfaces'
|
|
14
|
+
import type { ErrorMessages } from '../../../types'
|
|
15
|
+
|
|
16
|
+
import { useBackendErrorInfo } from './error'
|
|
17
|
+
|
|
18
|
+
interface UseAlertOptions {
|
|
19
|
+
flash: Ref<Flash>
|
|
20
|
+
caller?: UseAlertCallerType
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface UseAlertCallerType {
|
|
24
|
+
externalErrors?: Ref<ErrorMessages<string>>
|
|
25
|
+
clearAccount?: () => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useAlert<BER extends object = BackendErrorResource>({
|
|
29
|
+
flash,
|
|
30
|
+
caller,
|
|
31
|
+
}: UseAlertOptions): {
|
|
32
|
+
backendErrorInfo: Ref<BackendErrorInfo<BER>, BER>
|
|
33
|
+
setError: (
|
|
34
|
+
error:
|
|
35
|
+
| NuxtError<ErrorsResource<ErrorMessages<string>> | BER>
|
|
36
|
+
| FetchError<ErrorsResource<ErrorMessages<string>> | BER>,
|
|
37
|
+
options?: {
|
|
38
|
+
off?: boolean
|
|
39
|
+
},
|
|
40
|
+
) => void
|
|
41
|
+
} {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
const { $i18n } = useNuxtApp() as any
|
|
44
|
+
const { backendErrorInfo, clearBackendErrorInfo } = useBackendErrorInfo<BER>()
|
|
45
|
+
|
|
46
|
+
const setError = function (
|
|
47
|
+
error:
|
|
48
|
+
| NuxtError<ErrorsResource<ErrorMessages<string>> | BER>
|
|
49
|
+
| FetchError<ErrorsResource<ErrorMessages<string>> | BER>,
|
|
50
|
+
options?: { off?: boolean },
|
|
51
|
+
): void {
|
|
52
|
+
const off = options?.off ?? false
|
|
53
|
+
|
|
54
|
+
clearBackendErrorInfo()
|
|
55
|
+
backendErrorInfo.value.status = error.status
|
|
56
|
+
if (off) {
|
|
57
|
+
switch (error.status) {
|
|
58
|
+
case 401:
|
|
59
|
+
// flash.value.alert = $i18n.t('backend.error.login')
|
|
60
|
+
if (caller && 'clearAccount' in caller && caller.clearAccount) caller.clearAccount()
|
|
61
|
+
break
|
|
62
|
+
// default:
|
|
63
|
+
// flash.value.alert = $i18n.t('backend.error.api', { message: error.message })
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
switch (error.status) {
|
|
67
|
+
case 401:
|
|
68
|
+
flash.value.alert = $i18n.t('backend.error.login')
|
|
69
|
+
if (caller && 'clearAccount' in caller && caller.clearAccount) caller.clearAccount()
|
|
70
|
+
break
|
|
71
|
+
case 404:
|
|
72
|
+
{
|
|
73
|
+
const backendError = error.data as BER
|
|
74
|
+
backendErrorInfo.value.error = backendError
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
case 422: {
|
|
78
|
+
if (caller && 'externalErrors' in caller && caller.externalErrors && error.data) {
|
|
79
|
+
const { errors } = error.data as ErrorsResource<ErrorMessages<string>>
|
|
80
|
+
// globalThis.console.log(errors)
|
|
81
|
+
caller.externalErrors.value = errors
|
|
82
|
+
}
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
default:
|
|
86
|
+
flash.value.alert = $i18n.t('backend.error.api', { message: error.message })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { backendErrorInfo, setError }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type UseAlertType = ReturnType<typeof useAlert>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { QueryAPIOptions } from './backend'
|
|
2
|
+
|
|
3
|
+
export { createFetch, createRequestFetch, useOFetch, useMutationApi, useQueryApi } from './backend'
|
|
4
|
+
export { useExternalErrors, useAlert } from './backend'
|
|
5
|
+
|
|
6
|
+
export { useDate } from './util'
|
|
7
|
+
|
|
8
|
+
export type { UseAlertType } from './backend'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { parse, format } from '@formkit/tempo'
|
|
2
|
+
|
|
3
|
+
import { useLocale } from './use-locale'
|
|
4
|
+
|
|
5
|
+
export const useDatetimeLocal = () => {
|
|
6
|
+
const { locale } = useLocale()
|
|
7
|
+
|
|
8
|
+
const parseDT = (datetime: string, format: string): Date => {
|
|
9
|
+
return parse(datetime, format, locale.value)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const toISO8601 = (datetime: string): string => {
|
|
13
|
+
return format(parseDT(datetime, 'YYYY/MM/DD HH:mm'), 'YYYY-MM-DDTHH:mm', locale.value)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const fromISO8601 = (datetime: string): string => {
|
|
17
|
+
return format(parseDT(datetime, 'YYYY-MM-DDTHH:mm'), 'YYYY/MM/DD HH:mm', locale.value)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const upDTL = (datetime: string | null): string | null => {
|
|
21
|
+
return datetime ? toISO8601(datetime) : null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const downDTL = (datetime: string | null): string => {
|
|
25
|
+
return datetime ? fromISO8601(datetime) : ''
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const formatHTML = (datetime: string | null, fmt: string): string => {
|
|
29
|
+
return datetime
|
|
30
|
+
? format(parse(datetime, 'YYYY/MM/DD HH:mm', locale.value), fmt, locale.value)
|
|
31
|
+
: ''
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { upDTL, downDTL, toISO8601, formatHTML }
|
|
35
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function useEntity<M extends object, R extends object>() {
|
|
2
|
+
const create = ({ from }: { from: R }): M => {
|
|
3
|
+
const model: Partial<M> = {}
|
|
4
|
+
Object.assign(model, from)
|
|
5
|
+
return model as M
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const copy = ({ from, to }: { from: R | M; to: M }): void => {
|
|
9
|
+
Object.assign(to, from)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return { create, copy }
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Flash } from '../../interfaces'
|
|
2
|
+
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
export function useFlash() {
|
|
6
|
+
const flash = ref<Flash>({})
|
|
7
|
+
|
|
8
|
+
const clearFlash = (): void => {
|
|
9
|
+
flash.value = {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return { flash, clearFlash }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type UseFlashType = ReturnType<typeof useFlash>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useNuxtApp } from 'nuxt/app'
|
|
2
|
+
import { useBrowserLocale } from '#i18n'
|
|
3
|
+
|
|
4
|
+
export const useLocale = () => {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
const { $i18n } = useNuxtApp() as any
|
|
7
|
+
const { locale, availableLocales, fallbackLocale } = $i18n
|
|
8
|
+
const autoDetect = (): void => {
|
|
9
|
+
const browserLocale: string | null = useBrowserLocale()
|
|
10
|
+
// console.log(browserLocale)
|
|
11
|
+
// console.log(locales.value)
|
|
12
|
+
|
|
13
|
+
type AvailableLocales = (typeof $i18n.availableLocales)[number]
|
|
14
|
+
|
|
15
|
+
locale.value = (
|
|
16
|
+
(availableLocales as string[]).includes(browserLocale ?? '')
|
|
17
|
+
? browserLocale
|
|
18
|
+
: fallbackLocale.value
|
|
19
|
+
) as AvailableLocales
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { locale, autoDetect }
|
|
23
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useState } from 'nuxt/app'
|
|
2
|
+
|
|
3
|
+
export function useMoreScroll({
|
|
4
|
+
key = null,
|
|
5
|
+
page,
|
|
6
|
+
pages,
|
|
7
|
+
}: {
|
|
8
|
+
key?: string | null
|
|
9
|
+
page: number
|
|
10
|
+
pages: number
|
|
11
|
+
}) {
|
|
12
|
+
const toFirstUpper = (str: string): string => {
|
|
13
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const currentPage = useState<number>(
|
|
17
|
+
key ? `currentPageFor${toFirstUpper(key)}` : 'currentPage',
|
|
18
|
+
() => {
|
|
19
|
+
return 1
|
|
20
|
+
},
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const pagePrev = useState<boolean>(key ? `pagePrevFor${toFirstUpper(key)}` : 'pagePrev', () => {
|
|
24
|
+
return false
|
|
25
|
+
})
|
|
26
|
+
const pageNext = useState<boolean>(key ? `pageNextFor${toFirstUpper(key)}` : 'pageNext', () => {
|
|
27
|
+
return false
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const minPage = useState<number>(key ? `minPageFor${toFirstUpper(key)}` : 'minPage', () => {
|
|
31
|
+
return 1
|
|
32
|
+
})
|
|
33
|
+
const maxPage = useState<number>(key ? `maxPageFor${toFirstUpper(key)}` : 'maxPage', () => {
|
|
34
|
+
return 1
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const minMaxPage = () => {
|
|
38
|
+
minPage.value = currentPage.value < page ? currentPage.value : page
|
|
39
|
+
maxPage.value = currentPage.value > page ? currentPage.value : page
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const pagePrevNext = () => {
|
|
43
|
+
if (currentPage.value == 1) pagePrev.value = false
|
|
44
|
+
if (currentPage.value == pages) pageNext.value = false
|
|
45
|
+
// console.log(`page prev: ${pagePrev.value}`)
|
|
46
|
+
// console.log(`page next: ${pageNext.value}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const init = () => {
|
|
50
|
+
currentPage.value = page
|
|
51
|
+
pagePrev.value = true
|
|
52
|
+
pageNext.value = true
|
|
53
|
+
minMaxPage()
|
|
54
|
+
pagePrevNext()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const decrement = () => {
|
|
58
|
+
currentPage.value = minPage.value - 1
|
|
59
|
+
minMaxPage()
|
|
60
|
+
pagePrevNext()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const increment = () => {
|
|
64
|
+
currentPage.value = maxPage.value + 1
|
|
65
|
+
minMaxPage()
|
|
66
|
+
pagePrevNext()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { currentPage, pagePrev, pageNext, minPage, maxPage, init, decrement, increment }
|
|
70
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useRuntimeConfig } from 'nuxt/app'
|
|
2
|
+
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
import { format, tzDate } from '@formkit/tempo'
|
|
6
|
+
|
|
7
|
+
import { useLocale } from './use-locale'
|
|
8
|
+
import { useDatetimeLocal } from './use-datetime-local'
|
|
9
|
+
|
|
10
|
+
interface TimeZone {
|
|
11
|
+
client: string
|
|
12
|
+
server: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TZOptions {
|
|
16
|
+
text: string
|
|
17
|
+
value: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const useTimeZone = () => {
|
|
21
|
+
const runtimeConfig = useRuntimeConfig()
|
|
22
|
+
const { locale } = useLocale()
|
|
23
|
+
const { toISO8601, formatHTML } = useDatetimeLocal()
|
|
24
|
+
|
|
25
|
+
const serverTimeZone = runtimeConfig.public.timeZone as string
|
|
26
|
+
|
|
27
|
+
const timeZone = computed<TimeZone>(() => ({
|
|
28
|
+
client: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
29
|
+
server: serverTimeZone,
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
const tzOptions = computed<TZOptions[]>(() =>
|
|
33
|
+
Intl.supportedValuesOf('timeZone').map((e) => ({ text: e, value: e })),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const tzServerDate = (datetime: string): Date => {
|
|
37
|
+
return tzDate(toISO8601(datetime), timeZone.value.server)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const tzClientDate = (datetime: string): Date => {
|
|
41
|
+
return tzDate(toISO8601(datetime), timeZone.value.client)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const upTZ = (datetime: string | null): string => {
|
|
45
|
+
return timeZone.value.client === timeZone.value.server
|
|
46
|
+
? (datetime ?? '')
|
|
47
|
+
: datetime
|
|
48
|
+
? format({
|
|
49
|
+
date: tzServerDate(datetime),
|
|
50
|
+
format: 'YYYY/MM/DD HH:mm',
|
|
51
|
+
locale: locale.value,
|
|
52
|
+
tz: timeZone.value.client,
|
|
53
|
+
})
|
|
54
|
+
: ''
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const downTZ = (datetime: string | null): string => {
|
|
58
|
+
return timeZone.value.client === timeZone.value.server
|
|
59
|
+
? (datetime ?? '')
|
|
60
|
+
: datetime
|
|
61
|
+
? format({
|
|
62
|
+
date: tzClientDate(datetime),
|
|
63
|
+
format: 'YYYY/MM/DD HH:mm',
|
|
64
|
+
locale: locale.value,
|
|
65
|
+
tz: timeZone.value.server,
|
|
66
|
+
})
|
|
67
|
+
: ''
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const formatHtmlTZ = (datetime: string | null, fmt: string): string => {
|
|
71
|
+
return timeZone.value.client === timeZone.value.server
|
|
72
|
+
? formatHTML(datetime, fmt)
|
|
73
|
+
: datetime
|
|
74
|
+
? format({
|
|
75
|
+
date: tzServerDate(datetime),
|
|
76
|
+
format: fmt,
|
|
77
|
+
locale: locale.value,
|
|
78
|
+
tz: timeZone.value.client,
|
|
79
|
+
})
|
|
80
|
+
: ''
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { timeZone, tzOptions, upTZ, downTZ, formatHtmlTZ }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// export type UseTimeZoneType = ReturnType<typeof useTimeZone>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useDate } from './use-date'
|
package/app.config.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { BackendErrorInfo } from './backend-error-info'
|
package/nuxt.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vesperjs/nuxt",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"app",
|
|
6
|
+
"interfaces",
|
|
7
|
+
"types",
|
|
8
|
+
"nuxt.config.ts",
|
|
9
|
+
"app.config.ts"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./nuxt.config.ts",
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@nuxt/eslint": "latest",
|
|
15
|
+
"@nuxtjs/i18n": "10.2.3",
|
|
16
|
+
"@types/node": "^24.11.0",
|
|
17
|
+
"eslint": "^9.39.3",
|
|
18
|
+
"nuxt": "^4.3.1",
|
|
19
|
+
"ofetch": "1.5.1",
|
|
20
|
+
"oxfmt": "^0.41.0",
|
|
21
|
+
"typescript": "^5.9.3",
|
|
22
|
+
"vue": "^3.6.0-beta.8"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "nuxi dev .playground",
|
|
26
|
+
"dev:prepare": "nuxt prepare .playground",
|
|
27
|
+
"build": "nuxt build .playground",
|
|
28
|
+
"generate": "nuxt generate .playground",
|
|
29
|
+
"preview": "nuxt preview .playground",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"fmt": "oxfmt"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ErrorMessages<T extends string> = Partial<Record<T, string[]>>
|
package/types/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { ErrorMessages } from './error-messages'
|