feat: create Model

This commit is contained in:
Quicy
2026-01-19 16:57:20 +08:00
parent 7e22919c80
commit 1735e4e0bb
13 changed files with 429 additions and 12 deletions
@@ -0,0 +1,50 @@
<script setup lang="ts">
import type { AcceptableValue } from "reka-ui"
import type { HTMLAttributes } from "vue"
import { reactiveOmit, useVModel } from "@vueuse/core"
import { ChevronDownIcon } from "lucide-vue-next"
import { cn } from '#/lib/utils'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<{ modelValue?: AcceptableValue | AcceptableValue[], class?: HTMLAttributes["class"] }>()
const emit = defineEmits<{
"update:modelValue": AcceptableValue
}>()
const modelValue = useVModel(props, "modelValue", emit, {
passive: true,
defaultValue: "",
})
const delegatedProps = reactiveOmit(props, "class")
</script>
<template>
<div
class="group/native-select relative w-fit has-[select:disabled]:opacity-50"
data-slot="native-select-wrapper"
>
<select
v-bind="{ ...$attrs, ...delegatedProps }"
v-model="modelValue"
data-slot="native-select"
:class="cn(
'border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
props.class,
)"
>
<slot />
</select>
<ChevronDownIcon
class="text-muted-foreground pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 opacity-50 select-none"
aria-hidden="true"
data-slot="native-select-icon"
/>
</div>
</template>
@@ -0,0 +1,15 @@
<!-- @fallthroughAttributes true -->
<!-- @strictTemplates true -->
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>
<template>
<optgroup data-slot="native-select-optgroup" :class="cn('bg-popover text-popover-foreground', props.class)">
<slot />
</optgroup>
</template>
@@ -0,0 +1,15 @@
<!-- @fallthroughAttributes true -->
<!-- @strictTemplates true -->
<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from '#/lib/utils'
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>
<template>
<option data-slot="native-select-option" :class="cn('bg-popover text-popover-foreground', props.class)">
<slot />
</option>
</template>
@@ -0,0 +1,3 @@
export { default as NativeSelect } from "./NativeSelect.vue"
export { default as NativeSelectOptGroup } from "./NativeSelectOptGroup.vue"
export { default as NativeSelectOption } from "./NativeSelectOption.vue"
@@ -1,2 +1,2 @@
export { default as ScrollArea } from "./ScrollArea.vue"
export { default as ScrollBar } from "./ScrollBar.vue"
export { default as ScrollArea } from './ScrollArea.vue'
export { default as ScrollBar } from './ScrollBar.vue'
+1
View File
@@ -16,6 +16,7 @@ export * from './components/input/index'
export * from './components/input-group/index'
export * from './components/kbd/index'
export * from './components/label/index'
export * from './components/native-select/index'
export * from './components/radio-group/index'
export * from './components/scroll-area/index'
export * from './components/select/index'
+1
View File
@@ -15,6 +15,7 @@
"@memoh/ui": "workspace:*",
"@pinia/colada": "^0.21.1",
"@tailwindcss/vite": "^4.1.18",
"@tanstack/vue-table": "^8.21.3",
"@vee-validate/zod": "^4.15.1",
"axios": "^1.13.2",
"dotenv": "^17.2.3",
@@ -0,0 +1,208 @@
<template>
<section class="ml-auto">
<Dialog v-model:open="open">
<DialogTrigger as-child>
<Button variant="default">
Open Dialog
</Button>
</DialogTrigger>
<DialogContent class="sm:max-w-106.25">
<form @submit="addModel">
<DialogHeader>
<DialogTitle>添加Model</DialogTitle>
<DialogDescription class="mb-4">
使用不用厂商的大模型
</DialogDescription>
</DialogHeader>
<div class="grid ">
<FormField
v-slot="{ componentField }"
name="baseUrl"
>
<FormItem>
<FormLabel class="mb-2">
Base Url
</FormLabel>
<FormControl>
<Input
type="text"
placeholder="请输入Base Url"
v-bind="componentField"
autocomplete="baseurl"
/>
</FormControl>
<blockquote class="h-5">
<FormMessage />
</blockquote>
</FormItem>
</FormField>
<FormField
v-slot="{ componentField }"
name="apiKey"
>
<FormItem>
<FormLabel class="mb-2">
Api Key
</FormLabel>
<FormControl>
<Input
placeholder="请输入Api Key"
autocomplete="apiKey"
v-bind="componentField"
/>
</FormControl>
<blockquote class="h-5">
<FormMessage />
</blockquote>
</FormItem>
</FormField>
<FormField
v-slot="{ componentField }"
name="clientType"
>
<FormItem>
<FormLabel class="mb-2">
Client Type
</FormLabel>
<FormControl>
<Input
placeholder="请输入Api Key"
autocomplete="clientType"
v-bind="componentField"
/>
</FormControl>
<blockquote class="h-5">
<FormMessage />
</blockquote>
</FormItem>
</FormField>
<FormField
v-slot="{ componentField }"
name="name"
>
<FormItem>
<FormLabel class="mb-2">
Name
</FormLabel>
<FormControl>
<Input
placeholder="请输入Api Key"
autocomplete="name"
v-bind="componentField"
/>
</FormControl>
<blockquote class="h-5">
<FormMessage />
</blockquote>
</FormItem>
</FormField>
<FormField
v-slot="{ componentField }"
name="role"
>
<FormItem>
<FormLabel class="mb-2">
Role
</FormLabel>
<FormControl>
<Select v-bind="componentField">
<SelectTrigger
class="w-full"
>
<SelectValue
placeholder="Select a fruit"
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem
value="chat"
select
>
Chat
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</FormControl>
<blockquote class="h-5">
<FormMessage />
</blockquote>
</FormItem>
</FormField>
</div>
<DialogFooter class="mt-4">
<DialogClose as-child>
<Button variant="outline">
Cancel
</Button>
</DialogClose>
<Button type="submit">
添加Model
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</section>
</template>
<script setup lang="ts">
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
Input,
Button,
FormField,
FormControl,
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
FormItem,
FormLabel,
FormMessage
} from '@memoh/ui'
import { useForm } from 'vee-validate'
import { ref } from 'vue'
import { toTypedSchema } from '@vee-validate/zod'
import z from 'zod'
import request from '@/utils/request'
const formSchema = toTypedSchema(z.object({
baseUrl: z.string().min(1),
apiKey: z.string().min(1),
clientType: z.string().min(1),
name: z.string().min(1),
role: z.string().min(1),
}))
const form = useForm({
validationSchema:formSchema
})
const addModel=form.handleSubmit(async (modelInfo) => {
try {
await request({
url: '/model',
data: {
...modelInfo
}
})
open.value = false
} catch (err) {
return err
}
})
const open=ref(false)
</script>
@@ -0,0 +1,83 @@
<script setup lang="ts" generic="TData, TValue">
import type { ColumnDef } from '@tanstack/vue-table'
import {
FlexRender,
getCoreRowModel,
useVueTable,
} from '@tanstack/vue-table'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@memoh/ui'
const props = defineProps<{
columns: ColumnDef<TData, TValue>[]
data: TData[]
}>()
const table = useVueTable({
get data() { return props.data },
get columns() { return props.columns },
getCoreRowModel: getCoreRowModel(),
})
console.log(table.getHeaderGroups())
</script>
<template>
<div class="border rounded-md">
<Table>
<TableHeader>
<TableRow
v-for="headerGroup in table.getHeaderGroups()"
:key="headerGroup.id"
>
<TableHead
v-for="header in headerGroup.headers"
:key="header.id"
>
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<template v-if="table.getRowModel().rows?.length">
<TableRow
v-for="row in table.getRowModel().rows"
:key="row.id"
:data-state="row.getIsSelected() ? 'selected' : undefined"
>
<TableCell
v-for="cell in row.getVisibleCells()"
:key="cell.id"
>
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</TableCell>
</TableRow>
</template>
<template v-else>
<TableRow>
<TableCell
:colspan="columns.length"
class="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
</template>
</TableBody>
</Table>
</div>
</template>
-2
View File
@@ -107,10 +107,8 @@ import {
CardContent,
CardFooter,
Input,
Button,
FormControl,
FormField,
FormItem,
FormLabel,
+41 -3
View File
@@ -1,5 +1,43 @@
<script setup lang="ts">
// import type { Payment } from '@/components/columns'
import { ref, h } from 'vue'
import CreateModel from '@/components/CreateModel/index.vue'
import DataTable from '@/components/DataTable/index.vue'
const modelData=ref([])
async function getData() {
return [
{
id: '728ed52f',
amount: 100,
status: 'pending',
email: 'm@example.com',
},
]
}
const columns = [
{
accessorKey: 'amount',
header: () => h('div', { class: 'text-right' }, 'Amount'),
}
]
</script>
<template>
<section>
<h1>模型管理</h1>
</section>
<div class="w-full py-10 mx-auto">
<div class="flex mb-4">
<CreateModel />
</div>
<DataTable
:columns="columns"
:data="modelData"
/>
</div>
</template>
+7 -5
View File
@@ -6,13 +6,8 @@ export default (function () {
const axiosInstance = axios.create({
baseURL:'http://localhost:7002/'
})
axiosInstance.interceptors.request.use((config) => {
return config
}, (error) => Promise.reject(error))
axiosInstance.interceptors.response.use((response) => {
return response
}, (error) => {
if (error?.status === 401) {
@@ -23,6 +18,13 @@ export default (function () {
return Promise.reject(error)
})
return (params: AxiosRequestConfig,isToken=true) => {
axiosInstance.interceptors.request.use((config) => {
if (isToken) {
const token = localStorage.getItem('token')
config.headers['Authorization'] =`Bearer ${token}`
}
return config
}, (error) => Promise.reject(error))
return axiosInstance(params)
}
+3
View File
@@ -246,6 +246,9 @@ importers:
'@tailwindcss/vite':
specifier: ^4.1.18
version: 4.1.18(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))
'@tanstack/vue-table':
specifier: ^8.21.3
version: 8.21.3(vue@3.5.26(typescript@5.9.3))
'@vee-validate/zod':
specifier: ^4.15.1
version: 4.15.1(vue@3.5.26(typescript@5.9.3))(zod@4.3.5)