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 })
|
||||
}
|
||||
|
||||
export const updateWorkflowModel = async (data) => {
|
||||
return await request.put({ url: '/ai/workflow/updateWorkflowModel', data })
|
||||
export const testWorkflow = async (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 })
|
||||
}
|
||||
|
||||
// 获取租户精简信息列表
|
||||
export const getTenantList = () => {
|
||||
return request.get({ url: '/system/tenant/simple-list' })
|
||||
}
|
||||
|
||||
// 新增租户
|
||||
export const createTenant = (data: TenantVO) => {
|
||||
return request.post({ url: '/system/tenant/create', data })
|
||||
|
@ -91,7 +91,7 @@ const dialogStyle = computed(() => {
|
||||
icon="ep:close"
|
||||
hover-color="var(--el-color-primary)"
|
||||
color="var(--el-color-info)"
|
||||
@click="close"
|
||||
@click.stop="close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -237,7 +237,7 @@ const props = defineProps({
|
||||
const prefix = inject('prefix')
|
||||
const width = inject('width')
|
||||
|
||||
const formKey = ref('')
|
||||
const formKey = ref(undefined)
|
||||
const businessKey = ref('')
|
||||
const optionModelTitle = ref('')
|
||||
const fieldList = ref<any[]>([])
|
||||
@ -462,6 +462,7 @@ const updateElementExtensions = () => {
|
||||
const formList = ref([]) // 流程表单的下拉框的数据
|
||||
onMounted(async () => {
|
||||
formList.value = await FormApi.getFormSimpleList()
|
||||
formKey.value = parseInt(formKey.value)
|
||||
})
|
||||
|
||||
watch(
|
||||
|
@ -3,7 +3,14 @@ import axios, { AxiosError, AxiosInstance, AxiosResponse, InternalAxiosRequestCo
|
||||
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
||||
import qs from 'qs'
|
||||
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 { resetRouter } from '@/router'
|
||||
@ -24,7 +31,7 @@ export const isRelogin = { show: false }
|
||||
let requestList: any[] = []
|
||||
// 是否正在刷新中
|
||||
let isRefreshToken = false
|
||||
// 请求白名单,无须token的接口
|
||||
// 请求白名单,无须 token 的接口
|
||||
const whiteList: string[] = ['/login', '/refresh-token']
|
||||
|
||||
// 创建axios实例
|
||||
@ -55,6 +62,11 @@ service.interceptors.request.use(
|
||||
if (tenantEnable && tenantEnable === 'true') {
|
||||
const tenantId = getTenantId()
|
||||
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()
|
||||
// 防止 GET 请求缓存
|
||||
|
@ -10,6 +10,7 @@ export const CACHE_KEY = {
|
||||
// 用户相关
|
||||
ROLE_ROUTERS: 'roleRouters',
|
||||
USER: 'user',
|
||||
VisitTenantId: 'visitTenantId',
|
||||
// 系统设置
|
||||
IS_DARK: 'isDark',
|
||||
LANG: 'lang',
|
||||
@ -35,5 +36,6 @@ export const deleteUserCache = () => {
|
||||
const { wsCache } = useCache()
|
||||
wsCache.delete(CACHE_KEY.USER)
|
||||
wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
|
||||
wsCache.delete(CACHE_KEY.VisitTenantId)
|
||||
// 注意,不要清理 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 { LocaleDropdown } from '@/layout/components/LocaleDropdown'
|
||||
import RouterSearch from '@/components/RouterSearch/index.vue'
|
||||
import TenantVisit from '@/layout/components/TenantVisit/index.vue'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
|
||||
const { getPrefixCls, variables } = useDesign()
|
||||
|
||||
@ -41,6 +43,11 @@ const locale = computed(() => appStore.getLocale)
|
||||
// 消息图标
|
||||
const message = computed(() => appStore.getMessage)
|
||||
|
||||
// 租户切换权限
|
||||
const hasTenantVisitPermission = computed(
|
||||
() => import.meta.env.VITE_APP_TENANT_ENABLE === 'true' && checkPermi(['system:tenant:visit'])
|
||||
)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ToolHeader',
|
||||
setup() {
|
||||
@ -62,6 +69,7 @@ export default defineComponent({
|
||||
</div>
|
||||
) : undefined}
|
||||
<div class="h-full flex items-center">
|
||||
{hasTenantVisitPermission.value ? <TenantVisit /> : undefined}
|
||||
{screenfull.value ? (
|
||||
<Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
|
||||
) : undefined}
|
||||
|
@ -67,6 +67,14 @@ export const getTenantId = () => {
|
||||
return wsCache.get(CACHE_KEY.TenantId)
|
||||
}
|
||||
|
||||
export const setTenantId = (username: string) => {
|
||||
wsCache.set(CACHE_KEY.TenantId, username)
|
||||
export const setTenantId = (tenantId: number) => {
|
||||
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) => {
|
||||
message.alert(`对话异常! ${error}`)
|
||||
stopStream()
|
||||
// 需要抛出异常,禁止重试
|
||||
throw error
|
||||
},
|
||||
() => {
|
||||
stopStream()
|
||||
|
@ -80,6 +80,8 @@ const submit = (data: AiMindMapGenerateReqVO) => {
|
||||
onError(err) {
|
||||
console.error('生成思维导图失败', err)
|
||||
stopStream()
|
||||
// 需要抛出异常,禁止重试
|
||||
throw error
|
||||
},
|
||||
ctrl: ctrl.value
|
||||
})
|
||||
|
@ -4,7 +4,7 @@
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="120px"
|
||||
label-width="130px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="所属平台" prop="platform">
|
||||
@ -146,7 +146,10 @@ const formRules = reactive({
|
||||
platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }],
|
||||
type: [{ 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 apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表
|
||||
|
@ -13,11 +13,58 @@
|
||||
测试
|
||||
</el-button>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
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<{
|
||||
provider: any
|
||||
@ -25,9 +72,149 @@ defineProps<{
|
||||
|
||||
const tinyflowRef = 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 = () => {
|
||||
// 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
|
||||
})
|
||||
</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
|
||||
v-if="currentStep === 1"
|
||||
v-model="formData"
|
||||
:provider="provider"
|
||||
:provider="llmProvider"
|
||||
ref="workflowDesignRef"
|
||||
/>
|
||||
</div>
|
||||
@ -73,7 +73,8 @@ import { CommonStatusEnum } from '@/utils/constants'
|
||||
import * as WorkflowApi from '@/api/ai/workflow'
|
||||
import BasicInfo from './BasicInfo.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 { delView } = useTagsViewStore()
|
||||
@ -104,31 +105,35 @@ const formData: any = ref({
|
||||
graph: '',
|
||||
status: CommonStatusEnum.ENABLE
|
||||
})
|
||||
// TODO @lesan:待接入
|
||||
const provider = ref<any>()
|
||||
const llmProvider = ref<any>([])
|
||||
const workflowData = ref<any>({})
|
||||
provide('workflowData', workflowData)
|
||||
|
||||
/** 初始化数据 */
|
||||
const actionType = route.params.type as string
|
||||
const initData = async () => {
|
||||
// 编辑情况下,需要加载工作流配置
|
||||
if (actionType === 'update') {
|
||||
const workflowId = route.params.id as string
|
||||
formData.value = await WorkflowApi.getWorkflow(workflowId)
|
||||
workflowData.value = JSON.parse(formData.value.graph)
|
||||
}
|
||||
|
||||
const apiKeys = await ApiKeyApi.getApiKeySimpleList()
|
||||
provider.value = {
|
||||
// 加载模型列表
|
||||
const models = await ModelApi.getModelSimpleList(AiModelTypeEnum.CHAT)
|
||||
llmProvider.value = {
|
||||
llm: () =>
|
||||
apiKeys.map(({ id, name }) => ({
|
||||
models.map(({ id, name }) => ({
|
||||
value: id,
|
||||
label: name
|
||||
})),
|
||||
knowledge: () => [],
|
||||
internal: () => []
|
||||
}
|
||||
// TODO @lesan:知识库(可以看下 knowledge)
|
||||
// TODO @lesan:搜索引擎(这个之前有个 pr 搞了,,,可能来接下)
|
||||
|
||||
// 设置当前步骤
|
||||
currentStep.value = 0
|
||||
}
|
||||
|
||||
@ -164,17 +169,17 @@ const handleSave = async () => {
|
||||
|
||||
// 更新表单数据
|
||||
const data = {
|
||||
...formData.value
|
||||
...formData.value,
|
||||
graph: JSON.stringify(workflowData.value)
|
||||
}
|
||||
|
||||
data.graph = JSON.stringify(workflowData.value)
|
||||
|
||||
if (actionType === 'update') {
|
||||
await WorkflowApi.updateWorkflow(data)
|
||||
} else {
|
||||
await WorkflowApi.createWorkflow(data)
|
||||
}
|
||||
|
||||
// 保存成功,提示并跳转到列表页
|
||||
message.success('保存成功')
|
||||
delView(unref(router.currentRoute))
|
||||
await router.push({ name: 'AiWorkflow' })
|
||||
} catch (error: any) {
|
||||
|
@ -57,9 +57,11 @@ const submit = (data: WriteVO) => {
|
||||
},
|
||||
ctrl: abortController.value,
|
||||
onClose: stopStream,
|
||||
onError: (...err) => {
|
||||
console.error('写作异常', ...err)
|
||||
onError: (error) => {
|
||||
console.error('写作异常', error)
|
||||
stopStream()
|
||||
// 需要抛出异常,禁止重试
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
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'
|
||||
|
||||
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' // 上传地址
|
||||
|
||||
interface UploadData {
|
||||
|
@ -71,11 +71,12 @@
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.config.keyContent"
|
||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
:style="{ width: '100%' }"
|
||||
placeholder="请上传 apiclient_cert.p12 证书"
|
||||
readonly
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="" label-width="180px">
|
||||
@ -108,11 +109,12 @@
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.config.privateKeyContent"
|
||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
:style="{ width: '100%' }"
|
||||
placeholder="请上传 apiclient_key.pem 证书"
|
||||
readonly
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="" label-width="180px" prop="privateKeyContentFile">
|
||||
@ -145,6 +147,47 @@
|
||||
前往微信商户平台查看证书序列号
|
||||
</a>
|
||||
</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>
|
||||
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||
@ -184,7 +227,9 @@ const formData = ref<any>({
|
||||
keyContent: '',
|
||||
privateKeyContent: '',
|
||||
certSerialNo: '',
|
||||
apiV3Key: ''
|
||||
apiV3Key: '',
|
||||
publicKeyContent: '',
|
||||
publicKeyId: ''
|
||||
}
|
||||
})
|
||||
const formRules = {
|
||||
@ -201,6 +246,8 @@ const formRules = {
|
||||
{ required: true, message: '请上传 apiclient_key.pem 证书', 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' }]
|
||||
}
|
||||
const formRef = ref() // 表单 Ref
|
||||
@ -267,7 +314,9 @@ const resetForm = (appId, code) => {
|
||||
keyContent: '',
|
||||
privateKeyContent: '',
|
||||
certSerialNo: '',
|
||||
apiV3Key: ''
|
||||
apiV3Key: '',
|
||||
publicKeyContent: '',
|
||||
publicKeyId: ''
|
||||
}
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
@ -318,4 +367,15 @@ const keyContentUpload = async (event) => {
|
||||
}
|
||||
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>
|
||||
|
@ -6,7 +6,11 @@
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getAccessToken, getRefreshToken } from '@/utils/auth'
|
||||
|
||||
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>
|
||||
|
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;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--el-border-color);
|
||||
z-index: 3;
|
||||
|
Loading…
x
Reference in New Issue
Block a user