mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 08:22:41 +08:00
Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into dev
This commit is contained in:
commit
15f5963e68
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 32 KiB |
@ -20,6 +20,6 @@ export const deleteWorkflow = async (id) => {
|
|||||||
return await request.delete({ url: '/ai/workflow/delete?id=' + id })
|
return await request.delete({ url: '/ai/workflow/delete?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateWorkflowModel = async (data) => {
|
export const testWorkflow = async (data) => {
|
||||||
return await request.put({ url: '/ai/workflow/updateWorkflowModel', data })
|
return await request.post({ url: '/ai/workflow/test', data })
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,11 @@ export const getTenant = (id: number) => {
|
|||||||
return request.get({ url: '/system/tenant/get?id=' + id })
|
return request.get({ url: '/system/tenant/get?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取租户精简信息列表
|
||||||
|
export const getTenantList = () => {
|
||||||
|
return request.get({ url: '/system/tenant/simple-list' })
|
||||||
|
}
|
||||||
|
|
||||||
// 新增租户
|
// 新增租户
|
||||||
export const createTenant = (data: TenantVO) => {
|
export const createTenant = (data: TenantVO) => {
|
||||||
return request.post({ url: '/system/tenant/create', data })
|
return request.post({ url: '/system/tenant/create', data })
|
||||||
|
@ -91,7 +91,7 @@ const dialogStyle = computed(() => {
|
|||||||
icon="ep:close"
|
icon="ep:close"
|
||||||
hover-color="var(--el-color-primary)"
|
hover-color="var(--el-color-primary)"
|
||||||
color="var(--el-color-info)"
|
color="var(--el-color-info)"
|
||||||
@click="close"
|
@click.stop="close"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -237,7 +237,7 @@ const props = defineProps({
|
|||||||
const prefix = inject('prefix')
|
const prefix = inject('prefix')
|
||||||
const width = inject('width')
|
const width = inject('width')
|
||||||
|
|
||||||
const formKey = ref('')
|
const formKey = ref(undefined)
|
||||||
const businessKey = ref('')
|
const businessKey = ref('')
|
||||||
const optionModelTitle = ref('')
|
const optionModelTitle = ref('')
|
||||||
const fieldList = ref<any[]>([])
|
const fieldList = ref<any[]>([])
|
||||||
@ -462,6 +462,7 @@ const updateElementExtensions = () => {
|
|||||||
const formList = ref([]) // 流程表单的下拉框的数据
|
const formList = ref([]) // 流程表单的下拉框的数据
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
formList.value = await FormApi.getFormSimpleList()
|
formList.value = await FormApi.getFormSimpleList()
|
||||||
|
formKey.value = parseInt(formKey.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -3,7 +3,14 @@ import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestCo
|
|||||||
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
import { config } from '@/config/axios/config'
|
import { config } from '@/config/axios/config'
|
||||||
import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
|
import {
|
||||||
|
getAccessToken,
|
||||||
|
getRefreshToken,
|
||||||
|
getTenantId,
|
||||||
|
getVisitTenantId,
|
||||||
|
removeToken,
|
||||||
|
setToken
|
||||||
|
} from '@/utils/auth'
|
||||||
import errorCode from './errorCode'
|
import errorCode from './errorCode'
|
||||||
|
|
||||||
import { resetRouter } from '@/router'
|
import { resetRouter } from '@/router'
|
||||||
@ -24,7 +31,7 @@ export const isRelogin = { show: false }
|
|||||||
let requestList: any[] = []
|
let requestList: any[] = []
|
||||||
// 是否正在刷新中
|
// 是否正在刷新中
|
||||||
let isRefreshToken = false
|
let isRefreshToken = false
|
||||||
// 请求白名单,无须token的接口
|
// 请求白名单,无须 token 的接口
|
||||||
const whiteList: string[] = ['/login', '/refresh-token']
|
const whiteList: string[] = ['/login', '/refresh-token']
|
||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
@ -55,6 +62,11 @@ service.interceptors.request.use(
|
|||||||
if (tenantEnable && tenantEnable === 'true') {
|
if (tenantEnable && tenantEnable === 'true') {
|
||||||
const tenantId = getTenantId()
|
const tenantId = getTenantId()
|
||||||
if (tenantId) config.headers['tenant-id'] = tenantId
|
if (tenantId) config.headers['tenant-id'] = tenantId
|
||||||
|
// 只有登录时,才设置 visit-tenant-id 访问租户
|
||||||
|
const visitTenantId = getVisitTenantId()
|
||||||
|
if (config.headers.Authorization && visitTenantId) {
|
||||||
|
config.headers['visit-tenant-id'] = visitTenantId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const method = config.method?.toUpperCase()
|
const method = config.method?.toUpperCase()
|
||||||
// 防止 GET 请求缓存
|
// 防止 GET 请求缓存
|
||||||
|
@ -10,6 +10,7 @@ export const CACHE_KEY = {
|
|||||||
// 用户相关
|
// 用户相关
|
||||||
ROLE_ROUTERS: 'roleRouters',
|
ROLE_ROUTERS: 'roleRouters',
|
||||||
USER: 'user',
|
USER: 'user',
|
||||||
|
VisitTenantId: 'visitTenantId',
|
||||||
// 系统设置
|
// 系统设置
|
||||||
IS_DARK: 'isDark',
|
IS_DARK: 'isDark',
|
||||||
LANG: 'lang',
|
LANG: 'lang',
|
||||||
@ -35,5 +36,6 @@ export const deleteUserCache = () => {
|
|||||||
const { wsCache } = useCache()
|
const { wsCache } = useCache()
|
||||||
wsCache.delete(CACHE_KEY.USER)
|
wsCache.delete(CACHE_KEY.USER)
|
||||||
wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
|
wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
|
||||||
|
wsCache.delete(CACHE_KEY.VisitTenantId)
|
||||||
// 注意,不要清理 LoginForm 登录表单
|
// 注意,不要清理 LoginForm 登录表单
|
||||||
}
|
}
|
||||||
|
46
src/layout/components/TenantVisit/index.vue
Normal file
46
src/layout/components/TenantVisit/index.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-select
|
||||||
|
filterable
|
||||||
|
placeholder="请选择租户"
|
||||||
|
class="!w-180px"
|
||||||
|
v-model="value"
|
||||||
|
@change="handleChange"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option v-for="item in tenants" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import * as TenantApi from '@/api/system/tenant'
|
||||||
|
import { getVisitTenantId, setVisitTenantId } from '@/utils/auth'
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import { useTagsView } from '@/hooks/web/useTagsView'
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const tagsView = useTagsView() // 标签页操作
|
||||||
|
|
||||||
|
const value = ref(getVisitTenantId()) // 当前选中的租户 ID
|
||||||
|
const tenants = ref<any[]>([]) // 租户列表
|
||||||
|
|
||||||
|
const handleChange = (id: number) => {
|
||||||
|
// 设置访问租户 ID
|
||||||
|
setVisitTenantId(id)
|
||||||
|
// 关闭其他标签页,只保留当前页
|
||||||
|
tagsView.closeOther()
|
||||||
|
// 刷新当前页面
|
||||||
|
tagsView.refreshPage()
|
||||||
|
// 提示切换成功
|
||||||
|
const tenant = tenants.value.find((item) => item.id === id)
|
||||||
|
if (tenant) {
|
||||||
|
message.success(`切换当前租户为: ${tenant.name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
tenants.value = await TenantApi.getTenantList()
|
||||||
|
})
|
||||||
|
</script>
|
@ -8,8 +8,10 @@ import { Breadcrumb } from '@/layout/components/Breadcrumb'
|
|||||||
import { SizeDropdown } from '@/layout/components/SizeDropdown'
|
import { SizeDropdown } from '@/layout/components/SizeDropdown'
|
||||||
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
||||||
import RouterSearch from '@/components/RouterSearch/index.vue'
|
import RouterSearch from '@/components/RouterSearch/index.vue'
|
||||||
|
import TenantVisit from '@/layout/components/TenantVisit/index.vue'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
import { useDesign } from '@/hooks/web/useDesign'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
|
import { checkPermi } from '@/utils/permission'
|
||||||
|
|
||||||
const { getPrefixCls, variables } = useDesign()
|
const { getPrefixCls, variables } = useDesign()
|
||||||
|
|
||||||
@ -41,6 +43,11 @@ const locale = computed(() => appStore.getLocale)
|
|||||||
// 消息图标
|
// 消息图标
|
||||||
const message = computed(() => appStore.getMessage)
|
const message = computed(() => appStore.getMessage)
|
||||||
|
|
||||||
|
// 租户切换权限
|
||||||
|
const hasTenantVisitPermission = computed(
|
||||||
|
() => import.meta.env.VITE_APP_TENANT_ENABLE === 'true' && checkPermi(['system:tenant:visit'])
|
||||||
|
)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ToolHeader',
|
name: 'ToolHeader',
|
||||||
setup() {
|
setup() {
|
||||||
@ -62,6 +69,7 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
<div class="h-full flex items-center">
|
<div class="h-full flex items-center">
|
||||||
|
{hasTenantVisitPermission.value ? <TenantVisit /> : undefined}
|
||||||
{screenfull.value ? (
|
{screenfull.value ? (
|
||||||
<Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
|
<Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
|
@ -67,6 +67,14 @@ export const getTenantId = () => {
|
|||||||
return wsCache.get(CACHE_KEY.TenantId)
|
return wsCache.get(CACHE_KEY.TenantId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setTenantId = (username: string) => {
|
export const setTenantId = (tenantId: number) => {
|
||||||
wsCache.set(CACHE_KEY.TenantId, username)
|
wsCache.set(CACHE_KEY.TenantId, tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getVisitTenantId = () => {
|
||||||
|
return wsCache.get(CACHE_KEY.VisitTenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setVisitTenantId = (visitTenantId: number) => {
|
||||||
|
wsCache.set(CACHE_KEY.VisitTenantId, visitTenantId)
|
||||||
}
|
}
|
||||||
|
@ -462,6 +462,8 @@ const doSendMessageStream = async (userMessage: ChatMessageVO) => {
|
|||||||
(error) => {
|
(error) => {
|
||||||
message.alert(`对话异常! ${error}`)
|
message.alert(`对话异常! ${error}`)
|
||||||
stopStream()
|
stopStream()
|
||||||
|
// 需要抛出异常,禁止重试
|
||||||
|
throw error
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
stopStream()
|
stopStream()
|
||||||
|
@ -80,6 +80,8 @@ const submit = (data: AiMindMapGenerateReqVO) => {
|
|||||||
onError(err) {
|
onError(err) {
|
||||||
console.error('生成思维导图失败', err)
|
console.error('生成思维导图失败', err)
|
||||||
stopStream()
|
stopStream()
|
||||||
|
// 需要抛出异常,禁止重试
|
||||||
|
throw error
|
||||||
},
|
},
|
||||||
ctrl: ctrl.value
|
ctrl: ctrl.value
|
||||||
})
|
})
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="120px"
|
label-width="130px"
|
||||||
v-loading="formLoading"
|
v-loading="formLoading"
|
||||||
>
|
>
|
||||||
<el-form-item label="所属平台" prop="platform">
|
<el-form-item label="所属平台" prop="platform">
|
||||||
@ -146,7 +146,10 @@ const formRules = reactive({
|
|||||||
platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }],
|
platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }],
|
||||||
type: [{ required: true, message: '模型类型不能为空', trigger: 'blur' }],
|
type: [{ required: true, message: '模型类型不能为空', trigger: 'blur' }],
|
||||||
sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
|
sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
|
||||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||||
|
temperature: [{ required: true, message: '温度参数不能为空', trigger: 'blur' }],
|
||||||
|
maxTokens: [{ required: true, message: '回复数 Token 数不能为空', trigger: 'blur' }],
|
||||||
|
maxContexts: [{ required: true, message: '上下文数量不能为空', trigger: 'blur' }]
|
||||||
})
|
})
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表
|
const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表
|
||||||
|
@ -13,11 +13,58 @@
|
|||||||
测试
|
测试
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 测试窗口 -->
|
||||||
|
<el-drawer v-model="showTestDrawer" title="工作流测试" :modal="false">
|
||||||
|
<fieldset>
|
||||||
|
<legend class="ml-15px"><h3>运行参数配置</h3></legend>
|
||||||
|
<div class="p-20px">
|
||||||
|
<div
|
||||||
|
class="flex justify-around mb-10px"
|
||||||
|
v-for="(param, index) in params4Test"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<el-select class="w-200px!" v-model="param.key" placeholder="参数名">
|
||||||
|
<el-option
|
||||||
|
v-for="(value, key) in paramsOfStartNode"
|
||||||
|
:key="key"
|
||||||
|
:label="value?.description || key"
|
||||||
|
:value="key"
|
||||||
|
:disabled="!!value?.disabled"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input class="w-200px!" v-model="param.value" placeholder="参数值" />
|
||||||
|
<el-button type="danger" plain :icon="Delete" circle @click="removeParam(index)" />
|
||||||
|
</div>
|
||||||
|
<!-- TODO @lesan:是不是不用添加和删除参数,直接把必填和选填列出来,然后加上参数校验? -->
|
||||||
|
<el-button type="primary" plain @click="addParam">添加参数</el-button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="mt-20px bg-#f8f9fa">
|
||||||
|
<legend class="ml-15px"><h3>运行结果</h3></legend>
|
||||||
|
<div class="p-20px">
|
||||||
|
<div v-if="loading"> <el-text type="primary">执行中...</el-text></div>
|
||||||
|
<div v-else-if="error">
|
||||||
|
<el-text type="danger">{{ error }}</el-text>
|
||||||
|
</div>
|
||||||
|
<pre v-else-if="testResult" class="result-content"
|
||||||
|
>{{ JSON.stringify(testResult, null, 2) }}
|
||||||
|
</pre>
|
||||||
|
<div v-else> <el-text type="info">点击运行查看结果</el-text> </div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<el-button class="mt-20px w-100%" size="large" type="success" @click="goRun">
|
||||||
|
运行流程
|
||||||
|
</el-button>
|
||||||
|
</el-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Tinyflow from '@/components/Tinyflow/Tinyflow.vue'
|
import Tinyflow from '@/components/Tinyflow/Tinyflow.vue'
|
||||||
|
import * as WorkflowApi from '@/api/ai/workflow'
|
||||||
|
// TODO @lesan:要不使用 ICon 哪个组件哈
|
||||||
|
import { Delete } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
provider: any
|
provider: any
|
||||||
@ -25,9 +72,149 @@ defineProps<{
|
|||||||
|
|
||||||
const tinyflowRef = ref()
|
const tinyflowRef = ref()
|
||||||
const workflowData = inject('workflowData') as Ref
|
const workflowData = inject('workflowData') as Ref
|
||||||
|
const showTestDrawer = ref(false)
|
||||||
|
const params4Test = ref([])
|
||||||
|
const paramsOfStartNode = ref({})
|
||||||
|
const testResult = ref(null)
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref(null)
|
||||||
|
|
||||||
|
/** 展示工作流测试抽屉 */
|
||||||
const testWorkflowModel = () => {
|
const testWorkflowModel = () => {
|
||||||
// TODO @lesan 测试
|
showTestDrawer.value = !showTestDrawer.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 运行流程 */
|
||||||
|
const goRun = async () => {
|
||||||
|
try {
|
||||||
|
const val = tinyflowRef.value.getData()
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
testResult.value = null
|
||||||
|
/// 查找start节点
|
||||||
|
const startNode = getStartNode()
|
||||||
|
|
||||||
|
// 获取参数定义
|
||||||
|
const parameters = startNode.data?.parameters || []
|
||||||
|
const paramDefinitions = {}
|
||||||
|
parameters.forEach((param) => {
|
||||||
|
paramDefinitions[param.name] = param.dataType
|
||||||
|
})
|
||||||
|
|
||||||
|
// 参数类型转换
|
||||||
|
const convertedParams = {}
|
||||||
|
for (const { key, value } of params4Test.value) {
|
||||||
|
const paramKey = key.trim()
|
||||||
|
if (!paramKey) continue
|
||||||
|
|
||||||
|
let dataType = paramDefinitions[paramKey]
|
||||||
|
if (!dataType) {
|
||||||
|
dataType = 'String'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
convertedParams[paramKey] = convertParamValue(value, dataType)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`参数 ${paramKey} 转换失败: ${e.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
graph: JSON.stringify(val),
|
||||||
|
params: convertedParams
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await WorkflowApi.testWorkflow(data)
|
||||||
|
testResult.value = response
|
||||||
|
} catch (err) {
|
||||||
|
error.value = err.response?.data?.message || '运行失败,请检查参数和网络连接'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监听测试抽屉的开启,获取开始节点参数列表 */
|
||||||
|
watch(showTestDrawer, (value) => {
|
||||||
|
if (!value) return
|
||||||
|
|
||||||
|
/// 查找start节点
|
||||||
|
const startNode = getStartNode()
|
||||||
|
|
||||||
|
// 获取参数定义
|
||||||
|
const parameters = startNode.data?.parameters || []
|
||||||
|
const paramDefinitions = {}
|
||||||
|
|
||||||
|
// 加入参数选项方便用户添加非必须参数
|
||||||
|
parameters.forEach((param) => {
|
||||||
|
paramDefinitions[param.name] = param
|
||||||
|
})
|
||||||
|
|
||||||
|
function mergeIfRequiredButNotSet(target) {
|
||||||
|
let needPushList = []
|
||||||
|
for (let key in paramDefinitions) {
|
||||||
|
let param = paramDefinitions[key]
|
||||||
|
|
||||||
|
if (param.required) {
|
||||||
|
let item = target.find((item) => item.key === key)
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
needPushList.push({ key: param.name, value: param.defaultValue || '' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target.push(...needPushList)
|
||||||
|
}
|
||||||
|
// 自动装载需必填的参数
|
||||||
|
mergeIfRequiredButNotSet(params4Test.value)
|
||||||
|
|
||||||
|
paramsOfStartNode.value = paramDefinitions
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 获取开始节点 */
|
||||||
|
const getStartNode = () => {
|
||||||
|
const val = tinyflowRef.value.getData()
|
||||||
|
const startNode = val.nodes.find((node) => node.type === 'startNode')
|
||||||
|
if (!startNode) {
|
||||||
|
throw new Error('流程缺少开始节点')
|
||||||
|
}
|
||||||
|
return startNode
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加参数项 */
|
||||||
|
const addParam = () => {
|
||||||
|
params4Test.value.push({ key: '', value: '' })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除参数项 */
|
||||||
|
const removeParam = (index) => {
|
||||||
|
params4Test.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 类型转换函数 */
|
||||||
|
const convertParamValue = (value, dataType) => {
|
||||||
|
if (value === '') return null // 空值处理
|
||||||
|
|
||||||
|
switch (dataType) {
|
||||||
|
case 'String':
|
||||||
|
return String(value)
|
||||||
|
case 'Number':
|
||||||
|
const num = Number(value)
|
||||||
|
if (isNaN(num)) throw new Error('非数字格式')
|
||||||
|
return num
|
||||||
|
case 'Boolean':
|
||||||
|
if (value.toLowerCase() === 'true') return true
|
||||||
|
if (value.toLowerCase() === 'false') return false
|
||||||
|
throw new Error('必须为 true/false')
|
||||||
|
case 'Object':
|
||||||
|
case 'Array':
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`JSON格式错误: ${e.message}`)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`不支持的类型: ${dataType}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 表单校验 */
|
/** 表单校验 */
|
||||||
@ -47,3 +234,17 @@ defineExpose({
|
|||||||
validate
|
validate
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.result-content {
|
||||||
|
background: white;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
font-family: Monaco, Consolas, monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
<WorkflowDesign
|
<WorkflowDesign
|
||||||
v-if="currentStep === 1"
|
v-if="currentStep === 1"
|
||||||
v-model="formData"
|
v-model="formData"
|
||||||
:provider="provider"
|
:provider="llmProvider"
|
||||||
ref="workflowDesignRef"
|
ref="workflowDesignRef"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -73,7 +73,8 @@ import { CommonStatusEnum } from '@/utils/constants'
|
|||||||
import * as WorkflowApi from '@/api/ai/workflow'
|
import * as WorkflowApi from '@/api/ai/workflow'
|
||||||
import BasicInfo from './BasicInfo.vue'
|
import BasicInfo from './BasicInfo.vue'
|
||||||
import WorkflowDesign from './WorkflowDesign.vue'
|
import WorkflowDesign from './WorkflowDesign.vue'
|
||||||
import { ApiKeyApi } from '@/api/ai/model/apiKey'
|
import { ModelApi } from '@/api/ai/model/model'
|
||||||
|
import { AiModelTypeEnum } from '@/views/ai/utils/constants'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { delView } = useTagsViewStore()
|
const { delView } = useTagsViewStore()
|
||||||
@ -104,31 +105,35 @@ const formData: any = ref({
|
|||||||
graph: '',
|
graph: '',
|
||||||
status: CommonStatusEnum.ENABLE
|
status: CommonStatusEnum.ENABLE
|
||||||
})
|
})
|
||||||
// TODO @lesan:待接入
|
const llmProvider = ref<any>([])
|
||||||
const provider = ref<any>()
|
|
||||||
const workflowData = ref<any>({})
|
const workflowData = ref<any>({})
|
||||||
provide('workflowData', workflowData)
|
provide('workflowData', workflowData)
|
||||||
|
|
||||||
/** 初始化数据 */
|
/** 初始化数据 */
|
||||||
const actionType = route.params.type as string
|
const actionType = route.params.type as string
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
|
// 编辑情况下,需要加载工作流配置
|
||||||
if (actionType === 'update') {
|
if (actionType === 'update') {
|
||||||
const workflowId = route.params.id as string
|
const workflowId = route.params.id as string
|
||||||
formData.value = await WorkflowApi.getWorkflow(workflowId)
|
formData.value = await WorkflowApi.getWorkflow(workflowId)
|
||||||
workflowData.value = JSON.parse(formData.value.graph)
|
workflowData.value = JSON.parse(formData.value.graph)
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiKeys = await ApiKeyApi.getApiKeySimpleList()
|
// 加载模型列表
|
||||||
provider.value = {
|
const models = await ModelApi.getModelSimpleList(AiModelTypeEnum.CHAT)
|
||||||
|
llmProvider.value = {
|
||||||
llm: () =>
|
llm: () =>
|
||||||
apiKeys.map(({ id, name }) => ({
|
models.map(({ id, name }) => ({
|
||||||
value: id,
|
value: id,
|
||||||
label: name
|
label: name
|
||||||
})),
|
})),
|
||||||
knowledge: () => [],
|
knowledge: () => [],
|
||||||
internal: () => []
|
internal: () => []
|
||||||
}
|
}
|
||||||
|
// TODO @lesan:知识库(可以看下 knowledge)
|
||||||
|
// TODO @lesan:搜索引擎(这个之前有个 pr 搞了,,,可能来接下)
|
||||||
|
|
||||||
|
// 设置当前步骤
|
||||||
currentStep.value = 0
|
currentStep.value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,17 +169,17 @@ const handleSave = async () => {
|
|||||||
|
|
||||||
// 更新表单数据
|
// 更新表单数据
|
||||||
const data = {
|
const data = {
|
||||||
...formData.value
|
...formData.value,
|
||||||
|
graph: JSON.stringify(workflowData.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
data.graph = JSON.stringify(workflowData.value)
|
|
||||||
|
|
||||||
if (actionType === 'update') {
|
if (actionType === 'update') {
|
||||||
await WorkflowApi.updateWorkflow(data)
|
await WorkflowApi.updateWorkflow(data)
|
||||||
} else {
|
} else {
|
||||||
await WorkflowApi.createWorkflow(data)
|
await WorkflowApi.createWorkflow(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存成功,提示并跳转到列表页
|
||||||
|
message.success('保存成功')
|
||||||
delView(unref(router.currentRoute))
|
delView(unref(router.currentRoute))
|
||||||
await router.push({ name: 'AiWorkflow' })
|
await router.push({ name: 'AiWorkflow' })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
@ -57,9 +57,11 @@ const submit = (data: WriteVO) => {
|
|||||||
},
|
},
|
||||||
ctrl: abortController.value,
|
ctrl: abortController.value,
|
||||||
onClose: stopStream,
|
onClose: stopStream,
|
||||||
onError: (...err) => {
|
onError: (error) => {
|
||||||
console.error('写作异常', ...err)
|
console.error('写作异常', error)
|
||||||
stopStream()
|
stopStream()
|
||||||
|
// 需要抛出异常,禁止重试
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import type { UploadProps, UploadRawFile } from 'element-plus'
|
import type { UploadProps, UploadRawFile } from 'element-plus'
|
||||||
import { getAccessToken } from '@/utils/auth'
|
import { getRefreshToken } from '@/utils/auth'
|
||||||
import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload'
|
import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload'
|
||||||
|
|
||||||
const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 请求头
|
const HEADERS = { Authorization: 'Bearer ' + getRefreshToken() } // 请求头(解决 el-upload 上传过程中,无法刷新令牌的问题)
|
||||||
const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传地址
|
const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传地址
|
||||||
|
|
||||||
interface UploadData {
|
interface UploadData {
|
||||||
|
@ -71,11 +71,12 @@
|
|||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.keyContent"
|
v-model="formData.config.keyContent"
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
placeholder="请上传 apiclient_cert.p12 证书"
|
placeholder="请上传 apiclient_cert.p12 证书"
|
||||||
readonly
|
readonly
|
||||||
type="textarea"
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="" label-width="180px">
|
<el-form-item label="" label-width="180px">
|
||||||
@ -108,11 +109,12 @@
|
|||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.privateKeyContent"
|
v-model="formData.config.privateKeyContent"
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
placeholder="请上传 apiclient_key.pem 证书"
|
placeholder="请上传 apiclient_key.pem 证书"
|
||||||
readonly
|
readonly
|
||||||
type="textarea"
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="" label-width="180px" prop="privateKeyContentFile">
|
<el-form-item label="" label-width="180px" prop="privateKeyContentFile">
|
||||||
@ -145,6 +147,47 @@
|
|||||||
前往微信商户平台查看证书序列号
|
前往微信商户平台查看证书序列号
|
||||||
</a>
|
</a>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="public_key.pem 证书" label-width="180px" prop="config.publicKeyContent">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.config.publicKeyContent"
|
||||||
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请上传 public_key.pem 证书"
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" label-width="180px" prop="publicKeyContentFile">
|
||||||
|
<el-upload
|
||||||
|
ref="publicKeyContentFile"
|
||||||
|
:before-upload="pemFileBeforeUpload"
|
||||||
|
:http-request="publicKeyContentUpload"
|
||||||
|
:limit="1"
|
||||||
|
accept=".pem"
|
||||||
|
action=""
|
||||||
|
>
|
||||||
|
<el-button type="primary">
|
||||||
|
<Icon class="mr-5px" icon="ep:upload" />
|
||||||
|
点击上传
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="公钥 ID" label-width="180px" prop="config.publicKeyId">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.config.publicKeyId"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入公钥 ID"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label-width="180px">
|
||||||
|
<a
|
||||||
|
href="https://pay.weixin.qq.com/doc/v3/merchant/4012153196"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
微信支付公钥产品简介及使用说明
|
||||||
|
</a>
|
||||||
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label="备注" label-width="180px" prop="remark">
|
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||||
@ -184,7 +227,9 @@ const formData = ref<any>({
|
|||||||
keyContent: '',
|
keyContent: '',
|
||||||
privateKeyContent: '',
|
privateKeyContent: '',
|
||||||
certSerialNo: '',
|
certSerialNo: '',
|
||||||
apiV3Key: ''
|
apiV3Key: '',
|
||||||
|
publicKeyContent: '',
|
||||||
|
publicKeyId: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const formRules = {
|
const formRules = {
|
||||||
@ -201,6 +246,8 @@ const formRules = {
|
|||||||
{ required: true, message: '请上传 apiclient_key.pem 证书', trigger: 'blur' }
|
{ required: true, message: '请上传 apiclient_key.pem 证书', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
'config.certSerialNo': [{ required: true, message: '请输入证书序列号', trigger: 'blur' }],
|
'config.certSerialNo': [{ required: true, message: '请输入证书序列号', trigger: 'blur' }],
|
||||||
|
'config.publicKeyContent': [{ required: true, message: '请上传 public_key.pem 证书', trigger: 'blur' }],
|
||||||
|
'config.publicKeyId': [{ required: true, message: '请输入公钥 ID', trigger: 'blur' }],
|
||||||
'config.apiV3Key': [{ required: true, message: '请上传 api V3 密钥值', trigger: 'blur' }]
|
'config.apiV3Key': [{ required: true, message: '请上传 api V3 密钥值', trigger: 'blur' }]
|
||||||
}
|
}
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
@ -267,7 +314,9 @@ const resetForm = (appId, code) => {
|
|||||||
keyContent: '',
|
keyContent: '',
|
||||||
privateKeyContent: '',
|
privateKeyContent: '',
|
||||||
certSerialNo: '',
|
certSerialNo: '',
|
||||||
apiV3Key: ''
|
apiV3Key: '',
|
||||||
|
publicKeyContent: '',
|
||||||
|
publicKeyId: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
@ -318,4 +367,15 @@ const keyContentUpload = async (event) => {
|
|||||||
}
|
}
|
||||||
readFile.readAsDataURL(event.file) // 读成 base64
|
readFile.readAsDataURL(event.file) // 读成 base64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取 public_key.pem 到 publicKeyContent 字段
|
||||||
|
*/
|
||||||
|
const publicKeyContentUpload = async (event) => {
|
||||||
|
const readFile = new FileReader()
|
||||||
|
readFile.onload = (e: any) => {
|
||||||
|
formData.value.config.publicKeyContent = e.target.result
|
||||||
|
}
|
||||||
|
readFile.readAsText(event.file)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { getAccessToken, getRefreshToken } from '@/utils/auth'
|
||||||
|
|
||||||
defineOptions({ name: 'GoView' })
|
defineOptions({ name: 'GoView' })
|
||||||
|
|
||||||
const src = ref(import.meta.env.VITE_GOVIEW_URL)
|
const src = ref(
|
||||||
|
`${import.meta.env.VITE_GOVIEW_URL}?accessToken=${getAccessToken()}&refreshToken=${getRefreshToken()}`
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
15
src/views/report/jmreport/bi.vue
Normal file
15
src/views/report/jmreport/bi.vue
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<doc-alert title="大屏设计器" url="https://doc.iocoder.cn/screen/" />
|
||||||
|
|
||||||
|
<ContentWrap :bodyStyle="{ padding: '0px' }" class="!mb-0">
|
||||||
|
<IFrame :src="src" />
|
||||||
|
</ContentWrap>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getRefreshToken } from '@/utils/auth'
|
||||||
|
|
||||||
|
defineOptions({ name: 'JimuBI' })
|
||||||
|
|
||||||
|
// 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:积木报表无法方便的刷新访问令牌
|
||||||
|
const src = ref(import.meta.env.VITE_BASE_URL + '/drag/list?token=' + getRefreshToken())
|
||||||
|
</script>
|
@ -37,7 +37,6 @@ ${selector}:before {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 1px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--el-border-color);
|
background-color: var(--el-border-color);
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user