mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: create Model
This commit is contained in:
@@ -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 ScrollArea } from './ScrollArea.vue'
|
||||||
export { default as ScrollBar } from "./ScrollBar.vue"
|
export { default as ScrollBar } from './ScrollBar.vue'
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export * from './components/input/index'
|
|||||||
export * from './components/input-group/index'
|
export * from './components/input-group/index'
|
||||||
export * from './components/kbd/index'
|
export * from './components/kbd/index'
|
||||||
export * from './components/label/index'
|
export * from './components/label/index'
|
||||||
|
export * from './components/native-select/index'
|
||||||
export * from './components/radio-group/index'
|
export * from './components/radio-group/index'
|
||||||
export * from './components/scroll-area/index'
|
export * from './components/scroll-area/index'
|
||||||
export * from './components/select/index'
|
export * from './components/select/index'
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"@memoh/ui": "workspace:*",
|
"@memoh/ui": "workspace:*",
|
||||||
"@pinia/colada": "^0.21.1",
|
"@pinia/colada": "^0.21.1",
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
|
"@tanstack/vue-table": "^8.21.3",
|
||||||
"@vee-validate/zod": "^4.15.1",
|
"@vee-validate/zod": "^4.15.1",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"dotenv": "^17.2.3",
|
"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>
|
||||||
|
|
||||||
@@ -107,10 +107,8 @@ import {
|
|||||||
CardContent,
|
CardContent,
|
||||||
CardFooter,
|
CardFooter,
|
||||||
Input,
|
Input,
|
||||||
|
|
||||||
Button,
|
Button,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
|
|||||||
@@ -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>
|
<template>
|
||||||
<section>
|
<div class="w-full py-10 mx-auto">
|
||||||
<h1>模型管理</h1>
|
<div class="flex mb-4">
|
||||||
</section>
|
<CreateModel />
|
||||||
|
</div>
|
||||||
|
<DataTable
|
||||||
|
:columns="columns"
|
||||||
|
:data="modelData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -6,13 +6,8 @@ export default (function () {
|
|||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL:'http://localhost:7002/'
|
baseURL:'http://localhost:7002/'
|
||||||
})
|
})
|
||||||
|
|
||||||
axiosInstance.interceptors.request.use((config) => {
|
|
||||||
return config
|
|
||||||
}, (error) => Promise.reject(error))
|
|
||||||
|
|
||||||
axiosInstance.interceptors.response.use((response) => {
|
axiosInstance.interceptors.response.use((response) => {
|
||||||
|
|
||||||
return response
|
return response
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
if (error?.status === 401) {
|
if (error?.status === 401) {
|
||||||
@@ -23,6 +18,13 @@ export default (function () {
|
|||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
})
|
})
|
||||||
return (params: AxiosRequestConfig,isToken=true) => {
|
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)
|
return axiosInstance(params)
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+3
@@ -246,6 +246,9 @@ importers:
|
|||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.18
|
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))
|
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':
|
'@vee-validate/zod':
|
||||||
specifier: ^4.15.1
|
specifier: ^4.15.1
|
||||||
version: 4.15.1(vue@3.5.26(typescript@5.9.3))(zod@4.3.5)
|
version: 4.15.1(vue@3.5.26(typescript@5.9.3))(zod@4.3.5)
|
||||||
|
|||||||
Reference in New Issue
Block a user