feat: 模型列表

This commit is contained in:
shaohuzhang1 2023-11-27 18:08:58 +08:00
parent a1acfdc2be
commit 367e92c414
9 changed files with 259 additions and 68 deletions

View File

@ -73,11 +73,8 @@ const postChatOpen: (data: ApplicationFormType) => Promise<Result<any>> = (data)
"message": "string", "message": "string",
} }
*/ */
const postChatMessage: (chat_id: string, message: string) => Promise<Result<any>> = ( const postChatMessage: (chat_id: string, message: string) => Promise<any> = (chat_id, message) => {
chat_id, return postStream(`/api/${prefix}/chat_message/${chat_id}`, { message })
message
) => {
return postStream(`${prefix}/chat_message/${chat_id}`, { message })
} }
export default { export default {
getAllAppilcation, getAllAppilcation,

View File

@ -1,7 +1,7 @@
import { Result } from '@/request/Result' import { Result } from '@/request/Result'
import { get, post, del, put } from '@/request/index' import { get, post, del, put } from '@/request/index'
import { type Ref } from 'vue' import { type Ref } from 'vue'
import type { modelRequest, Provider } from '@/api/type/model' import type { modelRequest, Provider, ListModelRequest, Model } from '@/api/type/model'
const prefix = '/model' const prefix = '/model'
const prefix_provider = '/provider' const prefix_provider = '/provider'
@ -9,9 +9,13 @@ const prefix_provider = '/provider'
* *
* @params name, model_type, model_name * @params name, model_type, model_name
*/ */
const getModel: (data?: modelRequest) => Promise<Result<any>> = (data) => { const getModel: (
return get(`${prefix}`, data) request: ListModelRequest,
loading?: Ref<boolean>
) => Promise<Result<Array<Model>>> = (data, loading) => {
return get(`${prefix}`, data, loading)
} }
/** /**
* *
*/ */

View File

@ -1,3 +1,4 @@
import { store } from '@/stores'
interface modelRequest { interface modelRequest {
name: string name: string
model_type: string model_type: string
@ -19,4 +20,49 @@ interface Provider {
icon: string icon: string
} }
export type { modelRequest, Provider } interface ListModelRequest {
/**
*
*/
name?: string
/**
*
*/
model_type?: string
/**
*
*/
model_name?: string
/**
*
*/
provider?: string
}
interface Model {
/**
* id
*/
id: String
/**
*
*/
name: string
/**
*
*/
model_type: string
/**
*
*/
model_name: string
/**
*
*/
credential: any
/**
*
*/
provider: string
}
export type { modelRequest, Provider, ListModelRequest, Model }

View File

@ -128,22 +128,19 @@ function chatHandle() {
} }
function chatMessage(chatId: string) { function chatMessage(chatId: string) {
applicationApi applicationApi.postChatMessage(chatId, inputValue.value).then(async (response) => {
.postChatMessage(chatId, inputValue.value) const reader = response.body.getReader()
.then((response) => { while (true) {
console.log(response.data) const { done, value } = await reader.read()
response.data.on('data', (chunk) => { if (done) {
console.log(chunk) loading.value = false
// break
}) }
const decoder = new TextDecoder('utf-8')
// response.data.on('end', () => { const str = decoder.decode(value, { stream: true })
// // console.log('value', JSON.parse(str.replace('data:', '')))
// }) }
}) })
.catch(() => {
loading.value = false
})
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -3,9 +3,10 @@
<div class="card-header"> <div class="card-header">
<slot name="header"> <slot name="header">
<div class="title flex align-center"> <div class="title flex align-center">
<AppAvatar class="mr-12" shape="square" :size="32" v-if="showIcon"> <AppAvatar v-if="!slots.icon && showIcon" class="mr-12" shape="square" :size="32">
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" /> <img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
</AppAvatar> </AppAvatar>
<slot v-else name="icon"> </slot>
<h4 class="ellipsis-1">{{ title }}</h4> <h4 class="ellipsis-1">{{ title }}</h4>
</div> </div>
</slot> </slot>
@ -23,22 +24,27 @@
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, useSlots } from 'vue'
const slots = useSlots()
defineOptions({ name: 'CardBox' }) defineOptions({ name: 'CardBox' })
const props = defineProps({ const props = withDefaults(
title: { defineProps<{
type: String, /**
default: '标题' * 标题
}, */
description: { title?: string
type: String, /**
default: '' * 描述
}, */
showIcon: { description?: string
type: Boolean, /**
default: true * 是否展示icon
} */
}) showIcon?: boolean
}>(),
{ title: '标题', description: '', showIcon: true }
)
const show = ref(false) const show = ref(false)
function cardEnter() { function cardEnter() {

View File

@ -2,10 +2,18 @@
<div class="common-list"> <div class="common-list">
<el-scrollbar> <el-scrollbar>
<ul v-if="data.length > 0"> <ul v-if="data.length > 0">
<li
v-if="slots.prefix"
@click="clickHandle()"
:class="modelValue === undefined || modelValue === null ? 'active' : ''"
class="cursor"
>
<slot name="prefix"> </slot>
</li>
<template v-for="(item, index) in data" :key="index"> <template v-for="(item, index) in data" :key="index">
<li <li
@click.prevent="clickHandle(item, index)" @click.prevent="clickHandle(item)"
:class="current === index ? 'active' : ''" :class="modelValue === item ? 'active' : ''"
class="cursor" class="cursor"
> >
<slot :row="item" :index="index"> </slot> <slot :row="item" :index="index"> </slot>
@ -17,22 +25,27 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue' import { ref, watch, useSlots } from 'vue'
const slots = useSlots()
defineOptions({ name: 'CommonList' }) defineOptions({ name: 'CommonList' })
const props = defineProps({
data: { withDefaults(
type: Array<any>, defineProps<{
default: () => [] modelValue?: any
data: Array<any>
}>(),
{
data: () => []
} }
}) )
const emit = defineEmits(['click']) const emit = defineEmits(['click', 'update:modelValue'])
const current = ref(0) function clickHandle(row?: any) {
function clickHandle(row: any, index: number) {
current.value = index
emit('click', row) emit('click', row)
emit('update:modelValue', row)
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -242,5 +242,71 @@ export const iconMap: any = {
) )
]) ])
} }
},
'app-all-menu': {
iconReader: () => {
return h('i', [
h(
'svg',
{
style: { height: '100%', width: '100%' },
viewBox: '0 0 20 20',
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg'
},
[
h('path', {
d: 'M2.91683 2.0835H8.3335C8.79373 2.0835 9.16683 2.45659 9.16683 2.91683V8.3335C9.16683 8.79373 8.79373 9.16683 8.3335 9.16683H2.91683C2.45659 9.16683 2.0835 8.79373 2.0835 8.3335V2.91683C2.0835 2.45659 2.45659 2.0835 2.91683 2.0835ZM3.75016 3.75016V7.50016H7.50016V3.75016H3.75016Z',
fill: 'currentColor'
}),
h('path', {
d: 'M2.91683 10.8335H8.3335C8.79373 10.8335 9.16683 11.2066 9.16683 11.6668V17.0835C9.16683 17.5437 8.79373 17.9168 8.3335 17.9168H2.91683C2.45659 17.9168 2.0835 17.5437 2.0835 17.0835V11.6668C2.0835 11.2066 2.45659 10.8335 2.91683 10.8335ZM3.75016 16.2502H7.50016V12.5002H3.75016V16.2502Z',
fill: 'currentColor'
}),
h('path', {
d: 'M11.6668 2.0835H17.0835C17.5437 2.0835 17.9168 2.45659 17.9168 2.91683V8.3335C17.9168 8.79373 17.5437 9.16683 17.0835 9.16683H11.6668C11.2066 9.16683 10.8335 8.79373 10.8335 8.3335V2.91683C10.8335 2.45659 11.2066 2.0835 11.6668 2.0835ZM12.5002 7.50016H16.2502V3.75016H12.5002V7.50016Z',
fill: 'currentColor'
}),
h('path', {
d: 'M11.6668 10.8335H17.0835C17.5437 10.8335 17.9168 11.2066 17.9168 11.6668V17.0835C17.9168 17.5437 17.5437 17.9168 17.0835 17.9168H11.6668C11.2066 17.9168 10.8335 17.5437 10.8335 17.0835V11.6668C10.8335 11.2066 11.2066 10.8335 11.6668 10.8335ZM12.5002 12.5002V16.2502H16.2502V12.5002H12.5002Z',
fill: 'currentColor'
})
]
)
])
}
},
'app-all-menu-active': {
iconReader: () => {
return h('i', [
h(
'svg',
{
style: { height: '100%', width: '100%' },
viewBox: '0 0 20 20',
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg'
},
[
h('path', {
d: 'M8.33317 1.6665H2.49984C2.0396 1.6665 1.6665 2.0396 1.6665 2.49984V8.33317C1.6665 8.79341 2.0396 9.1665 2.49984 9.1665H8.33317C8.79341 9.1665 9.1665 8.79341 9.1665 8.33317V2.49984C9.1665 2.0396 8.79341 1.6665 8.33317 1.6665Z',
fill: 'currentColor'
}),
h('path', {
d: 'M8.33317 10.8332H2.49984C2.0396 10.8332 1.6665 11.2063 1.6665 11.6665V17.4998C1.6665 17.9601 2.0396 18.3332 2.49984 18.3332H8.33317C8.79341 18.3332 9.1665 17.9601 9.1665 17.4998V11.6665C9.1665 11.2063 8.79341 10.8332 8.33317 10.8332Z',
fill: 'currentColor'
}),
h('path', {
d: 'M17.4998 1.6665H11.6665C11.2063 1.6665 10.8332 2.0396 10.8332 2.49984V8.33317C10.8332 8.79341 11.2063 9.1665 11.6665 9.1665H17.4998C17.9601 9.1665 18.3332 8.79341 18.3332 8.33317V2.49984C18.3332 2.0396 17.9601 1.6665 17.4998 1.6665Z',
fill: 'currentColor'
}),
h('path', {
d: 'M17.4508 10.8332H11.7155C11.2282 10.8332 10.8332 11.2282 10.8332 11.7155V17.4508C10.8332 17.9381 11.2282 18.3332 11.7155 18.3332H17.4508C17.9381 18.3332 18.3332 17.9381 18.3332 17.4508V11.7155C18.3332 11.2282 17.9381 10.8332 17.4508 10.8332Z',
fill: 'currentColor'
})
]
)
])
}
} }
} }

View File

@ -38,6 +38,7 @@ instance.interceptors.request.use(
//设置响应拦截器 //设置响应拦截器
instance.interceptors.response.use( instance.interceptors.response.use(
(response: any) => { (response: any) => {
console.log('instance_response', response)
if (response.data) { if (response.data) {
if (response.status !== 200 && !(response.data instanceof Blob)) { if (response.status !== 200 && !(response.data instanceof Blob)) {
MsgError(response.data.message) MsgError(response.data.message)
@ -163,13 +164,27 @@ export const del: (
return promise(request({ url: url, method: 'delete', params, data }), loading) return promise(request({ url: url, method: 'delete', params, data }), loading)
} }
export const postStream: ( /**
url: string, *
data?: unknown, * @param url url地址
params?: unknown, * @param data body
loading?: NProgress | Ref<boolean> * @returns
) => Promise<Result<any> | any> = (url, data, params, loading) => { */
return request({ url: url, method: 'post', data, params, responseType: 'stream' }) export const postStream: (url: string, data?: unknown) => Promise<Result<any> | any> = (
url,
data
) => {
const { user } = useStore()
const token = user.getToken()
const headers: HeadersInit = { 'Content-Type': 'application/json' }
if (token) {
headers['AUTHORIZATION'] = `${token}`
}
return fetch(url, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
headers: headers
})
} }
export const exportExcel: ( export const exportExcel: (

View File

@ -3,7 +3,22 @@
<div class="template-manage flex main-calc-height"> <div class="template-manage flex main-calc-height">
<div class="template-manage__left p-8 border-r"> <div class="template-manage__left p-8 border-r">
<h4 class="p-16">供应商</h4> <h4 class="p-16">供应商</h4>
<common-list :data="provider_list" class="mt-8" v-loading="loading" @click="clickHandle"> <common-list
v-model="active_provider"
:data="provider_list"
class="mt-8"
v-loading="loading"
>
<template #prefix>
<div class="flex">
<AppIcon
style="height: 24px; width: 24px"
class="mr-8"
:iconName="active_provider ? 'app-all-menu' : 'app-all-menu-active'"
></AppIcon>
<span>全部模型</span>
</div>
</template>
<template #default="{ row }"> <template #default="{ row }">
<div class="flex"> <div class="flex">
<span :innerHTML="row.icon" alt="" style="height: 24px; width: 24px" class="mr-8" /> <span :innerHTML="row.icon" alt="" style="height: 24px; width: 24px" class="mr-8" />
@ -14,7 +29,22 @@
</div> </div>
<div class="template-manage__right p-24"> <div class="template-manage__right p-24">
<h4>全部模型</h4> <h4>全部模型</h4>
<Demo></Demo> <card-box :title="model.name" v-for="model in model_list">
<template #icon>
<AppAvatar
class="mr-12"
shape="square"
style="--el-avatar-bg-color: rgba(255, 255, 255, 0)"
:size="32"
>
<span style="height: 24px; width: 24px" :innerHTML="get_model_icon(model)"></span
></AppAvatar>
</template>
<template #description>
{{ model.model_type }}
{{ model.model_name }}
</template>
</card-box>
</div> </div>
</div> </div>
</LayoutContainer> </LayoutContainer>
@ -23,16 +53,33 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, reactive, watch } from 'vue' import { onMounted, ref, reactive, watch } from 'vue'
import ModelApi from '@/api/model' import ModelApi from '@/api/model'
import type { Provider } from '@/api/type/model' import type { Provider, Model } from '@/api/type/model'
import AppIcon from '@/components/icons/AppIcon.vue'
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const active_provider = ref<Provider>()
const provider_list = ref<Array<Provider>>([]) const provider_list = ref<Array<Provider>>([])
const get_model_icon = (model: Model) => {
function clickHandle(row: any) {} return provider_list.value.find((p) => p.provider === model.provider)?.icon
}
const model_list = ref<Array<Model>>([])
watch(
active_provider,
() => {
ModelApi.getModel(
active_provider.value ? { provider: active_provider.value.provider } : {}
).then((ok) => {
model_list.value = ok.data
})
},
{
immediate: true
}
)
onMounted(() => { onMounted(() => {
ModelApi.getProvider(loading).then((ok) => { ModelApi.getProvider(loading).then((ok) => {
provider_list.value = ok.data provider_list.value = [...ok.data]
}) })
}) })
</script> </script>