feat(model): add model and delete model

This commit is contained in:
Quicy
2026-01-20 14:12:54 +08:00
parent cb11ac2708
commit 42372ddea3
8 changed files with 140 additions and 49 deletions
+1
View File
@@ -17,6 +17,7 @@
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@tanstack/vue-table": "^8.21.3", "@tanstack/vue-table": "^8.21.3",
"@vee-validate/zod": "^4.15.1", "@vee-validate/zod": "^4.15.1",
"@vueuse/core": "^14.1.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"pinia": "^3.0.4", "pinia": "^3.0.4",
@@ -3,7 +3,7 @@
<Dialog v-model:open="open"> <Dialog v-model:open="open">
<DialogTrigger as-child> <DialogTrigger as-child>
<Button variant="default"> <Button variant="default">
Open Dialog 添加Model
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent class="sm:max-w-106.25"> <DialogContent class="sm:max-w-106.25">
@@ -132,7 +132,7 @@
</FormField> </FormField>
<FormField <FormField
v-slot="{ componentField }" v-slot="{ componentField }"
name="role" name="type"
> >
<FormItem> <FormItem>
<FormLabel class="mb-2"> <FormLabel class="mb-2">
@@ -145,14 +145,10 @@
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectItem <SelectItem value="chat">
value="chat"
>
Chat Chat
</SelectItem> </SelectItem>
<SelectItem <SelectItem value="embedding">
value="embedding"
>
embedding embedding
</SelectItem> </SelectItem>
</SelectGroup> </SelectGroup>
@@ -206,40 +202,65 @@ import {
FormMessage FormMessage
} from '@memoh/ui' } from '@memoh/ui'
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import { ref } from 'vue' import { inject, watch, type Ref,ref } from 'vue'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import z from 'zod' import z from 'zod'
import request from '@/utils/request' import request from '@/utils/request'
import { useMutation,useQueryCache } from '@pinia/colada' import { useMutation, useQueryCache } from '@pinia/colada'
const formSchema = toTypedSchema(z.object({ const formSchema = toTypedSchema(z.object({
modelId:z.string().min(1),
baseUrl: z.string().min(1), baseUrl: z.string().min(1),
apiKey: z.string().min(1), apiKey: z.string().min(1),
clientType: z.string().min(1), clientType: z.string().min(1),
name: z.string().min(1), name: z.string().min(1),
role: z.string().min(1), type: z.string().min(1),
})) }))
const form = useForm({ const form = useForm({
validationSchema: formSchema validationSchema: formSchema
}) })
const queryCache=useQueryCache() const queryCache = useQueryCache()
type ModelInfoType= Parameters<(Parameters<typeof form.handleSubmit>)[0]>[0]
const { mutate: createModel } = useMutation({ const { mutate: createModel } = useMutation({
mutation: (modelInfo: Parameters<(Parameters<typeof form.handleSubmit>)[0]>[0]) => request({ mutation: (modelInfo:ModelInfoType ) => request({
url: '/model', url: '/model',
data: { data: {
...modelInfo, ...modelInfo,
modelId:'fwoi0fjwfiwefwjfiowefoi'
}, },
method:'post' method: 'post'
}), }),
onSettled: () => queryCache.invalidateQueries({ key: ['models'], exact: true }) onSettled: () => { open.value = false; queryCache.invalidateQueries({ key: ['models'], exact: true })}
})
const addModel = form.handleSubmit(async (modelInfo) => {
createModel(modelInfo)
}) })
const open = ref(false) const { mutate: updateModel } = useMutation({
mutation: (modelInfo: ModelInfoType) => request({
url: `/model/${editInfo.value?.id}`,
data: {
...modelInfo,
},
method: 'PUT'
}),
onSettled: () => { open.value = false; queryCache.invalidateQueries({ key: ['models'], exact: true }) }
})
const addModel = form.handleSubmit(async (modelInfo) => {
if (editInfo.value?.id) {
updateModel(modelInfo)
} else {
createModel(modelInfo)
}
})
const open = inject<Ref<boolean>>('open',ref(false))
const editInfo = inject('editModelInfo',ref<null|(ModelInfoType&{id:string})>(null))
watch(open, () => {
if (open.value && editInfo?.value) {
form.setValues(editInfo.value)
}
}, {
immediate:true
})
</script> </script>
@@ -5,7 +5,6 @@ import {
getCoreRowModel, getCoreRowModel,
useVueTable, useVueTable,
} from '@tanstack/vue-table' } from '@tanstack/vue-table'
import { import {
Table, Table,
TableBody, TableBody,
@@ -25,7 +24,6 @@ const table = useVueTable({
get columns() { return props.columns }, get columns() { return props.columns },
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
}) })
console.log(table.getHeaderGroups())
</script> </script>
<template> <template>
+1 -1
View File
@@ -119,7 +119,7 @@ import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate' import { useForm } from 'vee-validate'
import * as z from 'zod' import * as z from 'zod'
import request from '@/utils/request' import request from '@/utils/request'
import { useUserStore } from '@/store/user' import { useUserStore } from '@/store/User.ts'
import { ref } from 'vue' import { ref } from 'vue'
const router = useRouter() const router = useRouter()
+73 -21
View File
@@ -1,37 +1,69 @@
<script setup lang="ts"> <script setup lang="ts">
// import type { Payment } from '@/components/columns' // import type { Payment } from '@/components/columns'
import { watch, h, computed } from 'vue' import { h, computed, ref, provide, watch } from 'vue'
import CreateModel from '@/components/CreateModel/index.vue' import CreateModel from '@/components/CreateModel/index.vue'
import { useQuery } from '@pinia/colada' import { useQuery,useMutation,useQueryCache } from '@pinia/colada'
import {
Button,
} from '@memoh/ui'
import DataTable from '@/components/DataTable/index.vue' import DataTable from '@/components/DataTable/index.vue'
import request from '@/utils/request' import request from '@/utils/request'
import {type ColumnDef } from '@tanstack/vue-table' import { type ColumnDef } from '@tanstack/vue-table'
interface ModelType { interface ModelType {
apiKey:string, apiKey: string,
baseUrl: string, baseUrl: string,
clientType: 'OpenAI'|'Anthropic'|'Google', clientType: 'OpenAI' | 'Anthropic' | 'Google',
modelId:string, modelId: string,
name:string, name: string,
type:'chat'|'embedding' type: 'chat' | 'embedding',
id:string
} }
const columns:ColumnDef<ModelType>[] = [ const openDialogModel = ref(false)
const editModelInfo = ref<ModelType & {id:string} |null>(null)
provide('open', openDialogModel)
provide('editModelInfo', editModelInfo)
watch(openDialogModel, () => {
if (!openDialogModel.value) {
editModelInfo.value=null
}
}, {
immediate:true
})
const cacheQuery=useQueryCache()
const {
mutate: deleteModel,
} = useMutation({
mutation: (id: string) =>
request({
url: `model/${id}`,
method: 'DELETE'
}),
onSettled: () => {
cacheQuery.invalidateQueries({
key:['models']
})
}
})
const columns: ColumnDef<ModelType>[] = [
{ {
accessorKey: 'modelId', accessorKey: 'modelId',
header: () => h('div', { class: 'text-left py-4' }, 'Name'), header: () => h('div', { class: 'text-left py-4' }, 'Name'),
cell({row}) { cell({ row }) {
return h('div',{ class: 'text-left py-4' },row.getValue('modelId')) return h('div', { class: 'text-left py-4' }, row.getValue('modelId'))
} }
}, },
{ {
accessorKey: 'baseUrl', accessorKey: 'baseUrl',
header: () => h('div', { class: 'text-left' }, 'Base Url'), header: () => h('div', { class: 'text-left' }, 'Base Url'),
}, },
{ {
accessorKey: 'apiKey', accessorKey: 'apiKey',
header: () => h('div', { class: 'text-left' }, 'Api Key'), header: () => h('div', { class: 'text-left' }, 'Api Key'),
@@ -41,16 +73,30 @@ const columns:ColumnDef<ModelType>[] = [
header: () => h('div', { class: 'text-left' }, 'Client Type'), header: () => h('div', { class: 'text-left' }, 'Client Type'),
}, },
{ {
accessorKey: 'Name', accessorKey: 'name',
header: () => h('div', { class: 'text-left' }, 'Name'), header: () => h('div', { class: 'text-left' }, 'Name'),
}, },
{ {
accessorKey: 'type', accessorKey: 'type',
header: () => h('div', { class: 'text-left' }, 'Type'), header: () => h('div', { class: 'text-left' }, 'Type'),
},
{
accessorKey: 'control',
header: () => h('div', { class: 'text-center' }, '操作'),
cell: ({row}) => h('div', { class: ' w-full flex justify-around' }, [h(Button, {
'onClick': () => {
editModelInfo.value=row.original
openDialogModel.value = true
}
}, () => '编辑'), h(Button, {
variant: 'destructive', onClick() {
deleteModel(row.original.id)
}},()=>'删除')])
} }
] ]
const {data:modelData}=useQuery({ const { data: modelData } = useQuery({
key: ['models'], key: ['models'],
query() { query() {
return request({ return request({
@@ -58,20 +104,26 @@ const {data:modelData}=useQuery({
}) })
} }
}) })
const displayFormat = computed(() => { const displayFormat = computed(() => {
return modelData.value?.data?.items?.map((currentModel:{model: ModelType,id:'string' })=>currentModel.model)??[] return modelData.value?.data?.items?.map((currentModel: { model: Omit<ModelType,'id'>, id: 'string' }) => ({id:currentModel.id,...currentModel.model})) ?? []
}) })
</script> </script>
<template> <template>
<div class="w-full py-10 mx-auto"> <div class="w-full py-10 mx-auto">
<div class="flex mb-4"> <div class="flex mb-4">
<CreateModel /> <CreateModel />
</div>
<div class="[&_td:last-child]:w-45">
<DataTable
:columns="columns"
:data="displayFormat"
/>
</div> </div>
<DataTable
:columns="columns"
:data="displayFormat"
/>
</div> </div>
</template> </template>
+1 -1
View File
@@ -1,6 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
const routes = [ const routes = [
{ {
path: '/', path: '/',
@@ -72,6 +71,7 @@ const router = createRouter({
}) })
router.beforeEach((to) => { router.beforeEach((to) => {
const token = localStorage.getItem('token') const token = localStorage.getItem('token')
if (to.fullPath !== '/login') { if (to.fullPath !== '/login') {
return token ? true : { name: 'Login' } return token ? true : { name: 'Login' }
} else { } else {
+19 -3
View File
@@ -1,5 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { reactive } from 'vue' import { reactive,watch } from 'vue'
import { useLocalStorage } from '@vueuse/core'
import { useRouter } from 'vue-router'
type user={ type user={
@@ -8,6 +10,8 @@ type user={
'role': string, 'role': string,
'displayName': string 'displayName': string
} }
export const useUserStore = defineStore('user', () => { export const useUserStore = defineStore('user', () => {
const userInfo = reactive<user>({ const userInfo = reactive<user>({
'id': '', 'id': '',
@@ -16,20 +20,32 @@ export const useUserStore = defineStore('user', () => {
'displayName': '' 'displayName': ''
}) })
const localToken=useLocalStorage('token','')
const login = (userData: user,token:string) => { const login = (userData: user,token:string) => {
localStorage.setItem('token',token) localToken.value=token
for (const key of Object.keys(userData) as (keyof user)[]) { for (const key of Object.keys(userData) as (keyof user)[]) {
userInfo[key] = userData[key] userInfo[key] = userData[key]
} }
} }
const exitLogin = () => { const exitLogin = () => {
localStorage.removeItem('token') localToken.value=''
for (const key of Object.keys(userInfo) as (keyof user)[]) { for (const key of Object.keys(userInfo) as (keyof user)[]) {
userInfo[key]='' userInfo[key]=''
} }
} }
const router=useRouter()
watch(localToken, () => {
if (!localToken.value) {
exitLogin()
router.replace({name:'Login'})
}
}, {
immediate: true
})
return { return {
userInfo, userInfo,
login, login,
+3
View File
@@ -252,6 +252,9 @@ importers:
'@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)
'@vueuse/core':
specifier: ^14.1.0
version: 14.1.0(vue@3.5.26(typescript@5.9.3))
axios: axios:
specifier: ^1.13.2 specifier: ^1.13.2
version: 1.13.2 version: 1.13.2