parent
1bc33d2784
commit
ac2d981a23
|
@ -62,7 +62,7 @@
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"[typescriptreact]": {
|
"[typescriptreact]": {
|
||||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||||
|
|
|
@ -20,41 +20,41 @@ import request from '@/config/axios'
|
||||||
export const AppApi = {
|
export const AppApi = {
|
||||||
// 获取应用列表
|
// 获取应用列表
|
||||||
getAppList: async (params: any) => {
|
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) => {
|
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) => {
|
getApp: async (id: string) => {
|
||||||
return await request.get({ url: `/aigc/app/${id}` })
|
return await request.get({ url: `/chat/aigc/app/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 根据模型ID获取应用
|
// 根据模型ID获取应用
|
||||||
getAppByModelId: async (id: string) => {
|
getAppByModelId: async (id: string) => {
|
||||||
return await request.get({ url: `/aigc/app/byModelId/${id}` })
|
return await request.get({ url: `/chat/aigc/app/byModelId/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取应用API通道
|
// 获取应用API通道
|
||||||
getAppApiChannel: async (appId: string) => {
|
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) => {
|
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) => {
|
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) => {
|
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 = {
|
export const AppApiManagement = {
|
||||||
// 获取API列表
|
// 获取API列表
|
||||||
getApiList: async (params: any) => {
|
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分页数据
|
// 获取API分页数据
|
||||||
getApiPage: async (params: any) => {
|
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详情
|
// 获取API详情
|
||||||
getApi: async (id: string) => {
|
getApi: async (id: string) => {
|
||||||
return await request.get({ url: `/aigc/app/api/${id}` })
|
return await request.get({ url: `/chat/aigc/app/api/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 新增API
|
// 新增API
|
||||||
createApi: async (data: any) => {
|
createApi: async (data: any) => {
|
||||||
return await request.post({ url: '/aigc/app/api', data })
|
return await request.post({ url: '/chat/aigc/app/api', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 更新API
|
// 更新API
|
||||||
updateApi: async (data: any) => {
|
updateApi: async (data: any) => {
|
||||||
return await request.put({ url: '/aigc/app/api', data })
|
return await request.put({ url: '/chat/aigc/app/api', data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除API
|
// 删除API
|
||||||
deleteApi: async (id: string) => {
|
deleteApi: async (id: string) => {
|
||||||
return await request.delete({ url: `/aigc/app/api/${id}` })
|
return await request.delete({ url: `/chat/aigc/app/api/${id}` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 发布API
|
// 发布API
|
||||||
publishApi: async (id: string) => {
|
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
|
// 下线API
|
||||||
offlineApi: async (id: string) => {
|
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
|
onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void
|
||||||
) {
|
) {
|
||||||
return request.post({
|
return request.post({
|
||||||
url: '/aigc/chat/completions',
|
url: '/chat/aigc/chat/completions',
|
||||||
data,
|
data,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
onDownloadProgress,
|
onDownloadProgress,
|
||||||
|
@ -33,26 +33,26 @@ export function chat(
|
||||||
|
|
||||||
export function clean(conversationId: string | null) {
|
export function clean(conversationId: string | null) {
|
||||||
return request.delete({
|
return request.delete({
|
||||||
url: `/aigc/chat/messages/clean/${conversationId}`
|
url: `/chat/aigc/chat/messages/clean/${conversationId}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMessages(conversationId?: string | number) {
|
export function getMessages(conversationId?: string | number) {
|
||||||
return request.get({
|
return request.get({
|
||||||
url: `/aigc/chat/messages/${conversationId}`
|
url: `/chat/aigc/chat/messages/${conversationId}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAppInfo(params: any) {
|
export function getAppInfo(params: any) {
|
||||||
return request.get({
|
return request.get({
|
||||||
url: `/aigc/app/info`,
|
url: `/chat/aigc/app/info`,
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getImageModels() {
|
export function getImageModels() {
|
||||||
return request.get({
|
return request.get({
|
||||||
url: '/aigc/chat/getImageModels'
|
url: '/chat/aigc/chat/getImageModels'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export function getImageModels() {
|
||||||
*/
|
*/
|
||||||
export function genImage(data: any) {
|
export function genImage(data: any) {
|
||||||
return request.post({
|
return request.post({
|
||||||
url: '/aigc/chat/image',
|
url: '/chat/aigc/chat/image',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ export function genImage(data: any) {
|
||||||
*/
|
*/
|
||||||
export function genMindMap(data: any) {
|
export function genMindMap(data: any) {
|
||||||
return request.post({
|
return request.post({
|
||||||
url: '/aigc/chat/mindmap',
|
url: '/chat/aigc/chat/mindmap',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class OssApi {
|
||||||
*/
|
*/
|
||||||
static policy() {
|
static policy() {
|
||||||
return request.get({
|
return request.get({
|
||||||
url: '/oss/policy'
|
url: '/chat/oss/policy'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export class OssApi {
|
||||||
*/
|
*/
|
||||||
static list(params: any) {
|
static list(params: any) {
|
||||||
return request.get({
|
return request.get({
|
||||||
url: '/oss/list',
|
url: '/chat/oss/list',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -44,19 +44,18 @@ export class OssApi {
|
||||||
*/
|
*/
|
||||||
static del(objectName: string) {
|
static del(objectName: string) {
|
||||||
return request.delete({
|
return request.delete({
|
||||||
url: `/oss/${objectName}`
|
url: `/chat/oss/${objectName}`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传文件
|
* 上传文件
|
||||||
*/
|
*/
|
||||||
static upload(data: FormData) {
|
static uploadUrl = '/chat/aigc/oss/upload'
|
||||||
return request.post({
|
|
||||||
url: '/aigc/oss/upload',
|
static upload(data: any) {
|
||||||
headers: {
|
return request.upload({
|
||||||
'Content-Type': 'multipart/form-data',
|
url: '/chat/aigc/oss/upload',
|
||||||
},
|
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -66,7 +65,7 @@ export class OssApi {
|
||||||
*/
|
*/
|
||||||
static getUrl(objectName: string) {
|
static getUrl(objectName: string) {
|
||||||
return request.get({
|
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> => {
|
const getElFormRef = (): ComponentRef<typeof ElForm> => {
|
||||||
return unref(elFormRef) as ComponentRef<typeof ElForm>
|
return unref(elFormRef) as ComponentRef<typeof ElForm>
|
||||||
}
|
}
|
||||||
|
@ -129,7 +131,8 @@ export default defineComponent({
|
||||||
addSchema,
|
addSchema,
|
||||||
setSchema,
|
setSchema,
|
||||||
getElFormRef,
|
getElFormRef,
|
||||||
clearForm
|
clearForm,
|
||||||
|
setValue
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听表单结构化数组,重新生成formModel
|
// 监听表单结构化数组,重新生成formModel
|
||||||
|
|
|
@ -79,7 +79,9 @@ const props = defineProps({
|
||||||
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||||
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||||
showDelete: propTypes.bool.def(true), // 是否显示删除按钮
|
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 { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
@ -99,7 +101,7 @@ const deleteImg = () => {
|
||||||
emit('update:modelValue', '')
|
emit('update:modelValue', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
const { uploadUrl, httpRequest } = useUpload()
|
const { uploadUrl, httpRequest } = useUpload({ customApi: props.customApi })
|
||||||
|
|
||||||
const editImg = () => {
|
const editImg = () => {
|
||||||
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
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 => {
|
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||||
|
let params = res
|
||||||
|
if (props.successBefore) {
|
||||||
|
params = props.successBefore(res)
|
||||||
|
}
|
||||||
message.success('上传成功')
|
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'
|
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()
|
const uploadUrl = getUploadUrl()
|
||||||
// 是否使用前端直连上传
|
// 是否使用前端直连上传
|
||||||
|
@ -40,7 +40,8 @@ export const useUpload = () => {
|
||||||
// 模式二:后端上传
|
// 模式二:后端上传
|
||||||
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
FileApi.updateFile({ file: options.file })
|
const Api = defaultParams?.customApi || FileApi.updateFile
|
||||||
|
Api({ file: options.file })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
resolve(res)
|
resolve(res)
|
||||||
|
|
|
@ -149,7 +149,7 @@ service.interceptors.response.use(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (code === 500) {
|
} else if (code === 500) {
|
||||||
ElMessage.error(t('sys.api.errMsg500'))
|
ElMessage.error(msg || t('sys.api.errMsg500'))
|
||||||
return Promise.reject(new Error(msg))
|
return Promise.reject(new Error(msg))
|
||||||
} else if (code === 901) {
|
} else if (code === 901) {
|
||||||
ElMessage.error({
|
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 './FormCreate/index.scss';
|
||||||
@use './theme.scss';
|
@use './theme.scss';
|
||||||
@use 'element-plus/theme-chalk/dark/css-vars.css';
|
@use 'element-plus/theme-chalk/dark/css-vars.css';
|
||||||
|
@import './highligt.css';
|
||||||
.reset-margin [class*='el-icon'] + span {
|
.reset-margin [class*='el-icon'] + span {
|
||||||
margin-left: 2px !important;
|
margin-left: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,6 @@ async function fetchData() {
|
||||||
try {
|
try {
|
||||||
const data = await getAppInfo({
|
const data = await getAppInfo({
|
||||||
appId: id,
|
appId: id,
|
||||||
conversationId: null
|
|
||||||
})
|
})
|
||||||
form.value = data
|
form.value = data
|
||||||
appStore.info = 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>
|
<script lang="ts" setup>
|
||||||
import { useAppStore } from '@/views/ai/app/store';
|
import { useAppStore } from '@/views/ai/app/store';
|
||||||
|
@ -30,7 +16,7 @@
|
||||||
<div class="p-2 flex justify-between items-center">
|
<div class="p-2 flex justify-between items-center">
|
||||||
<div class="text-md font-bold">Prompt 提示词</div>
|
<div class="text-md font-bold">Prompt 提示词</div>
|
||||||
</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
|
<el-input
|
||||||
type="textarea"
|
type="textarea"
|
||||||
v-model="appStore.info.prompt"
|
v-model="appStore.info.prompt"
|
||||||
|
|
|
@ -18,7 +18,7 @@ async function show() {
|
||||||
|
|
||||||
function onAdd(item) {
|
function onAdd(item) {
|
||||||
appStore.addKnowledge(item)
|
appStore.addKnowledge(item)
|
||||||
ElMessage.success('关联成功')
|
// ElMessage.success('关联成功')
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRemove(item) {
|
function onRemove(item) {
|
||||||
|
|
|
@ -11,6 +11,7 @@ const knowledgeRef = ref()
|
||||||
|
|
||||||
async function onSaveModel(val) {
|
async function onSaveModel(val) {
|
||||||
appStore.modelId = val.id
|
appStore.modelId = val.id
|
||||||
|
console.log(val)
|
||||||
emit('update')
|
emit('update')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import hljs from 'highlight.js';
|
import hljs from 'highlight.js';
|
||||||
import javascript from 'highlight.js/lib/languages/javascript';
|
import javascript from 'highlight.js/lib/languages/javascript';
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
hljs.registerLanguage('javascript', javascript);
|
hljs.registerLanguage('javascript', javascript);
|
||||||
|
|
||||||
const url = `http://langchat.cn`;
|
const url = `http://langchat.cn`;
|
||||||
const request = `
|
const request = `
|
||||||
POST /v1/chat/completions HTTP/1.1
|
POST /v1/chat/completions HTTP/1.1
|
||||||
|
@ -74,40 +74,75 @@ fetch(url, {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
});
|
});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 高亮所有代码块
|
||||||
|
document.querySelectorAll('pre code').forEach((el) => {
|
||||||
|
hljs.highlightElement(el as HTMLElement);
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-4 bg-white h-full overflow-auto rounded">
|
<el-scrollbar class="p-4 bg-white h-full overflow-auto rounded">
|
||||||
<n-config-provider :hljs="hljs" class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<div>
|
<div>
|
||||||
<n-alert title="API URL(API接口格式遵循OpenAI格式)" type="info" />
|
<el-alert
|
||||||
<div class="bg-[#18181c] mt-2 py-2 px-4 overflow-x-auto rounded">
|
title="API URL(API接口格式遵循OpenAI格式)"
|
||||||
<n-code :code="url" class="text-white" language="JavaScript" />
|
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>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<n-alert title="Request" type="info" />
|
<el-alert
|
||||||
<div class="bg-[#18181c] mt-2 py-2 px-4 overflow-x-auto rounded">
|
title="Request"
|
||||||
<n-code :code="request" class="text-white" language="JavaScript" />
|
type="info"
|
||||||
</div>
|
: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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<n-alert title="Response(Stream)" type="info" />
|
<el-alert
|
||||||
<div class="bg-[#18181c] py-2 mt-2 px-4 overflow-x-auto rounded">
|
title="Response(Stream)"
|
||||||
<n-code :code="response" class="text-white" language="JavaScript" />
|
type="info"
|
||||||
</div>
|
: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>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<n-alert title="API请求示例" type="info" />
|
<el-alert
|
||||||
<div class="bg-[#18181c] mt-2 py-2 px-4 overflow-x-auto rounded">
|
title="API请求示例"
|
||||||
<n-code :code="demo" class="text-white" language="javascript" />
|
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>
|
||||||
</div>
|
</div>
|
||||||
</n-config-provider>
|
</el-scrollbar>
|
||||||
</div>
|
|
||||||
</template>
|
</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 { FormSchema } from '@/types/form'
|
||||||
import { AppApi } from '@/api/new-ai/app'
|
import { AppApi } from '@/api/new-ai/app'
|
||||||
import { ElMessage } from 'element-plus'
|
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 emit = defineEmits(['reload'])
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const formData = ref({})
|
const formData = ref({})
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const schemas = ref<FormSchema[]>([
|
const schemas = ref<Array<FormSchema>>([
|
||||||
{
|
{
|
||||||
field: 'name',
|
field: 'name',
|
||||||
label: '应用名称',
|
label: '应用名称',
|
||||||
|
@ -23,8 +26,26 @@ const schemas = ref<FormSchema[]>([
|
||||||
},
|
},
|
||||||
componentProps: {
|
componentProps: {
|
||||||
placeholder: '请输入应用名称'
|
placeholder: '请输入应用名称'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'modelId',
|
||||||
|
label: '关联模型',
|
||||||
|
component: 'Input',
|
||||||
|
formItemProps: {
|
||||||
|
rules: [{ required: true, message: '请选择关联模型' }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'cover',
|
||||||
|
label: '应用图标',
|
||||||
|
component: 'Input',
|
||||||
|
formItemProps: {
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入应用图标'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'description',
|
field: 'description',
|
||||||
|
@ -41,29 +62,6 @@ const schemas = ref<FormSchema[]>([
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
rows: 4,
|
rows: 4,
|
||||||
placeholder: '请输入应用描述'
|
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 () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
|
||||||
const form = formRef.value.getElFormRef()
|
const form = formRef.value.getElFormRef()
|
||||||
await form.validate()
|
await form.validate()
|
||||||
const values = formRef.value.formModel
|
const values = formRef.value.formModel
|
||||||
|
const Api = isEdit.value ? AppApi.updateApp : AppApi.createApp
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
values.modelId = values.modelId?.[1]
|
||||||
if (isEdit.value) {
|
await Api(values).finally(() => (loading.value = false))
|
||||||
await AppApi.updateApp(values)
|
ElMessage.success(isEdit.value ? '更新应用成功' : '创建应用成功')
|
||||||
ElMessage.success('更新应用成功')
|
|
||||||
} else {
|
|
||||||
await AppApi.createApp(values)
|
|
||||||
ElMessage.success('创建应用成功')
|
|
||||||
}
|
|
||||||
close()
|
close()
|
||||||
emit('reload')
|
emit('reload')
|
||||||
} catch (error) {
|
}
|
||||||
console.error('Failed to save app:', error)
|
const handleImport = (params: Record<any, any>) => {
|
||||||
ElMessage.error(isEdit.value ? '更新应用失败' : '创建应用失败')
|
return {
|
||||||
} finally {
|
data: params.data.url
|
||||||
loading.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show,
|
show,
|
||||||
close
|
close
|
||||||
|
@ -134,12 +125,14 @@ defineExpose({
|
||||||
width="500px"
|
width="500px"
|
||||||
@close="close"
|
@close="close"
|
||||||
>
|
>
|
||||||
<Form
|
<Form ref="formRef" :model="formData" :schema="schemas" v-loading="loading">
|
||||||
ref="formRef"
|
<template #modelId="scope">
|
||||||
:model="formData"
|
<ModelSelect :id="scope.modelId" class="w-full" v-model="scope.modelId" />
|
||||||
:schema="schemas"
|
</template>
|
||||||
v-loading="loading"
|
<template #cover="scoped">
|
||||||
/>
|
<UploadImg v-model="scoped.cover" :custom-api="OssApi.upload" :success-before="handleImport"/>
|
||||||
|
</template>
|
||||||
|
</Form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button type="primary" @click="handleSubmit" :loading="loading">确认</el-button>
|
<el-button type="primary" @click="handleSubmit" :loading="loading">确认</el-button>
|
||||||
<el-button @click="close" :loading="loading">取消</el-button>
|
<el-button @click="close" :loading="loading">取消</el-button>
|
||||||
|
@ -148,4 +141,24 @@ defineExpose({
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<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>
|
</style>
|
||||||
|
|
|
@ -20,30 +20,15 @@ const router = useRouter()
|
||||||
|
|
||||||
// 数据
|
// 数据
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableData = ref<TableDataItem[]>([
|
const tableData = ref<TableDataItem[]>([])
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: '',
|
|
||||||
description: '',
|
|
||||||
apiKey: '',
|
|
||||||
provider: '',
|
|
||||||
channel: '',
|
|
||||||
createTime: ''
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
try {
|
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const res = await AppApi.getAppList({})
|
const res = await AppApi.getAppList({}).finally(() => {
|
||||||
tableData.value = res.data || []
|
|
||||||
} catch (error) {
|
|
||||||
ElMessage.error('获取应用列表失败')
|
|
||||||
console.error('Failed to fetch app list:', error)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
})
|
||||||
|
tableData.value = res || []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增应用
|
// 新增应用
|
||||||
|
@ -86,7 +71,6 @@ const handleInfo = (record: any) => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadData()
|
loadData()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -109,15 +93,11 @@ onMounted(() => {
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col
|
<el-col
|
||||||
v-for="item in tableData.concat(tableData).concat(tableData).concat(tableData)"
|
v-for="item in tableData"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:span="6"
|
:span="6"
|
||||||
>
|
>
|
||||||
<el-card
|
<el-card class="app-card cursor-pointer w-full" shadow="hover" @click="handleInfo(item)">
|
||||||
class="app-card cursor-pointer w-full"
|
|
||||||
shadow="hover"
|
|
||||||
@click="handleInfo(item)"
|
|
||||||
>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="sm:mx-4">
|
<div class="sm:mx-4">
|
||||||
|
@ -135,7 +115,7 @@ onMounted(() => {
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="app-card-content flex justify-between items-center">
|
<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">
|
<el-dropdown trigger="hover" width="500px">
|
||||||
<div
|
<div
|
||||||
:class="[activeDropdownId === item.id ? 'bg-gray-200' : 'hover:bg-gray-200']"
|
: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="handleEdit(item)">编辑此应用</el-dropdown-item>
|
||||||
<el-dropdown-item @click.stop="handleDelete(item)">删除此应用</el-dropdown-item>
|
<el-dropdown-item @click.stop="handleDelete(item)">删除此应用</el-dropdown-item>
|
||||||
<el-dropdown-item disabled divided>
|
<el-dropdown-item disabled divided>
|
||||||
<div class='w-150px'>
|
<div class="w-150px">
|
||||||
<div>信息</div>
|
<div>信息</div>
|
||||||
<span class='text-xs text-stone-500'>
|
<span class="text-xs text-stone-500"> 创建时间:{{ item.createTime }} </span>
|
||||||
创建时间:{{item.createTime}}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
|
@ -162,6 +139,9 @@ onMounted(() => {
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col v-if="!tableData.length" :span="18">
|
||||||
|
<el-empty description="暂无数据"/>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<EditCom ref="editRef" @reload="loadData" />
|
<EditCom ref="editRef" @reload="loadData" />
|
||||||
|
|
|
@ -25,13 +25,11 @@
|
||||||
|
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const id = route.params.id;
|
const appId = route.params.id;
|
||||||
const data = await getAppInfo({
|
const data = await getAppInfo({ appId }).finally(() => {
|
||||||
appId: id,
|
loading.value = false
|
||||||
conversationId: null,
|
|
||||||
});
|
});
|
||||||
form.value = data;
|
form.value = data;
|
||||||
loading.value = false;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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" />
|
<Header title="AI聊天助手" @reload="fetch" />
|
||||||
<main ref="contentRef" class="flex-1 overflow-hidden overflow-y-auto">
|
<main ref="contentRef" class="flex-1 overflow-hidden overflow-y-auto">
|
||||||
<Chat />
|
<Chat />
|
||||||
|
@ -34,12 +34,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:v-deep(.n-tabs.n-tabs--top .n-tab-pane) {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
.chat-card {
|
.chat-card {
|
||||||
box-sizing: border-box;
|
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 {
|
main {
|
||||||
height: calc(100% - 60px);
|
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>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, toRaw } from 'vue'
|
import { onMounted, ref, toRaw } from 'vue'
|
||||||
|
@ -69,12 +55,12 @@ onMounted(async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
function onUpdate(val: any) {
|
function onUpdate(val: any) {
|
||||||
|
console.log(val, 'val')
|
||||||
const group = options.value.find(g => g.children.some(c => c.value === val))
|
const group = options.value.find(g => g.children.some(c => c.value === val))
|
||||||
if (!group) return
|
if (!group) return
|
||||||
|
console.log(group, 'group')
|
||||||
const option = group.children.find(c => c.value === val)
|
const option = group.children.find(c => c.value === val)
|
||||||
if (!option) return
|
if (!option) return
|
||||||
|
|
||||||
const obj = option.raw
|
const obj = option.raw
|
||||||
emit('update', {
|
emit('update', {
|
||||||
id: obj.id,
|
id: obj.id,
|
||||||
|
@ -88,16 +74,19 @@ function onUpdate(val: any) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-cascader
|
<el-select
|
||||||
v-model="modelId"
|
v-model="modelId"
|
||||||
:options="options"
|
|
||||||
:size="size"
|
:size="size"
|
||||||
:props="{
|
:props="{
|
||||||
expandTrigger: 'hover'
|
expandTrigger: 'hover'
|
||||||
}"
|
}"
|
||||||
placeholder="请选择关联模型"
|
placeholder="请选择关联模型"
|
||||||
@change="onUpdate"
|
@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>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
Loading…
Reference in New Issue