Misc changes
This commit is contained in:
1 parent
5c44febc65
commit
1ef0cfb419
7 files changed
+147
-9
No files matched your search
@@ -0,0 +1,85 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { type Option } from "~/ui/SearchSelect.vue"
|
||||||
|
|
||||||
|
type InputType = "text" | "file" | "search-select"
|
||||||
|
|
||||||
|
interface BaseField<T> {
|
||||||
|
key: keyof T
|
||||||
|
label?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextField<T> extends BaseField<T> {
|
||||||
|
type?: "text"
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileField<T> extends BaseField<T> {
|
||||||
|
type: "file"
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SearchSelectField<T> extends BaseField<T> {
|
||||||
|
type: "search-select"
|
||||||
|
fetchOptions: (q: string) => Promise<Option[]>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Field<T = any> = TextField<T> | FileField<T> | SearchSelectField<T>
|
||||||
|
|
||||||
|
interface ModalFormProps<T> {
|
||||||
|
item?: T
|
||||||
|
fields: Field<T>[]
|
||||||
|
title?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<ModalFormProps<any>>()
|
||||||
|
const open = defineModel("open", { required: true })
|
||||||
|
const emit = defineEmits<{ (e: "save", value: any): boolean }>()
|
||||||
|
|
||||||
|
let localItem = reactive<any>({})
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
if (emit("save", localItem)) open.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal v-model:open="open" width="40%">
|
||||||
|
<p v-if="props.title" class="title">{{ props.title }}</p>
|
||||||
|
|
||||||
|
<form class="form" @submit.prevent="save">
|
||||||
|
<template v-for="field in props.fields">
|
||||||
|
<SearchSelect
|
||||||
|
v-if="field.type == 'search-select'"
|
||||||
|
v-model:value="localItem[field.key]"
|
||||||
|
:fetch-options="field.fetchOptions"
|
||||||
|
:label="field.label || field.key"
|
||||||
|
:predefined="props.item?.[field.key]"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-else
|
||||||
|
:type="field.type"
|
||||||
|
:key="field.key"
|
||||||
|
:label="field.label || field.key"
|
||||||
|
v-model:value="localItem[field.key]"
|
||||||
|
full
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<Button type="submit" full center-text>Save</Button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use "@/styles/util" as *;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include fontSize(20px);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File renamed without changes.
@@ -3,9 +3,9 @@ import defu from "defu"
|
|||||||
|
|
||||||
export function useApiFetch<DataT = any, ErrorT = any>(
|
export function useApiFetch<DataT = any, ErrorT = any>(
|
||||||
url: string | Request | Ref<string | Request> | (() => string | Request),
|
url: string | Request | Ref<string | Request> | (() => string | Request),
|
||||||
options: UseFetchOptions<DataT, ErrorT> = {},
|
options?: UseFetchOptions<DataT, ErrorT>,
|
||||||
authorize: boolean = true
|
authorize: boolean = true
|
||||||
) {
|
): ReturnType<typeof useFetch<DataT, ErrorT>> {
|
||||||
const baseUrl = import.meta.env.VITE_PUBLIC_BACKEND
|
const baseUrl = import.meta.env.VITE_PUBLIC_BACKEND
|
||||||
|
|
||||||
if (!baseUrl) throw new Error("BACKEND URL not set")
|
if (!baseUrl) throw new Error("BACKEND URL not set")
|
||||||
@@ -39,10 +39,7 @@ export function useApiFetch<DataT = any, ErrorT = any>(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = defu(options || {}, defaults) as UseFetchOptions<
|
const config: UseFetchOptions<DataT, ErrorT> = defu(options ?? {}, defaults)
|
||||||
DataT,
|
|
||||||
ErrorT
|
|
||||||
>
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return useFetch<DataT, ErrorT>(url, config)
|
return useFetch<DataT, ErrorT>(url, config)
|
||||||
|
|||||||
+3
-1
@@ -1,11 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{ width?: string }>()
|
||||||
|
|
||||||
const open = defineModel("open")
|
const open = defineModel("open")
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Transition>
|
<Transition>
|
||||||
<div class="modal-container" v-show="open" @click="open = false">
|
<div class="modal-container" v-show="open" @click="open = false">
|
||||||
<Card @click.stop>
|
<Card @click.stop :width="props.width">
|
||||||
<slot />
|
<slot />
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+19
-1
@@ -5,7 +5,9 @@ export interface Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SearchSelectProps {
|
interface SearchSelectProps {
|
||||||
|
label?: string
|
||||||
fetchOptions: (q: string) => Promise<Option[]>
|
fetchOptions: (q: string) => Promise<Option[]>
|
||||||
|
predefined?: Option
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<SearchSelectProps>()
|
const props = defineProps<SearchSelectProps>()
|
||||||
@@ -15,6 +17,16 @@ const query = ref<string>("")
|
|||||||
const options = ref<Option[]>([])
|
const options = ref<Option[]>([])
|
||||||
const show = ref<boolean>(false)
|
const show = ref<boolean>(false)
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (!props.predefined) return
|
||||||
|
|
||||||
|
console.log(props.predefined)
|
||||||
|
|
||||||
|
query.value = props.predefined.label
|
||||||
|
value.value = props.predefined.value
|
||||||
|
nextTick(() => (options.value = []))
|
||||||
|
})
|
||||||
|
|
||||||
watch(query, async (q) => {
|
watch(query, async (q) => {
|
||||||
options.value = q ? await props.fetchOptions(q) : []
|
options.value = q ? await props.fetchOptions(q) : []
|
||||||
show.value = true
|
show.value = true
|
||||||
@@ -29,7 +41,13 @@ const select = (item: Option) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="search-select" @blur="show = false">
|
<div class="search-select" @blur="show = false">
|
||||||
<Input v-model:value="query" @focus="show = true" type="text" full />
|
<Input
|
||||||
|
v-model:value="query"
|
||||||
|
@focus="show = true"
|
||||||
|
:label="props.label"
|
||||||
|
type="text"
|
||||||
|
full
|
||||||
|
/>
|
||||||
|
|
||||||
<ul v-if="options.length && show">
|
<ul v-if="options.length && show">
|
||||||
<li v-for="opt in options" :key="opt.value" @click="select(opt)">
|
<li v-for="opt in options" :key="opt.value" @click="select(opt)">
|
||||||
|
|||||||
+26
-1
@@ -16,7 +16,7 @@ const { rows, data } = defineProps<TableProps>()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table>
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
@@ -40,3 +40,28 @@ const { rows, data } = defineProps<TableProps>()
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use "@/styles/variables" as *;
|
||||||
|
@use "@/styles/util" as *;
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
border-collapse: collapse;
|
||||||
|
@include fontSize(15px);
|
||||||
|
|
||||||
|
& th,
|
||||||
|
& td {
|
||||||
|
border-bottom: 1px solid $color-border;
|
||||||
|
padding: 0.5rem 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& thead {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -17,3 +17,14 @@ export interface Artist extends Base {
|
|||||||
export interface Genre extends Base {
|
export interface Genre extends Base {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Track extends Base {
|
||||||
|
name: string
|
||||||
|
file: string
|
||||||
|
lyrics: {
|
||||||
|
synced: boolean
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
album: Album
|
||||||
|
artists: Artist[]
|
||||||
|
}
|
||||||
Reference in new issue
Block a user