Share api document (#168)

This commit is contained in:
Scarqin 2022-11-01 21:41:26 +08:00 committed by GitHub
parent 2a64e7d3a2
commit 0746c235f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
160 changed files with 7665 additions and 4662 deletions

21
.dockerignore Normal file
View File

@ -0,0 +1,21 @@
.DS_Store
node_modules/
dist/
release/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
.vercel
docker-compose.dev.yml
Dockerfile.dev

View File

@ -2,6 +2,7 @@ name: Release
on:
push:
# branches: [for debug]
tags:
- 'v*.*.*'

3
.gitignore vendored
View File

@ -54,3 +54,6 @@ Thumbs.db
# *-lock.json
-error.log
.vercel
nginx-test.conf
docker-compose.dev.yml
Dockerfile.dev

View File

@ -1,3 +1,26 @@
# [1.9.0](https://github.com/eolinker/eoapi/compare/v1.8.2...v1.9.0) (2022-10-19)
### Features
* mock lead to download in web ([65c7ecf](https://github.com/eolinker/eoapi/commit/65c7ecffe795844fd24621c05e412a8af17eca09))
## [1.8.2](https://github.com/eolinker/eoapi/compare/v1.8.1...v1.8.2) (2022-10-18)
### Bug Fixes
* xml2json parser bug ([e2459a2](https://github.com/eolinker/eoapi/commit/e2459a23fc74a7696f5b6f3296c512d28939f6c7))
### Features
* keep queryParams stay in page ([baad303](https://github.com/eolinker/eoapi/commit/baad3038cbffd38cfea128f92d3cc3073a786e5a))
## [1.8.1](https://github.com/eolinker/eoapi/compare/v1.8.0...v1.8.1) (2022-10-13)

26
Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM node:lts-alpine as builder
WORKDIR /test-server
# api 测试服务端口
ENV NODE_SERVER_PORT 4201
# websocket 测试服务端口
ENV EOAPI_WEBSOCKET_POST 4202
COPY /src/workbench/node /test-server
RUN yarn install
EXPOSE 4201 4202
CMD ["yarn", "start:all"]
FROM nginx:alpine as production
ENV NODE_ENV production
COPY ./src/workbench/browser/dist/ /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

39
docker-compose.yml Normal file
View File

@ -0,0 +1,39 @@
version: '3'
services:
eoapi:
# build: 从当前路径构建镜像
build:
context: .
dockerfile: Dockerfile
target: production
image: eolinker/eoapi:1.9.0
container_name: eoapi
restart: always
extra_hosts:
- 'host.docker.internal:host-gateway'
ports:
- '8000:80'
networks:
- eoapi_net
eoapi-test-server:
# build: 从当前路径构建镜像
build:
context: .
dockerfile: Dockerfile
target: builder
image: eolinker/eoapi-test-server:1.9.0
container_name: eoapi-test-server
restart: always
extra_hosts:
- 'host.docker.internal:host-gateway'
ports:
- '4201:4201'
- '4202:4202'
networks:
- eoapi_net
networks:
eoapi_net:
name: eoapi_net

86
nginx.conf Normal file
View File

@ -0,0 +1,86 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
absolute_redirect off; #取消绝对路径的重定向
sendfile on;
default_type application/octet-stream;
gzip on;
gzip_http_version 1.1;
gzip_disable "MSIE [1-6]\.";
gzip_min_length 256;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 9;
root /usr/share/nginx/html;
location / {
return 301 $scheme://$http_host/zh;
}
location /zh {
alias /usr/share/nginx/html/zh;
index index.html index.htm;
try_files $uri $uri/ /zh/index.html;
}
location /en {
alias /usr/share/nginx/html/en;
index index.html index.htm;
try_files $uri $uri/ /en/index.html;
}
# api测试服务
location /api/unit {
proxy_pass http://eoapi-test-server:4201; # 转发规则
proxy_set_header Host $proxy_host; # 修改转发请求头让3000端口的应用可以受到真实的请求
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# websocket测试服务
location ~/socket.io/(.*) {
proxy_pass http://eoapi-test-server:4202; # 转发规则
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
}
# 后端服务
location ^~ /api {
proxy_pass http://eoapi-remote-server:3000; # 转发规则
proxy_set_header Host $proxy_host; # 修改转发请求头让3000端口的应用可以受到真实的请求
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 后端服务兼容旧客户端
location ~ ^/(system|workspace|user|auth|[0-9]+/[0-9]+/api_data|[0-9]+/[0-9]+/group|[0-9]+/[0-9]+/group|[0-9]+/[0-9]+/environment|[0-9]+/[0-9]+/api_test_history|[0-9]+/[0-9]+/mock|[0-9]+/project) {
proxy_pass http://eoapi-remote-server:3000; # 转发规则
proxy_set_header Host $proxy_host; # 修改转发请求头让3000端口的应用可以受到真实的请求
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
rewrite "^/(.*)$" /api/$1 break;
}
# location ^~ /zh/(.*)/assets/(.*)$ {
# alias /usr/share/nginx/html/zh/assets/$2;
# return 501 /usr/share/nginx/html/zh/assets/$2;
#}
# location ^~ /zh/\.(gif|jpg|jpeg|png|css|js|ico|svg|ttf|woff2|woff|txt)$ {
# alias /usr/share/nginx/html/zh/$1;
# return 502 /usr/share/nginx/html/zh/$1;
#}
}

View File

@ -14,10 +14,11 @@
"scripts": {
"postinstall": "electron-builder install-app-deps && npx patch-package",
"start": "npm-run-all -p start:workbench electron:serve",
"start:web": "cd src/workbench/browser && npm run ng:serve",
"start:testServer": "cd src/workbench/node/server && yarn dev",
"serve:web": "cd src/workbench/browser && npm run ng:serve",
"start:testServer": "cd src/workbench/node && yarn dev",
"start:workbench": "cd src/workbench/browser && yarn start",
"build:workbench": "cd src/workbench/browser && yarn build",
"docker:build:workbench": "cd src/workbench/browser && yarn build:docker:web",
"test:workbench": "cd src/workbench/browser && yarn test",
"electron:serve": "wait-on tcp:4200 && npm run electron:dev",
"electron:static": "npm run electron:tsc && electron .",
@ -47,6 +48,7 @@
"express": "4.18.1",
"fix-path": "3.0.0",
"form-data": "^4.0.0",
"http-server": "14.1.1",
"iconv-lite": "^0.6.3",
"jquery": "^3.3.0",
"jsdom": "^11.5.1",

View File

@ -1,91 +0,0 @@
import { ModuleInfo, ModuleType } from 'eo/platform/node/extension-manager/types';
import { getViewBounds, SidePosition, ViewBounds, ViewZone } from 'eo/shared/common/bounds';
import { BrowserView, BrowserWindow } from 'electron';
import * as path from 'path';
import { BrowserViewInstance } from 'eo/platform/electron-main/browserView/browserView';
import { processEnv } from 'eo/platform/node/constant';
export class AppViews {
mainModuleID: string;
view: BrowserView;
sidePosition: SidePosition = SidePosition.left;
constructor(private win: BrowserWindow) {}
/**
* app模块
* @param module
*/
create(module: ModuleInfo) {
if (module && module.moduleType === ModuleType.app) {
if (module.isApp && this.mainModuleID !== module.moduleID) {
this.mainModuleID = module.moduleID;
// this.sidePosition = module.sidePosition;
}
this.createAppView(module);
if (module.main_node) {
const main_node = require(module.main_node);
if (main_node.module && typeof main_node.module === 'object') {
const _fun = main_node.module;
_fun.setup({
appView: this.view,
});
}
}
}
return this.view;
}
/**
*
*/
remove() {
if (!this.view) {
return;
}
this.view.webContents.closeDevTools();
this.win.removeBrowserView(this.view);
this.view = undefined;
this.mainModuleID = undefined;
}
rebuildBounds(sideWidth?: number) {
if (!this.view) {
return;
}
const windBounds = this.win.getContentBounds();
const _bounds: ViewBounds = getViewBounds(
ViewZone.main,
windBounds.width,
windBounds.height,
this.sidePosition,
sideWidth
);
this.view.setBounds(_bounds);
}
/**
*
* @param module
*/
private createAppView(module: ModuleInfo) {
const windBounds = this.win.getContentBounds();
const _bounds: ViewBounds = getViewBounds(ViewZone.main, windBounds.width, windBounds.height, this.sidePosition);
let _view = new BrowserViewInstance({
bounds: _bounds,
partition: `<${module.moduleID}>`,
preloadPath: path.join(__dirname, '../../', 'platform', 'electron-browser', 'preload.js'),
preload: module.preload,
viewPath: processEnv === 'development' && module.main_debug ? module.main_debug : module.main,
}).init(this.win);
this.remove();
this.view = _view;
this.view.webContents.openDevTools();
this.view.webContents.once('did-finish-load', () => {
_view.setBackgroundColor('#FFF');
});
this.view.webContents.once('dom-ready', () => {
this.rebuildBounds();
require('@electron/remote/main').enable(this.view.webContents);
});
}
}

View File

@ -187,64 +187,20 @@ try {
}
}
});
ipcMain.on('eo-storage', (event, args) => {
let returnValue: any;
if (args.type === 'default' || args.type === 'remote') {
eoBrowserWindow.win.webContents.send('eo-storage', args);
returnValue = null;
} else if (args.type === 'sync') {
deleteFile(storageTemp);
eoBrowserWindow.win.webContents.send('eo-storage', args);
let data = readJson(storageTemp);
let count: number = 0;
while (data === null) {
if (count > 1500) {
data = {
// status: StorageResStatus.error,
status: 500,
data: 'storage sync load error',
};
break;
}
data = readJson(storageTemp);
++count;
}
deleteFile(storageTemp);
returnValue = data;
} else if (args.type === 'result') {
let view = subView.appView ? subView.appView?.view.webContents : eoBrowserWindow.win.webContents;
view.send('storageCallback', args.result);
}
});
// 这里可以封装成类+方法匹配调用不用多个if else
['on', 'handle'].forEach((eventName) =>
ipcMain[eventName]('eo-sync', async (event, arg) => {
let returnValue: any;
if (arg.action === 'getApiAccessRules') {
// 后期加入权限生成根据moduleID上层moduleID应用范围等
// 或者是像Android, 跳出权限列表让用户自己选择确认放开的权限。
const output: string[] = ['getModules', 'getAppModuleList', 'getSlideModuleList', 'hook'];
returnValue = output;
} else if (arg.action === 'getModules') {
returnValue = moduleManager.getModules(true);
if (arg.action === 'getModules') {
returnValue = moduleManager.getModules();
} else if (arg.action === 'getModule') {
returnValue = moduleManager.getModule(arg.data.moduleID);
} else if (arg.action === 'getAppModuleList') {
returnValue = moduleManager.getAppModuleList();
returnValue = moduleManager.getModule(arg.data.id);
} else if (arg.action === 'installModule') {
const data = await moduleManager.installExt(arg.data);
if (data.code === 0) {
//subView.mainView.view.webContents.send('moduleUpdate');
}
returnValue = Object.assign(data, { modules: moduleManager.getModules() });
} else if (arg.action === 'uninstallModule') {
const data = await moduleManager.uninstall(arg.data);
if (data.code === 0) {
// subView.mainView.view.webContents.send('moduleUpdate');
}
returnValue = Object.assign(data, { modules: moduleManager.getModules() });
} else if (arg.action === 'getSideModuleList') {
returnValue = moduleManager.getSideModuleList(subView.appView?.mainModuleID || 'default');
} else if (arg.action === 'getFeatures') {
returnValue = moduleManager.getFeatures();
} else if (arg.action === 'getFeature') {
@ -257,19 +213,16 @@ try {
returnValue = configuration.deleteModuleSettings(arg.data.moduleID);
} else if (arg.action === 'getSettings') {
returnValue = configuration.getSettings();
} else if (arg.action === 'getModuleSettings') {
returnValue = configuration.getModuleSettings(arg.data.moduleID);
} else if (arg.action === 'getSidePosition') {
returnValue = subView.appView?.sidePosition;
// 获取mock服务地址
} else if (arg.action === 'getExtensionSettings') {
returnValue = configuration.getExtensionSettings(arg.data.moduleID);
} else if (arg.action === 'getMockUrl') {
// 获取mock服务地址
returnValue = mockServer.getMockUrl();
// 获取websocket服务端口
} else if (arg.action === 'getWebsocketPort') {
// 获取websocket服务端口
returnValue = websocketPort;
// 重置并初始化mock路由
} else if (arg.action === 'hook') {
returnValue = 'hook返回';
} else if (arg.action === 'getExtensionPagePathByName') {
returnValue = moduleManager.setupExtensionPageServer(arg.data.extName);
} else {
returnValue = 'Invalid data';
}

View File

@ -10,7 +10,7 @@ export class EoUpdater {
// url: 'https://packages.eoapi.io',
// });
// 是否自动更新
// autoUpdater.autoDownload = window.eo.getModuleSettings('common.app.autoUpdate') !== false;
// autoUpdater.autoDownload = window.eo.getExtensionSettings('common.app.autoUpdate') !== false;
if (appVersion.includes('beta')) autoUpdater.channel = 'beta';
console.log('appVersion', appVersion, autoUpdater.channel);
}

View File

@ -1,10 +1,10 @@
import { ModuleInfo } from 'eo/platform/node/extension-manager';
import { ModuleInfo } from "eo/platform/node/extension-manager/types";
/**
* Single extension i18 service,chain call
*/
export class TranslateService {
// Default key in package.json translate replace directly
defaultKeys = ['moduleName', 'description', 'author', 'logo'];
defaultKeys = ['moduleName','title','description', 'author', 'logo'];
constructor(private module: ModuleInfo, private locale) {}
translate() {
return this.translateDefaultKey().translateVariableKey().get();

View File

@ -1,7 +1,5 @@
import { I18N } from './i18n';
const { ipcRenderer } = require('electron');
// 可以加上条件判断根据不同模块id哪些允许放出
const apiAccessRules = ipcRenderer.sendSync('eo-sync', { action: 'getApiAccessRules' }) || [];
// 正在安装中的插件任务列表
const installTask = new Map();
// 正在卸载中的插件任务列表
@ -25,67 +23,38 @@ const featureModules = new Map();
window.eo = {
name: 'Eoapi public api',
version: '1.0.0',
};
// 边栏显示
// window.eo.sidePosition = ipcRenderer.sendSync('eo-sync', { action: 'getSidePosition' }) || 'left';
// 获取模块列表
window.eo.getModules = () => {
return ipcRenderer.sendSync('eo-sync', { action: 'getModules' });
};
// 获取某个模块
window.eo.getModule = (moduleID) => {
return ipcRenderer.sendSync('eo-sync', { action: 'getModule', data: { moduleID: moduleID } });
};
// 获取App应用列表
window.eo.getAppModuleList = () => {
return ipcRenderer.sendSync('eo-sync', { action: 'getAppModuleList' });
};
// 获取边栏应用列表
window.eo.getSideModuleList = () => {
return ipcRenderer.sendSync('eo-sync', { action: 'getSideModuleList' });
};
// 获取所有功能点列表
window.eo.getFeatures = () => {
return ipcRenderer.sendSync('eo-sync', { action: 'getFeatures' });
};
// 获取某个功能点
window.eo.getFeature = (featureKey) => {
return ipcRenderer.sendSync('eo-sync', { action: 'getFeature', data: { featureKey: featureKey } });
};
// 加载feature模块
window.eo.loadFeatureModule = async (moduleID) => {
if (!featureModules.has(moduleID)) {
try {
const module = window.eo.getModule(moduleID);
window.eo._currentExtensionID = moduleID;
const _module = await require(module.baseDir);
featureModules.set(moduleID, _module);
} catch (e) {
console.log(e);
// 获取模块列表
getModules() {
return ipcRenderer.sendSync('eo-sync', { action: 'getModules' });
},
getModule (id) {
return ipcRenderer.sendSync('eo-sync', { action: 'getModule', data: { id: id } });
},
getFeatures(){
return ipcRenderer.sendSync('eo-sync', { action: 'getFeatures' });
},
getFeature(featureKey) {
return ipcRenderer.sendSync('eo-sync', { action: 'getFeature', data: { featureKey: featureKey } });
},
loadFeatureModule(id){
if (!featureModules.has(id)) {
try {
const module = window.eo.getModule(id);
window.eo._currentExtensionID = id;
const _module = require(module.baseDir);
featureModules.set(id, _module);
} catch (e) {
console.log(e);
}
}
}
return featureModules.get(moduleID);
return featureModules.get(id);
},
};
// 卸载feature模块
window.eo.unloadFeatureModule = (moduleID) => {
featureModules.delete(moduleID);
};
// Hook请求返回
if (apiAccessRules.includes('hook')) {
window.eo.hook = (data) => {
return ipcRenderer.sendSync('eo-sync', { action: 'hook', data });
};
}
// 临时测试用
window.eo.tempApi = (params) => {
return ipcRenderer.sendSync('eo-sync', params);
};
window.eo.autoResize = (sideWidth) => {
ipcRenderer.send('eo-sync', { action: 'autoResize', data: { sideWidth: sideWidth } });
};
window.eo.getModules = () => {
return ipcRenderer.sendSync('eo-sync', { action: 'getModules' });
window.eo.unloadFeatureModule = (id) => {
featureModules.delete(id);
};
window.eo.installModule = (name, isLocal = false) => {
installTask.set(name, []);
const result = ipcRenderer.invoke('eo-sync', { action: 'installModule', data: { name, isLocal } });
@ -145,9 +114,6 @@ window.eo.uninstallModule = (name, isLocal = false) => {
});
return result;
};
window.eo.openApp = (inputArg) => {
return ipcRenderer.sendSync('eo-sync', { action: 'openApp', data: inputArg });
};
window.eo.getInstallTask = () => installTask;
window.eo.getUninstallTask = () => uninstallTask;
window.eo.getExtIsInTask = (name, callback) => {
@ -166,40 +132,9 @@ window.eo.getExtIsInTask = (name, callback) => {
return false;
};
window.eo.storage = (args, callback: any) => {
const key = `${args.action}_${Date.now()}`;
storageCallback.set(key, callback);
args.type = 'default';
args.callback = key;
ipcRenderer.send('eo-storage', args);
window.eo.getExtensionPagePathByName = (extName: string) => {
return ipcRenderer.invoke('eo-sync', { action: 'getExtensionPagePathByName', data: { extName } });
};
window.eo.storageSync = (args) => {
console.log('run preload storageSync');
args.type = 'sync';
return ipcRenderer.sendSync('eo-storage', args);
};
// window.eo.storageRemote = (args) => {
// console.log('run preload storageRemote');
// args.type = 'remote';
// const shareObject = window.require('@electron/remote').getGlobal('shareObject');
// shareObject.storageResult = null;
// ipcRenderer.send('eo-storage', args);
// let output: any = shareObject.storageResult;
// let count: number = 0;
// while (output === null) {
// if (count > 1500) {
// output = {
// status: 'error',
// data: 'storage remote load error',
// };
// break;
// }
// output = shareObject.storageResult;
// ++count;
// }
// shareObject.storageResult = null;
// return output;
// };
window.eo.saveSettings = (settings) => {
// console.log('window.eo.saveSettings', settings);
@ -226,8 +161,8 @@ window.eo.getSettings = () => {
};
window.eo.i18n = new I18N();
window.eo.getModuleSettings = (moduleID) => {
return ipcRenderer.sendSync('eo-sync', { action: 'getModuleSettings', data: { moduleID: moduleID } });
window.eo.getExtensionSettings = (moduleID) => {
return ipcRenderer.sendSync('eo-sync', { action: 'getExtensionSettings', data: { moduleID: moduleID } });
};
// 注册单个mock路由
window.eo.registerMockRoute = ({ method, path, data }) => {

View File

@ -1,41 +0,0 @@
import { throws } from 'assert';
import { BrowserWindow, BrowserView, session, BrowserViewConstructorOptions } from 'electron';
import { ViewBounds } from 'eo/shared/common/bounds';
import { proxyOpenExternal } from 'eo/shared/common/browserView';
import { BrowserViewOpts } from './browserView.type';
export class BrowserViewInstance {
bounds: ViewBounds;
constructor(private opts: BrowserViewOpts) {
this.bounds = this.opts.bounds;
}
init(win: BrowserWindow) {
let viewOps: BrowserViewConstructorOptions = {
webPreferences: {
webSecurity: false,
nodeIntegration: true,
contextIsolation: false,
devTools: true,
webviewTag: true,
},
};
if (this.opts.preloadPath) {
const partition = this.opts.partition || '<core-module>';
const ses = session.fromPartition(partition);
ses.setPreloads([this.opts.preloadPath]);
viewOps.webPreferences.session = ses;
}
if (this.opts.preload) {
viewOps.webPreferences.preload = this.opts.preload;
}
let view = new BrowserView(viewOps);
view.webContents.loadURL(this.opts.viewPath);
// view.webContents.on("console-message",(e,level,message)=>{
// console.log(message)
// })
// view.webContents.openDevTools();
win.addBrowserView(view);
view.setBounds(this.bounds);
proxyOpenExternal(view);
return view;
}
}

View File

@ -1,9 +0,0 @@
import { ViewBounds } from 'eo/shared/common/bounds';
export interface BrowserViewOpts {
bounds: ViewBounds;
partition?: string;
preloadPath?: string;
viewPath: string;
preload?: string;
}

View File

@ -0,0 +1,3 @@
globalThis.eo = {
getExtensionSetting() {},
};

View File

@ -88,7 +88,7 @@ export class Configuration implements ConfigurationInterface {
* @param section
* @returns
*/
getModuleSettings<T = any>(section?: string): T {
getExtensionSettings<T = any>(section?: string): T {
return this.getConfiguration(section);
}

View File

@ -6,7 +6,7 @@ export interface ConfigurationInterface {
saveModuleSettings: (moduleID: string, settings: ConfigurationValueInterface) => boolean;
deleteModuleSettings: (moduleID: string) => boolean;
getSettings: () => ConfigurationValueInterface;
getModuleSettings: (moduleID: string) => ConfigurationValueInterface;
getExtensionSettings: (moduleID: string) => ConfigurationValueInterface;
}
export interface ConfigurationValueInterface {

View File

@ -1,7 +1,6 @@
import * as path from 'path';
import { ModuleHandlerOptions, ModuleInfo } from '../types';
import { fileExists, readFile, readJson } from 'eo/shared/node/file';
import { isNotEmpty } from 'eo/shared/common/common';
import { getLocaleData } from 'eo/platform/node/i18n';
import { LanguageService } from 'eo/app/electron-main/language.service';
import { TranslateService } from 'eo/platform/common/i18n';
@ -53,24 +52,20 @@ export class CoreHandler {
// Check that the file exists locally
moduleInfo.introduction =
readFile(path.join(baseDir, `README.${lang}.md`)) || readFile(path.join(baseDir, `README.md`));
moduleInfo.main = 'file://' + path.join(moduleInfo.baseDir, moduleInfo.main);
if (moduleInfo.preload?.length > 0) {
moduleInfo.preload = path.join(moduleInfo.baseDir, moduleInfo.preload);
if(moduleInfo.main){
moduleInfo.main = 'file://' + path.join(moduleInfo.baseDir, moduleInfo.main);
}
if (moduleInfo.main_node?.length > 0) {
moduleInfo.main_node = path.join(moduleInfo.baseDir, moduleInfo.main_node);
if(moduleInfo.node){
moduleInfo.node = 'file://' + path.join(moduleInfo.baseDir, moduleInfo.node);
}
if (moduleInfo.logo?.length > 0 && !moduleInfo.logo.startsWith('http') && !moduleInfo.logo.includes('icon-')) {
moduleInfo.logo = 'file://' + path.join(moduleInfo.baseDir, moduleInfo.logo);
}
if (!moduleInfo.belongs || !isNotEmpty(moduleInfo.belongs)) {
moduleInfo.belongs = ['default'];
}
if (typeof moduleInfo.author === 'object') {
moduleInfo.author = moduleInfo.author['name'] || '';
}
} catch (e) {
console.log(`Get module ${moduleInfo?.moduleID} error:${e}`);
console.log(`Get module ${moduleInfo?.name} error:${e}`);
moduleInfo = {} as ModuleInfo;
}
return moduleInfo;

View File

@ -86,11 +86,11 @@ export class ModuleHandler extends CoreHandler {
*/
private operatePackage(result: any[], moduleList: string[], action: Action) {
if (Array.isArray(result)) {
const moduleNames = moduleList.map((n) => n.split('@')[0]);
const names = moduleList.map((n) => n.split('@')[0]);
const packagePath = path.join(this.baseDir, 'package.json');
result.forEach(([name]) => {
const [pkgName, pkgVersion] = name.split('@');
if (moduleNames.includes(pkgName)) {
if (names.includes(pkgName)) {
const packageJSON = fs.readFileSync(packagePath);
const packageObj = JSON.parse(packageJSON.toString());
const dependencieKeys = Object.keys(packageObj.dependencies);
@ -115,7 +115,7 @@ export class ModuleHandler extends CoreHandler {
return;
}
npmCli.commands[command](moduleList, (err, data) => {
console.log('command', command);
// console.log('command', command);
process.chdir(this.baseDir);
if (err) {
return reject(err);
@ -135,9 +135,7 @@ export class ModuleHandler extends CoreHandler {
args = args.concat(`--proxy=${this.proxy}`);
}
}
console.log(args);
const npm = spawn('npm', args, { cwd: this.baseDir });
// console.log('2==>', npm);
let output = '';
npm.stdout
.on('data', (data: string) => {

View File

@ -1,61 +0,0 @@
import { ModuleRuntime, ModuleType, ModuleInfo, ModuleLoaderInterface } from '../types';
/**
*
*
*/
export class ModuleLoader implements ModuleLoaderInterface {
/**
*
*/
private readonly runtime: ModuleRuntime;
/**
*
*/
private readonly moduleRequire: any;
constructor(_runtime: ModuleRuntime, _moduleRequire?: any) {
this.runtime = _runtime;
this.moduleRequire = _moduleRequire || require;
}
/**
*
* @param modules
*/
loadModules(modules: Array<ModuleInfo>): void {
modules.forEach((module: ModuleInfo) => {
this.loadModule(module);
})
}
/**
*
*
* main运行环境只能加载system和app模块
* render运行环境只能加载ui和feature模块
* web运行环境只能加载web支持的模块
* @param module ModuleInfo
*/
loadModule(module: ModuleInfo): void {
if ((this.runtime === ModuleRuntime.main && ![ModuleType.system, ModuleType.app].indexOf(module.moduleType))
|| (this.runtime === ModuleRuntime.render && ![ModuleType.feature].indexOf(module.moduleType))) {
console.log(`The [${module.moduleType}] module [${module.name}] can not run in runtime [${this.runtime}].`);
return;
}
if (this.runtime === ModuleRuntime.main) {
console.log('load from main');
const _module = this.moduleRequire(module.baseDir)();
console.log(_module);
} else if (this.runtime === ModuleRuntime.render) {
console.log('load from render');
const _module = this.moduleRequire(module.baseDir)();
console.log(_module);
} else if (this.runtime === ModuleRuntime.web) {
console.log('load from web');
// todo with script src
}
// 加入hooks列表
}
}

View File

@ -1,12 +1,26 @@
import { MODULE_DIR as baseDir } from 'eo/shared/electron-main/constant';
import { ModuleHandler } from './handler';
import { ModuleHandlerResult, ModuleInfo, ModuleManagerInfo, ModuleManagerInterface, ModuleType } from '../types';
import {
ModuleHandlerResult,
ModuleInfo,
ModuleManagerInfo,
ModuleManagerInterface,
ExtensionTabView,
SidebarView,
FeatureInfo,
} from '../types';
import { isNotEmpty } from 'eo/shared/common/common';
import { processEnv } from '../../constant';
import http from 'axios';
import { DATA_DIR } from '../../../../shared/electron-main/constant';
import { promises, readFileSync } from 'fs';
import { ELETRON_APP_CONFIG } from '../../../../enviroment';
import { createServer } from 'http-server/lib/http-server';
import path from 'node:path';
import portfinder from 'portfinder';
import { lstat } from 'fs/promises';
const extTabViewServerMap = new Map<string, ExtensionTabView>();
const extSidebarViewServerMap = new Map<string, SidebarView>();
// * npm pkg name
const defaultExtension = [{ name: 'eoapi-export-openapi' }, { name: 'eoapi-import-openapi' }];
@ -24,7 +38,7 @@ export class ModuleManager implements ModuleManagerInterface {
/**
* extension list
*/
private installExtension = [];
private installExtension: ModuleManagerInfo[] = [];
/**
*
@ -34,7 +48,7 @@ export class ModuleManager implements ModuleManagerInterface {
/**
*
*/
private readonly features: Map<string, Map<string, object>>;
private readonly features: Map<string, Map<string, FeatureInfo>>;
constructor() {
this.moduleHandler = new ModuleHandler({ baseDir: baseDir });
@ -43,7 +57,6 @@ export class ModuleManager implements ModuleManagerInterface {
this.init();
this.updateAll();
}
async getRemoteExtension() {
const { data } = await http.get(`${ELETRON_APP_CONFIG.EXTENSION_URL}/list`);
return data.data.map(({ name, version }) => ({ name, version }));
@ -76,6 +89,12 @@ export class ModuleManager implements ModuleManagerInterface {
const result = await this.moduleHandler.uninstall([{ name: module.name }], module.isLocal || false);
if (result.code === 0) {
this.delete(moduleInfo);
[extTabViewServerMap, extSidebarViewServerMap].forEach((item) => {
if (item.has(module.name)) {
item.get(module.name).server.close();
item.delete(module.name);
}
});
}
return result;
}
@ -121,44 +140,13 @@ export class ModuleManager implements ModuleManagerInterface {
this.refresh(module);
});
}
/**
* app列表
*/
getAppModuleList(): Array<ModuleInfo> {
const output: Array<ModuleInfo> = [];
const modules: Map<string, ModuleInfo> = this.moduleBelongs();
modules?.forEach((module: ModuleInfo) => {
if (module.isApp) {
(module.main = processEnv === 'development' && module.main_debug ? module.main_debug : module.main),
output.push(module);
}
});
return output;
}
/**
*
*/
getSideModuleList(moduleID: string): Array<ModuleInfo> {
const output: Array<ModuleInfo> = [];
const modules: Map<string, ModuleInfo> = this.moduleBelongs();
modules.get(moduleID)?.sideItems?.forEach((_moduleID: string) => {
if (modules.has(_moduleID)) {
output.push(modules.get(_moduleID));
}
});
return output;
}
/**
*
* belongs为true
* @param belongs
*/
getModules(belongs: boolean = false): Map<string, ModuleInfo> {
if (belongs) {
return this.moduleBelongs();
}
getModules(): Map<string, ModuleInfo> {
return this.modules;
}
@ -175,11 +163,8 @@ export class ModuleManager implements ModuleManagerInterface {
* belongs为true
* @param belongs
*/
getModule(moduleID: string, belongs: boolean = false): ModuleInfo {
if (belongs) {
return this.moduleBelongs().get(moduleID);
}
return this.modules.get(moduleID);
getModule(id: string): ModuleInfo {
return this.modules.get(id);
}
/**
@ -197,7 +182,7 @@ export class ModuleManager implements ModuleManagerInterface {
*/
private set(moduleInfo: ModuleInfo) {
// 避免重置
this.modules.set(moduleInfo.moduleID, moduleInfo);
this.modules.set(moduleInfo.name, moduleInfo);
this.setFeatures(moduleInfo);
}
@ -211,7 +196,7 @@ export class ModuleManager implements ModuleManagerInterface {
if (!this.features.has(key)) {
this.features.set(key, new Map());
}
this.features.get(key).set(moduleInfo.moduleID, { name: moduleInfo.name, ...value });
this.features.get(key).set(moduleInfo.name, { extensionID: moduleInfo.name, ...value });
});
}
}
@ -222,7 +207,7 @@ export class ModuleManager implements ModuleManagerInterface {
*/
private delete(moduleInfo: ModuleInfo) {
// 避免删除核心
this.modules.delete(moduleInfo.moduleID);
this.modules.delete(moduleInfo.name);
this.deleteFeatures(moduleInfo);
}
@ -234,7 +219,7 @@ export class ModuleManager implements ModuleManagerInterface {
if (moduleInfo.features && typeof moduleInfo.features === 'object' && isNotEmpty(moduleInfo.features)) {
for (const key in moduleInfo.features) {
if (this.features.has(key)) {
this.features.get(key).delete(moduleInfo.moduleID);
this.features.get(key).delete(moduleInfo.name);
}
}
}
@ -245,7 +230,7 @@ export class ModuleManager implements ModuleManagerInterface {
* @param moduleInfo
*/
private setup(moduleInfo: ModuleInfo) {
if (moduleInfo && isNotEmpty(moduleInfo.moduleID)) {
if (moduleInfo && isNotEmpty(moduleInfo.name)) {
this.set(moduleInfo);
}
}
@ -254,55 +239,86 @@ export class ModuleManager implements ModuleManagerInterface {
* package.json文件得到本地安装的模块列表
*/
private init() {
const moduleNames: string[] = this.moduleHandler.list();
moduleNames.forEach((moduleName: string) => {
const names: string[] = this.moduleHandler.list();
names.forEach((name: string) => {
// 这里要加上try catch避免异常
const moduleInfo: ModuleInfo = this.moduleHandler.info(moduleName);
const moduleInfo: ModuleInfo = this.moduleHandler.info(name);
this.setup(moduleInfo);
});
}
/**
*
* @returns
*/
private moduleBelongs(): Map<string, ModuleInfo> {
const newModules: Map<string, ModuleInfo> = new Map();
const sideItems = new Map();
this.modules?.forEach((module: ModuleInfo) => {
// 如果包含自己则是主应用
// 后期加入权限限制是否能成为顶层应用
const belongs: string[] = module.belongs || ['default'];
module.isApp = belongs.includes(module.moduleID);
newModules.set(module.moduleID, module);
belongs.forEach((belong: string) => {
// let _modules: string[];
if (module.moduleType === ModuleType.app) {
/*
if (!sideItems.has(belong)) {
_modules = [];
} else {
_modules = sideItems.get(belong);
}
*/
const _modules: string[] = sideItems.get(belong) || [];
// 如果指定上层是自己,自己放最前面
if (module.moduleID === belong) {
_modules.unshift(module.moduleID);
} else {
_modules.push(module.moduleID);
}
sideItems.set(belong, _modules);
getExtFeatures(extName: string) {}
async getExtPageInfo(
extName: string,
featureName: string,
ServerMap: typeof extSidebarViewServerMap
): Promise<SidebarView> {
try {
const extPath = this.moduleHandler.getModuleDir(extName);
const stats = await lstat(extPath);
const feature = require(path.join(extPath, 'package.json')).features[featureName];
// 是否为软连接是则为本地开发需要提供本地开发web服务地址
if (stats.isSymbolicLink()) {
if (feature?.debugUrl) {
return {
url: feature?.debugUrl,
...feature,
};
}
});
});
sideItems?.forEach((value: Array<string>, key: string) => {
const _current: ModuleInfo = newModules.get(key);
if (_current && _current.isApp) {
_current.sideItems = value;
newModules.set(key, _current);
}
});
return newModules;
// 生产环境需要提供 html 入口文件地址(pageEntry字段)
if (feature?.url) {
if (ServerMap.has(extName)) {
return ServerMap.get(extName);
}
const port = await portfinder.getPortPromise();
const pageDir = path.parse(path.join(extPath, feature?.url)).dir;
console.log('extension pageDir', pageDir);
const server = createServer({ root: pageDir });
server.listen(port);
const url = `http://127.0.0.1:${port}`;
ServerMap.set(extName, {
...feature,
url,
server,
});
return ServerMap.get(extName);
}
return Promise.reject('该插件package.json缺少sidebarView字段');
} catch (error) {
return Promise.reject(error);
}
}
setupExtensionPageServer() {}
async getExtTabs(extName: string): Promise<ExtensionTabView[]> {
const result = [];
for (let index = 0; index < this.installExtension.length; index++) {
try {
const sidebarView = await this.getExtPageInfo(
this.installExtension[index].name,
'sidebarView',
extSidebarViewServerMap
);
result.push(sidebarView);
} catch (error) {}
}
return result;
}
async getSidebarViews(): Promise<SidebarView[]> {
const result = [];
for (let index = 0; index < this.installExtension.length; index++) {
try {
const sidebarView = await this.getExtPageInfo(
this.installExtension[index].name,
'sidebarView',
extSidebarViewServerMap
);
result.push(sidebarView);
} catch (error) {}
}
return result;
}
}

View File

@ -1,3 +1,5 @@
import { ModuleInfo } from "eo/platform/node/extension-manager/types";
/**
*
* baseDir
@ -17,3 +19,32 @@ export interface ModuleHandlerResult {
code: number;
data: string;
}
/**
*
* name
* isLocal (link, unlink安装与卸载)
*/
export interface ModuleManagerInfo {
name: string;
isLocal?: boolean;
}
/**
*
* install
* uninstall
* refresh
* getModules
*/
export interface ModuleManagerInterface {
installExt: any;
install: (module: ModuleManagerInfo) => Promise<ModuleHandlerResult>;
uninstall: (module: ModuleManagerInfo) => Promise<ModuleHandlerResult>;
refresh: (module: ModuleManagerInfo) => void;
refreshAll: () => void;
getModule: (id: string) => ModuleInfo;
getModules: () => Map<string, ModuleInfo>;
getFeature: (featureKey: string) => Map<string, object>;
getFeatures: () => Map<string, Map<string, object>>;
setupExtensionPageServer: (extName: string) => any;
}

View File

@ -1,3 +1,2 @@
export * from './handler';
export * from './manager';
export * from './loader';
export * from './module';

View File

@ -1,18 +0,0 @@
import {ModuleInfo} from './manager';
/**
* .
*/
export interface ModuleLoaderInterface {
loadModules: (modules: Array<ModuleInfo>) => void;
loadModule: (module: ModuleInfo) => void;
}
/**
*
*/
export enum ModuleRuntime {
main = 'main',
render = 'render',
web = 'web'
}

View File

@ -1,131 +0,0 @@
import { SidePosition } from 'eo/shared/common/bounds';
import { ModuleHandlerResult } from './handler';
/**
*
* system
* app
* ui
* feature
*/
export enum ModuleType {
system = 'system',
app = 'app',
feature = 'feature',
}
export interface I18nLocale {
locale: string;
package: any;
}
/**
*
*/
export interface ModuleInfo {
// npm package name
name: string;
// author
author: string;
// extension version
version: string;
// extension description
description: string;
// extension intro,from README.md
introduction: string;
// extension ID
moduleID: string;
// extension name
moduleName: string;
// extension type
//!TODO what usage
moduleType: ModuleType;
// extension logo
logo: string;
// manifest code file
main: string;
// main node script
main_node?: string;
// 入口开发调试
main_debug?: string;
// inject script before start app
preload?: string;
// 判断是不是顶层App
//!TODO use feature contribution to control page
isApp?: boolean;
// 模块对应上层模块ID
//!TODO what usage?
belongs?: Array<string>;
// 下层关联模块ID集合
//!TODO what usage?
sideItems?: Array<string>;
// 模块路径
baseDir?: string;
// 配置项
configuration?: ModuleConfiguration;
/** 贡献点 */
contributes: ModuleContributes;
// 功能点配置
features?: {
[index: string]: any;
};
i18n?: I18nLocale[];
}
/**
*
*/
export type ModuleContributes = {
configuration: ModuleConfiguration;
};
/**
*
*/
export interface ModuleConfiguration {
title: string;
properties: {
[index: string]: ModuleConfigurationField;
};
}
/**
*
*/
export interface ModuleConfigurationField {
type: string | Array<string>;
default: string | number | null;
label: string;
description?: string;
required?: boolean;
}
/**
*
* name
* isLocal (link, unlink安装与卸载)
*/
export interface ModuleManagerInfo {
name: string;
isLocal?: boolean;
}
/**
*
* install
* uninstall
* refresh
* getModules
*/
export interface ModuleManagerInterface {
installExt: any;
install: (module: ModuleManagerInfo) => Promise<ModuleHandlerResult>;
uninstall: (module: ModuleManagerInfo) => Promise<ModuleHandlerResult>;
refresh: (module: ModuleManagerInfo) => void;
refreshAll: () => void;
getModule: (moduleID: string, belongs?: boolean) => ModuleInfo;
getModules: (belongs?: boolean) => Map<string, ModuleInfo>;
getAppModuleList: () => Array<ModuleInfo>;
getSideModuleList: (moduleID: string) => Array<ModuleInfo>;
getFeature: (featureKey: string) => Map<string, object>;
getFeatures: () => Map<string, Map<string, object>>;
}

View File

@ -0,0 +1,113 @@
import { createServer } from 'http-server/lib/http-server';
export type FeatureInfo = {
icon: string;
title: string;
description: string;
//Function name
action: string;
//ExportAPI.Filename
filename?: string;
//*Field for browser generate by code,not actually in package.json
extensionID: string;
//!Will deprecated
label: string;
};
/**
*
*/
export interface ModuleInfo {
//Unique npm package name
name: string;
version: string;
author: string | { name: 'string' };
//Entry js file,webRender enviroment
main: string;
// extension description
description: string;
//* Eoapi extend
//Entry js file,node enviroment
node: string;
title: string;
// extension logo
logo: string;
//Contribution Feature
features?: {
configuration: ModuleConfiguration;
i18n?: I18nLocale;
extensionTabView: ExtensionTabView;
sidebarView: SidebarView;
importAPI: FeatureInfo;
exportAPI: FeatureInfo;
syncAPI: FeatureInfo;
//Random feature
[index: string]: any;
//!Will Deprecated
'apimanage.export': FeatureInfo;
'apimanage.import': FeatureInfo;
'apimanage.sync': FeatureInfo;
};
//*Field for browser generate by code,not actually in package.json
//Extension intro,from README.md
introduction: string;
//file location
baseDir: string;
//*Only exist in HTTP request(from extension server) moduleInfo
i18n: {
locale: string;
package: any | object;
}[];
//!Will Deprecated
// 模块ID用于关联
moduleID: string;
// 模块名称,用于显示
moduleName: string;
}
/**
*
*/
interface ModuleConfiguration {
title: string;
properties: {
[index: string]: ModuleConfigurationField;
};
}
/**
*
*/
interface ModuleConfigurationField {
type: string | Array<string>;
default: string | number | null;
label: string;
description?: string;
required?: boolean;
}
export type ExtensionTabView = {
name: string;
url: string;
debugUrl: string;
server: HttpServer;
};
type HttpServer = ReturnType<typeof createServer>;
export type SidebarView = {
icon: string;
title: string;
url: string;
debugUrl: string;
server: HttpServer;
};
export interface I18nLocale {
sourceLocale: string;
locales: string[];
}

View File

@ -1,4 +1,4 @@
import { I18nLocale, ModuleInfo } from 'eo/platform/node/extension-manager';
import { ModuleInfo } from 'eo/platform/node/extension-manager/types';
interface LooseObject {
[key: string]: any;
@ -6,7 +6,7 @@ interface LooseObject {
const localeStorage: LooseObject = {};
/**
* Get locale file from extension i18 file dir
* @param moduleID
* @param module
* @returns json
*/
function getLocaleFile(module: ModuleInfo, lang): Object {
@ -26,12 +26,14 @@ function getSupportLang(module: ModuleInfo) {
export function getLocaleData(module: ModuleInfo, lang): Object | null {
let supportLang = getSupportLang(module);
if (!supportLang.includes(lang)) {
console.log(`Error: extenaion ${module.moduleID} can't find the i18n package ${lang}`);
console.error(`Error: extension ${module.title || module.moduleName} can't find the i18n package ${lang}`);
return null;
}
localeStorage[module.moduleID] = localeStorage[module.moduleID] || {};
if (!localeStorage[module.moduleID][lang]) {
localeStorage[module.moduleID][lang] = getLocaleFile(module, lang);
//Get and storage locale data
localeStorage[module.name] = localeStorage[module.name] || {};
if (!localeStorage[module.name][lang]) {
localeStorage[module.name][lang] = getLocaleFile(module, lang);
}
return localeStorage[module.moduleID][lang];
return localeStorage[module.name][lang];
}

View File

@ -69,7 +69,7 @@ export class MockServer {
this.app.all('/mock/:mockID/*', (req, res, next) => {
// if (!protocolReg.test(req.url)) {
// match request type
const isMatchType = this.configuration.getModuleSettings<boolean>('eoapi-features.mock.matchType');
const isMatchType = this.configuration.getExtensionSettings<boolean>('eoapi-features.mock.matchType');
if (req.params.mockID || isMatchType !== false) {
this.view.webContents.send('getMockApiList', JSON.parse(jsonStringify(req)));
ipcMain.once('getMockApiList', (event, message) => {
@ -93,7 +93,7 @@ export class MockServer {
this.app.all('/:workspaceID/:projectID/mock/:mockID/*', (req, res, next) => {
// if (!protocolReg.test(req.url)) {
// match request type
const isMatchType = this.configuration.getModuleSettings<boolean>('eoapi-features.mock.matchType');
const isMatchType = this.configuration.getExtensionSettings<boolean>('eoapi-features.mock.matchType');
if (req.params.mockID || isMatchType !== false) {
this.view.webContents.send('getMockApiList', JSON.parse(jsonStringify(req)));
ipcMain.once('getMockApiList', (event, message) => {

View File

@ -1,93 +0,0 @@
/**
*
*/
export enum SidePosition {
left = 'left',
right = 'right',
top = 'top',
bottom = 'bottom'
};
/**
*
*/
export enum ViewZone {
top = 'top',
bottom = 'bottom',
side = 'side',
main = 'main'
}
/**
*
*/
export interface ViewBounds {
x: number;
y: number;
width: number;
height: number;
}
/**
* bounds
* @param zone
* @param width
* @param height
* @param sidePosition
*/
export const getViewBounds = (zone: ViewZone, width: number, height: number, sidePosition?: SidePosition, sideWidth?: number): ViewBounds => {
return calculateViewBounds(width, height, sidePosition, sideWidth).get(zone);
};
/**
*
* TODO
* @param width
* @param height
* @param sidePosition
*/
export const calculateViewBounds = (width: number, height: number, sidePosition?: SidePosition, sideWidth?: number): Map<ViewZone, ViewBounds> => {
const position: SidePosition = sidePosition || SidePosition.left;
const _topHeight: number = 50;
const _bottomHeight: number = 0;
const _sideWidth: number = sideWidth || 50;
const _mainHeight: number = height - _topHeight - _bottomHeight;
let topBounds: ViewBounds = {x: 0, y: 0, width: width, height: _topHeight};
let bottomBounds: ViewBounds = {x: 0, y: (height - _bottomHeight), width: width, height: _bottomHeight};
let sideBounds: ViewBounds = {x: 0, y: 0, width: _sideWidth, height: _mainHeight };
let mainBounds: ViewBounds = {x: 0, y: 0, width: width, height: _mainHeight};
switch (position) {
case SidePosition.left:
sideBounds.y = topBounds.height;
mainBounds.x = sideBounds.width;
mainBounds.y = topBounds.height;
mainBounds.width -= mainBounds.x;
break;
case SidePosition.right:
sideBounds.y = topBounds.height;
sideBounds.x = width - sideBounds.width;
mainBounds.y = topBounds.height;
mainBounds.width = sideBounds.x;
break;
case SidePosition.top:
sideBounds.height = sideBounds.width;
sideBounds.width = width;
sideBounds.y = topBounds.height;
mainBounds.height = height - topBounds.height - sideBounds.height - bottomBounds.height;
mainBounds.y = topBounds.height + sideBounds.height;
break;
case SidePosition.bottom:
sideBounds.height = sideBounds.width;
sideBounds.width = width;
mainBounds.y = topBounds.height;
mainBounds.height -= sideBounds.height;
sideBounds.y = topBounds.height + mainBounds.height;
break;
}
return new Map([
[ViewZone.top, topBounds],
[ViewZone.bottom, bottomBounds],
[ViewZone.side, sideBounds],
[ViewZone.main, mainBounds]
]);
};

View File

@ -12,6 +12,7 @@ src/**/*.js
!src/ng1/**/*.js
!src/assets/libs/*.js
*.js.map
proxy.conf.json
# dependencies
node_modules

View File

@ -1 +1 @@
{"$schema":"./node_modules/@angular/cli/lib/config/schema.json","cli":{"analytics":false,"defaultCollection":"@angular-eslint/schematics"},"version":1,"newProjectRoot":"projects","projects":{"eoapi":{"root":"","i18n":{"sourceLocale":{"code":"en","baseHref":""},"locales":{"zh":{"translation":"src/locale/messages.zh.xlf","baseHref":""}}},"sourceRoot":"src","projectType":"application","schematics":{"@schematics/angular:application":{"strict":true}},"architect":{"build":{"builder":"@angular-builders/custom-webpack:browser","options":{"localize":true,"aot":true,"outputPath":"dist","index":"src/index.html","main":"src/main.ts","tsConfig":"src/tsconfig.app.json","polyfills":"src/polyfills.ts","assets":["src/icon.ico","src/assets",{"glob":"**/*","input":"../../../node_modules/monaco-editor/min/vs","output":"/assets/vs/"}],"styles":[{"input":"src/assets/theme/classic_forest.scss","bundleName":"classic_forest","inject":false},"src/assets/theme/antd.less","src/styles.scss","src/assets/font/iconfont.css","src/ng1/index.css"],"scripts":["src/ng1/lib/angular/angular.js","src/ng1/app.module.js","src/ng1/component/select-default.js","src/ng1/component/sort-and-filter.js","src/ng1/component/auto-complete.js","src/ng1/component/list-block.js","src/ng1/directive/get-dom-length.directive.js","src/ng1/directive/drop-down-menu.directive.js","src/ng1/directive/sort.directive.js","src/ng1/directive/drop-change-space.directive.js","src/ng1/directive/inner-html.directive.js","src/ng1/directive/insert-html.directive.js","src/ng1/directive/copy-common.directive.js","src/assets/libs/protocolcheck.js"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true},"allowedCommonJsDependencies":["brace","qs","rxjs"]},"configurations":{"dev":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"localize":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"devCn":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"localize":["zh"],"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"production":{"optimization":true,"outputHashing":"all","sourceMap":false,"namedChunks":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":true,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.prod.ts"}]}}},"serve":{"builder":"@angular-builders/custom-webpack:dev-server","options":{"browserTarget":"eoapi:build"},"configurations":{"dev":{"browserTarget":"eoapi:build:dev"},"devCn":{"browserTarget":"eoapi:build:devCn"},"production":{"browserTarget":"eoapi:build:production"}}},"extract-i18n":{"builder":"@angular-devkit/build-angular:extract-i18n","options":{"browserTarget":"eoapi:build"}},"test":{"builder":"@angular-builders/custom-webpack:karma","options":{"main":"src/test.ts","polyfills":"src/polyfills-test.ts","tsConfig":"src/tsconfig.spec.json","karmaConfig":"src/karma.conf.js","scripts":[],"styles":["src/styles.scss"],"assets":["src/assets"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true}}},"lint":{"builder":"@angular-eslint/builder:lint","options":{"lintFilePatterns":["src/**/*.ts","src/**/*.html"]}}}},"eoapi-e2e":{"root":"e2e","projectType":"application","architect":{"lint":{"builder":"@angular-eslint/builder:lint","options":{"lintFilePatterns":["e2e/**/*.ts"]}}}}},"defaultProject":"eoapi","schematics":{"@schematics/angular:component":{"prefix":"eo","style":"scss"},"@schematics/angular:directive":{"prefix":"eo"}}}
{"$schema":"./node_modules/@angular/cli/lib/config/schema.json","cli":{"analytics":false,"defaultCollection":"@angular-eslint/schematics"},"version":1,"newProjectRoot":"projects","projects":{"eoapi":{"root":"","i18n":{"sourceLocale":{"code":"en","baseHref":""},"locales":{"zh":{"translation":"src/locale/messages.zh.xlf","baseHref":""}}},"sourceRoot":"src","projectType":"application","schematics":{"@schematics/angular:application":{"strict":true}},"architect":{"build":{"builder":"@angular-builders/custom-webpack:browser","options":{"localize":true,"aot":true,"outputPath":"dist","index":"src/index.html","main":"src/main.ts","tsConfig":"src/tsconfig.app.json","polyfills":"src/polyfills.ts","assets":["src/icon.ico","src/assets",{"glob":"**/*","input":"../../../node_modules/monaco-editor/min/vs","output":"/assets/vs/"}],"styles":[{"input":"src/assets/theme/classic_forest.scss","bundleName":"classic_forest","inject":false},"src/assets/theme/antd.less","src/styles.scss","src/assets/font/iconfont.css","src/ng1/index.css"],"scripts":["src/ng1/lib/angular/angular.js","src/ng1/app.module.js","src/ng1/component/select-default.js","src/ng1/component/sort-and-filter.js","src/ng1/component/auto-complete.js","src/ng1/component/list-block.js","src/ng1/directive/get-dom-length.directive.js","src/ng1/directive/drop-down-menu.directive.js","src/ng1/directive/sort.directive.js","src/ng1/directive/drop-change-space.directive.js","src/ng1/directive/inner-html.directive.js","src/ng1/directive/insert-html.directive.js","src/ng1/directive/copy-common.directive.js","src/assets/libs/protocolcheck.js"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true},"allowedCommonJsDependencies":["brace","qs","rxjs"]},"configurations":{"dev":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"localize":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"devCn":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"localize":["zh"],"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"production":{"optimization":true,"outputHashing":"all","sourceMap":false,"namedChunks":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":true,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.prod.ts"}]},"docker":{"optimization":true,"outputHashing":"all","sourceMap":false,"namedChunks":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":true,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.docker.ts"}]}}},"serve":{"builder":"@angular-builders/custom-webpack:dev-server","options":{"browserTarget":"eoapi:build"},"configurations":{"dev":{"browserTarget":"eoapi:build:dev"},"devCn":{"browserTarget":"eoapi:build:devCn"},"production":{"browserTarget":"eoapi:build:production"}}},"extract-i18n":{"builder":"@angular-devkit/build-angular:extract-i18n","options":{"browserTarget":"eoapi:build"}},"test":{"builder":"@angular-builders/custom-webpack:karma","options":{"main":"src/test.ts","polyfills":"src/polyfills-test.ts","tsConfig":"src/tsconfig.spec.json","karmaConfig":"src/karma.conf.js","scripts":[],"styles":["src/styles.scss"],"assets":["src/assets"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true}}},"lint":{"builder":"@angular-eslint/builder:lint","options":{"lintFilePatterns":["src/**/*.ts","src/**/*.html"]}}}},"eoapi-e2e":{"root":"e2e","projectType":"application","architect":{"lint":{"builder":"@angular-eslint/builder:lint","options":{"lintFilePatterns":["e2e/**/*.ts"]}}}}},"defaultProject":"eoapi","schematics":{"@schematics/angular:component":{"prefix":"eo","style":"scss"},"@schematics/angular:directive":{"prefix":"eo"}}}

View File

@ -1,6 +1,9 @@
const fs = require('fs');
const { execSync } = require('child_process');
class webPlatformBuilder {
constructor(environment) {
this.environment = environment;
}
resetBuildConfig(json) {
delete json.projects.eoapi.i18n.sourceLocale.baseHref;
Object.keys(json.projects.eoapi.i18n.locales).forEach((val) => {
@ -9,7 +12,7 @@ class webPlatformBuilder {
return json;
}
executeBuild() {
execSync('ng build -c production', { stdio: 'inherit' });
execSync(`ng build -c ${this.environment}`, { stdio: 'inherit' });
fs.writeFile(
'./dist/index.html',
`<!DOCTYPE html>
@ -22,7 +25,7 @@ class webPlatformBuilder {
try{
lang=JSON.parse(window.localStorage.getItem("LOCAL_SETTINGS_KEY"))["eoapi-language"]=='en-US'?'en':'zh';
}catch(e){
}
let baseDir="/"+lang+'/'
let search={};
@ -53,10 +56,10 @@ class appPlatformBuilder {
}
}
class PlatformBuilder {
constructor(platForm) {
constructor(platForm, environment) {
switch (platForm) {
case 'web': {
this.instance = new webPlatformBuilder();
this.instance = new webPlatformBuilder(environment);
break;
}
case 'app': {
@ -79,5 +82,6 @@ class PlatformBuilder {
}
}
let platform = process.argv[2] || 'app';
let platformBuilder = new PlatformBuilder(platform);
let environment = process.argv[3] || 'production';
let platformBuilder = new PlatformBuilder(platform, environment);
platformBuilder.build();

View File

@ -0,0 +1,21 @@
import { Render } from 'ecode/dist/render';
export class ApiContent extends Render {
constructor() {
super({ children: [] });
}
render() {
return {
type: 'element',
imports: [
{
target: [{ name: 'ApiModule', type: 'module' }],
from: 'eo/workbench/browser/src/app/pages/api/api.module',
},
],
template: `<eo-api></eo-api>`,
data: [],
methods: [],
};
}
}

View File

@ -28,3 +28,4 @@ export { HTTPS } from './service/httpS';
// * 业务
export { ManageAccess } from './manageAccess';
export { ApiContent } from './apiContent';

View File

@ -22,7 +22,7 @@ export class EventS extends Render {
`;
}
send(name, data = '{}') {
return `this.message.send({type: '${name}', data: ${data}})`;
return EventS.send(name, (data = '{}'));
}
render() {
return {
@ -47,4 +47,7 @@ export class EventS extends Render {
methods: [],
};
}
static send(name, data = '{}') {
return `this.message.send({ type: '${name}', data: ${data} })`;
}
}

View File

@ -1,21 +1,107 @@
import { Render } from 'ecode/dist/render';
export class Tree extends Render {
constructor({ data }) {
id;
showline;
clickMethod;
constructor({ id, showline = false, click = [] }) {
super({ children: [] });
this.id = Render.toCamel(id);
this.showline = showline;
this.clickMethod = Render.callbackRender(click);
}
setData(data) {
return Tree.setData(this.id, data);
}
render() {
const mainMethods = [
`async handleClickTree${this.id}Data($event) {
${this.clickMethod}
}`,
];
return {
type: 'element',
imports: [
{
target: [{ name: 'NzTreeModule', type: 'module' }],
from: 'ng-zorro-antd/tree',
target: [{ name: 'ApiModule', type: 'module' }],
from: 'eo/workbench/browser/src/app/pages/api/api.module',
},
],
template: `<nz-tree [nzData]="nodes" nzShowLine (nzClick)="nzEvent($event)"></nz-tree>`,
data: [],
methods: [],
template: `<eo-api-group-tree ${this.showline ? 'nzShowLine' : ''}></eo-api-group-tree>`,
data: [
// {
// name: `tree${this.id}Data`,
// init: `[
// {
// title: '0-0',
// key: '00',
// expanded: true,
// children: [
// {
// title: '0-0-0',
// key: '000',
// expanded: true,
// children: [
// { title: '0-0-0-0', key: '0000', isLeaf: true },
// { title: '0-0-0-1', key: '0001', isLeaf: true },
// { title: '0-0-0-2', key: '0002', isLeaf: true }
// ]
// },
// {
// title: '0-0-1',
// key: '001',
// children: [
// { title: '0-0-1-0', key: '0010', isLeaf: true },
// { title: '0-0-1-1', key: '0011', isLeaf: true },
// { title: '0-0-1-2', key: '0012', isLeaf: true }
// ]
// },
// {
// title: '0-0-2',
// key: '002'
// }
// ]
// },
// {
// title: '0-1',
// key: '01',
// children: [
// {
// title: '0-1-0',
// key: '010',
// children: [
// { title: '0-1-0-0', key: '0100', isLeaf: true },
// { title: '0-1-0-1', key: '0101', isLeaf: true },
// { title: '0-1-0-2', key: '0102', isLeaf: true }
// ]
// },
// {
// title: '0-1-1',
// key: '011',
// children: [
// { title: '0-1-1-0', key: '0110', isLeaf: true },
// { title: '0-1-1-1', key: '0111', isLeaf: true },
// { title: '0-1-1-2', key: '0112', isLeaf: true }
// ]
// }
// ]
// },
// {
// title: '0-2',
// key: '02',
// isLeaf: true
// }
// ]`,
// type: ['any[]'],
// },
],
init: [],
methods: [...mainMethods],
};
}
static setData(id, data) {
return `
\/\/ * set tree data
this.tree${Render.toCamel(id)}Data = ${data}`;
}
}

View File

@ -8,7 +8,8 @@
"start:zh": "ng serve -c devCn -o",
"build": "node ./build/build.js",
"build:web": "node ./build/build.js web",
"ng:serve": "ng serve -c dev -o",
"build:docker:web": "node ./build/build.js web docker",
"ng:serve": "ng serve -c dev -o --proxy-config=proxy.conf.json",
"lang:gen": "ng extract-i18n --output-path src/locale",
"test": "ng test --watch=false",
"test:watch": "ng test",
@ -32,6 +33,7 @@
"@angular/upgrade": "^14.0.3",
"@ant-design/icons-angular": "14.1.0",
"@babel/runtime": "7.18.3",
"@micro-zoe/micro-app": "1.0.0-alpha.9",
"@ngxs/store": "3.7.4",
"ajv": "8.11.0",
"js-beautify": "1.14.4",

View File

@ -1,10 +0,0 @@
{
"/api": {
"target": "https://290135738u.oicp.vip/",
"secure": false,
"pathRewrite": {
"^/api": ""
},
"logLevel": "debug"
}
}

View File

@ -1,6 +1,7 @@
import { NgModule } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './shared/components';
import { APP_BASE_HREF } from '@angular/common';
const routes: Routes = [
{
@ -23,5 +24,9 @@ const routes: Routes = [
RouterModule.forRoot(routes, { useHash: !!(window && window.process && window.process.type) ? true : false }),
],
exports: [RouterModule],
// 👇 设置基础路由
providers: [
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppRoutingModule {}

View File

@ -1,6 +1,6 @@
import { Component } from '@angular/core';
import { ElectronService, ThemeService } from './core/services';
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service';
import { ThemeService } from './core/services';
import { WebExtensionService } from 'eo/workbench/browser/src/app/shared/services/web-extension/webExtension.service';
@Component({
selector: 'eo-root',
@ -10,12 +10,11 @@ import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/
`,
})
export class AppComponent {
constructor(private theme: ThemeService, private dataSource: DataSourceService, private electron: ElectronService) {
constructor(
private theme: ThemeService,
private webExtensionService: WebExtensionService
) {
this.webExtensionService.init();
this.theme.changeTheme();
//Check Connection at fisrt
if (!this.dataSource.isRemote || !this.electron.isElectron) {
return;
}
this.dataSource.checkRemoteAndTipModal();
}
}

View File

@ -1,9 +1,10 @@
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule, LOCALE_ID } from '@angular/core';
import { NgModule, LOCALE_ID, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { EouiModule } from 'eo/workbench/browser/src/app/eoui/eoui.module';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
//Other module
import { CoreModule } from './core/core.module';
@ -17,7 +18,6 @@ import { UpgradeModule } from '@angular/upgrade/static';
import { IndexedDBStorage } from 'eo/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/';
import { HttpStorage } from 'eo/workbench/browser/src/app/shared/services/storage/http/lib';
import { StorageService } from 'eo/workbench/browser/src/app/shared/services/storage';
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service';
import { SettingService } from 'eo/workbench/browser/src/app/core/services/settings/settings.service';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';
@ -33,13 +33,16 @@ import { registerLocaleData } from '@angular/common';
import { en_US, NZ_I18N, zh_CN } from 'ng-zorro-antd/i18n';
import en from '@angular/common/locales/en';
import zh from '@angular/common/locales/zh';
import { Vue3Component } from 'eo/workbench/browser/src/app/pages/vue3/vue3.component';
registerLocaleData(en);
registerLocaleData(zh);
@NgModule({
declarations: [AppComponent],
declarations: [AppComponent, Vue3Component],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
BrowserModule,
BrowserAnimationsModule,
CoreModule,
@ -54,7 +57,6 @@ registerLocaleData(zh);
SettingService,
ExtensionService,
StorageService,
DataSourceService,
IndexedDBStorage,
HttpStorage,
NzMessageService,
@ -65,7 +67,6 @@ registerLocaleData(zh);
deps: ['$injector'],
},
{ provide: HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true },
// { provide: HTTP_INTERCEPTORS, useClass: RemoteUrlInterceptor, multi: true },
{
provide: NZ_I18N,
useFactory: (localId: string) => {
@ -80,6 +81,7 @@ registerLocaleData(zh);
},
],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {
constructor(private upgrade: UpgradeModule, private lang: LanguageService, private appService: AppService) {

View File

@ -8,19 +8,18 @@ import {
StorageResStatus,
} from 'eo/workbench/browser/src/app/shared/services/storage/index.model';
import { StorageService } from 'eo/workbench/browser/src/app/shared/services/storage/storage.service';
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service';
@Injectable()
export class AppService {
private ipcRenderer: IpcRenderer = window.require?.('electron')?.ipcRenderer;
constructor(private storageService: StorageService, private dataSource: DataSourceService) {}
constructor(private storageService: StorageService) {}
init() {
if (this.ipcRenderer) {
this.ipcRenderer.on('getMockApiList', async (event, req = {}) => {
const sender = event.sender;
const isEnabledMatchType = window.eo?.getModuleSettings?.('eoapi-features.mock.matchType') !== false;
const isEnabledMatchType = window.eo?.getExtensionSettings?.('eoapi-features.mock.matchType') !== false;
const { mockID } = req.params;
if (Number.isInteger(Number(mockID))) {
try {

View File

@ -26,14 +26,16 @@ export class LanguageService {
return;
}
this.systemLanguage = localeID;
const localePath = (this.languages.find((val) => val.value === localeID) || this.languages[0]).path;
// const localePath = (this.languages.find((val) => val.value === localeID) || this.languages[0]).path;
if (this.electron.isElectron) {
this.electron.ipcRenderer.send('message', {
action: 'changeLanguage',
data: this.systemLanguage,
});
} else {
window.location.href = `/${localePath}`;
const url = window.location.href;
const langHash = new Map().set('zh-Hans', 'zh').set('en-US', 'en');
window.location.replace(url.replace(/\/(zh|en)\/home\//, `/${langHash.get(localeID)}/home/`));
}
}
}

View File

@ -1,10 +0,0 @@
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service'
@Component({
selector: 'eo-',
template: ``
})
export class undefinedComponent implements OnInit {
constructor(public api: RemoteService) {}
async ngOnInit(): void {}
}

View File

@ -1,37 +1,23 @@
import {
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
Validators
} from '@angular/forms'
import { ViewChild, ElementRef, Component, OnInit } from '@angular/core'
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service'
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service'
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service'
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service'
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { ViewChild, ElementRef, Component, OnInit } from '@angular/core';
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service';
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service';
@Component({
selector: 'eo-account',
template: `<h2 class="text-lg flex justify-between items-center">
<span class="font-bold text-lg mb-2" i18n>Account</span>
</h2>
<h2
class="text-lg flex justify-between items-center"
id="eoapi-account-username"
>
<h2 class="text-lg flex justify-between items-center" id="eoapi-account-username">
<span class="font-bold text-base mb-2" i18n>Username</span>
</h2>
<section class="w-1/2">
<form nz-form [formGroup]="validateUsernameForm" nzLayout="vertical">
<nz-form-item>
<nz-form-control nzErrorTip="Please input your username;">
<input
type="text"
nz-input
formControlName="username"
placeholder=""
i18n-placeholder
/>
<input type="text" nz-input formControlName="username" placeholder="" i18n-placeholder />
</nz-form-control>
</nz-form-item>
@ -42,7 +28,7 @@ import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-m
type="submit"
class="w-[84px]"
nzType="primary"
(click)="btnxxkunjCallback()"
(click)="btnw9ec5mCallback()"
i18n
>
Save
@ -51,72 +37,39 @@ import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-m
</form>
<section class="h-4"></section>
</section>
<h2
class="text-lg flex justify-between items-center"
id="eoapi-account-password"
>
<h2 class="text-lg flex justify-between items-center" id="eoapi-account-password">
<span class="font-bold text-base mb-2" i18n>Password</span>
</h2>
<section class="w-1/2">
<form nz-form [formGroup]="validatePasswordForm" nzLayout="vertical">
<nz-form-item>
<nz-form-label [nzSpan]="24" nzRequired i18n
>Current password</nz-form-label
>
<nz-form-label [nzSpan]="24" nzRequired i18n>Current password</nz-form-label>
<nz-form-control nzErrorTip="Please input your current password;">
<input
type="password"
nz-input
formControlName="oldPassword"
placeholder=""
i18n-placeholder
/>
<input type="password" nz-input formControlName="oldPassword" placeholder="" i18n-placeholder />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="24" nzRequired i18n
>New password</nz-form-label
>
<nz-form-label [nzSpan]="24" nzRequired i18n>New password</nz-form-label>
<nz-form-control nzErrorTip="Please input your new password;">
<input
type="password"
nz-input
formControlName="newPassword"
placeholder=""
i18n-placeholder
/>
<input type="password" nz-input formControlName="newPassword" placeholder="" i18n-placeholder />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="24" nzRequired i18n
>Confirm new password</nz-form-label
>
<nz-form-label [nzSpan]="24" nzRequired i18n>Confirm new password</nz-form-label>
<nz-form-control [nzErrorTip]="confirmPasswordErrorTpl">
<input
type="password"
nz-input
formControlName="confirmPassword"
placeholder=""
i18n-placeholder
/>
<input type="password" nz-input formControlName="confirmPassword" placeholder="" i18n-placeholder />
<ng-template #confirmPasswordErrorTpl let-control>
<ng-container *ngIf="control.hasError('required')" i18n>
Please input your confirm new password;
</ng-container>
<ng-container *ngIf="control.hasError('isEqual')" i18n>
Please confirm your password;
</ng-container>
<ng-container *ngIf="control.hasError('isEqual')" i18n> Please confirm your password; </ng-container>
<ng-container *ngIf="control.hasError('minlength')" i18n>
Min length is 6;
</ng-container>
<ng-container *ngIf="control.hasError('minlength')" i18n> Min length is 6; </ng-container>
<ng-container *ngIf="control.hasError('maxlength')" i18n>
Max length is 11;
</ng-container>
<ng-container *ngIf="control.hasError('maxlength')" i18n> Max length is 11; </ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
@ -128,7 +81,7 @@ import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-m
type="submit"
class="w-[84px]"
nzType="primary"
(click)="btncf3rvjCallback()"
(click)="btnn016ppCallback()"
i18n
>
Reset
@ -136,13 +89,13 @@ import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-m
</section>
</form>
</section>
<section class="h-4"></section> `
<section class="h-4"></section> `,
})
export class AccountComponent implements OnInit {
validateUsernameForm
isSaveUsernameBtnLoading
validatePasswordForm
isResetBtnBtnLoading
validateUsernameForm;
isSaveUsernameBtnLoading;
validatePasswordForm;
isResetBtnBtnLoading;
constructor(
public fb: UntypedFormBuilder,
public user: UserService,
@ -150,16 +103,16 @@ export class AccountComponent implements OnInit {
public api: RemoteService,
public eMessage: EoMessageService
) {
this.validateUsernameForm = UntypedFormGroup
this.isSaveUsernameBtnLoading = false
this.validatePasswordForm = UntypedFormGroup
this.isResetBtnBtnLoading = false
this.validateUsernameForm = UntypedFormGroup;
this.isSaveUsernameBtnLoading = false;
this.validatePasswordForm = UntypedFormGroup;
this.isResetBtnBtnLoading = false;
}
async ngOnInit(): Promise<void> {
// * Init Username form
this.validateUsernameForm = this.fb.group({
username: [null, [Validators.required]]
})
username: [null, [Validators.required]],
});
// * Init Password form
this.validatePasswordForm = this.fb.group({
@ -167,96 +120,86 @@ export class AccountComponent implements OnInit {
newPassword: [null, [Validators.required]],
confirmPassword: [
null,
[
Validators.required,
Validators.minLength(6),
Validators.maxLength(11),
this.dynamicPasswordValidator
]
]
})
[Validators.required, Validators.minLength(6), Validators.maxLength(11), this.dynamicPasswordValidator],
],
});
// * get Username form values
this.validateUsernameForm.patchValue({
username: this.user.userProfile?.username
})
username: this.user.userProfile?.username,
});
}
async btnxxkunjCallback() {
async btnw9ec5mCallback() {
// * click event callback
this.isSaveUsernameBtnLoading = true
this.isSaveUsernameBtnLoading = true;
const btnSaveUsernameRunning = async () => {
const { username: user } = this.validateUsernameForm.value
const { username: user } = this.validateUsernameForm.value;
const [data, err]: any = await this.api.api_userUpdateUserProfile({
username: user
})
username: user,
});
if (err) {
this.eMessage.error($localize`Sorry, username is be used`)
this.eMessage.error($localize`Sorry, username is already in use`);
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
const [pData, pErr]: any = await this.api.api_userReadProfile(null)
const [pData, pErr]: any = await this.api.api_userReadProfile(null);
if (pErr) {
if (pErr.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.user.setUserProfile(pData)
this.eMessage.success($localize`Username update success !`)
}
await btnSaveUsernameRunning()
this.isSaveUsernameBtnLoading = false
this.user.setUserProfile(pData);
this.eMessage.success($localize`Username update success !`);
};
await btnSaveUsernameRunning();
this.isSaveUsernameBtnLoading = false;
}
dynamicPasswordValidator = (
control: UntypedFormControl
): { [s: string]: boolean } => {
if (
control.value &&
control.value !== this.validatePasswordForm.controls.newPassword.value
) {
return { isEqual: true, error: true }
dynamicPasswordValidator = (control: UntypedFormControl): { [s: string]: boolean } => {
if (control.value && control.value !== this.validatePasswordForm.controls.newPassword.value) {
return { isEqual: true, error: true };
}
return {}
}
async btncf3rvjCallback() {
return {};
};
async btnn016ppCallback() {
// * click event callback
this.isResetBtnBtnLoading = true
this.isResetBtnBtnLoading = true;
const btnResetBtnRunning = async () => {
const { oldPassword: oldPassword } = this.validatePasswordForm.value
const { newPassword: newPassword } = this.validatePasswordForm.value
const { oldPassword: oldPassword } = this.validatePasswordForm.value;
const { newPassword: newPassword } = this.validatePasswordForm.value;
const [data, err]: any = await this.api.api_userUpdatePsd({
oldPassword,
newPassword
})
newPassword,
});
if (err) {
this.eMessage.error($localize`Validation failed`)
this.eMessage.error($localize`Validation failed`);
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.user.setLoginInfo(data)
this.eMessage.success($localize`Password reset success !`)
this.user.setLoginInfo(data);
this.eMessage.success($localize`Password reset success !`);
// * Clear password form
this.validatePasswordForm.reset()
}
await btnResetBtnRunning()
this.isResetBtnBtnLoading = false
this.validatePasswordForm.reset();
};
await btnResetBtnRunning();
this.isResetBtnBtnLoading = false;
}
}

View File

@ -6,6 +6,7 @@ import { TabItem } from 'eo/workbench/browser/src/app/pages/api/tab/tab.model';
import { isEmptyObj } from '../../utils/index.utils';
import { MessageService } from '../../shared/services/message';
import { Router } from '@angular/router';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
@Injectable()
export class ApiTabService {
componentRef;
@ -15,27 +16,47 @@ export class ApiTabService {
return this.BASIC_TABS.find((val) => this.router.url.includes(val.pathname));
}
private changeContent$: Subject<any> = new Subject();
BASIC_TABS: Partial<TabItem>[] = [
{
pathname: '/home/api/http/test',
module: 'test',
type: 'edit',
title: $localize`New Request`,
extends: { method: 'POST' },
},
{ pathname: '/home/api/http/edit', module: 'edit', isFixed: true, type: 'edit', title: $localize`New API` },
{ pathname: '/home/api/http/detail', module: 'detail', type: 'preview', title: $localize`Preview` },
{
pathname: '/home/api/ws/test',
module: 'test',
isFixed: true,
type: 'edit',
extends: { method: 'WS' },
title: $localize`New Websocket`,
},
{ pathname: '/home/api/http/mock', module: 'mock', type: 'preview', title: 'Mock' },
];
constructor(private messageService: MessageService, private router: Router) {
BASIC_TABS: Partial<TabItem>[] = this.status.isShare
? [
{
pathname: '/home/share/http/test',
module: 'test',
type: 'edit',
title: $localize`New Request`,
extends: { method: 'POST' },
},
{ pathname: '/home/share/http/detail', module: 'detail', type: 'preview', title: $localize`Preview` },
{
pathname: '/home/share/ws/test',
module: 'test',
isFixed: true,
type: 'preview',
extends: { method: 'WS' },
title: $localize`New Websocket`,
},
]
: [
{
pathname: '/home/api/http/test',
module: 'test',
type: 'edit',
title: $localize`New Request`,
extends: { method: 'POST' },
},
{ pathname: '/home/api/http/edit', module: 'edit', isFixed: true, type: 'edit', title: $localize`New API` },
{ pathname: '/home/api/http/detail', module: 'detail', type: 'preview', title: $localize`Preview` },
{
pathname: '/home/api/ws/test',
module: 'test',
isFixed: true,
type: 'edit',
extends: { method: 'WS' },
title: $localize`New Websocket`,
},
{ pathname: '/home/api/http/mock', module: 'mock', type: 'preview', title: 'Mock' },
];
constructor(private messageService: MessageService, private router: Router, private status: StatusService) {
this.changeContent$.pipe(debounceTime(150)).subscribe((inData) => {
this.afterContentChanged(inData);
});
@ -124,9 +145,9 @@ export class ApiTabService {
}
this.bindChildComponentChangeEvent();
if (!this.componentRef.init) {
if (!this.componentRef?.init) {
this.changeContent$.next({ when: 'init', url });
console.warn('EO_ERROR:Child componentRef need has init function for reflesh data when router change');
console.error('EO_ERROR:Child componentRef need has init function for reflesh data when router change');
return;
}
//?Why should use getCurrentTab()?
@ -183,7 +204,7 @@ export class ApiTabService {
`EO_ERROR:Child componentRef[${this.componentRef.constructor.name}] need has isFormChange function check model change`
);
}
let currentHasChanged = currentTab.hasChanged;
let currentHasChanged = currentTab.extends?.hasChanged?.[contentID] || false;
switch (inData.when) {
case 'editing': {
// Saved APIs do not need to verify changes

View File

@ -10,7 +10,12 @@
>
<!-- <div class="side-container"> -->
<nz-content class="api-container-tabs">
<nz-tabset nzCentered [nzAnimated]="false" [(nzSelectedIndex)]="tabsIndex">
<nz-tabset
[ngClass]="{ 'share-group-tabset': status.isShare }"
nzCentered
[nzAnimated]="false"
[(nzSelectedIndex)]="tabsIndex"
>
<nz-tab [nzTitle]="apiTitle">
<ng-template #apiTitle>
<span
@ -27,7 +32,7 @@
<eo-api-group-tree></eo-api-group-tree>
</div>
</nz-tab>
<nz-tab [nzTitle]="historyTitle">
<nz-tab [nzTitle]="historyTitle" *ngIf="!status.isShare">
<ng-template #historyTitle>
<span
i18n-nzTooltipTitle
@ -74,8 +79,14 @@
<nz-option *ngFor="let item of envList" [nzValue]="item.uuid" [nzLabel]="item.name"></nz-option>
</nz-select>
<ng-template #renderTemplate>
<nz-divider></nz-divider>
<a class="text-sx manager-env" nz-button nzType="link" (click)="gotoEnvManager()" i18n
<nz-divider *ngIf="!status.isShare"></nz-divider>
<a
*ngIf="!status.isShare"
class="text-sx manager-env"
nz-button
nzType="link"
(click)="gotoEnvManager()"
i18n
>Manage Environment</a
>
</ng-template>
@ -106,23 +117,28 @@
*ngIf="this.id"
nzLinkRouter
>
<nz-tab *ngFor="let tab of TABS">
<nz-tab *ngFor="let tab of renderTabs">
<p *ngIf="web.isWeb && tab.onlyDestop">
<span *nzTabLink nz-tab-link (click)="goDownload($event)">{{ tab.title }}</span>
</p>
<span *ngIf="!web.isWeb || !tab.onlyDestop">
<div *ngIf="!web.isWeb || !tab.onlyDestop">
<a *nzTabLink nz-tab-link [routerLink]="['http/' + tab.routerLink]" queryParamsHandling="merge">
{{ tab.title }}</a
>
</span>
{{ tab.title }} <nz-badge *ngIf="apiTabComponent.getCurrentTab()?.extends?.hasChanged?.[tab.routerLink]" nzStatus="success"></nz-badge
></a>
</div>
</nz-tab>
</nz-tabset>
</div>
<section class="relative" [ngStyle]="{ paddingRight: activeBar ? dyWidth + 'px' : '40px' }">
<section class="relative" [ngStyle]="{ paddingRight: countPaddingRight() }">
<!-- Get router child component by activate -->
<router-outlet (activate)="onActivate($event)"></router-outlet>
<eo-split-x *ngIf="activeBar" (x)="handleDrag($event)" [init]="[200, dyWidth, 400]"></eo-split-x>
<eo-split-x
*ngIf="activeBar && !status.isShare"
(x)="handleDrag($event)"
[init]="[200, dyWidth, 400]"
></eo-split-x>
<div
*ngIf="!status.isShare"
class="absolute top-0 bottom-0 right-0 flex right-bar"
[ngClass]="{ 'left-line': !activeBar }"
[ngStyle]="{ width: activeBar ? dyWidth + 'px' : '40px' }"
@ -132,7 +148,6 @@
[ngClass]="activeBar ? 'active' : ''"
(click)="toggleRightBar()"
nz-tooltip
[nzTooltipMouseEnterDelay]="0.7"
[nzTooltipMouseLeaveDelay]="0"
i18n-nzTooltipTitle="@@Environment Setting"
nzTooltipTitle="Environment Setting"

View File

@ -2,7 +2,6 @@ nz-content {
width: 100%;
background-color: var(--MAIN_BG);
}
:lang(zh) {
nz-select {
width: 132px;
@ -45,7 +44,6 @@ nz-divider {
background-color: rgba(0, 0, 0, 0.05);
}
}
::ng-deep {
eo-api {
display: flex;
@ -108,6 +106,11 @@ nz-divider {
align-items: center;
justify-content: space-between;
}
.share-group-tabset{
.ant-tabs-nav{
display: none;
}
}
.content_container {
border-top: 1px solid var(--BORDER);
.inside_page_tabset {
@ -206,12 +209,4 @@ nz-divider {
}
}
}
.eo-tab-theme-icon {
width: 7px;
height: 7px;
display: inline-block;
border-radius: 50%;
background-color: var(--MAIN_THEME_COLOR);
margin-left: 5px;
}
}

View File

@ -10,6 +10,10 @@ import { ApiTabComponent } from 'eo/workbench/browser/src/app/pages/api/tab/api-
import { ApiTabService } from './api-tab.service';
import { NzResizeEvent } from 'ng-zorro-antd/resizable';
import { WebService } from 'eo/workbench/browser/src/app/core/services';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { ShareService } from 'eo/workbench/browser/src/app/shared/services/share.service';
const DY_WIDTH_KEY = 'DY_WIDTH';
const LEFT_SIDER_WIDTH_KEY = 'LEFT_SIDER_WIDTH_KEY';
@ -42,9 +46,11 @@ export class ApiComponent implements OnInit, OnDestroy {
*/
id: number;
pageID: number;
renderTabs = [];
TABS = [
{
routerLink: 'detail',
isShare: true,
title: $localize`:@@API Detail:Preview`,
},
{
@ -53,6 +59,7 @@ export class ApiComponent implements OnInit, OnDestroy {
},
{
routerLink: 'test',
isShare: true,
title: $localize`Test`,
},
{
@ -79,7 +86,11 @@ export class ApiComponent implements OnInit, OnDestroy {
private messageService: MessageService,
private storage: StorageService,
public web: WebService,
private store: Store
private store: Store,
public status: StatusService,
private workspace: WorkspaceService,
private http: RemoteService,
private share: ShareService
) {}
get envUuid(): number | null {
return Number(localStorage.getItem('env:selected')) || 0;
@ -109,6 +120,7 @@ export class ApiComponent implements OnInit, OnDestroy {
this.watchDataSourceChange();
this.initEnv();
this.watchEnvChange();
this.renderTabs = this.status.isShare ? this.TABS.filter((it) => it.isShare) : this.TABS;
}
ngOnDestroy() {
this.destroy$.next();
@ -154,6 +166,12 @@ export class ApiComponent implements OnInit, OnDestroy {
// });
}
countPaddingRight() {
if (this.status.isShare) {
return '0px';
}
return this.activeBar ? this.dyWidth + 'px' : '40px';
}
onResizeEnd() {
this.isDragging = false;
}
@ -178,11 +196,20 @@ export class ApiComponent implements OnInit, OnDestroy {
localStorage.setItem(DY_WIDTH_KEY, String(this.dyWidth));
}
handleEnvSelectStatus(event: boolean) {}
private changeStoreEnv(uuid) {
private async changeStoreEnv(uuid) {
if (uuid == null) {
this.store.dispatch(new Change(null));
return;
}
if (this.status.isShare) {
const [data, err]: any = await this.http.api_shareDocGetEnv({
uniqueID: this.share.shareId,
});
if (err) {
return;
}
return this.store.dispatch(new Change(data));
}
this.storage.run('environmentLoadAllByProjectID', [1], (result: StorageRes) => {
if (result.status === StorageResStatus.success) {
const data = result.data.find((val) => val.uuid === Number(uuid));
@ -226,7 +253,16 @@ export class ApiComponent implements OnInit, OnDestroy {
}
private getAllEnv(uuid?: number) {
const projectID = 1;
return new Promise((resolve) => {
return new Promise(async (resolve) => {
if (this.status.isShare) {
const [data, err]: any = await this.http.api_shareDocGetEnv({
uniqueID: this.share.shareId,
});
if (err) {
return resolve([]);
}
return resolve(data || []);
}
this.storage.run('environmentLoadAllByProjectID', [projectID], async (result: StorageRes) => {
if (result.status === StorageResStatus.success) {
return resolve(result.data || []);

View File

@ -29,6 +29,8 @@ import { NzSelectModule } from 'ng-zorro-antd/select';
import { NzModalModule } from 'ng-zorro-antd/modal';
import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm';
import { NzUploadModule } from 'ng-zorro-antd/upload';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
import { ApiGroupTreeComponent } from './group/tree/api-group-tree.component';
import { ApiTabComponent } from './tab/api-tab.component';
@ -74,6 +76,7 @@ const COMPONENTS = [
NzSpinModule,
EouiModule,
EnvModule,
NzBadgeModule,
NzCardModule,
NzModalModule,
NzSelectModule,
@ -81,7 +84,7 @@ const COMPONENTS = [
SharedModule,
],
declarations: [...COMPONENTS],
exports: [],
exports: [ApiComponent],
providers: [ElectronService, ApiService, ApiTabService, ApiTabOperateService, ApiTabStorageService, IndexedDBStorage],
})
export class ApiModule {}

View File

@ -4,6 +4,9 @@ import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-m
import { MessageService } from '../../shared/services/message';
import { StorageService } from '../../shared/services/storage';
import { Router } from '@angular/router';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { ShareService } from 'eo/workbench/browser/src/app/shared/services/share.service';
@Injectable({
providedIn: 'root',
})
@ -12,10 +15,23 @@ export class ApiService {
private messageService: MessageService,
private message: EoMessageService,
private router: Router,
private storage: StorageService
private storage: StorageService,
private status: StatusService,
private http: RemoteService,
private share: ShareService
) {}
get(uuid): Promise<ApiData> {
return new Promise((resolve) => {
return new Promise(async (resolve) => {
if (this.status.isShare) {
const [data, err]: any = await this.http.api_shareDocGetApiDetail({
uniqueID: this.share.shareId,
apiDataUUID: uuid,
});
if (err) {
return;
}
return resolve(data);
}
this.storage.run('apiDataLoad', [uuid], (result: StorageRes) => {
if (result.status === StorageResStatus.success) {
resolve(result.data);

View File

@ -7,8 +7,7 @@
</nz-form-item>
</form>
<p *ngIf="isDelete" i18n>
Data from <strong title="{{ group.name }}">{{
group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name
}}</strong>
Data from
<strong title="{{ group.name }}">{{ group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name }}</strong>
will be deleted. This cannot be undone. Are you sure you want to delete?
</p>

View File

@ -1,9 +1,22 @@
<header class="flex pt-2">
<input nz-input type="text" class="flex-1 px-3 eo-search-input" i18n-placeholder="@@Search" placeholder="Search"
[(ngModel)]="searchValue" />
<button nzType="primary" nz-button class="flex w-[20px] items-center justify-center ml-3 text-base shrink-0"
nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomRight"
(click)="operateApiEvent({ event: $event, eventName: 'addAPI' })">
<input
nz-input
type="text"
class="flex-1 px-3 eo-search-input"
i18n-placeholder="@@Search"
placeholder="Search"
[(ngModel)]="searchValue"
/>
<button
*ngIf="!status.isShare"
nzType="primary"
nz-button
class="flex w-[20px] items-center justify-center ml-3 text-base shrink-0"
nz-dropdown
[nzDropdownMenu]="menu"
nzPlacement="bottomRight"
(click)="operateApiEvent({ event: $event, eventName: 'addAPI' })"
>
<eo-iconpark-icon name="plus" size="16px"></eo-iconpark-icon>
</button>
<nz-dropdown-menu #menu="nzDropdownMenu">
@ -21,10 +34,21 @@
<!-- Custom Group -->
<div class="group_container group_tree pt10">
<nz-tree *ngIf="treeNodes.length" [nzData]="treeNodes" [nzSelectedKeys]="nzSelectedKeys" #apiGroup
[nzSearchValue]="searchValue" [nzHideUnMatched]="true" [nzExpandedKeys]="expandKeys"
(nzClick)="clickTreeItem($event)" (nzExpandChange)="toggleExpand()" nzDraggable nzBlockNode
(nzOnDrop)="treeItemDrop($event)" [nzTreeTemplate]="nzTreeTemplate"></nz-tree>
<nz-tree
*ngIf="treeNodes.length"
[nzData]="treeNodes"
[nzSelectedKeys]="nzSelectedKeys"
#apiGroup
[nzSearchValue]="searchValue"
[nzHideUnMatched]="true"
[nzExpandedKeys]="expandKeys"
(nzClick)="clickTreeItem($event)"
(nzExpandChange)="toggleExpand()"
[nzDraggable]="isEdit"
nzBlockNode
(nzOnDrop)="treeItemDrop($event)"
[nzTreeTemplate]="nzTreeTemplate"
></nz-tree>
<nz-skeleton [nzLoading]="apiDataLoading && !treeNodes.length" [nzActive]="true">
<ng-template #nzTreeTemplate let-node let-origin="origin">
<div [style.--tree-level]="node.level">
@ -33,7 +57,7 @@
<div class="overflow-hidden f_row_ac text-ellipsis">
<span class="text_omit node_title">{{ node.title }}</span>
</div>
<span class="flex tree_node_operate">
<span class="flex tree_node_operate" *ngIf="isEdit">
<button nz-dropdown [nzDropdownMenu]="groupMenu" class="flex items-center">
<eo-iconpark-icon class="mr5" name="more" size="16px"></eo-iconpark-icon>
</button>
@ -60,15 +84,17 @@
<div class="overflow-hidden f_row_ac text-ellipsis">
<b class="method_text method_text_{{ node.origin.method }} mr5" *ngIf="node.origin.method">{{
node.origin.method
}}</b>
}}</b>
<span class="text_omit node_title">{{ node.title }}</span>
</div>
<div class="flex tree_node_operate">
<button class="flex items-center"
(click)="operateApiEvent({ event: $event, eventName: 'testApi', node: node })">
<button
class="flex items-center"
(click)="operateApiEvent({ event: $event, eventName: 'testApi', node: node })"
>
<eo-iconpark-icon class="mr5" name="lightning" size="15px"></eo-iconpark-icon>
</button>
<button class="flex items-center" nz-dropdown [nzDropdownMenu]="apiDataMenu">
<button class="flex items-center" nz-dropdown [nzDropdownMenu]="apiDataMenu" *ngIf="isEdit">
<eo-iconpark-icon class="mr5" name="more" size="16px"></eo-iconpark-icon>
</button>
<nz-dropdown-menu #apiDataMenu="nzDropdownMenu">
@ -76,12 +102,16 @@
<li nz-menu-item (click)="operateApiEvent({ event: $event, eventName: 'editApi', node: node })">
<a i18n>Edit</a>
</li>
<li nz-menu-item
(click)="operateApiEvent({ event: $event, eventName: 'copyApi', node: apiDataItems[node.key] })">
<li
nz-menu-item
(click)="operateApiEvent({ event: $event, eventName: 'copyApi', node: apiDataItems[node.key] })"
>
<a i18n="@@Copy">Copy</a>
</li>
<li nz-menu-item
(click)="operateApiEvent({ event: $event, eventName: 'deleteApi', node: apiDataItems[node.key] })">
<li
nz-menu-item
(click)="operateApiEvent({ event: $event, eventName: 'deleteApi', node: apiDataItems[node.key] })"
>
<a i18n="@@Delete">Delete</a>
</li>
</ul>
@ -91,8 +121,6 @@
</div>
</ng-template>
<nz-empty *ngIf="!treeNodes.length" nzNotFoundImage="simple">
</nz-empty>
<nz-empty *ngIf="!treeNodes.length" nzNotFoundImage="simple"> </nz-empty>
</nz-skeleton>
</div>

View File

@ -37,7 +37,7 @@
::ng-deep {
.group_tree {
overflow: hidden auto;
height: calc(100vh - var(--NAVBAR_HEIGHT) - var(--FOOTER_HEIGHT) - 80px);
height: calc(100vh - var(--NAVBAR_HEIGHT) - var(--FOOTER_HEIGHT) - 120px);
.ant-tree-indent-unit {
width: 8px;
}
@ -48,9 +48,6 @@
}
}
}
.web-page-body .group_tree {
height: calc(100vh - var(--NAVBAR_HEIGHT) - var(--FOOTER_HEIGHT) - 87px);
}
}
.ant-dropdown-menu {
min-width: 100px;

View File

@ -18,6 +18,9 @@ import { ApiService } from 'eo/workbench/browser/src/app/pages/api/api.service';
import { ImportApiComponent } from 'eo/workbench/browser/src/app/shared/components/import-api/import-api.component';
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service';
import { ProjectService } from 'eo/workbench/browser/src/app/shared/services/project/project.service';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { ShareService } from 'eo/workbench/browser/src/app/shared/services/share.service';
@Component({
selector: 'eo-api-group-tree',
templateUrl: './api-group-tree.component.html',
@ -64,20 +67,25 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy {
},
];
nzSelectedKeys: number[] = [];
isEdit: boolean;
private destroy$: Subject<void> = new Subject<void>();
constructor(
public electron: ElectronService,
private router: Router,
private route: ActivatedRoute,
private modalService: ModalService,
private message: EoMessageService,
private messageService: MessageService,
private storage: StorageService,
public electron: ElectronService,
private apiService: ApiService,
private projectService: ProjectService,
private nzModalService: NzModalService
private nzModalService: NzModalService,
public status: StatusService,
private http: RemoteService,
private share: ShareService
) {}
ngOnInit(): void {
this.isEdit = !this.status.isShare;
this.buildGroupTreeData();
this.watchApiAction();
this.watchRouterChange();
@ -107,9 +115,27 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy {
callback?.();
});
getProjectCollections() {
async getProjectCollections() {
this.apiDataLoading = true;
return new Promise((resolve) => {
return new Promise(async (resolve) => {
if (this.status.isShare) {
const [res, err]: any = await this.http.api_shareDocGetAllApi(
{
uniqueID: this.share.shareId,
},
'/api'
);
if (err) {
resolve(false);
return;
}
const { groups, apis } = res;
this.getGroups(groups);
this.getApis(apis);
this.apiDataLoading = false;
resolve(true);
return;
}
this.storage.run('projectCollections', [this.projectService.currentProjectID], (result: StorageRes) => {
if (result.status === StorageResStatus.success) {
const { groups, apis } = result.data;
@ -190,7 +216,6 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy {
case 'deleteApiSuccess':
case 'updateGroupSuccess': {
const group = inArg.data?.group;
if (
inArg.type === 'updateGroupSuccess' &&
group?.parentID &&
@ -198,7 +223,6 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy {
) {
this.expandKeys.push(`group-${group?.parentID}`);
}
this.buildGroupTreeData();
break;
}
@ -212,24 +236,25 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy {
* @param inArg NzFormatEmitEvent
*/
operateApiEvent(inArg: NzFormatEmitEvent | any): void {
const prefix = this.status.isShare ? 'home/share' : '/home/api';
inArg.event.stopPropagation();
switch (inArg.eventName) {
case 'testApi':
case 'editApi':
case 'detailApi': {
this.router.navigate([`/home/api/http/${inArg.eventName.replace('Api', '')}`], {
queryParams: { uuid: inArg.node.key },
this.router.navigate([`${prefix}/http/${inArg.eventName.replace('Api', '')}`], {
queryParams: { uuid: inArg.node.key,shareId: this.share.shareId},
});
break;
}
case 'jumpOverview': {
this.router.navigate(['/home/api/overview'], {
this.router.navigate([`${prefix}/overview`], {
queryParams: { uuid: 'overview' },
});
break;
}
case 'addAPI': {
this.router.navigate(['/home/api/http/edit'], {
this.router.navigate([`${prefix}/http/edit`], {
queryParams: { groupID: inArg.node?.origin.key.replace('group-', '') },
});
break;

View File

@ -22,7 +22,7 @@ export class HistoryComponent implements OnInit {
constructor(private storage: StorageService, private router: Router, private message: MessageService) {}
async ngOnInit() {
const result = await this.loadAllTest();
this.historyList = result.reverse();
this.historyList = (result||[]).reverse();
this.message
.get()
.pipe(takeUntil(this.destroy$))
@ -39,7 +39,6 @@ export class HistoryComponent implements OnInit {
return new Promise<any[]>((resolve) => {
this.storage.run('apiTestHistoryLoadAllByProjectID', [1], (result: StorageRes) => {
if (result.status === StorageResStatus.success) {
console.log(result.data);
resolve(result.data);
} else {
console.error(result.data);

View File

@ -11,6 +11,9 @@ import { treeToListHasLevel } from '../../../../utils/tree/tree.utils';
import { reverseObj } from '../../../../utils/index.utils';
import { StorageService } from '../../../../shared/services/storage';
import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { ShareService } from 'eo/workbench/browser/src/app/shared/services/share.service';
@Component({
selector: 'api-detail',
templateUrl: './api-detail.component.html',
@ -28,7 +31,14 @@ export class ApiDetailComponent implements OnInit {
{ title: $localize`Created Type`, slot: 'createWay', width: '18%' },
{ title: 'URL', slot: 'url', width: '42%' },
];
constructor(private route: ActivatedRoute, private storage: StorageService, public electron: ElectronService) {}
constructor(
private route: ActivatedRoute,
private storage: StorageService,
private status: StatusService,
public electron: ElectronService,
private http: RemoteService,
private share: ShareService
) {}
ngOnInit(): void {
this.init();
}
@ -39,13 +49,30 @@ export class ApiDetailComponent implements OnInit {
if (id) {
this.model = (await this.getApiByUuid(Number(id))) as ApiData;
} else {
console.error("Can't no find api");
console.error(`Can't no find api`);
}
}
this.eoOnInit.emit(this.model);
}
getApiByUuid(id: number) {
return new Promise((resolve) => {
return new Promise(async (resolve) => {
if (this.status.isShare) {
const [data, err]: any = await this.http.api_shareDocGetApiDetail({
apiDataUUID: id,
uniqueID: this.share.shareId,
});
if (err) {
return;
}
['requestBody', 'responseBody'].forEach((tableName) => {
if (['xml', 'json'].includes(data[`${tableName}Type`])) {
data[tableName] = treeToListHasLevel(data[tableName]);
}
});
this.model = data;
resolve(this.model);
return;
}
this.storage.run('apiDataLoad', [id], (result: StorageRes) => {
if (result.status === StorageResStatus.success) {
['requestBody', 'responseBody'].forEach((tableName) => {

View File

@ -22,14 +22,25 @@
<nz-input-group nzCompact>
<nz-form-item nz-col>
<nz-form-control class="w_250" i18n-nzErrorTip nzErrorTip="Please select an API group">
<nz-tree-select nzAllowClear="false" [nzExpandedKeys]="expandKeys" [nzDropdownMatchSelectWidth]="false"
[nzNodes]="groups" [(ngModel)]="model.groupID" [nzShowSearch]="true" #apiGroup formControlName="groupID">
<nz-tree-select
nzAllowClear="false"
[nzExpandedKeys]="expandKeys"
[nzDropdownMatchSelectWidth]="false"
[nzNodes]="groups"
[(ngModel)]="model.groupID"
[nzShowSearch]="true"
#apiGroup
formControlName="groupID"
>
</nz-tree-select>
</nz-form-control>
</nz-form-item>
<nz-form-item nz-col class="fg1">
<nz-form-control i18n-nzErrorTip nzErrorTip="Please enter API name"
[nzValidateStatus]="this.validateForm.controls.name">
<nz-form-control
i18n-nzErrorTip
nzErrorTip="Please enter API name"
[nzValidateStatus]="this.validateForm.controls.name"
>
<input type="text" [(ngModel)]="model.name" name="name" id="name" nz-input formControlName="name" />
</nz-form-control>
</nz-form-item>
@ -45,24 +56,36 @@
<span i18n>Request Headers</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.requestHeaders)">{{
model.requestHeaders | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-edit-header class="eo_theme_iblock bbd bld brd" (modelChange)="emitChangeFun()"
[model]="model.requestHeaders"></eo-api-edit-header>
<eo-api-edit-header
class="eo_theme_iblock bbd bld brd"
(modelChange)="emitChangeFun()"
[model]="model.requestHeaders"
></eo-api-edit-header>
</nz-tab>
<!-- Request Body -->
<nz-tab [nzTitle]="bodyTitleTmp" [nzForceRender]="true">
<ng-template #bodyTitleTmp>
<span i18n>Body</span>
<span class="eo-tab-theme-icon" *ngIf="
<nz-badge
class="ml-1"
*ngIf="
['formData', 'json', 'xml'].includes(model.requestBodyType)
? bindGetApiParamNum(model.requestBody)
: model.requestBody?.length
"></span>
"
nzStatus="success"
></nz-badge>
</ng-template>
<eo-api-edit-body class="eo_theme_iblock bbd bld brd" [(bodyType)]="model.requestBodyType"
[(model)]="model.requestBody" (modelChange)="emitChangeFun()"
[supportType]="['formData', 'json', 'xml', 'raw', 'binary']" [(jsonRootType)]="model.requestBodyJsonType">
<eo-api-edit-body
class="eo_theme_iblock bbd bld brd"
[(bodyType)]="model.requestBodyType"
[(model)]="model.requestBody"
(modelChange)="emitChangeFun()"
[supportType]="['formData', 'json', 'xml', 'raw', 'binary']"
[(jsonRootType)]="model.requestBodyJsonType"
>
</eo-api-edit-body>
</nz-tab>
<nz-tab [nzTitle]="queryTitleTmp" [nzForceRender]="true">
@ -70,20 +93,26 @@
<span i18n>Query</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.queryParams)">{{
model.queryParams | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-edit-query (modelChange)="emitChangeFun()" class="eo_theme_iblock bbd bld brd"
[model]="model.queryParams"></eo-api-edit-query>
<eo-api-edit-query
(modelChange)="emitChangeFun()"
class="eo_theme_iblock bbd bld brd"
[model]="model.queryParams"
></eo-api-edit-query>
</nz-tab>
<nz-tab [nzTitle]="restTitleTmp" [nzForceRender]="true">
<ng-template #restTitleTmp>
<span i18n>REST</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.restParams)">{{
model.restParams | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-edit-rest class="eo_theme_iblock bbd bld brd" [model]="model.restParams"
(modelChange)="emitChangeFun()"></eo-api-edit-rest>
<eo-api-edit-rest
class="eo_theme_iblock bbd bld brd"
[model]="model.restParams"
(modelChange)="emitChangeFun()"
></eo-api-edit-rest>
</nz-tab>
</nz-tabset>
</nz-collapse-panel>
@ -97,24 +126,36 @@
<span i18n>Response Headers</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.responseHeaders)">{{
model.responseHeaders | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-edit-header class="eo_theme_iblock bbd bld brd" [model]="model.responseHeaders"
(modelChange)="emitChangeFun()">
<eo-api-edit-header
class="eo_theme_iblock bbd bld brd"
[model]="model.responseHeaders"
(modelChange)="emitChangeFun()"
>
</eo-api-edit-header>
</nz-tab>
<nz-tab [nzTitle]="responseTitleTmp" [nzForceRender]="true">
<ng-template #responseTitleTmp>
<span i18n>Response</span>
<span class="eo-tab-theme-icon" *ngIf="
['formData', 'json', 'xml'].includes(model.responseBodyType)
? bindGetApiParamNum(model.responseBody)
: model.responseBody?.length
"></span>
<nz-badge
class="ml-1"
*ngIf="
['formData', 'json', 'xml'].includes(model.responseBodyType)
? bindGetApiParamNum(model.responseBody)
: model.responseBody?.length
"
nzStatus="success"
></nz-badge>
</ng-template>
<eo-api-edit-body class="eo_theme_iblock bbd bld brd" [(bodyType)]="model.responseBodyType"
[(model)]="model.responseBody" (modelChange)="emitChangeFun()"
[supportType]="['json', 'xml', 'raw', 'binary']" [(jsonRootType)]="model.responseBodyJsonType">
<eo-api-edit-body
class="eo_theme_iblock bbd bld brd"
[(bodyType)]="model.responseBodyType"
[(model)]="model.responseBody"
(modelChange)="emitChangeFun()"
[supportType]="['json', 'xml', 'raw', 'binary']"
[(jsonRootType)]="model.responseBodyJsonType"
>
</eo-api-edit-body>
</nz-tab>
</nz-tabset>

View File

@ -24,6 +24,7 @@ import { ApiParamsNumPipe } from '../../../../shared/pipes/api-param-num.pipe';
import { ApiEditService } from 'eo/workbench/browser/src/app/pages/api/http/edit/api-edit.service';
import { ApiEditUtilService } from './api-edit-util.service';
import { EoMessageService } from '../../../../eoui/message/eo-message.service';
import { after } from 'lodash-es';
@Component({
selector: 'eo-api-edit-edit',
templateUrl: './api-edit.component.html',
@ -83,6 +84,8 @@ export class ApiEditComponent implements OnInit, OnDestroy {
this.model = result;
}
}
//! Rest may generate from url
this.watchUri();
//Storage origin api data
if (!this.initialModel) {
if (!id) {
@ -92,9 +95,9 @@ export class ApiEditComponent implements OnInit, OnDestroy {
this.initialModel = eoDeepCopy(this.model);
}
}
this.initBasicForm();
this.watchBasicForm();
this.watchUri();
this.changeGroupID$.next(this.model.groupID);
this.validateForm.patchValue(this.model);
this.eoOnInit.emit(this.model);
@ -130,7 +133,7 @@ export class ApiEditComponent implements OnInit, OnDestroy {
if (result.status === StorageResStatus.success) {
this.message.success(title);
this.initialModel = this.apiEditUtil.getFormdataFromApiData(eoDeepCopy(result.data));
if(busEvent==='addApi'){
if (busEvent === 'addApi') {
this.router.navigate(['/home/api/http/detail'], {
queryParams: {
pageID: Number(this.route.snapshot.queryParams.pageID),

View File

@ -31,6 +31,7 @@ import { ApiParamsExtraSettingComponent } from './extra-setting/api-params-extra
import { ApiEditUtilService } from './api-edit-util.service';
import { ApiEditService } from 'eo/workbench/browser/src/app/pages/api/http/edit/api-edit.service';
import { RouterModule } from '@angular/router';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
const NZ_COMPONETS = [
NzDropDownModule,
@ -46,6 +47,7 @@ const NZ_COMPONETS = [
NzDividerModule,
NzAffixModule,
NzPopconfirmModule,
NzBadgeModule
];
const COMPONENTS = [
ApiEditComponent,

View File

@ -6,11 +6,16 @@
<div class="mt-[20px]">
<eo-table [(model)]="mocklList" [columns]="mockListColumns" [hideEmptyLine]="true">
<ng-template cell="name" let-scope="scope" let-index="index">
<div class=" w-[120px]">{{ scope.name }}</div>
<div class="w-[120px]">{{ scope.name }}</div>
</ng-template>
<ng-template cell="url" let-scope="scope" let-index="index">
<span i18n-nzTooltipTitle nzTooltipTitle="Click to Copy" nzTooltipPlacement="top" nz-tooltip
(click)="copyText(scope.url)">
<span
i18n-nzTooltipTitle
nzTooltipTitle="Click to Copy"
nzTooltipPlacement="top"
nz-tooltip
(click)="copyText(scope.url)"
>
<span nz-typography nzEllipsis [nzContent]="scope.url"> </span>
</span>
</ng-template>
@ -19,14 +24,28 @@
</ng-template>
<ng-template cell="action" let-scope="scope" let-index="index">
<div class="flex">
<a nz-button nzType="link" *ngIf="scope.name || scope.url" [ngSwitch]="scope.createWay"
(click)="addOrEditModal(index)">
<a
nz-button
nzType="link"
*ngIf="scope.name || scope.url"
[ngSwitch]="scope.createWay"
(click)="addOrEditModal(index)"
>
<span *ngSwitchCase="'system'" i18n>Preview</span>
<span *ngSwitchDefault i18n>Edit</span>
</a>
<a nz-button nzType="link" *ngIf="(scope.name || scope.url) && scope.createWay !== 'system'" nz-popconfirm
i18n-nzPopconfirmTitle nzPopconfirmTitle="Are you sure you want to delete this Mock?"
nzPopconfirmPlacement="topRight" (nzOnConfirm)="handleDeleteMockItem(index)" i18n="@@Delete">Delete</a>
<a
nz-button
nzType="link"
*ngIf="(scope.name || scope.url) && scope.createWay !== 'system'"
nz-popconfirm
i18n-nzPopconfirmTitle
nzPopconfirmTitle="Are you sure you want to delete this Mock?"
nzPopconfirmPlacement="topRight"
(nzOnConfirm)="handleDeleteMockItem(index)"
i18n="@@Delete"
>Delete</a
>
</div>
</ng-template>
</eo-table>
@ -46,8 +65,14 @@
<nz-form-item>
<nz-form-label i18n nzFor="currentEditMock.response">Response</nz-form-label>
<nz-form-control>
<eo-monaco-editor [(code)]="responseStr" (codeChange)="rawDataChange()" [maxLine]="15" class="h-[200px]"
[config]="{ readOnly: isSystemMock }" [eventList]="['type', 'format', 'copy', 'search', 'replace']">
<eo-monaco-editor
[(code)]="responseStr"
(codeChange)="rawDataChange()"
[maxLine]="15"
class="h-[200px]"
[config]="{ readOnly: isSystemMock }"
[eventList]="['type', 'format', 'copy', 'search', 'replace']"
>
</eo-monaco-editor>
</nz-form-control>
</nz-form-item>

View File

@ -346,7 +346,7 @@ export class ApiTestUtilService {
});
return result;
}
private filterCommonHeader(headers) {
private filterCommonHeader(headers=[]) {
const commonHeader = [
'content-type',
'accept',

View File

@ -9,15 +9,33 @@
<nz-select class="!w-[106px] flex-none" [(ngModel)]="model.request.method" formControlName="method">
<nz-option *ngFor="let item of REQUEST_METHOD" [nzLabel]="item.key" [nzValue]="item.value"></nz-option>
</nz-select>
<div *ngIf="env.hostUri" nz-typography nzEllipsis class="env_front_uri" nzTooltipTitle="{{ env.hostUri }}"
nzTooltipPlacement="bottom" nz-tooltip>
<div
*ngIf="env.hostUri"
nz-typography
nzEllipsis
class="env_front_uri"
nzTooltipTitle="{{ env.hostUri }}"
nzTooltipPlacement="bottom"
nz-tooltip
>
{{ env.hostUri }}
</div>
<nz-form-item nz-col class="fg1">
<nz-form-control [nzValidateStatus]="this.validateForm.controls.uri" i18n-nzErrorTip
nzErrorTip="Please enter URL">
<input type="text" i18n-placeholder placeholder="Enter URL" name="uri" nz-input formControlName="uri"
[(ngModel)]="model.request.uri" (change)="changeUri()" />
<nz-form-control
[nzValidateStatus]="this.validateForm.controls.uri"
i18n-nzErrorTip
nzErrorTip="Please enter URL"
>
<input
type="text"
i18n-placeholder
placeholder="Enter URL"
name="uri"
nz-input
formControlName="uri"
[(ngModel)]="model.request.uri"
(change)="changeUri()"
/>
</nz-form-control>
</nz-form-item>
<button type="submit" id="btn-test" nz-button nzType="primary" class="ml10 w_100" (click)="clickTest()">
@ -25,41 +43,66 @@
<span *ngIf="status === 'testing'" i18n>Abort</span>
<span *ngIf="status === 'testing' && waitSeconds" class="ml-1">{{ waitSeconds }}</span>
</button>
<button type="button"
*ngIf="!route.snapshot.queryParams.uuid || route.snapshot.queryParams.uuid.includes('history_')" nz-button
nzType="default" (click)="saveApi()" class="ml10" i18n>
<button
type="button"
*ngIf="
!statusS.isShare &&
(!route.snapshot.queryParams.uuid || route.snapshot.queryParams.uuid.includes('history_'))
"
nz-button
nzType="default"
(click)="saveApi()"
class="ml10"
i18n
>
Save as API
</button>
</nz-input-group>
</form>
<!-- Request Info -->
<nz-tabset [nzTabBarStyle]="{ 'padding-left': '10px' }" [nzAnimated]="false"
[(nzSelectedIndex)]="model.requestTabIndex">
<nz-tabset
[nzTabBarStyle]="{ 'padding-left': '10px' }"
[nzAnimated]="false"
[(nzSelectedIndex)]="model.requestTabIndex"
>
<!-- Request Headers -->
<nz-tab [nzTitle]="headerTitleTmp" [nzForceRender]="true">
<ng-template #headerTitleTmp>
<span i18n="@@RequestHeaders">Headers</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.request.requestHeaders)">{{
model.request.requestHeaders | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-test-header class="eo_theme_iblock bbd" [(model)]="model.request.requestHeaders"
(modelChange)="emitChangeFun('requestHeaders')"></eo-api-test-header>
<eo-api-test-header
class="eo_theme_iblock bbd"
[(model)]="model.request.requestHeaders"
(modelChange)="emitChangeFun('requestHeaders')"
></eo-api-test-header>
</nz-tab>
<!--Request Info -->
<nz-tab [nzTitle]="bodyTitleTmp" [nzForceRender]="true">
<ng-template #bodyTitleTmp>
<span i18n>Body</span>
<span class="eo-tab-theme-icon" *ngIf="
<nz-badge
class="ml-1"
*ngIf="
['formData', 'json', 'xml'].includes(model.request.requestBodyType)
? bindGetApiParamNum(model.request.requestBody)
: model.request.requestBody?.length
"></span>
"
nzStatus="success"
></nz-badge>
</ng-template>
<eo-api-test-body class="eo_theme_iblock bbd" [(contentType)]="model.contentType"
(contentTypeChange)="changeContentType($event)" [(bodyType)]="model.request.requestBodyType"
(bodyTypeChange)="changeBodyType($event)" [(model)]="model.request.requestBody"
(modelChange)="emitChangeFun('requestBody')" [supportType]="['formData', 'raw', 'binary']">
<eo-api-test-body
class="eo_theme_iblock bbd"
[(contentType)]="model.contentType"
(contentTypeChange)="changeContentType($event)"
[(bodyType)]="model.request.requestBodyType"
(bodyTypeChange)="changeBodyType($event)"
[(model)]="model.request.requestBody"
(modelChange)="emitChangeFun('requestBody')"
[supportType]="['formData', 'raw', 'binary']"
>
</eo-api-test-body>
</nz-tab>
<nz-tab [nzTitle]="queryTitleTmp" [nzForceRender]="true">
@ -67,46 +110,67 @@
<span i18n>Query</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.request.queryParams)">{{
model.request.queryParams | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-test-query class="eo_theme_iblock bbd" [model]="model.request.queryParams"
(modelChange)="emitChangeFun('queryParams')"></eo-api-test-query>
<eo-api-test-query
class="eo_theme_iblock bbd"
[model]="model.request.queryParams"
(modelChange)="emitChangeFun('queryParams')"
></eo-api-test-query>
</nz-tab>
<nz-tab [nzTitle]="restTitleTmp" [nzForceRender]="true">
<ng-template #restTitleTmp>
<span i18n>REST</span>
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(model.request.restParams)">{{
model.request.restParams | apiParamsNum
}}</span>
}}</span>
</ng-template>
<eo-api-test-rest class="eo_theme_iblock bbd" [(model)]="model.request.restParams"
(modelChange)="emitChangeFun('restParams')"></eo-api-test-rest>
<eo-api-test-rest
class="eo_theme_iblock bbd"
[(model)]="model.request.restParams"
(modelChange)="emitChangeFun('restParams')"
></eo-api-test-rest>
</nz-tab>
<nz-tab [nzTitle]="preScriptTitleTmp" [nzForceRender]="true">
<ng-template #preScriptTitleTmp>
<span i18n>Pre-request Script</span>
<span class="eo-tab-theme-icon" *ngIf="model.beforeScript?.trim()"></span>
<nz-badge class="ml-1" *ngIf="model.beforeScript?.trim()" nzStatus="success"></nz-badge>
</ng-template>
<eo-api-script *ngIf="model.requestTabIndex === 4" [(code)]="model.beforeScript"
(codeChange)="emitChangeFun('beforeScript')" [treeData]="BEFORE_DATA"
[completions]="beforeScriptCompletions" class="eo_theme_iblock bbd"></eo-api-script>
<eo-api-script
*ngIf="model.requestTabIndex === 4"
[(code)]="model.beforeScript"
(codeChange)="emitChangeFun('beforeScript')"
[treeData]="BEFORE_DATA"
[completions]="beforeScriptCompletions"
class="eo_theme_iblock bbd"
></eo-api-script>
</nz-tab>
<nz-tab [nzTitle]="suffixScriptTitleTmp" [nzForceRender]="true">
<ng-template #suffixScriptTitleTmp>
<span i18n>After-response Script</span>
<span class="eo-tab-theme-icon" *ngIf="model.afterScript?.trim()"></span>
<nz-badge class="ml-1" *ngIf="model.afterScript?.trim()" nzStatus="success"></nz-badge>
</ng-template>
<eo-api-script *ngIf="model.requestTabIndex === 5" [(code)]="model.afterScript"
(codeChange)="emitChangeFun('afterScript')" [treeData]="AFTER_DATA" [completions]="afterScriptCompletions"
class="eo_theme_iblock bbd"></eo-api-script>
<eo-api-script
*ngIf="model.requestTabIndex === 5"
[(code)]="model.afterScript"
(codeChange)="emitChangeFun('afterScript')"
[treeData]="AFTER_DATA"
[completions]="afterScriptCompletions"
class="eo_theme_iblock bbd"
></eo-api-script>
</nz-tab>
</nz-tabset>
</div>
<div class="bottom_container scroll_container" bottom>
<!-- Response -->
<nz-tabset [nzTabBarStyle]="{ 'padding-left': '10px' }" [(nzSelectedIndex)]="model.responseTabIndex"
[nzTabBarExtraContent]="extraTemplate" [nzAnimated]="false" class="mt-2.5 response_container"
(nzSelectChange)="handleBottomTabSelect($event)">
<nz-tabset
[nzTabBarStyle]="{ 'padding-left': '10px' }"
[(nzSelectedIndex)]="model.responseTabIndex"
[nzTabBarExtraContent]="extraTemplate"
[nzAnimated]="false"
class="response_container"
(nzSelectChange)="handleBottomTabSelect($event)"
>
<nz-tab i18n-nzTitle nzTitle="Response">
<eo-api-test-result-response [model]="model.testResult.response" [uri]="model.request.uri">
</eo-api-test-result-response>
@ -118,8 +182,10 @@
</div>
<nz-tab i18n-nzTitle nzTitle="Body" [nzForceRender]="true">
<!-- TODO use isRequestBodyLoaded -->
<eo-api-test-result-request-body *ngIf="model.responseTabIndex === 2"
[model]="model.testResult.request.requestBody || ''">
<eo-api-test-result-request-body
*ngIf="model.responseTabIndex === 2"
[model]="model.testResult.request.requestBody || ''"
>
</eo-api-test-result-request-body>
</nz-tab>
<nz-tab i18n-nzTitle nzTitle="Request Headers">
@ -129,8 +195,15 @@
<ng-template #extraTemplate>
<div *ngIf="model.responseTabIndex === 0" class="py-[6px] px-[12px] flex items-center">
<!-- <span nz-icon nzType="download" nzTheme="outline">下载</span> -->
<eo-iconpark-icon name="download" *ngIf="!isEmpty(model.testResult?.response)" class="cursor-pointer"
i18n-nzTooltipTitle nz-tooltip nzTooltipTitle="Save As File" (click)="downloadFile()">
<eo-iconpark-icon
name="download"
*ngIf="!isEmpty(model.testResult?.response)"
class="cursor-pointer"
i18n-nzTooltipTitle
nz-tooltip
nzTooltipTitle="Save As File"
(click)="downloadFile()"
>
</eo-iconpark-icon>
</div>
</ng-template>

View File

@ -35,6 +35,7 @@ import { transferUrlAndQuery } from 'eo/workbench/browser/src/app/utils/api';
import { getGlobals, setGlobals } from 'eo/workbench/browser/src/app/shared/services/api-test/api-test.utils';
import { ApiTestResultResponseComponent } from 'eo/workbench/browser/src/app/pages/api/http/test/result-response/api-test-result-response.component';
import { isEmpty } from 'lodash-es';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
const API_TEST_DRAG_TOP_HEIGHT_KEY = 'API_TEST_DRAG_TOP_HEIGHT';
interface testViewModel {
@ -102,6 +103,7 @@ export class ApiTestComponent implements OnInit, OnDestroy {
private apiTestUtil: ApiTestUtilService,
private testServer: TestServerService,
private messageService: MessageService,
public statusS: StatusService,
private lang: LanguageService
) {
// TODO Select demo api when first open Eoapi
@ -115,7 +117,6 @@ export class ApiTestComponent implements OnInit, OnDestroy {
// testBtn && testBtn.click();
// }, 600);
// }
this.initBasicForm();
this.testServer.init((message) => {
this.receiveMessage(message);
@ -183,8 +184,8 @@ export class ApiTestComponent implements OnInit, OnDestroy {
if (!this.initialModel) {
this.initialModel = eoDeepCopy(this.model);
}
this.cdRef.detectChanges();
this.eoOnInit.emit(this.model);
this.cdRef.detectChanges();
}
clickTest() {
if (!this.checkForm()) {
@ -377,7 +378,7 @@ export class ApiTestComponent implements OnInit, OnDestroy {
//If test sucess,addHistory
//Only has statusCode need save report
if (!message.response.statusCode) {
if (!message.response.statusCode || this.statusS.isShare) {
return;
}
this.addHistory(message.history, Number(queryParams.uuid));

View File

@ -44,6 +44,7 @@ import { TestServerRemoteService } from 'eo/workbench/browser/src/app/shared/ser
import { NzUploadModule } from 'ng-zorro-antd/upload';
import { RouterModule } from '@angular/router';
import { ApiSharedModule } from 'eo/workbench/browser/src/app/pages/api/api-shared.module';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
const NZ_COMPONETS = [
NzDropDownModule,
@ -63,7 +64,7 @@ const NZ_COMPONETS = [
NzAlertModule,
NzTypographyModule,
NzUploadModule,
ApiSharedModule,
NzBadgeModule
];
const COMPONENTS = [
ApiTestComponent,
@ -89,6 +90,7 @@ const COMPONENTS = [
CommonModule,
...NZ_COMPONETS,
EouiModule,
ApiSharedModule,
SharedModule,
ParamsImportModule,
],

View File

@ -29,7 +29,6 @@ export class ApiTestResultResponseComponent implements OnInit, OnChanges {
) {}
ngOnChanges(changes) {
console.log('this.model', this.model);
if (changes.model && this.model) {
this.codeStatus = this.apiTest.getHTTPStatus(this.model?.statusCode);
if (!this.responseIsImg) {
@ -46,7 +45,17 @@ export class ApiTestResultResponseComponent implements OnInit, OnChanges {
}
downloadResponseText() {
this.blobUrl = this.responseIsImg ? this.uri : getBlobUrl(this.model.body, this.model.contentType);
let code = this.model.body;
try {
if (['longText', 'stream'].includes(this.model.responseType)) {
code = window.atob(code);
} else {
code = JSON.stringify(typeof code === 'string' ? JSON.parse(code) : code, null, 4);
}
} catch {
code = String(code);
}
this.blobUrl = this.responseIsImg ? this.uri : getBlobUrl(code, this.model.contentType);
const blobFileName = decodeURI(this.model.blobFileName || 'test_response');
const tmpAElem = document.createElement('a');
if ('download' in tmpAElem) {

View File

@ -1,10 +1,13 @@
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { ApiTabStorageService } from 'eo/workbench/browser/src/app/pages/api/tab/api-tab-storage.service';
import { TabItem, TabOperate } from 'eo/workbench/browser/src/app/pages/api/tab/tab.model';
import { storageTab, TabItem, TabOperate } from 'eo/workbench/browser/src/app/pages/api/tab/tab.model';
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service';
import { eoDeepCopy } from 'eo/workbench/browser/src/app/utils/index.utils';
import { APP_CONFIG } from 'eo/workbench/browser/src/environments/environment';
import { ShareService } from 'eo/workbench/browser/src/app/shared/services/share.service';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
/**
* Api tab service operate tabs array add/replace/close...
* Tab change by url change(router event)
@ -20,18 +23,22 @@ export class ApiTabOperateService {
* Tab basic info
*/
BASIC_TABS: Partial<TabItem>[];
//* Allow development mode debug not exist router
private allowNotExistRouter = !APP_CONFIG.production;
constructor(
private tabStorage: ApiTabStorageService,
private messageService: MessageService,
private router: Router,
private message: EoMessageService
private message: EoMessageService,
private share: ShareService,
private status: StatusService
) {}
//Init tab info
//Maybe from tab cache info or router url
init(BASIC_TABS) {
this.BASIC_TABS = BASIC_TABS;
this.tabStorage.init();
const tabCache = this.tabStorage.getPersistenceStorage();
const tabCache = this.parseChangeRouter(this.tabStorage.getPersistenceStorage());
//No cache
if (!tabCache || !tabCache.tabOrder?.length) {
this.operateTabAfterRouteChange({
@ -62,18 +69,21 @@ export class ApiTabOperateService {
});
return;
}
//Tab from url
try {
//If current url did't match exist tab,throw error
const existTab = this.getSameContentTab(this.generateTabFromUrl(this.router.url));
if (existTab) {
this.operateTabAfterRouteChange({
url: this.router.url,
});
return;
}
this.getSameContentTab(this.generateTabFromUrl(this.router.url));
//If current url is valid tab url,select it
this.operateTabAfterRouteChange({
url: this.router.url,
});
return;
} catch (e) {
console.error(e);
if (this.allowNotExistRouter) {
return;
}
}
//Tab from last choose
const targetTab = this.getTabByIndex(tabCache.selectedIndex || 0);
@ -85,10 +95,10 @@ export class ApiTabOperateService {
*
* @returns tabItem
*/
newDefaultTab(path?) {
newDefaultTab(routerStr?) {
const tabItem = Object.assign(
{},
eoDeepCopy(this.BASIC_TABS.find((val) => val.pathname === path) || this.BASIC_TABS[0])
eoDeepCopy(this.BASIC_TABS.find((val) => val.pathname.includes(routerStr)) || this.BASIC_TABS[0])
);
tabItem.params = {};
tabItem.uuid = tabItem.params.pageID = Date.now();
@ -126,6 +136,12 @@ export class ApiTabOperateService {
if (!tab) {
return;
}
if (this.status.isShare) {
this.router.navigate([tab.pathname], {
queryParams: { pageID: tab.uuid, ...tab.params, shareId: this.share.shareId },
});
return;
}
this.router.navigate([tab.pathname], {
queryParams: { pageID: tab.uuid, ...tab.params },
});
@ -170,7 +186,7 @@ export class ApiTabOperateService {
const params: any = {};
const basicTab = this.BASIC_TABS.find((val) => urlArr[0].includes(val.pathname));
if (!basicTab) {
throw new Error(`EO_ERROR: Please check this router has added in BASIC_TABS,current route:${url}`);
throw new Error(`EO_ERROR: Please check this router has added in BASIC_TABS,current route: ${url}`);
}
// Parse query params
new URLSearchParams(urlArr[1]).forEach((value, key) => {
@ -215,8 +231,9 @@ export class ApiTabOperateService {
*/
operateTabAfterRouteChange(res: { url: string }) {
const pureTab = this.getBasicInfoFromUrl(res.url);
const nextTab = this.generateTabFromUrl(res.url);
const existTab = this.getSameContentTab(pureTab);
const nextTab = this.generateTabFromUrl(res.url);
//!Every tab must has pageID
//If lack pageID,Jump to exist tab item to keep same pageID and so on
if (!pureTab.uuid) {
@ -403,4 +420,22 @@ export class ApiTabOperateService {
private updateChildView() {
this.messageService.send({ type: 'tabContentInit', data: {} });
}
private parseChangeRouter(cache: storageTab) {
if (!cache) {
return;
}
//If router not exist basic tab,filter it
cache.tabOrder = cache.tabOrder.filter((id) => {
const tabItem = cache.tabsByID[id];
if (!tabItem) {
return false;
}
const hasExist = this.BASIC_TABS.find((val) => val.pathname === tabItem.pathname);
if (!hasExist) {
delete cache.tabsByID[id];
}
return hasExist;
});
return cache;
}
}

View File

@ -16,9 +16,7 @@ export class ApiTabStorageService {
init() {
this.tabOrder = [];
this.tabsByID = new Map();
this.cacheName = `${
this.workspace.isLocal? 'local' : this.workspace.currentWorkspaceID
}_TabCache`;
this.cacheName = `${this.workspace.isLocal ? 'local' : this.workspace.currentWorkspaceID}_TabCache`;
}
addTab(tabItem) {
if (this.tabsByID.has(tabItem.uuid)) {
@ -55,8 +53,10 @@ export class ApiTabStorageService {
* @param data
*/
setPersistenceStorage(selectedIndex, opts) {
//! remote datasource may change
// if (this.dataSource.dataSourceType === 'http') {return;}
//TODO remote datasource may change
if (this.dataSource.dataSourceType === 'http') {
}
let tabsByID = Object.fromEntries(this.tabsByID);
Object.values(tabsByID).forEach((val) => {
if (val.type === 'preview') {
@ -77,24 +77,11 @@ export class ApiTabStorageService {
})
);
}
parseChangeRouter(cache: storageTab) {
const map = {
'/home/api/edit': '/home/api/http/edit',
'/home/api/test': '/home/api/http/test',
'/home/api/detail': '/home/api/http/detail',
'/home/api/mock': '/home/api/http/mock',
};
cache.tabOrder.forEach((id) => {
const tabItem = cache.tabsByID[id];
tabItem.pathname = map[tabItem.pathname] || tabItem.pathname;
});
return cache;
}
getPersistenceStorage(): storageTab {
let result: any = null;
try {
result = JSON.parse(window.localStorage.getItem(this.cacheName) as string);
result = this.parseChangeRouter(result);
} catch (e) {}
return result;
}

View File

@ -14,24 +14,23 @@
[ngClass]="{ 'fixed-tab-item-container': tabStorage.tabsByID.get(uuid)?.isFixed }"
>
<!-- loading -->
<nz-spin nzSimple class="mr10" [nzSize]="'small'" *ngIf="tabStorage.tabsByID.get(uuid).isLoading"></nz-spin>
<nz-spin nzSimple class="mr10" [nzSize]="'small'" *ngIf="tabStorage.tabsByID.get(uuid)?.isLoading"></nz-spin>
<eo-iconpark-icon
class="mr5"
name="{{ tabStorage.tabsByID.get(uuid).icon }}"
name="{{ tabStorage.tabsByID.get(uuid)?.icon }}"
size="14px"
*ngIf="tabStorage.tabsByID.get(uuid).icon"
*ngIf="tabStorage.tabsByID.get(uuid)?.icon"
></eo-iconpark-icon>
<span
class="mr5 method-text method_text_{{ tabStorage.tabsByID.get(uuid).extends.method }}"
*ngIf="tabStorage.tabsByID.get(uuid).extends?.method"
>{{ tabStorage.tabsByID.get(uuid).extends.method }}</span
>
<span class="text_omit tab_text" [title]="tabStorage.tabsByID.get(uuid).title">
{{ tabStorage.tabsByID.get(uuid).title }}</span
<!-- {{ tabStorage.tabsByID.get(uuid) | json }} -->
<span class="mr5 method-text method_text_{{ tabStorage.tabsByID.get(uuid)?.extends?.method || 'WS' }}">{{
tabStorage.tabsByID.get(uuid)?.extends?.method || 'WS'
}}</span>
<span class="text_omit tab_text" [title]="tabStorage.tabsByID.get(uuid)?.title">
{{ tabStorage.tabsByID.get(uuid)?.title }}</span
>
<!-- Close/HasEdit -->
<div class="flex items-center tab-item-button-group">
<span class="tab-has-edit-icon eo-tab-theme-icon" *ngIf="tabStorage.tabsByID.get(uuid).hasChanged"></span>
<nz-badge class="tab-has-edit-icon" *ngIf="tabStorage.tabsByID.get(uuid)?.hasChanged" nzStatus="success"></nz-badge>
<button
aria-label="Close tab"
(click)="closeTab({ $event: $event, index: i, tab: tabStorage.tabsByID.get(uuid) })"
@ -56,7 +55,7 @@
<nz-dropdown-menu #addMenu="nzDropdownMenu">
<ul nz-menu>
<li nz-menu-item (click)="newTab()">HTTP</li>
<li nz-menu-item (click)="newTab('/home/api/ws/test')">Websocket</li>
<li nz-menu-item (click)="newTab('ws')">Websocket</li>
</ul>
</nz-dropdown-menu>
<a nz-dropdown [nzDropdownMenu]="menu">

View File

@ -76,8 +76,14 @@
width: 100%;
font-style: italic;
.tab-has-edit-icon {
width: 8px;
height: 8px;
.ant-badge-status-dot{
width: 8px;
height: 8px;
}
.ant-badge-status-text{
display: none;
}
}
.tab-item-button-group {
position: absolute;

View File

@ -7,6 +7,7 @@ import { TabItem, TabOperate } from 'eo/workbench/browser/src/app/pages/api/tab/
import { ModalService } from '../../../shared/services/modal.service';
import { KeyValue } from '@angular/common';
import { NzTabsCanDeactivateFn } from 'ng-zorro-antd/tabs';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
@Component({
selector: 'eo-api-tab',
templateUrl: './api-tab.component.html',
@ -23,7 +24,8 @@ export class ApiTabComponent implements OnInit, OnDestroy {
public tabStorage: ApiTabStorageService,
public tabOperate: ApiTabOperateService,
private modal: ModalService,
private router: Router
private router: Router,
public status: StatusService
) {}
ngOnInit(): void {
this.tabOperate.init(this.list);
@ -92,12 +94,12 @@ export class ApiTabComponent implements OnInit, OnDestroy {
modal.destroy();
this.tabOperate.closeTab(index);
},
}
},
],
});
}
//Quick see tabs change in templete,for debug,can be deleted
//! just for debug
// ! just for debug
getConsoleTabs() {
const tabs = [];
this.tabStorage.tabOrder.forEach((uuid) => {

View File

@ -15,6 +15,7 @@ import { isEmptyObj } from 'eo/workbench/browser/src/app/utils/index.utils';
import { ApiParamsNumPipe } from '../../../shared/pipes/api-param-num.pipe';
import { ApiTestHeaders, ApiTestQuery } from 'eo/workbench/browser/src/app/shared/services/api-test/api-test.model';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
interface testViewModel {
requestTabIndex: number;
protocol: string;
@ -60,7 +61,8 @@ export class WebsocketComponent implements OnInit, OnDestroy {
private electron: ElectronService,
private testService: ApiTestService,
private modal: ModalService,
private message: MessageService
private message: MessageService,
private status: StatusService
) {
this.initBasicForm();
}
@ -88,7 +90,7 @@ export class WebsocketComponent implements OnInit, OnDestroy {
? APP_CONFIG.REMOTE_SOCKET_URL
: `ws://localhost:${port || 13928}`
}`,
{ transports: ['websocket'] }
{ path: '/socket.io', transports: ['websocket'] }
);
this.socket.on('connect_error', (error) => {
// * conncet socketIO is failed
@ -170,6 +172,9 @@ export class WebsocketComponent implements OnInit, OnDestroy {
isExpand: false,
});
const { requestTabIndex, msg, ...data } = this.model;
if (this.status.isShare) {
return;
}
const res = await this.testService.addHistory({ protocol: 'websocket', ...data }, 0);
if (res) {
this.message.send({ type: 'updateHistory', data: {} });

View File

@ -0,0 +1,75 @@
import { Component, Input, OnInit } from '@angular/core';
import { EventCenterForMicroApp } from '@micro-zoe/micro-app';
import { StorageService } from 'eo/workbench/browser/src/app/shared/services/storage/storage.service';
import microApp from '@micro-zoe/micro-app';
(window as any).eventCenterForAppNameVite = new EventCenterForMicroApp('appname-custom-tab');
@Component({
selector: 'eo-custom-tab',
template: `
<div style="transform: translate(0)">
<micro-app
*ngIf="url"
[attr.name]="name"
[attr.url]="url"
default-page="/"
shadowDOM
[data]="microAppData"
(created)="handleCreate()"
(beforemount)="handleBeforeMount()"
(mounted)="handleMount()"
(unmount)="handleUnmount()"
(error)="handleError()"
(datachange)="handleDataChange($event)"
></micro-app>
</div>
`,
})
export class CustomTabComponent implements OnInit {
@Input() name = ``;
@Input() url = ``;
microAppData = { msg: '来自基座的数据' };
constructor(private storage: StorageService) {}
ngOnInit(): void {}
/**
* vite
*/
handleCreate(): void {
console.log('child-vite 创建了');
}
handleBeforeMount(): void {
console.log('child-vite 即将被渲染');
}
handleMount(): void {
console.log('child-vite 已经渲染完成');
this.storage.run('groupLoadAllByProjectID', [1], (result) => {
if (result.status === 200) {
this.microAppData = result.data;
// 发送数据给子应用 my-appsetData第二个参数只接受对象类型
microApp.setData(this.name, { data: this.microAppData });
console.log('this.microAppData', this.microAppData);
}
});
// setTimeout(() => {
// this.microAppData = { msg: '来自基座的新数据' };
// }, 2000);
}
handleUnmount(): void {
console.log('child-vite 卸载了');
}
handleError(): void {
console.log('child-vite 加载出错了');
}
handleDataChange(e): void {
console.log('来自子应用 child-vite 的数据:', e.detail.data);
}
}

View File

@ -16,9 +16,9 @@ import { NzMessageService } from 'ng-zorro-antd/message';
<form nz-form [nzLayout]="'vertical'" [formGroup]="validateForm" class="form">
<nz-form-item nz-col class="fg1" *ngFor="let field of objectKeys(properties)">
<ng-container *ngIf="properties[field]?.label">
<ng-container *ngIf="properties[field]?.title || properties[field]?.label">
<nz-form-label nzFor="{{ field }}" [nzRequired]="properties[field]?.required" class="label">
{{ properties[field]?.label }}
{{ properties[field]?.title || properties[field]?.label }}
</nz-form-label>
</ng-container>
<!-- -->
@ -28,7 +28,11 @@ import { NzMessageService } from 'ng-zorro-antd/message';
>
{{ properties[field]?.description }}
</div>
<nz-form-control i18n-nzErrorTip nzErrorTip="Please Enter {{ properties[field]?.label }}" class="form-control">
<nz-form-control
i18n-nzErrorTip
nzErrorTip="Please Enter {{ properties[field]?.title || properties[field]?.label }}"
class="form-control"
>
<!-- -->
<!-- <ng-container *ngIf="properties[field]?.type === 'string'"> -->
<input
@ -37,7 +41,9 @@ import { NzMessageService } from 'ng-zorro-antd/message';
id="{{ field }}"
[disabled]="properties[field]?.disabled"
i18n-placeholder
placeholder="{{ properties[field]?.placeholder ?? 'Please Enter ' + properties[field]?.label }}"
placeholder="{{
(properties[field]?.placeholder ?? 'Please Enter ' + properties[field]?.title) || properties[field]?.label
}}"
formControlName="{{ field }}"
[(ngModel)]="localSettings[field]"
/>
@ -73,12 +79,11 @@ import { NzMessageService } from 'ng-zorro-antd/message';
})
export class ExtensionSettingComponent implements OnInit {
@Input() configuration = {} as any;
@Input() extName: string;
localSettings = {} as Record<string, any>;
validateForm!: FormGroup;
objectKeys = Object.keys;
get properties() {
return this.configuration?.properties || {};
}
properties = {};
constructor(
public languageService: LanguageService,
@ -92,14 +97,24 @@ export class ExtensionSettingComponent implements OnInit {
}
private init() {
this.formatProperties();
this.localSettings = this.settingService.getSettings();
const controls = {};
this.setSettingsModel(this.configuration.properties, controls);
this.setSettingsModel(this.properties, controls);
this.validateForm = this.fb.group(controls);
}
formatProperties() {
const prefix = `${this.extName}.`;
this.properties = Object.entries(this.configuration?.properties).reduce((prev, [key, value]) => {
const newKey = key.startsWith(prefix) ? key : `${prefix}${key}`;
prev[newKey] = value;
return prev;
}, {});
}
/**
* set data
*

View File

@ -9,10 +9,14 @@
<div class="mr-2">
<nz-avatar nzSize="small" class="!w-[20px] !h-[20px]" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
</div>
<span class="font-bold text-[14px]">{{ extensionDetail.moduleName }}</span>
<span class="font-bold text-[14px]">{{ extensionDetail.title || extensionDetail.moduleName }}</span>
<ng-container *ngIf="extensionDetail?.installed">
<nz-switch [(ngModel)]="isEnable" nzSize="small" class="ml-2.5"
(ngModelChange)="handleEnableExtension($event)">
<nz-switch
[(ngModel)]="isEnable"
nzSize="small"
class="ml-2.5"
(ngModelChange)="handleEnableExtension($event)"
>
</nz-switch>
</ng-container>
</div>
@ -21,21 +25,33 @@
<section class="h-full px-4 max-w-[80vw] mx-auto flex flex-col">
<p class="w-full text-[#888] text-[12px]">{{ extensionDetail.description }}</p>
<div>
<button nz-button [nzSize]="extensionDetail?.installed ? 'small' : 'default'"
[nzType]="extensionDetail?.installed ? 'default' : 'primary'" class="mt-[8px]" [nzLoading]="isOperating"
(click)="handleInstall()">
<button
nz-button
[nzSize]="extensionDetail?.installed ? 'small' : 'default'"
[nzType]="extensionDetail?.installed ? 'default' : 'primary'"
class="mt-[8px]"
[nzLoading]="isOperating"
(click)="handleInstall()"
>
<span *ngIf="extensionDetail?.installed" i18n>Uninstall</span>
<span *ngIf="!extensionDetail?.installed" i18n>Install</span>
</button>
</div>
<nz-tabset [(nzSelectedIndex)]="nzSelectedIndex" [nzAnimated]="false"
<nz-tabset
[(nzSelectedIndex)]="nzSelectedIndex"
[nzAnimated]="false"
[class]="{ settings: nzSelectedIndex === 0 && extensionDetail?.features?.configuration }"
(nzSelectChange)="handleTabChange($event)">
(nzSelectChange)="handleTabChange($event)"
>
<!-- <nz-tab *ngIf="pagePath" i18n-nzTitle nzTitle="CustomTab">
<eo-custom-tab [url]="pagePath" [name]="extName"></eo-custom-tab>
</nz-tab> -->
<!-- Setting -->
<nz-tab *ngIf="extensionDetail?.features?.configuration && extensionDetail?.installed" i18n-nzTitle
nzTitle="Settings">
<eo-extension-setting [configuration]="extensionDetail?.features?.configuration"></eo-extension-setting>
<eo-extension-setting [configuration]="extensionDetail?.features?.configuration"
[extName]="extensionDetail.name"></eo-extension-setting>
</nz-tab>
<!-- Details -->
<nz-tab i18n-nzTitle="@@ExtensionDetail" nzTitle="Details">

View File

@ -6,6 +6,7 @@ import { ExtensionService } from '../extension.service';
import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
import { WebService } from '../../../core/services/web/web.service';
import { PROTOCOL } from 'eo/workbench/browser/src/app/shared/constants/protocol';
import { WebExtensionService } from 'eo/workbench/browser/src/app/shared/services/web-extension/webExtension.service';
@Component({
selector: 'eo-extension-detail',
@ -20,6 +21,8 @@ export class ExtensionDetailComponent implements OnInit {
isNotLoaded = true;
extensionDetail: EoExtensionInfo;
nzSelectedIndex = 0;
pagePath = '';
extName = '';
changeLog = '';
changeLogNotFound = false;
@ -29,7 +32,8 @@ export class ExtensionDetailComponent implements OnInit {
private router: Router,
private webService: WebService,
public electron: ElectronService,
private language: LanguageService
private language: LanguageService,
private webExtensionService: WebExtensionService
) {
this.getDetail();
}
@ -37,16 +41,14 @@ export class ExtensionDetailComponent implements OnInit {
ngOnInit(): void {}
async handleInstall() {
if (this.electron.isElectron) {
this.manageExtension(this.extensionDetail?.installed ? 'uninstall' : 'install', this.extensionDetail?.name);
} else {
this.webService.jumpToClient($localize`Eoapi Client is required to install this extension.`);
}
this.manageExtension(this.extensionDetail?.installed ? 'uninstall' : 'install', this.extensionDetail?.name);
}
async getDetail() {
const extName = this.route.snapshot.queryParams.name;
this.isOperating = window.eo?.getExtIsInTask(extName, ({ type, status }) => {
this.extName = this.route.snapshot.queryParams.name;
this.fetchExtensionPage(this.extName);
this.isOperating = window.eo?.getExtIsInTask(this.extName, ({ type, status }) => {
if (type === 'install' && status === 'success') {
this.extensionDetail.installed = true;
}
@ -55,11 +57,11 @@ export class ExtensionDetailComponent implements OnInit {
}
this.isOperating = false;
});
this.extensionDetail = await this.extensionService.getDetail(this.route.snapshot.queryParams.id, extName);
this.extensionDetail = await this.extensionService.getDetail(this.route.snapshot.queryParams.id, this.extName);
this.isEnable = this.extensionService.isEnable(this.extensionDetail.name);
if (!this.extensionDetail?.installed) {
if (!this.extensionDetail?.installed || this.webService.isWeb) {
await this.fetchReadme(this.language.systemLanguage);
}
this.isNotLoaded = false;
@ -71,6 +73,22 @@ export class ExtensionDetailComponent implements OnInit {
this.fetchChangelog(this.language.systemLanguage);
}
fetchExtensionPage = async (extName: string) => {
this.pagePath='http://127.0.0.1:8080';
// try {
// const res = await window.eo.getExtensionPagePathByName(extName);
// console.log('extName res', res);
// this.pagePath = res;
// } catch (e) {
// console.error('getExtensionPagePathByName err', e);
// fetch(`https://unpkg.com/${extName}/page/index.html`).then((res) => {
// if (res.status === 200) {
// this.pagePath = res.url + '/child/react17/';
// }
// });
// }
};
async fetchChangelog(locale = '') {
//Default locale en-US
if (locale === 'en-US') {
@ -147,23 +165,27 @@ ${log}
manageExtension(operate: string, id) {
this.isOperating = true;
/**
* * WARNING:Sending a synchronous message will block the whole
* renderer process until the reply is received, so use this method only as a last
* resort. It's much better to use the asynchronous version, `invoke()`.
*/
setTimeout(async () => {
switch (operate) {
case 'install': {
this.extensionDetail.installed = await this.extensionService.install(id);
this.handleEnableExtension(true);
this.getDetail();
if (this.electron.isElectron) {
this.extensionDetail.installed = await this.extensionService.install(id);
this.handleEnableExtension(true);
this.getDetail();
} else {
this.extensionDetail.installed = await this.webExtensionService.installExtension(this.extensionDetail.name);
}
break;
}
case 'uninstall': {
this.extensionDetail.installed = !(await this.extensionService.uninstall(id));
this.handleEnableExtension(false);
this.fetchReadme(this.language.systemLanguage);
if (this.electron.isElectron) {
this.extensionDetail.installed = !(await this.extensionService.uninstall(id));
this.handleEnableExtension(false);
this.fetchReadme(this.language.systemLanguage);
} else {
this.webExtensionService.unInstallExtension(this.extensionDetail.name);
this.extensionDetail.installed = false;
}
break;
}
}

View File

@ -1,13 +1,15 @@
import { NgModule } from '@angular/core';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ExtensionSettingComponent } from './components/extensions.component';
import { CustomTabComponent } from './components/custom-tab.component';
import { ExtensionDetailComponent } from './extension-detail.component';
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
@NgModule({
imports: [SharedModule, FormsModule, CommonModule],
declarations: [ExtensionSettingComponent, ExtensionDetailComponent],
declarations: [ExtensionSettingComponent, ExtensionDetailComponent, CustomTabComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class ExtensionDetailModule {}

View File

@ -53,13 +53,11 @@ export class ExtensionComponent implements OnInit {
ngOnInit(): void {
this.watchRouterChange();
this.setSelectedKeys();
if (this.electron.isElectron) {
this.treeNodes.push({
key: 'installed',
title: $localize`Installed`,
isLeaf: true,
});
}
this.treeNodes.push({
key: 'installed',
title: $localize`Installed`,
isLeaf: true,
});
}
onSeachChange(keyword) {

View File

@ -1,4 +1,4 @@
import { ModuleInfo } from 'eo/platform/node/extension-manager/types/index';
import { ModuleInfo } from 'eo/platform/node/extension-manager/types';
export enum ExtensionGroupType {
all = 'all',

View File

@ -2,11 +2,12 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
import { lastValueFrom } from 'rxjs';
import { ModuleInfo } from 'eo/platform/node/extension-manager/types/index';
import { ModuleInfo } from 'eo/platform/node/extension-manager/types';
import { TranslateService } from 'eo/platform/common/i18n';
import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
import { APP_CONFIG } from 'eo/workbench/browser/src/environments/environment';
import { DISABLE_EXTENSION_NAMES } from 'eo/workbench/browser/src/app/shared/constants/storageKeys';
import { WebExtensionService } from 'eo/workbench/browser/src/app/shared/services/web-extension/webExtension.service';
@Injectable({
providedIn: 'root',
@ -17,32 +18,32 @@ export class ExtensionService {
extensionIDs: Array<string> = [];
HOST = '';
localExtensions: Map<string, ModuleInfo>;
constructor(private http: HttpClient, private electron: ElectronService, private language: LanguageService) {
constructor(
private http: HttpClient,
private electron: ElectronService,
private language: LanguageService,
private webExtensionService: WebExtensionService
) {
this.localExtensions = this.getExtensions();
this.extensionIDs = this.updateExtensionIDs();
this.HOST = this.electron.isElectron ? APP_CONFIG.EXTENSION_URL : APP_CONFIG.MOCK_URL;
}
private getExtensions() {
// Local extension
return window.eo?.getModules() || new Map();
if (this.electron.isElectron) {
return window.eo?.getModules() || new Map();
} else {
const webeExts = this.webExtensionService.installedList.map((n) => [n.name, n.pkgInfo]);
return new Map(webeExts as any);
}
}
getInstalledList() {
// Local extension exception for ignore list
return Array.from(this.localExtensions.values()).filter((it) => this.extensionIDs.includes(it.moduleID));
return Array.from(this.localExtensions.values()).filter((it) => this.extensionIDs.includes(it.name));
}
isInstalled(name) {
const installList = this.getInstalledList();
return installList.includes(name);
}
private translateModule(module: ModuleInfo) {
const lang = this.language.systemLanguage;
const locale = module.i18n?.find((val) => val.locale === lang)?.package;
if (!locale) {
return module;
}
module = new TranslateService(module, locale).translate();
return module;
}
public async requestList() {
const result: any = await lastValueFrom(this.http.get(`${this.HOST}/list?locale=${this.language.systemLanguage}`));
const installList = this.getInstalledList();
@ -129,4 +130,16 @@ export class ExtensionService {
.filter((it) => it)
.filter((it) => !this.ignoreList.includes(it));
}
private translateModule(module: ModuleInfo) {
const lang = this.language.systemLanguage;
//If extension from web,transalte package content from http moduleInfo
//Locale extension will translate from local i18n file
const locale = module.i18n?.find((val) => val.locale === lang)?.package;
if (!locale) {
return module;
}
module = new TranslateService(module, locale).translate();
return module;
}
}

View File

@ -2,20 +2,16 @@
<div class="grid grid-cols-2 gap-6 py-5 list-block">
<div
class="bd_all w-full min-h-[140px] p-5 flex flex-col flex-wrap plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
*ngFor="let it of renderList"
(click)="clickExtension($event, it)"
>
*ngFor="let it of renderList" (click)="clickExtension($event, it)">
<nz-skeleton [nzLoading]="loading" [nzActive]="true" [nzAvatar]="true">
<div class="flex justify-between w-full">
<div class="flex">
<div
class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-[length:90%] bg-center bg-no-repeat mr-[20px]"
[ngClass]="{ 'bg-gray-100': it.logo }"
[ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }"
></div>
<div class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-[length:90%] bg-center bg-no-repeat mr-[20px]"
[ngClass]="{ 'bg-gray-100': it.logo }" [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }">
</div>
<div class="flex flex-col">
<div class="flex flex-col flex-auto">
<span class="text-lg font-bold">{{ it.moduleName }}</span>
<span class="text-lg font-bold">{{ it.title || it.moduleName }}</span>
<span class="my-2 text-gray-400">{{ it.author }}</span>
<span class="my-1 desc">{{ it.description }}</span>
</div>
@ -29,19 +25,13 @@
</div>
</div>
<div *ngIf="type === 'installed'">
<nz-switch
click-stop-propagation
[(ngModel)]="it.isEnable"
(ngModelChange)="handleEnableExtension($event, it)"
></nz-switch>
<nz-switch click-stop-propagation [(ngModel)]="it.isEnable"
(ngModelChange)="handleEnableExtension($event, it)"></nz-switch>
</div>
<div *ngIf="type !== 'installed'">
<span
*ngIf="electron.isElectron ? extensionService.localExtensions.has(it.moduleID) : it.installed"
<span *ngIf="extensionService.localExtensions.has(it.moduleID)"
class="p-1 text-xs text-green-700 border-green-700 rounded-sm bd_all whitespace-nowrap"
i18n
>Installed</span
>
i18n>Installed</span>
</div>
</div>
</nz-skeleton>

View File

@ -13,9 +13,7 @@ class ExtensionList {
this.list = list;
}
search(keyword: string) {
return this.list.filter(
(it) => it.moduleID.includes(keyword) || it.name.includes(keyword) || it.keywords?.includes(keyword)
);
return this.list.filter((it) => it.name.includes(keyword) || it.keywords?.includes(keyword));
}
}
@Component({
@ -87,7 +85,7 @@ export class ExtensionListComponent implements OnInit {
.navigate(['home/extension/detail'], {
queryParams: {
type: this.route.snapshot.queryParams.type,
id: item.moduleID,
id: item.name,
name: item.name,
tab: event?.target?.dataset?.id === 'details' ? 1 : 0,
},

View File

@ -1,13 +1,13 @@
import { NzModalService } from 'ng-zorro-antd/modal'
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service'
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service'
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service'
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service'
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service'
import { distinct } from 'rxjs/operators'
import { interval } from 'rxjs'
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service'
import { Component, OnInit } from '@angular/core'
import { NzModalService } from 'ng-zorro-antd/modal';
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service';
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service';
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service';
import { distinct } from 'rxjs/operators';
import { interval } from 'rxjs';
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'eo-member',
@ -15,17 +15,12 @@ import { Component, OnInit } from '@angular/core'
[nzFooter]="null"
[(nzVisible)]="isInvateModalVisible"
(nzOnCancel)="handleInvateModalCancel()"
(nzAfterClose)="etmsgjfCallback()"
(nzAfterClose)="ehe4islCallback()"
nzTitle="Add people to the workspace"
i18n-nzTitle
>
<ng-container *nzModalContent>
<input
nz-input
[(ngModel)]="inputPersonValue"
i18n-placeholder
placeholder="Search by username"
/>
<input nz-input [(ngModel)]="inputPersonValue" i18n-placeholder placeholder="Search by username" />
<section class="h-4"></section>
<button
nz-button
@ -33,8 +28,8 @@ import { Component, OnInit } from '@angular/core'
class=""
nzType="primary"
nzBlock
(click)="btn7ykkd6Callback()"
[disabled]="btnrgj8s9Status()"
(click)="btn0r9zcbCallback()"
[disabled]="btnguixdgStatus()"
i18n
>
Select a member above
@ -49,27 +44,24 @@ import { Component, OnInit } from '@angular/core'
[nzLoading]="isAddPeopleBtnLoading"
class=""
nzType="primary"
(click)="btnuvurciCallback()"
[disabled]="btnsqcmi2Status()"
(click)="btnf5umnoCallback()"
[disabled]="btny703n5Status()"
i18n
>
Add people
</button>
</h2>
<section class="py-5">
<eo-manage-access
[data]="memberList"
(eoOnRemove)="ew833r8Callback($event)"
></eo-manage-access>
<eo-manage-access [data]="memberList" (eoOnRemove)="e97uoiuCallback($event)"></eo-manage-access>
</section>
</section>`
</section>`,
})
export class MemberComponent implements OnInit {
isInvateModalVisible
inputPersonValue
isSelectBtnLoading
isAddPeopleBtnLoading
memberList
isInvateModalVisible;
inputPersonValue;
isSelectBtnLoading;
isAddPeopleBtnLoading;
memberList;
constructor(
public modal: NzModalService,
public user: UserService,
@ -79,154 +71,149 @@ export class MemberComponent implements OnInit {
public workspace: WorkspaceService,
public dataSource: DataSourceService
) {
this.isInvateModalVisible = false
this.inputPersonValue = ''
this.isSelectBtnLoading = false
this.isAddPeopleBtnLoading = false
this.memberList = []
this.isInvateModalVisible = false;
this.inputPersonValue = '';
this.isSelectBtnLoading = false;
this.isAddPeopleBtnLoading = false;
this.memberList = [];
}
async ngOnInit(): Promise<void> {
this.message
.get()
.pipe(distinct(({ type }) => type, interval(400)))
.subscribe(async ({ type, data }) => {})
.subscribe(async ({ type, data }) => {});
const url = this.dataSource.mockUrl
const url = this.dataSource.remoteServerUrl;
if (url === '') {
this.message.send({ type: 'need-config-remote', data: {} })
return
this.message.send({ type: 'need-config-remote', data: {} });
return;
}
const { id: currentWorkspaceID } = this.workspace.currentWorkspace
const { id: currentWorkspaceID } = this.workspace.currentWorkspace;
const [wData, wErr]: any = await this.api.api_workspaceMember({
workspaceID: currentWorkspaceID
})
workspaceID: currentWorkspaceID,
});
if (wErr) {
if (wErr.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
// * 对成员列表进行排序
const Owner = wData.filter((it) => it.roleName === 'Owner')
const Member = wData.filter((it) => it.roleName !== 'Owner')
this.memberList = Owner.concat(Member)
const Owner = wData.filter((it) => it.roleName === 'Owner');
const Member = wData.filter((it) => it.roleName !== 'Owner');
this.memberList = Owner.concat(Member);
}
handleInvateModalCancel(): void {
// * 关闭弹窗
this.isInvateModalVisible = false
this.isInvateModalVisible = false;
}
async etmsgjfCallback() {
async ehe4islCallback() {
// * nzAfterClose event callback
{
// * auto clear form
this.inputPersonValue = ''
this.inputPersonValue = '';
}
this.inputPersonValue = ''
this.inputPersonValue = '';
}
async btn7ykkd6Callback() {
async btn0r9zcbCallback() {
// * click event callback
this.isSelectBtnLoading = true
this.isSelectBtnLoading = true;
const btnSelectRunning = async () => {
const username = this.inputPersonValue
const [uData, uErr]: any = await this.api.api_userSearch({ username })
const username = this.inputPersonValue;
const [uData, uErr]: any = await this.api.api_userSearch({ username });
if (uErr) {
if (uErr.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
if (uData.length === 0) {
this.eMessage.error(
$localize`Could not find a user matching ${username}`
)
return
this.eMessage.error($localize`Could not find a user matching ${username}`);
return;
}
const [user] = uData
const { id } = user
const [user] = uData;
const { id } = user;
const { id: workspaceID } = this.workspace.currentWorkspace
const { id: workspaceID } = this.workspace.currentWorkspace;
const [aData, aErr]: any = await this.api.api_workspaceAddMember({
workspaceID,
userIDs: [id]
})
userIDs: [id],
});
if (aErr) {
if (aErr.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.eMessage.success($localize`Add new member success`)
this.eMessage.success($localize`Add new member success`);
// * 关闭弹窗
this.isInvateModalVisible = false
this.isInvateModalVisible = false;
const { id: currentWorkspaceID } = this.workspace.currentWorkspace
const { id: currentWorkspaceID } = this.workspace.currentWorkspace;
const [wData, wErr]: any = await this.api.api_workspaceMember({
workspaceID: currentWorkspaceID
})
workspaceID: currentWorkspaceID,
});
if (wErr) {
if (wErr.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
// * 对成员列表进行排序
const Owner = wData.filter((it) => it.roleName === 'Owner')
const Member = wData.filter((it) => it.roleName !== 'Owner')
this.memberList = Owner.concat(Member)
}
await btnSelectRunning()
this.isSelectBtnLoading = false
const Owner = wData.filter((it) => it.roleName === 'Owner');
const Member = wData.filter((it) => it.roleName !== 'Owner');
this.memberList = Owner.concat(Member);
};
await btnSelectRunning();
this.isSelectBtnLoading = false;
}
btnrgj8s9Status() {
btnguixdgStatus() {
// * disabled status status
return this.inputPersonValue === ''
return this.inputPersonValue === '';
}
async btnuvurciCallback() {
async btnf5umnoCallback() {
// * click event callback
this.isAddPeopleBtnLoading = true
this.isAddPeopleBtnLoading = true;
const btnAddPeopleRunning = async () => {
// * 唤起弹窗
this.isInvateModalVisible = true
this.isInvateModalVisible = true;
{
{
}
{
}
}
}
await btnAddPeopleRunning()
this.isAddPeopleBtnLoading = false
};
await btnAddPeopleRunning();
this.isAddPeopleBtnLoading = false;
}
btnsqcmi2Status() {
btny703n5Status() {
// * disabled status status
return
return (
this.workspace.currentWorkspaceID !== -1 &&
this.workspace.authEnum.canEdit
)
return;
return this.workspace.currentWorkspaceID !== -1 && this.workspace.authEnum.canEdit;
}
async ew833r8Callback($event) {
async e97uoiuCallback($event) {
// * eoOnRemove event callback
const confirm = () =>
@ -237,50 +224,50 @@ export class MemberComponent implements OnInit {
nzOkDanger: true,
nzOkText: $localize`Delete`,
nzOnOk: () => resolve(true),
nzOnCancel: () => resolve(false)
})
})
const isOk = await confirm()
nzOnCancel: () => resolve(false),
});
});
const isOk = await confirm();
if (!isOk) {
return
return;
}
const { id: workspaceID } = this.workspace.currentWorkspace
const { id: workspaceID } = this.workspace.currentWorkspace;
const { id } = $event
const { id } = $event;
const [data, err]: any = await this.api.api_workspaceRemoveMember({
workspaceID,
userIDs: [id]
})
userIDs: [id],
});
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
const { id: currentWorkspaceID } = this.workspace.currentWorkspace
const { id: currentWorkspaceID } = this.workspace.currentWorkspace;
const [wData, wErr]: any = await this.api.api_workspaceMember({
workspaceID: currentWorkspaceID
})
workspaceID: currentWorkspaceID,
});
if (wErr) {
if (wErr.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
// * 对成员列表进行排序
const Owner = wData.filter((it) => it.roleName === 'Owner')
const Member = wData.filter((it) => it.roleName !== 'Owner')
this.memberList = Owner.concat(Member)
const Owner = wData.filter((it) => it.roleName === 'Owner');
const Member = wData.filter((it) => it.roleName !== 'Owner');
this.memberList = Owner.concat(Member);
}
}

View File

@ -1,35 +1,20 @@
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { NzModalService, NzModalModule } from 'ng-zorro-antd/modal'
import { NzInputModule } from 'ng-zorro-antd/input'
import { FormsModule } from '@angular/forms'
import { NzButtonModule } from 'ng-zorro-antd/button'
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service'
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service'
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service'
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service'
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service'
import { distinct } from 'rxjs/operators'
import { interval } from 'rxjs'
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service'
import { ManageAccessComponent } from 'eo/workbench/browser/src/app/shared/components/manage-access/manage-access.component'
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module'
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NzModalService, NzModalModule } from 'ng-zorro-antd/modal';
import { NzInputModule } from 'ng-zorro-antd/input';
import { FormsModule } from '@angular/forms';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service';
import { ManageAccessComponent } from 'eo/workbench/browser/src/app/shared/components/manage-access/manage-access.component';
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
import { MemberRoutingModule } from './member-routing.module'
import { MemberComponent } from './member.component'
import { MemberRoutingModule } from './member-routing.module';
import { MemberComponent } from './member.component';
@NgModule({
imports: [
MemberRoutingModule,
CommonModule,
NzModalModule,
NzInputModule,
FormsModule,
NzButtonModule,
SharedModule
],
imports: [MemberRoutingModule, CommonModule, NzModalModule, NzInputModule, FormsModule, NzButtonModule, SharedModule],
declarations: [MemberComponent, ManageAccessComponent],
exports: [],
providers: [NzModalService, MessageService, RemoteService]
providers: [NzModalService, MessageService],
})
export class MemberModule {}

View File

@ -6,7 +6,13 @@
<img class="mx-4" src="https://img.shields.io/github/stars/eolinker/eoapi?style=social" alt="" />
</a>
<!-- Select workspace -->
<div class="flex items-center can_be_click" nz-dropdown nzTrigger="click" [nzDropdownMenu]="workspaceMenu">
<div
*ngIf="!status.isShare"
class="flex items-center can_be_click"
nz-dropdown
nzTrigger="click"
[nzDropdownMenu]="workspaceMenu"
>
<eo-iconpark-icon
class="mr-[5px]"
name="link-cloud-{{ workspaceService.currentWorkspace?.id !== -1 ? 'sucess' : 'faild' }}"
@ -54,9 +60,42 @@
</ul>
</nz-dropdown-menu>
</div>
<div class="flex items-center">
<div class="flex items-center can_be_click">
<button
*ngIf="!workspaceService.isLocal && !status.isShare"
nz-button
nzType="default"
class="mx-2 btn_scondary"
nz-popover
[nzPopoverContent]="contentTemplate"
nzPopoverPlacement="bottomRight"
nzPopoverTrigger="click"
i18n
>
Share
</button>
<ng-template #contentTemplate>
<div class="w-[360px] pb-4">
<p i18n class="font-bold">Share via link</p>
<p i18n class="pb-2 text-xs text-gray-400">
This link will be updated with the API content. Everyone can access it without logging in
</p>
<nz-input-group nzSearch [nzAddOnAfter]="suffixIconButton">
<input readonly type="text" nz-input [value]="shareLink" />
</nz-input-group>
</div>
</ng-template>
<ng-template #suffixIconButton>
<button nz-button nzType="primary" *ngIf="!isCopy" (click)="handleCopy()">Copy</button>
<button nz-button nzType="default" *ngIf="isCopy" class="text-[#158565]">Copied</button>
</ng-template>
<nz-select *ngIf="status.isShare" [(ngModel)]="langValue" nzBorderless (ngModelChange)="handleSwitchLang($event)">
<nz-option nzValue="en-US" nzLabel="English"></nz-option>
<nz-option nzValue="zh-Hans" nzLabel="中文"></nz-option>
</nz-select>
<!-- setting -->
<span
*ngIf="!status.isShare"
class="flex items-center justify-center mx-1 icon"
i18n-nzTooltipTitle
nz-tooltip
@ -73,7 +112,6 @@
<nz-dropdown-menu #helpMenu="nzDropdownMenu">
<ul nz-menu>
<a href="https://docs.eoapi.io" target="_blank" nz-menu-item i18n>Document</a>
<a href="https://developer.eoapi.io" target="_blank" nz-menu-item i18n>Developer Support</a>
<li nz-menu-divider></li>
<a
href="https://github.com/eolinker/eoapi/issues/new?assignees=&labels=&template=bug_report.yml&environment={{
@ -87,7 +125,13 @@
</ul>
</nz-dropdown-menu>
<!-- User -->
<span class="flex items-center justify-center mx-1 icon" nz-dropdown [nzDropdownMenu]="UserMenu">
<span
*ngIf="!status.isShare"
class="flex items-center justify-center mx-1 icon"
nz-dropdown
[nzDropdownMenu]="UserMenu"
>
<eo-iconpark-icon name="me"> </eo-iconpark-icon>
</span>
<nz-dropdown-menu #UserMenu="nzDropdownMenu">
@ -140,7 +184,7 @@
</span>
</div>
<!-- Web download client -->
<div *ngIf="!electron.isElectron">
<div *ngIf="!electron.isElectron && !status.isShare">
<div class="btn py-1.5 px-2 mx-1 flex items-center" nz-dropdown i18n [nzDropdownMenu]="download">
<span class="flex items-center delete-icon">
<eo-iconpark-icon name="down-two" size="14px"></eo-iconpark-icon>

View File

@ -34,9 +34,6 @@
left: 0;
top: 0;
padding: 0 8px;
// background-color: var(--NAVBAR_BG);
// color: var(--NAVBAR_TEXT);
// border-bottom: 1px solid var(--NAVBAR_BORDER_BOTTOM);
border-bottom: 1px solid var(--BORDER);
height: var(--NAVBAR_HEIGHT);
.logo {
@ -72,3 +69,8 @@
font-size: 12px;
}
}
.btn_scondary{
color:var(--BTN_PRIMARY_BG);
border-color:var(--BTN_PRIMARY_BG);
}

View File

@ -1,14 +1,19 @@
import { Component, OnInit } from '@angular/core';
import { ElectronService, WebService } from '../../core/services';
import { ModuleInfo } from 'eo/platform/node/extension-manager';
import { ModuleInfo } from 'eo/platform/node/extension-manager/types';
import { NzModalService } from 'ng-zorro-antd/modal';
import { SettingComponent } from '../../shared/components/setting/setting.component';
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service';
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service';
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { distinct } from 'rxjs/operators';
import { interval } from 'rxjs';
import { copy } from 'eo/workbench/browser/src/app/utils/index.utils';
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service';
import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
@Component({
selector: 'eo-navbar',
templateUrl: './navbar.component.html',
@ -23,6 +28,9 @@ export class NavbarComponent implements OnInit {
modules: Map<string, ModuleInfo>;
resourceInfo = this.web.resourceInfo;
issueEnvironment;
shareLink = '';
langValue;
isCopy = false;
constructor(
public electron: ElectronService,
private web: WebService,
@ -30,9 +38,14 @@ export class NavbarComponent implements OnInit {
private message: MessageService,
public workspaceService: WorkspaceService,
public userService: UserService,
public dataSourceService: DataSourceService
public dataSourceService: DataSourceService,
public status: StatusService,
private http: RemoteService,
private eoMessage: EoMessageService,
private lang: LanguageService
) {
this.issueEnvironment = this.getEnviroment();
this.langValue = this.lang.systemLanguage;
if (this.workspaceService.currentWorkspace?.id !== -1) {
this.workspaceService.getWorkspaceInfo(this.workspaceService.currentWorkspace.id);
}
@ -63,44 +76,75 @@ export class NavbarComponent implements OnInit {
action: 'close',
});
}
ngOnInit(): void {
if (this.electron.isElectron) {
this.modules = window.eo.getAppModuleList();
} else {
this.modules = new Map();
handleCopy() {
if (this.isCopy) {
return;
}
const isOk = copy(this.shareLink);
if (isOk) {
this.isCopy = true;
interval(700).subscribe(() => {
this.isCopy = false;
});
}
}
async ngOnInit(): Promise<void> {
this.modules = new Map();
this.shareLink = await this.getShareLink();
this.message
.get()
.pipe(distinct(({ type }) => type, interval(400)))
.subscribe(({ type }) => {
.subscribe(async ({ type }) => {
if (type === 'open-setting') {
this.openSettingModal();
return;
}
if (type === 'update-share-link') {
// * request share link
this.shareLink = await this.getShareLink();
}
});
}
loginOrSign() {
if (this.web.isWeb) {
return this.web.jumpToClient($localize`Eoapi Client is required to sign in`);
async getShareLink() {
if (this.workspaceService.isLocal) {
return '';
}
if (!this.userService.isLogin) {
return '';
}
if (this.status.isShare) {
return '';
}
const [res, err]: any = await this.http.api_shareCreateShare({});
if (err) {
return '';
}
const langHash = new Map().set('zh-Hans', 'zh').set('en-US', 'en');
return `${this.dataSourceService?.remoteServerUrl || window.location.host}/${langHash.get(
this.lang.systemLanguage
)}/home/share/http/test?shareId=${res.uniqueID}`.replace(
/\/{2,}(zh|en)\/home\/share/,
`/${langHash.get(this.lang.systemLanguage)}/home/share`
);
}
loginOrSign() {
this.dataSourceService.checkRemoteCanOperate();
}
loginOut() {
this.message.send({ type: 'logOut', data: {} });
}
async addWorkspace() {
if (this.web.isWeb) {
return this.web.jumpToClient($localize`Eoapi Client is required to add workspace`);
// 1. 如果配置了远程地址
} else {
this.dataSourceService.checkRemoteCanOperate(() => {
this.message.send({ type: 'addWorkspace', data: {} });
});
}
this.dataSourceService.checkRemoteCanOperate(() => {
this.message.send({ type: 'addWorkspace', data: {} });
});
}
getModules(): Array<ModuleInfo> {
return Array.from(this.modules.values());
}
handleSwitchLang(event) {
this.lang.changeLanguage(event);
}
/**
*

View File

@ -4,10 +4,11 @@ import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
import { NavbarComponent } from 'eo/workbench/browser/src/app/pages/navbar/navbar.component';
import { SettingModule } from 'eo/workbench/browser/src/app/shared/components/setting/setting.module';
import { CommonModule } from '@angular/common';
import { NzSelectModule } from 'ng-zorro-antd/select';
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
@NgModule({
imports: [CommonModule, NzDropDownModule, NzToolTipModule, SettingModule, SharedModule],
imports: [CommonModule, NzDropDownModule, NzSelectModule, NzToolTipModule, SettingModule, SharedModule],
declarations: [NavbarComponent],
exports: [NavbarComponent],
})

View File

@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
import { PagesComponent } from './pages.component';
import { PageBlankComponent } from '../shared/components/page-blank/page-blank.component';
import { Vue3Component } from 'eo/workbench/browser/src/app/pages/vue3/vue3.component';
const routes: Routes = [
{
path: '',
@ -37,6 +38,15 @@ const routes: Routes = [
path: 'extension',
loadChildren: () => import('./extension/extension.module').then((m) => m.ExtensionModule),
},
// {
// path: 'app-vue3',
// children: [
// {
// path: '**',
// component: Vue3Component,
// },
// ],
// },
],
},
];

View File

@ -7,7 +7,10 @@ import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
styleUrls: ['./pages.component.scss'],
})
export class PagesComponent implements OnInit {
isShowNotification = false;
constructor(public electron: ElectronService) {}
ngOnInit(): void {}
isShowNotification;
constructor(public electron: ElectronService) {
this.isShowNotification = false;
}
ngOnInit(): void {
}
}

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CommonModule, LocationStrategy } from '@angular/common';
import { PagesRoutingModule } from './pages-routing.module';
import { SettingModule } from '../shared/components/setting/setting.module';
import { PagesComponent } from './pages.component';
@ -10,6 +10,7 @@ import { UserModalComponent } from './user-modal.component';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzAlertModule } from 'ng-zorro-antd/alert';
@NgModule({
imports: [PagesRoutingModule, SettingModule, EouiModule, CommonModule, NzAlertModule, SharedModule, NavbarModule],
declarations: [PagesComponent, UserModalComponent],

View File

@ -7,6 +7,35 @@ const routes: Routes = [
{
path: '',
component: ShareComponent,
children: [
{
path: '',
redirectTo: 'http',
pathMatch: 'full',
},
{
path: 'http',
children: [
{
path: 'detail',
loadChildren: () => import('../pages/api/http/detail/api-detail.module').then((m) => m.ApiDetailModule),
},
{
path: 'test',
loadChildren: () => import('../pages/api/http/test/api-test.module').then((m) => m.ApiTestModule),
},
],
},
{
path: 'ws',
children: [
{
path: 'test',
loadChildren: () => import('../pages/api/websocket/websocket.module').then((m) => m.WebsocketModule),
},
],
},
],
},
];

View File

@ -1,10 +1,17 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ShareService } from 'eo/workbench/browser/src/app/shared/services/share.service';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
@Component({
selector: 'eo-share',
template: `<div>share</div>`,
template: `<section class="flex flex-col"><eo-api></eo-api></section>`,
})
export class ShareComponent implements OnInit {
constructor() {}
async ngOnInit(): Promise<void> {}
constructor(private route: ActivatedRoute, private share: ShareService, private status: StatusService) {}
async ngOnInit(): Promise<void> {
this.route.queryParams.subscribe(({ shareId }) => {
this.share.setShareId(shareId);
});
}
}

View File

@ -1,13 +1,14 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { ApiModule } from 'eo/workbench/browser/src/app/pages/api/api.module'
import { ShareRoutingModule } from './share-routing.module';
import { ShareComponent } from './share.component';
import { ShareRoutingModule } from './share-routing.module'
import { ShareComponent } from './share.component'
@NgModule({
imports: [ShareRoutingModule, CommonModule],
imports: [ShareRoutingModule, CommonModule, ApiModule],
declarations: [ShareComponent],
exports: [],
providers: [],
providers: []
})
export class ShareModule {}

View File

@ -1,20 +1,17 @@
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service'
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service'
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service'
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service'
import { ProjectService } from 'eo/workbench/browser/src/app/shared/services/project/project.service'
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service'
import { distinct } from 'rxjs/operators'
import { interval } from 'rxjs'
import { NzModalService } from 'ng-zorro-antd/modal'
import {
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
Validators
} from '@angular/forms'
import { ViewChild, ElementRef, Component, OnInit } from '@angular/core'
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service'
import { UserService } from 'eo/workbench/browser/src/app/shared/services/user/user.service';
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service';
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/storage/remote.service';
import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service';
import { ProjectService } from 'eo/workbench/browser/src/app/shared/services/project/project.service';
import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service';
import { distinct } from 'rxjs/operators';
import { interval } from 'rxjs';
import { NzModalService } from 'ng-zorro-antd/modal';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { ViewChild, ElementRef, Component, OnInit } from '@angular/core';
import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/workspace/workspace.service';
import { WebService } from 'eo/workbench/browser/src/app/core/services';
import { StatusService } from 'eo/workbench/browser/src/app/shared/services/status.service';
@Component({
selector: 'eo-user-modal',
@ -22,14 +19,13 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzFooter]="modalSyncFooter"
[(nzVisible)]="isSyncModalVisible"
(nzOnCancel)="handleSyncModalCancel()"
(nzAfterClose)="ek3bbgpCallback()"
(nzAfterClose)="e7odmm4Callback()"
nzTitle="Do you want to upload local data to the cloud ?"
i18n-nzTitle
>
<ng-container *nzModalContent>
<span i18n>
After confirmation, the system will create a cloud space to upload the
local data to the cloud.
After confirmation, the system will create a cloud space to upload the local data to the cloud.
</span>
<nz-alert
nzType="warning"
@ -44,7 +40,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzLoading]="isSyncCancelBtnLoading"
class=""
nzType="default"
(click)="btn4wxiipCallback()"
(click)="btnsgs0ckCallback()"
i18n
>
Cancel
@ -54,7 +50,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzLoading]="isSyncSyncBtnLoading"
class=""
nzType="primary"
(click)="btnddl2g5Callback()"
(click)="btnsf8zsrCallback()"
i18n
>
Sync
@ -65,18 +61,13 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzFooter]="modalCheckConnectFooter"
[(nzVisible)]="isCheckConnectModalVisible"
(nzOnCancel)="handleCheckConnectModalCancel()"
(nzAfterClose)="e3xpndmCallback()"
(nzAfterClose)="e4pgjfkCallback()"
nzTitle="Check your connection"
i18n-nzTitle
>
<ng-container *nzModalContent>
<span i18n> Can't connect right now, click to retry or </span>
<span
style="color: #1890ff"
class="cursor-pointer"
(click)="texte83xg2Callback()"
i18n
>
<span style="color: #1890ff" class="cursor-pointer" (click)="textiqd22iCallback()" i18n>
config in the configuration
</span>
</ng-container>
@ -86,7 +77,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzLoading]="isCheckConnectCancelBtnLoading"
class=""
nzType="default"
(click)="btn85p48gCallback()"
(click)="btnzls4ymCallback()"
i18n
>
Cancel
@ -96,7 +87,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzLoading]="isCheckConnectRetryBtnLoading"
class=""
nzType="primary"
(click)="btnb778v9Callback()"
(click)="btn0mu0b2Callback()"
i18n
>
Retry
@ -108,7 +99,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzWidth]="400"
[(nzVisible)]="isLoginModalVisible"
(nzOnCancel)="handleLoginModalCancel()"
(nzAfterClose)="e31iieeCallback()"
(nzAfterClose)="euu4ezrCallback()"
nzTitle="Sign In/Up"
i18n-nzTitle
>
@ -138,13 +129,9 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
i18n-placeholder
/>
<ng-template #passwordErrorTpl let-control>
<ng-container *ngIf="control.hasError('required')" i18n>
Please input your password;
</ng-container>
<ng-container *ngIf="control.hasError('required')" i18n> Please input your password; </ng-container>
<ng-container *ngIf="control.hasError('minlength')" i18n>
Min length is 6;
</ng-container>
<ng-container *ngIf="control.hasError('minlength')" i18n> Min length is 6; </ng-container>
</ng-template>
</nz-form-control>
</nz-form-item>
@ -157,7 +144,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
class="h-10 mt-2"
nzType="primary"
nzBlock
(click)="btnwvmlg8Callback()"
(click)="btnvz94ljCallback()"
i18n
>
Sign In/Up
@ -171,18 +158,13 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzFooter]="null"
[(nzVisible)]="isOpenSettingModalVisible"
(nzOnCancel)="handleOpenSettingModalCancel()"
(nzAfterClose)="ejr6w2fCallback()"
(nzAfterClose)="e95oi5lCallback()"
nzTitle="Open setting"
i18n-nzTitle
>
<ng-container *nzModalContent>
<span i18n> If you want to collaborate, please </span>
<span
style="color: #1890ff"
class="cursor-pointer"
(click)="textnrbqvsCallback()"
i18n
>
<span style="color: #1890ff" class="cursor-pointer" (click)="textqdb64pCallback()" i18n>
open the settings
</span>
<span i18n> and fill in the configuration </span>
@ -192,16 +174,12 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
[nzFooter]="null"
[(nzVisible)]="isAddWorkspaceModalVisible"
(nzOnCancel)="handleAddWorkspaceModalCancel()"
(nzAfterClose)="epruwutCallback()"
(nzAfterClose)="ebdsz2aCallback()"
nzTitle="Add Workspace"
i18n-nzTitle
>
<ng-container *nzModalContent>
<form
nz-form
[formGroup]="validateWorkspaceNameForm"
nzLayout="horizontal"
>
<form nz-form [formGroup]="validateWorkspaceNameForm" nzLayout="horizontal">
<nz-form-item>
<nz-form-control nzErrorTip="Please input your new work name;">
<input
@ -222,7 +200,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
type="button"
class="mr-3"
nzType="default"
(click)="btnfjbf3iCallback()"
(click)="btn66ztjiCallback()"
i18n
>
Cancel
@ -233,7 +211,7 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
type="submit"
class=""
nzType="primary"
(click)="btnkh8m1jCallback()"
(click)="btnd4wbcjCallback()"
i18n
>
Save
@ -241,26 +219,26 @@ import { WorkspaceService } from 'eo/workbench/browser/src/app/shared/services/w
</section>
</form>
</ng-container>
</nz-modal>`
</nz-modal>`,
})
export class UserModalComponent implements OnInit {
isSyncModalVisible
isSyncCancelBtnLoading
isSyncSyncBtnLoading
isCheckConnectModalVisible
isCheckConnectCancelBtnLoading
isCheckConnectRetryBtnLoading
isLoginModalVisible
validateLoginForm
@ViewChild('usernameLoginRef') usernameLoginRef: ElementRef<HTMLInputElement>
isLoginBtnBtnLoading
isOpenSettingModalVisible
isAddWorkspaceModalVisible
validateWorkspaceNameForm
isSyncModalVisible;
isSyncCancelBtnLoading;
isSyncSyncBtnLoading;
isCheckConnectModalVisible;
isCheckConnectCancelBtnLoading;
isCheckConnectRetryBtnLoading;
isLoginModalVisible;
validateLoginForm;
@ViewChild('usernameLoginRef') usernameLoginRef: ElementRef<HTMLInputElement>;
isLoginBtnBtnLoading;
isOpenSettingModalVisible;
isAddWorkspaceModalVisible;
validateWorkspaceNameForm;
@ViewChild('newWorkNameWorkspaceNameRef')
newWorkNameWorkspaceNameRef: ElementRef<HTMLInputElement>
isCancelBtnLoading
isSaveBtnLoading
newWorkNameWorkspaceNameRef: ElementRef<HTMLInputElement>;
isCancelBtnLoading;
isSaveBtnLoading;
constructor(
public user: UserService,
public message: MessageService,
@ -270,22 +248,24 @@ export class UserModalComponent implements OnInit {
public dataSource: DataSourceService,
public modal: NzModalService,
public fb: UntypedFormBuilder,
public workspace: WorkspaceService
public workspace: WorkspaceService,
private web: WebService,
private status: StatusService
) {
this.isSyncModalVisible = false
this.isSyncCancelBtnLoading = false
this.isSyncSyncBtnLoading = false
this.isCheckConnectModalVisible = false
this.isCheckConnectCancelBtnLoading = false
this.isCheckConnectRetryBtnLoading = false
this.isLoginModalVisible = false
this.validateLoginForm = UntypedFormGroup
this.isLoginBtnBtnLoading = false
this.isOpenSettingModalVisible = false
this.isAddWorkspaceModalVisible = false
this.validateWorkspaceNameForm = UntypedFormGroup
this.isCancelBtnLoading = false
this.isSaveBtnLoading = false
this.isSyncModalVisible = false;
this.isSyncCancelBtnLoading = false;
this.isSyncSyncBtnLoading = false;
this.isCheckConnectModalVisible = false;
this.isCheckConnectCancelBtnLoading = false;
this.isCheckConnectRetryBtnLoading = false;
this.isLoginModalVisible = false;
this.validateLoginForm = UntypedFormGroup;
this.isLoginBtnBtnLoading = false;
this.isOpenSettingModalVisible = false;
this.isAddWorkspaceModalVisible = false;
this.validateWorkspaceNameForm = UntypedFormGroup;
this.isCancelBtnLoading = false;
this.isSaveBtnLoading = false;
}
async ngOnInit(): Promise<void> {
this.message
@ -294,438 +274,443 @@ export class UserModalComponent implements OnInit {
.subscribe(async ({ type, data }) => {
if (type === 'login') {
// * 唤起弹窗
this.isLoginModalVisible = true
this.isLoginModalVisible = true;
{
{
{
// * auto focus
setTimeout(() => {
this.usernameLoginRef?.nativeElement.focus()
}, 300)
this.usernameLoginRef?.nativeElement.focus();
}, 300);
}
}
}
return
return;
}
if (type === 'clear-user') {
this.user.clearAuth()
this.user.clearAuth();
this.user.setUserProfile({
id: -1,
password: '',
username: '',
workspaces: []
})
return
workspaces: [],
});
return;
}
if (type === 'http-401') {
const { id } = this.workspace.currentWorkspace
const { id } = this.workspace.currentWorkspace;
if (id === -1) {
return
return;
}
// * 唤起弹窗
this.isLoginModalVisible = true
this.isLoginModalVisible = true;
{
{
{
// * auto focus
setTimeout(() => {
this.usernameLoginRef?.nativeElement.focus()
}, 300)
this.usernameLoginRef?.nativeElement.focus();
}, 300);
}
}
}
return
return;
}
if (type === 'logOut') {
this.workspace.setCurrentWorkspaceID(-1)
this.workspace.setCurrentWorkspaceID(-1);
this.user.setUserProfile({
id: -1,
password: '',
username: '',
workspaces: []
})
workspaces: [],
});
{
this.workspace.setWorkspaceList([])
this.workspace.setWorkspaceList([]);
}
this.workspace.setCurrentWorkspace(
this.workspace.getLocalWorkspaceInfo()
)
this.eMessage.success($localize`Successfully logged out !`)
const refreshToken = this.user.refreshToken
this.user.clearAuth()
this.workspace.setCurrentWorkspace(this.workspace.getLocalWorkspaceInfo());
this.eMessage.success($localize`Successfully logged out !`);
const refreshToken = this.user.refreshToken;
this.user.clearAuth();
{
const [data, err]: any = await this.api.api_authLogout({
refreshToken
})
refreshToken,
});
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
}
return
return;
}
if (type === 'ping-fail') {
this.eMessage.error($localize`Connect failed`)
this.eMessage.error($localize`Connect failed`);
// * 唤起弹窗
this.isCheckConnectModalVisible = true
this.isCheckConnectModalVisible = true;
{
}
return
return;
}
if (type === 'ping-success') {
this.eMessage.success($localize`Connect success`)
return
this.eMessage.success($localize`Connect success`);
return;
}
if (type === 'need-config-remote') {
// * 唤起弹窗
this.isOpenSettingModalVisible = true
this.isOpenSettingModalVisible = true;
return
return;
}
if (type === 'addWorkspace') {
// * 唤起弹窗
this.isAddWorkspaceModalVisible = true
this.isAddWorkspaceModalVisible = true;
{
{
// * auto focus
setTimeout(() => {
this.newWorkNameWorkspaceNameRef?.nativeElement.focus()
}, 300)
this.newWorkNameWorkspaceNameRef?.nativeElement.focus();
}, 300);
}
}
return
return;
}
if (type === 'retry') {
// * 唤起弹窗
this.isCheckConnectModalVisible = true
this.isCheckConnectModalVisible = true;
{
}
return
return;
}
})
});
// * Init Login form
this.validateLoginForm = this.fb.group({
username: [null, [Validators.required]],
password: [null, [Validators.required, Validators.minLength(6)]]
})
password: [null, [Validators.required, Validators.minLength(6)]],
});
// * Init WorkspaceName form
this.validateWorkspaceNameForm = this.fb.group({
newWorkName: [null, [Validators.required]]
})
newWorkName: [null, [Validators.required]],
});
const { id: workspaceID } = this.workspace.currentWorkspace
const [data, err]: any = await this.api.api_workspaceList({})
const { id: workspaceID } = this.workspace.currentWorkspace;
if (this.status.isShare) {
return;
}
const [data, err]: any = await this.api.api_workspaceList({});
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.workspace.setWorkspaceList(data)
this.workspace.setWorkspaceList(data);
if (workspaceID !== -1) {
const { projects } = await this.workspace.getWorkspaceInfo(workspaceID)
this.project.setCurrentProjectID(projects.at(0).uuid)
const { projects } = await this.workspace.getWorkspaceInfo(workspaceID);
this.project.setCurrentProjectID(projects.at(0).uuid);
}
const url = this.dataSource.mockUrl
const url = this.dataSource.remoteServerUrl;
if (url === '') {
// * 唤起弹窗
this.isOpenSettingModalVisible = true
// this.isOpenSettingModalVisible = true;
{
}
return
return;
}
const { id: currentWorkspaceID } = this.workspace.currentWorkspace
const { id: currentWorkspaceID } = this.workspace.currentWorkspace;
if (currentWorkspaceID === -1) {
return
return;
}
const status = this.dataSource.isConnectRemote
const status = this.dataSource.isConnectRemote;
if (!status) {
// * 唤起弹窗
this.isCheckConnectModalVisible = true
if (this.web.isWeb) {
return;
}
this.isCheckConnectModalVisible = true;
{
}
return
return;
}
}
handleSyncModalCancel(): void {
// * 关闭弹窗
this.isSyncModalVisible = false
this.isSyncModalVisible = false;
}
async ek3bbgpCallback() {
async e7odmm4Callback() {
// * nzAfterClose event callback
{
}
}
async btn4wxiipCallback() {
async btnsgs0ckCallback() {
// * click event callback
this.isSyncCancelBtnLoading = true
this.isSyncCancelBtnLoading = true;
const btnSyncCancelRunning = async () => {
// * 关闭弹窗
this.isSyncModalVisible = false
}
await btnSyncCancelRunning()
this.isSyncCancelBtnLoading = false
this.isSyncModalVisible = false;
};
await btnSyncCancelRunning();
this.isSyncCancelBtnLoading = false;
}
async btnddl2g5Callback() {
async btnsf8zsrCallback() {
// * click event callback
this.isSyncSyncBtnLoading = true
this.isSyncSyncBtnLoading = true;
const btnSyncSyncRunning = async () => {
const eData = await this.project.exportLocalProjectData()
const eData = await this.project.exportLocalProjectData();
const [data, err]: any = await this.api.api_workspaceUpload(eData)
const [data, err]: any = await this.api.api_workspaceUpload(eData);
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
const { workspace } = data
const list = this.workspace
.getWorkspaceList()
.filter((it) => it.id !== -1)
this.workspace.setWorkspaceList([...list, workspace])
this.workspace.setCurrentWorkspaceID(workspace)
const { workspace } = data;
const list = this.workspace.getWorkspaceList().filter((it) => it.id !== -1);
this.workspace.setWorkspaceList([...list, workspace]);
this.workspace.setCurrentWorkspaceID(workspace);
// * 关闭弹窗
this.isSyncModalVisible = false
}
await btnSyncSyncRunning()
this.isSyncSyncBtnLoading = false
this.isSyncModalVisible = false;
};
await btnSyncSyncRunning();
this.isSyncSyncBtnLoading = false;
}
handleCheckConnectModalCancel(): void {
// * 关闭弹窗
this.isCheckConnectModalVisible = false
this.isCheckConnectModalVisible = false;
}
async e3xpndmCallback() {
async e4pgjfkCallback() {
// * nzAfterClose event callback
{
}
}
async btn85p48gCallback() {
async btnzls4ymCallback() {
// * click event callback
this.isCheckConnectCancelBtnLoading = true
this.isCheckConnectCancelBtnLoading = true;
const btnCheckConnectCancelRunning = async () => {
// * 关闭弹窗
this.isCheckConnectModalVisible = false
}
await btnCheckConnectCancelRunning()
this.isCheckConnectCancelBtnLoading = false
this.isCheckConnectModalVisible = false;
};
await btnCheckConnectCancelRunning();
this.isCheckConnectCancelBtnLoading = false;
}
async btnb778v9Callback() {
async btn0mu0b2Callback() {
// * click event callback
this.isCheckConnectRetryBtnLoading = true
this.isCheckConnectRetryBtnLoading = true;
const btnCheckConnectRetryRunning = async () => {
this.dataSource.checkRemoteAndTipModal()
this.dataSource.checkRemoteAndTipModal();
// * 关闭弹窗
this.isCheckConnectModalVisible = false
}
await btnCheckConnectRetryRunning()
this.isCheckConnectRetryBtnLoading = false
this.isCheckConnectModalVisible = false;
};
await btnCheckConnectRetryRunning();
this.isCheckConnectRetryBtnLoading = false;
}
async texte83xg2Callback() {
async textiqd22iCallback() {
// * click event callback
this.message.send({ type: 'open-setting', data: {} });
// * 关闭弹窗
this.isOpenSettingModalVisible = false
this.isCheckConnectModalVisible = false;
this.isOpenSettingModalVisible = false;
}
handleLoginModalCancel(): void {
// * 关闭弹窗
this.isLoginModalVisible = false
this.isLoginModalVisible = false;
}
async e31iieeCallback() {
async euu4ezrCallback() {
// * nzAfterClose event callback
{
// * auto clear form
this.validateLoginForm.reset()
this.validateLoginForm.reset();
}
}
async btnwvmlg8Callback() {
async btnvz94ljCallback() {
// * click event callback
this.isLoginBtnBtnLoading = true
this.isLoginBtnBtnLoading = true;
const btnLoginBtnRunning = async () => {
const isOk = this.validateLoginForm.valid
const isOk = this.validateLoginForm.valid;
if (!isOk) {
this.eMessage.error($localize`Please check you username or password`)
return
this.eMessage.error($localize`Please check you username or password`);
return;
}
// * get login form values
const formData = this.validateLoginForm.value
const [data, err]: any = await this.api.api_authLogin(formData)
const formData = this.validateLoginForm.value;
const [data, err]: any = await this.api.api_authLogin(formData);
if (err) {
this.eMessage.error(
$localize`Please check the account/password, the account must be a mobile phone number or email !`
)
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
);
if ([401, 403].includes(err.status)) {
this.isLoginBtnBtnLoading = false;
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.user.setLoginInfo(data)
this.user.setLoginInfo(data);
// * 关闭弹窗
this.isLoginModalVisible = false
this.isLoginModalVisible = false;
this.message.send({ type: 'update-share-link', data: {} });
{
const [data, err]: any = await this.api.api_userReadProfile(null)
const [data, err]: any = await this.api.api_userReadProfile(null);
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.user.setUserProfile(data)
this.user.setUserProfile(data);
}
{
const [data, err]: any = await this.api.api_workspaceList({})
const [data, err]: any = await this.api.api_workspaceList({});
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.workspace.setWorkspaceList(data)
this.workspace.setWorkspaceList(data);
}
if (!data.isFirstLogin) {
return
return;
}
// * 唤起弹窗
this.isSyncModalVisible = true
this.isSyncModalVisible = true;
{
}
}
await btnLoginBtnRunning()
this.isLoginBtnBtnLoading = false
};
await btnLoginBtnRunning();
this.isLoginBtnBtnLoading = false;
}
handleOpenSettingModalCancel(): void {
// * 关闭弹窗
this.isOpenSettingModalVisible = false
this.isOpenSettingModalVisible = false;
}
async ejr6w2fCallback() {
async e95oi5lCallback() {
// * nzAfterClose event callback
{
}
}
async textnrbqvsCallback() {
async textqdb64pCallback() {
// * click event callback
this.message.send({ type: 'open-setting', data: {} })
this.message.send({ type: 'open-setting', data: {} });
// * 关闭弹窗
this.isOpenSettingModalVisible = false
this.isOpenSettingModalVisible = false;
}
handleAddWorkspaceModalCancel(): void {
// * 关闭弹窗
this.isAddWorkspaceModalVisible = false
this.isAddWorkspaceModalVisible = false;
}
async epruwutCallback() {
async ebdsz2aCallback() {
// * nzAfterClose event callback
{
// * auto clear form
this.validateWorkspaceNameForm.reset()
this.validateWorkspaceNameForm.reset();
}
}
async btnfjbf3iCallback() {
async btn66ztjiCallback() {
// * click event callback
this.isCancelBtnLoading = true
this.isCancelBtnLoading = true;
const btnCancelRunning = async () => {
// * 关闭弹窗
this.isAddWorkspaceModalVisible = false
}
await btnCancelRunning()
this.isCancelBtnLoading = false
this.isAddWorkspaceModalVisible = false;
};
await btnCancelRunning();
this.isCancelBtnLoading = false;
}
async btnkh8m1jCallback() {
async btnd4wbcjCallback() {
// * click event callback
this.isSaveBtnLoading = true
this.isSaveBtnLoading = true;
const btnSaveRunning = async () => {
const { newWorkName: title } = this.validateWorkspaceNameForm.value
const [data, err]: any = await this.api.api_workspaceCreate({ title })
const { newWorkName: title } = this.validateWorkspaceNameForm.value;
const [data, err]: any = await this.api.api_workspaceCreate({ title });
if (err) {
this.eMessage.error($localize`Add workspace Failed !`)
this.eMessage.error($localize`Add workspace Failed !`);
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.eMessage.success($localize`Create new workspace successfully !`)
this.eMessage.success($localize`Create new workspace successfully !`);
// * 关闭弹窗
this.isAddWorkspaceModalVisible = false
this.isAddWorkspaceModalVisible = false;
this.message.send({ type: 'update-share-link', data: {} });
{
const [lData, err]: any = await this.api.api_workspaceList({})
const [lData, err]: any = await this.api.api_workspaceList({});
if (err) {
if (err.status === 401) {
this.message.send({ type: 'clear-user', data: {} })
this.message.send({ type: 'clear-user', data: {} });
if (this.user.isLogin) {
return
return;
}
this.message.send({ type: 'http-401', data: {} })
this.message.send({ type: 'http-401', data: {} });
}
return
return;
}
this.workspace.setWorkspaceList(lData)
this.workspace.setCurrentWorkspace(data)
this.workspace.setWorkspaceList(lData);
this.workspace.setCurrentWorkspace(data);
}
}
await btnSaveRunning()
this.isSaveBtnLoading = false
};
await btnSaveRunning();
this.isSaveBtnLoading = false;
}
}

View File

@ -0,0 +1,5 @@
<div style="transform: translate(0)">
<micro-app [attr.name]="name" [attr.url]='url' shadowDOM [data]='microAppData' (created)='handleCreate()'
default-page='/' (beforemount)='handleBeforeMount()' (mounted)='handleMount()' (unmount)='handleUnmount()'
(error)='handleError()' (datachange)='handleDataChange($event)'></micro-app>
</div>

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Vue3Component } from './vue3.component';
describe('Vue3Component', () => {
let component: Vue3Component;
let fixture: ComponentFixture<Vue3Component>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ Vue3Component ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(Vue3Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,45 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-vue3',
templateUrl: './vue3.component.html',
styleUrls: ['./vue3.component.scss'],
})
export class Vue3Component implements OnInit {
url = `https://www.npmjs.com/`;
name = 'app-vue3';
constructor() {}
microAppData = { msg: '来自基座的数据' };
ngOnInit(): void {}
handleCreate(): void {
console.log('child-vue3 创建了');
}
handleBeforeMount(): void {
console.log('child-vue3 即将被渲染');
}
handleMount(): void {
console.log('child-vue3 已经渲染完成');
setTimeout(() => {
this.microAppData = { msg: '来自基座的新数据' };
}, 2000);
}
handleUnmount(): void {
console.log('child-vue3 卸载了');
}
handleError(): void {
console.log('child-vue3 加载出错了');
}
handleDataChange(e): void {
console.log('来自子应用 child-vue3 的数据:', e.detail.data);
}
}

Some files were not shown because too many files have changed in this diff Show More