parent
1bc33d2784
commit
ac2d981a23
|
@ -62,7 +62,7 @@
|
|||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
|
|
|
@ -20,41 +20,41 @@ import request from '@/config/axios'
|
|||
export const AppApi = {
|
||||
// 获取应用列表
|
||||
getAppList: async (params: any) => {
|
||||
return await request.get({ url: '/aigc/app/list', params })
|
||||
return await request.get({ url: '/chat/aigc/app/list', params })
|
||||
},
|
||||
|
||||
// 获取应用分页数据
|
||||
getAppPage: async (params: any) => {
|
||||
return await request.get({ url: '/aigc/app/page', params })
|
||||
return await request.get({ url: '/chat/aigc/app/page', params })
|
||||
},
|
||||
|
||||
// 获取应用详情
|
||||
getApp: async (id: string) => {
|
||||
return await request.get({ url: `/aigc/app/${id}` })
|
||||
return await request.get({ url: `/chat/aigc/app/${id}` })
|
||||
},
|
||||
|
||||
// 根据模型ID获取应用
|
||||
getAppByModelId: async (id: string) => {
|
||||
return await request.get({ url: `/aigc/app/byModelId/${id}` })
|
||||
return await request.get({ url: `/chat/aigc/app/byModelId/${id}` })
|
||||
},
|
||||
|
||||
// 获取应用API通道
|
||||
getAppApiChannel: async (appId: string) => {
|
||||
return await request.get({ url: `/aigc/app/channel/api/${appId}` })
|
||||
return await request.get({ url: `/chat/aigc/app/channel/api/${appId}` })
|
||||
},
|
||||
|
||||
// 新增应用
|
||||
createApp: async (data: any) => {
|
||||
return await request.post({ url: '/aigc/app', data })
|
||||
return await request.post({ url: '/chat/aigc/app', data })
|
||||
},
|
||||
|
||||
// 更新应用
|
||||
updateApp: async (data: any) => {
|
||||
return await request.put({ url: '/aigc/app', data })
|
||||
return await request.put({ url: '/chat/aigc/app', data })
|
||||
},
|
||||
|
||||
// 删除应用
|
||||
deleteApp: async (id: string) => {
|
||||
return await request.delete({ url: `/aigc/app/${id}` })
|
||||
return await request.delete({ url: `/chat/aigc/app/${id}` })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,41 +20,41 @@ import request from '@/config/axios'
|
|||
export const AppApiManagement = {
|
||||
// 获取API列表
|
||||
getApiList: async (params: any) => {
|
||||
return await request.get({ url: '/aigc/app/api/list', params })
|
||||
return await request.get({ url: '/chat/aigc/app/api/list', params })
|
||||
},
|
||||
|
||||
// 获取API分页数据
|
||||
getApiPage: async (params: any) => {
|
||||
return await request.get({ url: '/aigc/app/api/page', params })
|
||||
return await request.get({ url: '/chat/aigc/app/api/page', params })
|
||||
},
|
||||
|
||||
// 获取API详情
|
||||
getApi: async (id: string) => {
|
||||
return await request.get({ url: `/aigc/app/api/${id}` })
|
||||
return await request.get({ url: `/chat/aigc/app/api/${id}` })
|
||||
},
|
||||
|
||||
// 新增API
|
||||
createApi: async (data: any) => {
|
||||
return await request.post({ url: '/aigc/app/api', data })
|
||||
return await request.post({ url: '/chat/aigc/app/api', data })
|
||||
},
|
||||
|
||||
// 更新API
|
||||
updateApi: async (data: any) => {
|
||||
return await request.put({ url: '/aigc/app/api', data })
|
||||
return await request.put({ url: '/chat/aigc/app/api', data })
|
||||
},
|
||||
|
||||
// 删除API
|
||||
deleteApi: async (id: string) => {
|
||||
return await request.delete({ url: `/aigc/app/api/${id}` })
|
||||
return await request.delete({ url: `/chat/aigc/app/api/${id}` })
|
||||
},
|
||||
|
||||
// 发布API
|
||||
publishApi: async (id: string) => {
|
||||
return await request.put({ url: `/aigc/app/api/publish/${id}` })
|
||||
return await request.put({ url: `/chat/aigc/app/api/publish/${id}` })
|
||||
},
|
||||
|
||||
// 下线API
|
||||
offlineApi: async (id: string) => {
|
||||
return await request.put({ url: `/aigc/app/api/offline/${id}` })
|
||||
return await request.put({ url: `/chat/aigc/app/api/offline/${id}` })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export function chat(
|
|||
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void
|
||||
) {
|
||||
return request.post({
|
||||
url: '/aigc/chat/completions',
|
||||
url: '/chat/aigc/chat/completions',
|
||||
data,
|
||||
signal: controller.signal,
|
||||
onDownloadProgress,
|
||||
|
@ -33,26 +33,26 @@ export function chat(
|
|||
|
||||
export function clean(conversationId: string | null) {
|
||||
return request.delete({
|
||||
url: `/aigc/chat/messages/clean/${conversationId}`
|
||||
url: `/chat/aigc/chat/messages/clean/${conversationId}`
|
||||
})
|
||||
}
|
||||
|
||||
export function getMessages(conversationId?: string | number) {
|
||||
return request.get({
|
||||
url: `/aigc/chat/messages/${conversationId}`
|
||||
url: `/chat/aigc/chat/messages/${conversationId}`
|
||||
})
|
||||
}
|
||||
|
||||
export function getAppInfo(params: any) {
|
||||
return request.get({
|
||||
url: `/aigc/app/info`,
|
||||
url: `/chat/aigc/app/info`,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function getImageModels() {
|
||||
return request.get({
|
||||
url: '/aigc/chat/getImageModels'
|
||||
url: '/chat/aigc/chat/getImageModels'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ export function getImageModels() {
|
|||
*/
|
||||
export function genImage(data: any) {
|
||||
return request.post({
|
||||
url: '/aigc/chat/image',
|
||||
url: '/chat/aigc/chat/image',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ export function genImage(data: any) {
|
|||
*/
|
||||
export function genMindMap(data: any) {
|
||||
return request.post({
|
||||
url: '/aigc/chat/mindmap',
|
||||
url: '/chat/aigc/chat/mindmap',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export class OssApi {
|
|||
*/
|
||||
static policy() {
|
||||
return request.get({
|
||||
url: '/oss/policy'
|
||||
url: '/chat/oss/policy'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ export class OssApi {
|
|||
*/
|
||||
static list(params: any) {
|
||||
return request.get({
|
||||
url: '/oss/list',
|
||||
url: '/chat/oss/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
@ -44,19 +44,18 @@ export class OssApi {
|
|||
*/
|
||||
static del(objectName: string) {
|
||||
return request.delete({
|
||||
url: `/oss/${objectName}`
|
||||
url: `/chat/oss/${objectName}`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
static upload(data: FormData) {
|
||||
return request.post({
|
||||
url: '/aigc/oss/upload',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
static uploadUrl = '/chat/aigc/oss/upload'
|
||||
|
||||
static upload(data: any) {
|
||||
return request.upload({
|
||||
url: '/chat/aigc/oss/upload',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
@ -66,7 +65,7 @@ export class OssApi {
|
|||
*/
|
||||
static getUrl(objectName: string) {
|
||||
return request.get({
|
||||
url: `/oss/url/${objectName}`
|
||||
url: `/chat/oss/url/${objectName}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,9 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setValue = (key: string, value: any) => {
|
||||
formModel.value[key] = value
|
||||
}
|
||||
const getElFormRef = (): ComponentRef<typeof ElForm> => {
|
||||
return unref(elFormRef) as ComponentRef<typeof ElForm>
|
||||
}
|
||||
|
@ -129,7 +131,8 @@ export default defineComponent({
|
|||
addSchema,
|
||||
setSchema,
|
||||
getElFormRef,
|
||||
clearForm
|
||||
clearForm,
|
||||
setValue
|
||||
})
|
||||
|
||||
// 监听表单结构化数组,重新生成formModel
|
||||
|
|
|
@ -79,7 +79,9 @@ const props = defineProps({
|
|||
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||
showDelete: propTypes.bool.def(true), // 是否显示删除按钮
|
||||
showBtnText: propTypes.bool.def(true) // 是否显示按钮文字
|
||||
showBtnText: propTypes.bool.def(true), // 是否显示按钮文字
|
||||
customApi: propTypes.func.def(() => undefined), // 上传接口地址 ==> 非必传(默认为 '')
|
||||
successBefore: propTypes.func.def(() => undefined), // 上传成功之前的钩子 ==> 非必传(默认为 undefined)
|
||||
})
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
@ -99,7 +101,7 @@ const deleteImg = () => {
|
|||
emit('update:modelValue', '')
|
||||
}
|
||||
|
||||
const { uploadUrl, httpRequest } = useUpload()
|
||||
const { uploadUrl, httpRequest } = useUpload({ customApi: props.customApi })
|
||||
|
||||
const editImg = () => {
|
||||
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
||||
|
@ -117,8 +119,12 @@ const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
|||
|
||||
// 图片上传成功提示
|
||||
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
let params = res
|
||||
if (props.successBefore) {
|
||||
params = props.successBefore(res)
|
||||
}
|
||||
message.success('上传成功')
|
||||
emit('update:modelValue', res.data)
|
||||
emit('update:modelValue', params.data)
|
||||
}
|
||||
|
||||
// 图片上传错误提示
|
||||
|
|
|
@ -10,7 +10,7 @@ export const getUploadUrl = (): string => {
|
|||
return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/infra/file/upload'
|
||||
}
|
||||
|
||||
export const useUpload = () => {
|
||||
export const useUpload = (defaultParams?: { customApi: any }) => {
|
||||
// 后端上传地址
|
||||
const uploadUrl = getUploadUrl()
|
||||
// 是否使用前端直连上传
|
||||
|
@ -40,7 +40,8 @@ export const useUpload = () => {
|
|||
// 模式二:后端上传
|
||||
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
||||
return new Promise((resolve, reject) => {
|
||||
FileApi.updateFile({ file: options.file })
|
||||
const Api = defaultParams?.customApi || FileApi.updateFile
|
||||
Api({ file: options.file })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve(res)
|
||||
|
|
|
@ -149,7 +149,7 @@ service.interceptors.response.use(
|
|||
})
|
||||
}
|
||||
} else if (code === 500) {
|
||||
ElMessage.error(t('sys.api.errMsg500'))
|
||||
ElMessage.error(msg || t('sys.api.errMsg500'))
|
||||
return Promise.reject(new Error(msg))
|
||||
} else if (code === 901) {
|
||||
ElMessage.error({
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/* VSCode Dark+ Theme for highlight.js */
|
||||
|
||||
.hljs {
|
||||
background: #1e1e1e !important;
|
||||
color: #d4d4d4 !important;
|
||||
--el-scrollbar-opacity: 0.3;
|
||||
--el-scrollbar-bg-color: var(--el-text-color-secondary);
|
||||
--el-scrollbar-hover-opacity: 0.5;
|
||||
--el-scrollbar-hover-bg-color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.hljs::-webkit-scrollbar {
|
||||
width: 6px !important;
|
||||
}
|
||||
|
||||
.hljs::-webkit-scrollbar-thumb {
|
||||
background-color: var(--el-scrollbar-bg-color) !important;
|
||||
opacity: var(--el-scrollbar-opacity) !important;
|
||||
border-radius: 3px !important;
|
||||
}
|
||||
|
||||
.hljs::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--el-scrollbar-hover-bg-color) !important;
|
||||
opacity: var(--el-scrollbar-hover-opacity) !important;
|
||||
}
|
||||
|
||||
.hljs::-webkit-scrollbar-track {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.hljs-keyword {
|
||||
color: #569cd6 !important;
|
||||
}
|
||||
|
||||
.hljs-built_in {
|
||||
color: #4ec9b0 !important;
|
||||
}
|
||||
|
||||
.hljs-type {
|
||||
color: #4ec9b0 !important;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #569cd6 !important;
|
||||
}
|
||||
|
||||
.hljs-number {
|
||||
color: #b5cea8 !important;
|
||||
}
|
||||
|
||||
.hljs-regexp {
|
||||
color: #d16969 !important;
|
||||
}
|
||||
|
||||
.hljs-string {
|
||||
color: #ce9178 !important;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
|
||||
.hljs-symbol {
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
|
||||
.hljs-class {
|
||||
color: #4ec9b0 !important;
|
||||
}
|
||||
|
||||
.hljs-function {
|
||||
color: #dcdcaa !important;
|
||||
}
|
||||
|
||||
.hljs-title {
|
||||
color: #dcdcaa !important;
|
||||
}
|
||||
|
||||
.hljs-params {
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
|
||||
.hljs-comment {
|
||||
color: #6a9955 !important;
|
||||
}
|
||||
|
||||
.hljs-doctag {
|
||||
color: #608b4e !important;
|
||||
}
|
||||
|
||||
.hljs-meta,
|
||||
.hljs-meta .hljs-keyword {
|
||||
color: #9b9b9b !important;
|
||||
}
|
||||
|
||||
.hljs-meta .hljs-string {
|
||||
color: #ce9178 !important;
|
||||
}
|
||||
|
||||
.hljs-attr {
|
||||
color: #9cdcfe !important;
|
||||
}
|
||||
|
||||
.hljs-attribute {
|
||||
color: #9cdcfe !important;
|
||||
}
|
||||
|
||||
.hljs-name {
|
||||
color: #569cd6 !important;
|
||||
}
|
||||
|
||||
.hljs-section {
|
||||
color: #d4d4d4 !important;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #569cd6 !important;
|
||||
}
|
||||
|
||||
.hljs-variable {
|
||||
color: #9cdcfe !important;
|
||||
}
|
||||
|
||||
.hljs-template-variable {
|
||||
color: #9cdcfe !important;
|
||||
}
|
||||
|
||||
.hljs-template-tag {
|
||||
color: #569cd6 !important;
|
||||
}
|
||||
|
||||
/* 添加代码块样式 */
|
||||
pre code.hljs {
|
||||
display: block !important;
|
||||
padding: 1em !important;
|
||||
overflow-x: auto !important;
|
||||
border-radius: 6px !important;
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
@use './FormCreate/index.scss';
|
||||
@use './theme.scss';
|
||||
@use 'element-plus/theme-chalk/dark/css-vars.css';
|
||||
|
||||
@import './highligt.css';
|
||||
.reset-margin [class*='el-icon'] + span {
|
||||
margin-left: 2px !important;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ async function fetchData() {
|
|||
try {
|
||||
const data = await getAppInfo({
|
||||
appId: id,
|
||||
conversationId: null
|
||||
})
|
||||
form.value = data
|
||||
appStore.info = data
|
||||
|
|
|
@ -1,18 +1,4 @@
|
|||
<!--
|
||||
- Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||
-
|
||||
- Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||
- you may not use this file except in compliance with the License.
|
||||
- You may obtain a copy of the License at
|
||||
-
|
||||
- https://www.gnu.org/licenses/agpl-3.0.html
|
||||
-
|
||||
- Unless required by applicable law or agreed to in writing, software
|
||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
- See the License for the specific language governing permissions and
|
||||
- limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/views/ai/app/store';
|
||||
|
@ -30,7 +16,7 @@
|
|||
<div class="p-2 flex justify-between items-center">
|
||||
<div class="text-md font-bold">Prompt 提示词</div>
|
||||
</div>
|
||||
<div class="p-4 pt-0 h-full mb-10">
|
||||
<div class="p-4 pt-0 h-full mb-10" v-if="appStore.info">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="appStore.info.prompt"
|
||||
|
|
|
@ -18,7 +18,7 @@ async function show() {
|
|||
|
||||
function onAdd(item) {
|
||||
appStore.addKnowledge(item)
|
||||
ElMessage.success('关联成功')
|
||||
// ElMessage.success('关联成功')
|
||||
}
|
||||
|
||||
function onRemove(item) {
|
||||
|
|
|
@ -11,6 +11,7 @@ const knowledgeRef = ref()
|
|||
|
||||
async function onSaveModel(val) {
|
||||
appStore.modelId = val.id
|
||||
console.log(val)
|
||||
emit('update')
|
||||
}
|
||||
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
-->
|
||||
|
||||
<script lang="ts" setup>
|
||||
import hljs from 'highlight.js';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import hljs from 'highlight.js';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
hljs.registerLanguage('javascript', javascript);
|
||||
|
||||
const url = `http://langchat.cn`;
|
||||
const request = `
|
||||
hljs.registerLanguage('javascript', javascript);
|
||||
const url = `http://langchat.cn`;
|
||||
const request = `
|
||||
POST /v1/chat/completions HTTP/1.1
|
||||
Content-Type: application/json
|
||||
Authorization: 'Bearer YOUR_ACCESS_TOKEN'
|
||||
|
@ -31,9 +31,9 @@ Body:
|
|||
{ "role": "user", "content": "你好" }
|
||||
]
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
const response = `
|
||||
const response = `
|
||||
data: {"choices": [{"index": 0, "delta": {"content": "你好!"}, "finish_reason": null}], "session_id": null}
|
||||
|
||||
data: {"choices": [{"index": 0, "delta": {"content": "我能"}, "finish_reason": null}], "session_id": null}
|
||||
|
@ -43,9 +43,9 @@ data: {"choices": [{"index": 0, "delta": {"content": "为你"}, "finish_reason":
|
|||
data: {"choices": [{"index": 0, "delta": {"content": "做些什么?"}, "finish_reason": null}], "session_id": null}
|
||||
|
||||
data: {"choices": [{"index": 0, "delta": {}, "finish_reason": "stop", "usage": {"prompt_tokens": 9, "completion_tokens": 6, "total_tokens": 15}}], "session_id": null}
|
||||
`;
|
||||
`;
|
||||
|
||||
const demo = `
|
||||
const demo = `
|
||||
const url = 'http://langchat.cn/v1/chat/completions';
|
||||
const data = {
|
||||
"messages": [
|
||||
|
@ -73,41 +73,76 @@ fetch(url, {
|
|||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
`;
|
||||
`;
|
||||
|
||||
onMounted(() => {
|
||||
// 高亮所有代码块
|
||||
document.querySelectorAll('pre code').forEach((el) => {
|
||||
hljs.highlightElement(el as HTMLElement);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 bg-white h-full overflow-auto rounded">
|
||||
<n-config-provider :hljs="hljs" class="flex flex-col gap-4">
|
||||
<el-scrollbar class="p-4 bg-white h-full overflow-auto rounded">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<n-alert title="API URL(API接口格式遵循OpenAI格式)" type="info" />
|
||||
<div class="bg-[#18181c] mt-2 py-2 px-4 overflow-x-auto rounded">
|
||||
<n-code :code="url" class="text-white" language="JavaScript" />
|
||||
<el-alert
|
||||
title="API URL(API接口格式遵循OpenAI格式)"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<div class="bg-[#18181c] mt-10px py-2 px-4 overflow-x-auto rounded">
|
||||
<pre><code class="javascript">{{ url }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<n-alert title="Request" type="info" />
|
||||
<div class="bg-[#18181c] mt-2 py-2 px-4 overflow-x-auto rounded">
|
||||
<n-code :code="request" class="text-white" language="JavaScript" />
|
||||
</div>
|
||||
<el-alert
|
||||
title="Request"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-scrollbar class="bg-[#18181c] mt-10px py-2 px-4 overflow-x-auto rounded">
|
||||
<pre><code class="javascript">{{ request }}</code></pre>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<n-alert title="Response(Stream)" type="info" />
|
||||
<div class="bg-[#18181c] py-2 mt-2 px-4 overflow-x-auto rounded">
|
||||
<n-code :code="response" class="text-white" language="JavaScript" />
|
||||
</div>
|
||||
<el-alert
|
||||
title="Response(Stream)"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-scrollbar class="mt-10px bg-[#18181c] py-2 px-4 rounded">
|
||||
<pre><code class="javascript">{{ response }}</code></pre>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<n-alert title="API请求示例" type="info" />
|
||||
<div class="bg-[#18181c] mt-2 py-2 px-4 overflow-x-auto rounded">
|
||||
<n-code :code="demo" class="text-white" language="javascript" />
|
||||
<el-alert
|
||||
title="API请求示例"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-scrollbar class="mt-10px bg-[#18181c] py-2 px-4 rounded">
|
||||
<pre><code class="javascript">{{ demo }}</code></pre>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</n-config-provider>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
:deep(pre) {
|
||||
margin: 0;
|
||||
code {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -4,12 +4,15 @@ import { nextTick, ref } from 'vue'
|
|||
import { FormSchema } from '@/types/form'
|
||||
import { AppApi } from '@/api/new-ai/app'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import { OssApi } from '@/api/new-ai/oss'
|
||||
import type { UploadFile, UploadRequestOptions } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import ModelSelect from '@/views/common/ModelSelect.vue'
|
||||
const emit = defineEmits(['reload'])
|
||||
const visible = ref(false)
|
||||
const formData = ref({})
|
||||
const formRef = ref()
|
||||
const schemas = ref<FormSchema[]>([
|
||||
const schemas = ref<Array<FormSchema>>([
|
||||
{
|
||||
field: 'name',
|
||||
label: '应用名称',
|
||||
|
@ -23,8 +26,26 @@ const schemas = ref<FormSchema[]>([
|
|||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入应用名称'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
field: 'modelId',
|
||||
label: '关联模型',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
rules: [{ required: true, message: '请选择关联模型' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'cover',
|
||||
label: '应用图标',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
required: false
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入应用图标'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
|
@ -41,29 +62,6 @@ const schemas = ref<FormSchema[]>([
|
|||
type: 'textarea',
|
||||
rows: 4,
|
||||
placeholder: '请输入应用描述'
|
||||
},
|
||||
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
label: '应用图标',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
required: false,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请输入应用图标'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'modelId',
|
||||
label: '关联模型',
|
||||
component: 'Input',
|
||||
formItemProps: {
|
||||
required: false,
|
||||
},
|
||||
componentProps: {
|
||||
placeholder: '请选择关联模型'
|
||||
}
|
||||
}
|
||||
])
|
||||
|
@ -95,29 +93,22 @@ const show = async (data: any = {}) => {
|
|||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const form = formRef.value.getElFormRef()
|
||||
await form.validate()
|
||||
const values = formRef.value.formModel
|
||||
const Api = isEdit.value ? AppApi.updateApp : AppApi.createApp
|
||||
loading.value = true
|
||||
|
||||
if (isEdit.value) {
|
||||
await AppApi.updateApp(values)
|
||||
ElMessage.success('更新应用成功')
|
||||
} else {
|
||||
await AppApi.createApp(values)
|
||||
ElMessage.success('创建应用成功')
|
||||
}
|
||||
values.modelId = values.modelId?.[1]
|
||||
await Api(values).finally(() => (loading.value = false))
|
||||
ElMessage.success(isEdit.value ? '更新应用成功' : '创建应用成功')
|
||||
close()
|
||||
emit('reload')
|
||||
} catch (error) {
|
||||
console.error('Failed to save app:', error)
|
||||
ElMessage.error(isEdit.value ? '更新应用失败' : '创建应用失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
const handleImport = (params: Record<any, any>) => {
|
||||
return {
|
||||
data: params.data.url
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
|
@ -134,12 +125,14 @@ defineExpose({
|
|||
width="500px"
|
||||
@close="close"
|
||||
>
|
||||
<Form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:schema="schemas"
|
||||
v-loading="loading"
|
||||
/>
|
||||
<Form ref="formRef" :model="formData" :schema="schemas" v-loading="loading">
|
||||
<template #modelId="scope">
|
||||
<ModelSelect :id="scope.modelId" class="w-full" v-model="scope.modelId" />
|
||||
</template>
|
||||
<template #cover="scoped">
|
||||
<UploadImg v-model="scoped.cover" :custom-api="OssApi.upload" :success-before="handleImport"/>
|
||||
</template>
|
||||
</Form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="loading">确认</el-button>
|
||||
<el-button @click="close" :loading="loading">取消</el-button>
|
||||
|
@ -148,4 +141,24 @@ defineExpose({
|
|||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.avatar-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.avatar-uploader .el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -20,30 +20,15 @@ const router = useRouter()
|
|||
|
||||
// 数据
|
||||
const loading = ref(false)
|
||||
const tableData = ref<TableDataItem[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: '',
|
||||
description: '',
|
||||
apiKey: '',
|
||||
provider: '',
|
||||
channel: '',
|
||||
createTime: ''
|
||||
}
|
||||
])
|
||||
const tableData = ref<TableDataItem[]>([])
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const res = await AppApi.getAppList({})
|
||||
tableData.value = res.data || []
|
||||
} catch (error) {
|
||||
ElMessage.error('获取应用列表失败')
|
||||
console.error('Failed to fetch app list:', error)
|
||||
} finally {
|
||||
const res = await AppApi.getAppList({}).finally(() => {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
tableData.value = res || []
|
||||
}
|
||||
|
||||
// 新增应用
|
||||
|
@ -86,7 +71,6 @@ const handleInfo = (record: any) => {
|
|||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -109,15 +93,11 @@ onMounted(() => {
|
|||
</div>
|
||||
</el-col>
|
||||
<el-col
|
||||
v-for="item in tableData.concat(tableData).concat(tableData).concat(tableData)"
|
||||
v-for="item in tableData"
|
||||
:key="item.id"
|
||||
:span="6"
|
||||
>
|
||||
<el-card
|
||||
class="app-card cursor-pointer w-full"
|
||||
shadow="hover"
|
||||
@click="handleInfo(item)"
|
||||
>
|
||||
<el-card class="app-card cursor-pointer w-full" shadow="hover" @click="handleInfo(item)">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<div class="sm:mx-4">
|
||||
|
@ -135,7 +115,7 @@ onMounted(() => {
|
|||
</div>
|
||||
</template>
|
||||
<div class="app-card-content flex justify-between items-center">
|
||||
<p class="text-gray-600">{{ item.description || '暂无描述' }}</p>
|
||||
<p class="text-gray-600">{{ item.des || '暂无描述' }}</p>
|
||||
<el-dropdown trigger="hover" width="500px">
|
||||
<div
|
||||
:class="[activeDropdownId === item.id ? 'bg-gray-200' : 'hover:bg-gray-200']"
|
||||
|
@ -148,13 +128,10 @@ onMounted(() => {
|
|||
<el-dropdown-item @click.stop="handleEdit(item)">编辑此应用</el-dropdown-item>
|
||||
<el-dropdown-item @click.stop="handleDelete(item)">删除此应用</el-dropdown-item>
|
||||
<el-dropdown-item disabled divided>
|
||||
<div class='w-150px'>
|
||||
<div class="w-150px">
|
||||
<div>信息</div>
|
||||
<span class='text-xs text-stone-500'>
|
||||
创建时间:{{item.createTime}}
|
||||
</span>
|
||||
<span class="text-xs text-stone-500"> 创建时间:{{ item.createTime }} </span>
|
||||
</div>
|
||||
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
|
@ -162,6 +139,9 @@ onMounted(() => {
|
|||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col v-if="!tableData.length" :span="18">
|
||||
<el-empty description="暂无数据"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<EditCom ref="editRef" @reload="loadData" />
|
||||
|
|
|
@ -25,13 +25,11 @@
|
|||
|
||||
async function fetchData() {
|
||||
loading.value = true;
|
||||
const id = route.params.id;
|
||||
const data = await getAppInfo({
|
||||
appId: id,
|
||||
conversationId: null,
|
||||
const appId = route.params.id;
|
||||
const data = await getAppInfo({ appId }).finally(() => {
|
||||
loading.value = false
|
||||
});
|
||||
form.value = data;
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 pt-1 chat-card w-full h-full bg-white rounded-xl shadow-xl pt-20px">
|
||||
<div class="p-4 pt-1 chat-card w-full h-full bg-white rounded-xl shadow-md pt-20px">
|
||||
<Header title="AI聊天助手" @reload="fetch" />
|
||||
<main ref="contentRef" class="flex-1 overflow-hidden overflow-y-auto">
|
||||
<Chat />
|
||||
|
@ -34,12 +34,10 @@
|
|||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:v-deep(.n-tabs.n-tabs--top .n-tab-pane) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.chat-card {
|
||||
box-sizing: border-box;
|
||||
height: calc(100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - (var(--app-content-padding) * 3)) !important;
|
||||
height: calc(100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - (var(--app-content-padding) * 3) + 20px) !important;
|
||||
main {
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,4 @@
|
|||
<!--
|
||||
- Copyright (c) 2024 LangChat. TyCoding All Rights Reserved.
|
||||
-
|
||||
- Licensed under the GNU Affero General Public License, Version 3 (the "License");
|
||||
- you may not use this file except in compliance with the License.
|
||||
- You may obtain a copy of the License at
|
||||
-
|
||||
- https://www.gnu.org/licenses/agpl-3.0.html
|
||||
-
|
||||
- Unless required by applicable law or agreed to in writing, software
|
||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
- See the License for the specific language governing permissions and
|
||||
- limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, toRaw } from 'vue'
|
||||
|
@ -69,12 +55,12 @@ onMounted(async () => {
|
|||
})
|
||||
|
||||
function onUpdate(val: any) {
|
||||
console.log(val, 'val')
|
||||
const group = options.value.find(g => g.children.some(c => c.value === val))
|
||||
if (!group) return
|
||||
|
||||
console.log(group, 'group')
|
||||
const option = group.children.find(c => c.value === val)
|
||||
if (!option) return
|
||||
|
||||
const obj = option.raw
|
||||
emit('update', {
|
||||
id: obj.id,
|
||||
|
@ -88,16 +74,19 @@ function onUpdate(val: any) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<el-cascader
|
||||
<el-select
|
||||
v-model="modelId"
|
||||
:options="options"
|
||||
:size="size"
|
||||
:props="{
|
||||
expandTrigger: 'hover'
|
||||
}"
|
||||
placeholder="请选择关联模型"
|
||||
@change="onUpdate"
|
||||
/>
|
||||
>
|
||||
<el-option-group v-for="group in options" :key="group.label" :label="group.label">
|
||||
<el-option v-for="option in group.children" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
Loading…
Reference in New Issue