diff --git a/designer-demo/registry.js b/designer-demo/registry.js index 72bcf8296..91a8aef69 100644 --- a/designer-demo/registry.js +++ b/designer-demo/registry.js @@ -43,9 +43,11 @@ import { Canvas, EditorInfoService, AppService, - GenerateCodeService + GenerateCodeService, + http } from '@opentiny/tiny-engine' import engineConfig from './engine.config' +import { httpConfig } from './src/http' export default { root: { @@ -101,5 +103,6 @@ export default { plugins: [Materials, Tree, Page, Block, Datasource, Bridge, I18n, Script, State, Schema, Help, Robot], dsls: [{ id: 'engine.dsls.dslvue' }], settings: [Props, Styles, Events], - canvas: Canvas + canvas: Canvas, + http: [http, { options: httpConfig }] } diff --git a/packages/http/src/Login.vue b/designer-demo/src/http/Login.vue similarity index 100% rename from packages/http/src/Login.vue rename to designer-demo/src/http/Login.vue diff --git a/designer-demo/src/http/index.js b/designer-demo/src/http/index.js new file mode 100644 index 000000000..b250ec83f --- /dev/null +++ b/designer-demo/src/http/index.js @@ -0,0 +1,127 @@ +import { createApp } from 'vue' +import { useBroadcastChannel } from '@vueuse/core' +import { constants } from '@opentiny/tiny-engine-utils' +import { getMetaApi, http } from '@opentiny/tiny-engine' +import Login from './Login.vue' +import mockData from './mock' + +const { BROADCAST_CHANNEL } = constants + +const { post: globalNotify } = useBroadcastChannel({ name: BROADCAST_CHANNEL.Notify }) +const showError = (url, message) => { + globalNotify({ + type: 'error', + title: '接口报错', + message: `报错接口: ${url} \n报错信息: ${message ?? ''}` + }) +} + +const loginDom = document.createElement('div') +document.body.appendChild(loginDom) +const loginVM = createApp(Login).mount(loginDom) + +const procession = { + promiseLogin: null, + mePromise: {} +} + +window.lowcode = { + platformCenter: { + Session: { + rebuiltCallback: function () { + loginVM.closeLogin() + + procession.mePromise.resolve('login ok') + procession.promiseLogin = null + procession.mePromise = {} + } + } + } +} + + + +const LOGIN_EXPIRED_CODE = 401 + +export const errorResponse = (error) => { + // 用户信息失效时,弹窗提示登录 + const { response } = error + + const openLogin = () => { + return new Promise((resolve, reject) => { + if (!procession.promiseLogin) { + procession.promiseLogin = loginVM.openLogin(procession, '/api/rebuildSession') + procession.promiseLogin.then(() => { + getMetaApi('engine.service.http').request(response.config).then(resolve, reject) + // http.request(response.config).then(resolve, reject) + }) + } + }) + } + + if (response?.status === LOGIN_EXPIRED_CODE) { + // vscode 插件环境弹出输入框提示登录 + if (window.vscodeBridge) { + return Promise.resolve(true) + } + + // 浏览器环境弹出小窗登录 + if (response?.headers['x-login-url']) { + return openLogin() + } + } + + // 默认的 error response 显示接口错误 + showError(error.config?.url, error?.message) + + return response?.data.error ? Promise.reject(response.data.error) : Promise.reject(error.message) +} + +function getConfig(env = import.meta.env) { + const baseURL = env.VITE_ORIGIN || '' + + // 仅在本地开发时,启用 withCredentials + const dev = env.MODE?.includes('dev') + + // 获取租户 id + const getTenant = () => new URLSearchParams(location.search).get('tenant') + + return { + baseURL, + withCredentials: false, + headers: { + 'x-lowcode-mode': dev ? 'develop' : null, + 'x-lowcode-org': getTenant() + } + } +} + +const isVsCodeEnv = window.vscodeBridge +let environment = import.meta.env +const isDevelopEnv = environment.MODE?.includes('dev') +const isMock = environment.VITE_API_MOCK === 'mock' + +const preRequest = (config) => { + if (isDevelopEnv && config.url.match(/\/generate\//)) { + config.baseURL = '' + } + + if (isVsCodeEnv) { + config.baseURL = '' + } + + return config +} + +const { interceptors } = http + +export const httpConfig = { + config: getConfig, + interceptors: { + request: [preRequest], + response: [interceptors.response[0], errorResponse] + }, + mock: isMock, + mockData: isMock ? mockData : [] +} + diff --git a/packages/http/src/mock.js b/designer-demo/src/http/mock.js similarity index 100% rename from packages/http/src/mock.js rename to designer-demo/src/http/mock.js diff --git a/packages/design-core/index.js b/packages/design-core/index.js index 5acb5ee87..bde72849d 100644 --- a/packages/design-core/index.js +++ b/packages/design-core/index.js @@ -37,6 +37,8 @@ export { GenerateCodeService, PluginPanel } from '@opentiny/tiny-engine-common' export { default as defaultRegistry } from './registry' +export { default as http } from '@opentiny/tiny-engine-http' + export * from '@opentiny/tiny-engine-meta-register' export { EditorInfoService, AppService } from '@opentiny/tiny-engine-common' diff --git a/packages/http/index.js b/packages/http/index.js deleted file mode 100644 index dcf78045f..000000000 --- a/packages/http/index.js +++ /dev/null @@ -1,5 +0,0 @@ -import metaData from './meta' - -export default { - ...metaData -} diff --git a/packages/http/src/config.js b/packages/http/src/config.js deleted file mode 100644 index 54b57a8ca..000000000 --- a/packages/http/src/config.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2023 - present TinyEngine Authors. - * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. - * - * Use of this source code is governed by an MIT-style license. - * - * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, - * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR - * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. - * - */ - -export function getConfig(env = import.meta.env) { - const baseURL = env.VITE_ORIGIN - - // 仅在本地开发时,启用 withCredentials - const dev = env.MODE?.includes('dev') - - // 获取租户 id - const getTenant = () => new URLSearchParams(location.search).get('tenant') - return { - baseURL, - withCredentials: false, - headers: { - 'x-lowcode-mode': dev ? 'develop' : null, - 'x-lowcode-org': getTenant() - } - } -} diff --git a/packages/http/src/index.js b/packages/http/src/index.js index 510675105..bdc5aa33f 100644 --- a/packages/http/src/index.js +++ b/packages/http/src/index.js @@ -9,32 +9,14 @@ * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. * */ - -/* eslint-disable no-undef */ -import axios, { globalDefaults } from './axios' -import { createApp } from 'vue' import { useBroadcastChannel } from '@vueuse/core' -import Login from './Login.vue' -import { getConfig } from './config' -import mockData from './mock' import { constants } from '@opentiny/tiny-engine-utils' -import { initHook, HOOK_NAME } from '@opentiny/tiny-engine-meta-register' +import axios from './axios' const { BROADCAST_CHANNEL } = constants const { post: globalNotify } = useBroadcastChannel({ name: BROADCAST_CHANNEL.Notify }) -const procession = { - promiseLogin: null, - mePromise: {} -} - -const LOGIN_EXPIRED_CODE = 401 - -const loginDom = document.createElement('div') -document.body.appendChild(loginDom) -const loginVM = createApp(Login).mount(loginDom) - const showError = (url, message) => { globalNotify({ type: 'error', @@ -43,116 +25,75 @@ const showError = (url, message) => { }) } -window.lowcode = { - platformCenter: { - Session: { - rebuiltCallback: function () { - loginVM.closeLogin() +const preResponse = (res) => { + if (res.data?.error) { + showError(res.config?.url, res?.data?.error?.message) - procession.mePromise.resolve('login ok') - procession.promiseLogin = null - procession.mePromise = {} - } - } + return Promise.reject(res.data.error) } + + return res.data?.data } -let http // 封装axios的http实例 -let environment = import.meta.env // 当前设计器运行环境变量 +const errorResponse = (error) => { + // 用户信息失效时,弹窗提示登录 + const { response } = error -const isVsCodeEnv = window.vscodeBridge -const isMock = () => environment.VITE_API_MOCK === 'mock' + // 默认的 error response 显示接口错误 + showError(error.config?.url, error?.message) -export const createHttp = (options) => { - // 缓存http实例,避免每个请求重新创建实例 - if (http && !options.force) { - return http - } - const isDevelopEnv = environment.MODE?.includes('dev') - const axiosConfig = getConfig(environment) - http = axios(axiosConfig) - - // 如果未指定是否启用 mock,则本地开发时默认启用,模拟数据在 public/mock 目录下 - const { enableMock = isDevelopEnv } = options - enableMock && http.mock(mockData) + return response?.data.error ? Promise.reject(response.data.error) : Promise.reject(error.message) +} - const preRequest = (config) => { - if (isDevelopEnv && config.url.match(/\/generate\//)) { - config.baseURL = '' - } +const initialState = { + http: null +} - if (isVsCodeEnv) { - config.baseURL = '' +export default { + id: 'engine.service.http', + options: { + config: {}, + interceptors: { + request: [], + response: [preResponse, errorResponse] + }, + mock: false, + mockData: [] + }, + type: 'MetaService', + initialState, + apis: ({ state }) => ({ + http: state.http, + request: state.http.request, + get: state.http.get, + post: state.http.post, + put: state.http.put, + delete: state.http.delete + }), + init: ({ state, options }) => { + const { config, mock = false, mockData, interceptors } = options + + let axiosConfig = config + + // config 支持函数 + if (typeof axiosConfig === 'function') { + axiosConfig = axiosConfig() } - return config - } - - // 请求拦截器 - http.interceptors.request.use(preRequest) - - const preResponse = (res) => { - if (res.data?.error) { - showError(res.config?.url, res?.data?.error?.message) + const http = axios(axiosConfig) - return Promise.reject(res.data.error) + if (mock) { + http.mock(mockData) } - return res.data?.data - } - - const openLogin = () => { - return new Promise((resolve, reject) => { - if (!procession.promiseLogin) { - procession.promiseLogin = loginVM.openLogin(procession, '/api/rebuildSession') - procession.promiseLogin.then(() => { - http.request(response.config).then(resolve, reject) - }) - } - }) - } - - const errorResponse = (error) => { - // 用户信息失效时,弹窗提示登录 - const { response } = error - if (response?.status === LOGIN_EXPIRED_CODE) { - // vscode 插件环境弹出输入框提示登录 - if (window.vscodeBridge) { - return Promise.resolve(true) - } - - // 浏览器环境弹出小窗登录 - if (response?.headers['x-login-url']) { - return openLogin() - } + if (Array.isArray(interceptors.request)) { + http.interceptors.request.use(...interceptors.request) } - showError(error.config?.url, error?.message) - - return response?.data.error ? Promise.reject(response.data.error) : Promise.reject(error.message) - } - - // 响应拦截器 - http.interceptors.response.use(preResponse, errorResponse) - - return http -} + if (Array.isArray(interceptors.response)) { + http.interceptors.request.use(...interceptors.response) + } -/** - * 根据环境不同初始化设置http参数 - * @param {*} env: 当前环境变量 - */ -export const initHttp = ({ env }) => { - if (Object.keys(env).length) { - environment = env + state.http = http } - const baseURL = environment.VITE_ORIGIN - // 调用初始化方法前可能已经存在已经实例化的http,需要设置baseURL - http?.defaults('baseURL', baseURL) - globalDefaults('baseURL', baseURL) - http = createHttp({ force: true, enableMock: isMock() }) } - -export const useHttp = () => createHttp({ enableMock: isMock() }) - -initHook(HOOK_NAME.useHttp, useHttp, { useDefaultExport: true }) diff --git a/packages/http/vite.config.js b/packages/http/vite.config.js index afc974c25..959fe4441 100644 --- a/packages/http/vite.config.js +++ b/packages/http/vite.config.js @@ -30,9 +30,7 @@ export default defineConfig({ formats: ['es'] }, rollupOptions: { - output: { - banner: 'import "./style.css"' - }, + output: {}, external: ['vue', /@opentiny\/tiny-engine.*/, /@opentiny\/vue.*/] } }