diff --git a/.github/workflows/release-win.yml b/.github/workflows/release-win.yml deleted file mode 100644 index 2689c76d..00000000 --- a/.github/workflows/release-win.yml +++ /dev/null @@ -1,46 +0,0 @@ -# release.yml - -# workflow 's name -name: Build electron App for Win - -on: - push: - tags: - - 'v*.*.*' - -# Workflow's jobs -jobs: - # job's id - release: - # job's name - name: build and release electron app - # the type of machine to run the job on - runs-on: ${{ matrix.os }} - - # Platforms to build on/for - strategy: - matrix: - os: [windows-latest] - - steps: - # check out repository - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: 16 - registry-url: https://registry.npmjs.org/ - - # create electron-builder.json - - name: Create and populate electron-builer.json file - env: - ELE_BUILER_WIN: ${{ secrets.ELE_BUILER_WIN }} - shell: bash - run: | - echo $ELE_BUILER_WIN > electron-builer.json - - # install & build app for win - - run: yarn install --frozen-lockfile - - run: yarn global add @angular/cli - - run: yarn release - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..a9ec1c44 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,94 @@ +name: Release + +on: + push: + branches: + - feat/preview + +jobs: + release: + name: build and release electron app + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest, ubuntu-latest] + + steps: + - name: Check out git repository + uses: actions/checkout@v3.0.0 + + - name: Install Node.js + uses: actions/setup-node@v3.0.0 + with: + node-version: '16' + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v3 + id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: Install + run: | + yarn install --frozen-lockfile + echo "${{ secrets.QINIU_ENV_JS }}" > qiniu_env.js + + - name: Release for Windows + if: matrix.os == 'windows-latest' + run: | + yarn release + dir ./release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + + - name: Release for MacOS + if: matrix.os == 'macos-latest' + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} + P12_PASSWORD: ${{ secrets.P12_PASSWORD }} + # BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + # create variables + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + # PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + + # import certificate and provisioning profile from secrets + echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH + # echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode --output $PP_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # apply provisioning profile + # mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + # cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles + echo "${{ secrets.NOTARIZE_JS }}" > build/notarize.js + yarn release:m1 + yarn release + ls ./release + + # clean up keychain and provisioning profile + security delete-keychain $RUNNER_TEMP/app-signing.keychain-db + + - name: Release for Linux + if: matrix.os == 'ubuntu-latest' + run: | + yarn release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.gitignore b/.gitignore index 6d4da432..2d9f84c2 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ out/ !/api/*.js !/build/*.js !*.config.js +!upload.js scripts/notarize.js # dependencies diff --git a/.vscode/ts.code-snippets b/.vscode/ts.code-snippets new file mode 100644 index 00000000..69599685 --- /dev/null +++ b/.vscode/ts.code-snippets @@ -0,0 +1,28 @@ +{ + "Print to console": { + "scope": "typescript", + "prefix": "log", + "body": [ + "console.log('111$1');" + ], + "description": "Log output to console" + }, + "Create a Angular Component": { + "scope": "typescript", + "prefix": "comp", + "body": [ + "import { Component, OnInit } from '@angular/core';", + "@Component({", + "// standalone: true,", + "selector: '$1',", + "template: `
`,", + "styleUrls: []", + "})", + "export class $2Component implements OnInit {", + "constructor() {}", + "ngOnInit() {}", + "}" + ], + "description": "Create a new Angular Component" + } +} \ No newline at end of file diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 00000000..d6b93bc0 --- /dev/null +++ b/build/entitlements.mac.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + + diff --git a/build/notarize.js b/build/notarize.js new file mode 100644 index 00000000..60158866 --- /dev/null +++ b/build/notarize.js @@ -0,0 +1,3 @@ +exports.default = function notarizing(context) { + return context; +}; diff --git a/electron-builder.json b/electron-builder.json index 49fe8f9e..0b7ab86b 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -15,7 +15,13 @@ "out/app/common/**/*", "!**/*.ts" ], - "publish": ["github"], + "publish": [ + { + "provider": "generic", + "url": "https://packages.eoapi.io" + }, + "github" + ], "generateUpdatesFilesForAllChannels": true, "nsis": { "oneClick": false, @@ -38,11 +44,14 @@ "icon": "src/app/common/images/512x512.png", "hardenedRuntime": true, "gatekeeperAssess": false, + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist", "target": ["dmg", "zip"] }, "dmg": { "sign": false }, + "afterSign": "build/notarize.js", "linux": { "icon": "src/app/common/images/", "target": ["AppImage"] diff --git a/package.json b/package.json index 24ca4648..602b3ffc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "eoapi", - "version": "1.2.0", + "souceLocale": "zh-Hans", + "version": "1.2.4", "main": "out/app/electron-main/main.js", "description": "A lightweight, extensible API tool", "homepage": "https://github.com/eolinker/eoapi.git", @@ -21,8 +22,9 @@ "electron:dev:static": "npm run electron:tsc && electron .", "electron:dev": "npm run electron:tsc && electron . --development", "build": "npm-run-all -s build:workbench electron:tsc && electron-builder build", - "build:static": "npm run electron:tsc && electron-builder build", - "release": "npm-run-all -s build:workbench electron:tsc && electron-builder --publish=always", + "build:static": "npm-run-all -s build:workbench electron:tsc && electron-builder build", + "release": "npm-run-all -s build:workbench electron:tsc && electron-builder --publish=always && node upload.js", + "release:m1": "npm-run-all -s build:workbench electron:tsc && electron-builder -m=dmg --arm64 -p onTagOrDraft", "test": "npm-run-all --serial test:*", "electron:tsc": "tsc -p tsconfig.json", "copyfile:out": "copyfiles -u 1 src/**/*.json src/app/common/images/** out" @@ -33,8 +35,8 @@ "content-disposition": "^0.5.4", "copyfiles": "2.4.1", "crypto-js": "^4.1.1", - "electron-log": "^4.4.7", - "electron-updater": "^5.0.1", + "electron-log": "^4.4.8", + "electron-updater": "^5.0.5", "express": "4.18.1", "fix-path": "3.0.0", "form-data": "^4.0.0", @@ -42,27 +44,29 @@ "iconv-lite": "^0.6.3", "npm": "6.14.17", "portfinder": "1.0.28", - "resolve": "^1.22.0", + "qiniu": "^6.1.13", + "resolve": "^1.22.1", "rxjs": "7.5.5", "xml2js": "^0.4.23" }, "devDependencies": { - "@types/node": "17.0.32", - "@typescript-eslint/eslint-plugin": "5.23.0", - "@typescript-eslint/parser": "5.23.0", - "dmg-builder": "23.0.9", - "electron": "19.0.1", - "electron-builder": "23.0.9", + "@types/node": "18.0.0", + "@typescript-eslint/eslint-plugin": "5.29.0", + "@typescript-eslint/parser": "5.29.0", + "dmg-builder": "23.2.0", + "electron": "19.0.6", + "electron-builder": "23.1.0", "electron-notarize": "1.2.1", "electron-reload": "1.5.0", - "eslint": "8.15.0", + "eslint": "8.18.0", "eslint-plugin-import": "2.26.0", - "eslint-plugin-jsdoc": "39.2.9", + "eslint-plugin-jsdoc": "39.3.3", "eslint-plugin-prefer-arrow": "1.2.3", "npm-run-all": "4.1.5", - "ts-node": "10.7.0", - "typescript": "~4.6.4", - "wait-on": "6.0.1" + "ts-node": "10.8.1", + "typescript": "~4.7.4", + "wait-on": "6.0.1", + "yaml": "2.1.1" }, "__npminstall_done": false, "node-module-alias": { diff --git a/src/app/electron-browser/IndexedDB/src/index.ts b/src/app/electron-browser/IndexedDB/src/index.ts index 23c495dd..7ef76baa 100644 --- a/src/app/electron-browser/IndexedDB/src/index.ts +++ b/src/app/electron-browser/IndexedDB/src/index.ts @@ -1,89 +1,89 @@ -import { ipcRenderer, app } from 'electron'; -import { isNotEmpty } from 'eo/shared/common/common'; -import * as fs from 'fs'; -import * as path from 'path'; -import { - StorageRes, - StorageResStatus, - StorageHandleArgs, - StorageProcessType, -} from '../../../../workbench/browser/src/app/shared/services/storage/index.model'; -import { IndexedDBStorage } from '../../../../workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index'; +// import { ipcRenderer, app } from 'electron'; +// import { isNotEmpty } from 'eo/shared/common/common'; +// import * as fs from 'fs'; +// import * as path from 'path'; +// import { +// StorageRes, +// StorageResStatus, +// StorageHandleArgs, +// StorageProcessType, +// } from '../../../../workbench/browser/src/app/shared/services/storage/index.model'; +// import { IndexedDBStorage } from '../../../../workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index'; -class StorageService { - private ipcRenderer: typeof ipcRenderer; - private app: typeof app; - private fs: typeof fs; - private path: typeof path; - constructor() { - this.ipcRenderer = window.require('electron').ipcRenderer; - this.app = window.require('electron').app; - this.fs = window.require('fs'); - this.path = window.require('path'); - this.storageListen(); - } +// class StorageService { +// private ipcRenderer: typeof ipcRenderer; +// private app: typeof app; +// private fs: typeof fs; +// private path: typeof path; +// constructor() { +// this.ipcRenderer = window.require('electron').ipcRenderer; +// this.app = window.require('electron').app; +// this.fs = window.require('fs'); +// this.path = window.require('path'); +// this.storageListen(); +// } - /** - * 存储监听处理 - * @param args - */ - private storageListenHandle(args: StorageHandleArgs): void { - const action: string = args.action || undefined; - const handleResult: StorageRes = { - status: StorageResStatus.invalid, - data: undefined, - callback: args.callback || null, - }; - if (IndexedDBStorage && IndexedDBStorage[action] && typeof IndexedDBStorage[action] === 'function') { - IndexedDBStorage[action](...args.params).subscribe( - (result: any) => { - handleResult.data = result; - if (isNotEmpty(result)) { - handleResult.status = StorageResStatus.success; - } else { - handleResult.status = StorageResStatus.empty; - } - this.storageListenHandleNotify(args.type, handleResult); - }, - (error: any) => { - handleResult.status = StorageResStatus.error; - this.storageListenHandleNotify(args.type, handleResult); - } - ); - } else { - this.storageListenHandleNotify(args.type, handleResult); - } - } +// /** +// * 存储监听处理 +// * @param args +// */ +// private storageListenHandle(args: StorageHandleArgs): void { +// const action: string = args.action || undefined; +// const handleResult: StorageRes = { +// status: StorageResStatus.invalid, +// data: undefined, +// callback: args.callback || null, +// }; +// if (IndexedDBStorage && IndexedDBStorage[action] && typeof IndexedDBStorage[action] === 'function') { +// IndexedDBStorage[action](...args.params).subscribe( +// (result: any) => { +// handleResult.data = result; +// if (isNotEmpty(result)) { +// handleResult.status = StorageResStatus.success; +// } else { +// handleResult.status = StorageResStatus.empty; +// } +// this.storageListenHandleNotify(args.type, handleResult); +// }, +// (error: any) => { +// handleResult.status = StorageResStatus.error; +// this.storageListenHandleNotify(args.type, handleResult); +// } +// ); +// } else { +// this.storageListenHandleNotify(args.type, handleResult); +// } +// } - /** - * 数据存储监听通知返回 - * @param type - * @param result - */ - private storageListenHandleNotify(type: string, result: StorageRes): void { - try { - if (StorageProcessType.default === type) { - this.ipcRenderer.send('eo-storage', { type: 'result', result: result }); - } else if (StorageProcessType.sync === type) { - const storageTemp = this.path.join(this.app.getPath('home'), '.eo', 'tmp.storage'); - this.fs.writeFileSync(storageTemp, JSON.stringify(result)); - } else if (StorageProcessType.remote === type) { - window.require('@electron/remote').getGlobal('shareObject').storageResult = result; - } - } catch (e) { - console.log(e); - } - } +// /** +// * 数据存储监听通知返回 +// * @param type +// * @param result +// */ +// private storageListenHandleNotify(type: string, result: StorageRes): void { +// try { +// if (StorageProcessType.default === type) { +// this.ipcRenderer.send('eo-storage', { type: 'result', result: result }); +// } else if (StorageProcessType.sync === type) { +// const storageTemp = this.path.join(this.app.getPath('home'), '.eo', 'tmp.storage'); +// this.fs.writeFileSync(storageTemp, JSON.stringify(result)); +// } else if (StorageProcessType.remote === type) { +// window.require('@electron/remote').getGlobal('shareObject').storageResult = result; +// } +// } catch (e) { +// console.log(e); +// } +// } - /** - * 开启数据存储监听 - * @returns - */ - private storageListen(): void { - this.ipcRenderer.on('eo-storage', (event, args: StorageHandleArgs) => this.storageListenHandle(args)); - } +// /** +// * 开启数据存储监听 +// * @returns +// */ +// private storageListen(): void { +// this.ipcRenderer.on('eo-storage', (event, args: StorageHandleArgs) => this.storageListenHandle(args)); +// } - isElectron(): boolean { - return !!(window && window.process && window.process.type); - } -} +// isElectron(): boolean { +// return !!(window && window.process && window.process.type); +// } +// } diff --git a/src/app/electron-main/main.ts b/src/app/electron-main/main.ts index 14470a5b..3b1ff3f0 100644 --- a/src/app/electron-main/main.ts +++ b/src/app/electron-main/main.ts @@ -54,12 +54,19 @@ function createWindow(): BrowserWindow { contextIsolation: false, // false if you want to run e2e test with Spectron }, }); + // 启动mock服务 + mockServer.start(win as any); proxyOpenExternal(win); let loadPage = async () => { + let currentUrl = win.webContents.getURL(); + let locale = ['zh', 'en'].find((val) => currentUrl.includes(val)); const file: string = processEnv === 'development' ? 'http://localhost:4200' - : `file://${path.join(__dirname, '../../../src/workbench/browser/dist/index.html')}`; + : `file://${path.join( + __dirname, + `../../../src/workbench/browser/dist/${locale || app.getLocale()}/index.html` + )}`; win.loadURL(file); if (['development'].includes(processEnv)) { win.webContents.openDevTools({ @@ -69,8 +76,6 @@ function createWindow(): BrowserWindow { UnitWorkerModule.setup({ view: win, }); - // 启动mock服务 - await mockServer.start(win as any); }; win.webContents.on('did-fail-load', (event, errorCode) => { console.error('did-fail-load', errorCode); @@ -123,7 +128,7 @@ try { ipcMain.on('message', function (event, arg) { console.log('recieve render msg=>', arg, arg.action); //only action from mainView can be executed - if (event.frameId !== 1) return; + // if (event.frameId !== 1) return; switch (arg.action) { case 'minimize': { win.minimize(); diff --git a/src/app/electron-main/updater.ts b/src/app/electron-main/updater.ts index fd811216..9c7910fc 100644 --- a/src/app/electron-main/updater.ts +++ b/src/app/electron-main/updater.ts @@ -5,6 +5,10 @@ const appVersion = require('../../../package.json').version; export class EoUpdater { constructor() { this.watchLog(); + // autoUpdater.setFeedURL({ + // provider: 'generic', + // url: 'https://packages.eoapi.io', + // }); // 是否自动更新 // autoUpdater.autoDownload = window.eo.getModuleSettings('common.app.autoUpdate') !== false; if (appVersion.includes('beta')) autoUpdater.channel = 'beta'; diff --git a/src/platform/electron-browser/preload.ts b/src/platform/electron-browser/preload.ts index 605e074c..4eb23989 100644 --- a/src/platform/electron-browser/preload.ts +++ b/src/platform/electron-browser/preload.ts @@ -125,8 +125,9 @@ window.eo.storageRemote = (args) => { return output; }; -window.eo.saveSettings = ({ settings, nestedSettings }) => { - return ipcRenderer.sendSync('eo-sync', { action: 'saveSettings', data: { settings, nestedSettings } }); +window.eo.saveSettings = (settings) => { + // console.log('window.eo.saveSettings', settings); + return ipcRenderer.sendSync('eo-sync', { action: 'saveSettings', data: { settings } }); }; window.eo.saveModuleSettings = (moduleID, settings) => { diff --git a/src/platform/node/configuration/lib/index.ts b/src/platform/node/configuration/lib/index.ts index 2eadfc32..554ef45f 100644 --- a/src/platform/node/configuration/lib/index.ts +++ b/src/platform/node/configuration/lib/index.ts @@ -41,10 +41,10 @@ export class Configuration implements ConfigurationInterface { /** * 保存全局配置 */ - saveSettings({ settings = {}, nestedSettings = {} }): boolean { + saveSettings({ settings = {} }): boolean { + // console.log('settings', settings); let data = this.loadConfig(); data.settings = settings; - data.nestedSettings = nestedSettings; return this.saveConfig(data); } @@ -56,11 +56,7 @@ export class Configuration implements ConfigurationInterface { saveModuleSettings(moduleID: string, settings: ConfigurationValueInterface): boolean { let data = this.loadConfig(); data.settings ??= {}; - data.nestedSettings ??= {}; data.settings[moduleID] = settings; - const propArr = moduleID.split('.'); - const target = propArr.slice(0, -1).reduce((p, k) => p?.[k], data.nestedSettings); - target[propArr.at(-1)] = settings; return this.saveConfig(data); } @@ -93,13 +89,38 @@ export class Configuration implements ConfigurationInterface { * @returns */ getModuleSettings(section?: string): T { - const localSettings = this.getSettings(); - localSettings.nestedSettings ??= {}; - if (section) { - return section.split('.')?.reduce((p, k) => p?.[k], localSettings.nestedSettings); - } - return localSettings.nestedSettings; + return this.getConfiguration(section); } + + /** + * 根据key路径获取对应的配置的值 + * + * @param key + * @returns + */ + getConfiguration = (keyPath: string) => { + const localSettings = this.getSettings()?.settings || {}; + + if (Reflect.has(localSettings, keyPath)) { + return Reflect.get(localSettings, keyPath); + } + + const keys = Object.keys(localSettings); + const filterKeys = keys.filter((n) => n.startsWith(keyPath)); + if (filterKeys.length) { + return filterKeys.reduce((pb, ck) => { + const keyArr = ck.replace(`${keyPath}.`, '').split('.'); + const targetKey = keyArr.pop(); + const target = keyArr.reduce((p, v) => { + p[v] ??= {}; + return p[v]; + }, pb); + target[targetKey] = localSettings[ck]; + return pb; + }, {}); + } + return undefined; + }; } export default () => new Configuration(); diff --git a/src/platform/node/extension-manager/lib/core.ts b/src/platform/node/extension-manager/lib/core.ts index b50bcd5b..8d64cb2a 100644 --- a/src/platform/node/extension-manager/lib/core.ts +++ b/src/platform/node/extension-manager/lib/core.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import { ModuleHandlerOptions, ModuleInfo } from '../types'; import { fileExists, readJson } from 'eo/shared/node/file'; import { isNotEmpty } from 'eo/shared/common/common'; - +import { readFileSync } from 'node:fs'; /** * 核心模块管理器 * @class CoreHandler @@ -37,6 +37,7 @@ export class CoreHandler { try { const baseDir: string = this.getModuleDir(name); moduleInfo = readJson(path.join(baseDir, 'package.json')) as ModuleInfo; + moduleInfo.introduction = readFileSync(path.join(baseDir, 'README.md')).toString(); moduleInfo.baseDir = baseDir; moduleInfo.main = 'file://' + path.join(moduleInfo.baseDir, moduleInfo.main); if (moduleInfo.preload?.length > 0) { diff --git a/src/platform/node/extension-manager/types/manager.ts b/src/platform/node/extension-manager/types/manager.ts index 75c06b7e..02ec1f2e 100644 --- a/src/platform/node/extension-manager/types/manager.ts +++ b/src/platform/node/extension-manager/types/manager.ts @@ -26,6 +26,8 @@ export interface ModuleInfo { version: string; // 模块描述 description: string; + // 详细说明 + introduction: string; // 模块ID,用于关联 moduleID: string; // 模块名称,用于显示 diff --git a/src/platform/node/mock-server/index.ts b/src/platform/node/mock-server/index.ts index d85c6eff..21c3235a 100644 --- a/src/platform/node/mock-server/index.ts +++ b/src/platform/node/mock-server/index.ts @@ -35,7 +35,7 @@ export class MockServer { private mockUrl = ''; constructor() { - this.app = express(); + this.app ??= express(); this.createProxyServer(); } @@ -66,7 +66,7 @@ export class MockServer { // if (!protocolReg.test(req.url)) { // match request type const isMatchType = this.configuration.getModuleSettings('eoapi-features.mock.matchType'); - if (req.query.mockID || isMatchType) { + if (req.query.mockID || isMatchType !== false) { this.view.webContents.send('getMockApiList', JSON.parse(jsonStringify(req))); ipcMain.once('getMockApiList', (event, message) => { console.log('getMockApiList message', message); @@ -104,11 +104,11 @@ export class MockServer { .listen(_port, () => { const { port } = this.server.address() as AddressInfo; this.mockUrl = `http://127.0.0.1:${port}`; - console.log(`mock服务已启动:${this.mockUrl}`); + console.log(`mock service is started:${this.mockUrl}`); resolve(this.mockUrl); }) .on('error', (error) => { - console.error('mock服务启动失败: ' + error); + console.error('mock is failed to start: ' + error); reject(error); }); }); diff --git a/src/workbench/browser/.gitignore b/src/workbench/browser/.gitignore index 9cfb8227..f9689549 100644 --- a/src/workbench/browser/.gitignore +++ b/src/workbench/browser/.gitignore @@ -7,6 +7,7 @@ dist/ /app-builds /release src/**/*.js +!build/*.js !src/karma.conf.js !src/ng1/**/*.js *.js.map diff --git a/src/workbench/browser/angular.json b/src/workbench/browser/angular.json index 29feccbc..9d6876b0 100644 --- a/src/workbench/browser/angular.json +++ b/src/workbench/browser/angular.json @@ -1,267 +1 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "cli": { - "analytics": false, - "defaultCollection": "@angular-eslint/schematics" - }, - "version": 1, - "newProjectRoot": "projects", - "projects": { - "eoapi": { - "root": "", - "sourceRoot": "src", - "projectType": "application", - "schematics": { - "@schematics/angular:application": { - "strict": true - } - }, - "architect": { - "build": { - "builder": "@angular-builders/custom-webpack:browser", - "options": { - "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/@ant-design/icons-angular/src/inline-svg/", - "output": "/assets/" - } - ], - "styles": [ - { - "input": "src/assets/theme/classic_forest.scss", - "bundleName": "classic_forest", - "inject": false - }, - { - "input": "src/assets/theme/classic_sunrise.scss", - "bundleName": "classic_sunrise", - "inject": false - }, - { - "input": "src/assets/theme/classic_toy.scss", - "bundleName": "classic_toy", - "inject": false - }, - { - "input": "src/assets/theme/clean_cloud.scss", - "bundleName": "clean_cloud", - "inject": false - }, - { - "input": "src/assets/theme/clean_forest.scss", - "bundleName": "clean_forest", - "inject": false - }, - { - "input": "src/assets/theme/clean_sunrise.scss", - "bundleName": "clean_sunrise", - "inject": false - }, - { - "input": "src/assets/theme/night_black.scss", - "bundleName": "night_black", - "inject": false - }, - { - "input": "src/assets/theme/night_cmd.scss", - "bundleName": "night_cmd", - "inject": false - }, - { - "input": "src/assets/theme/night_dusk.scss", - "bundleName": "night_dusk", - "inject": false - }, - { - "input": "src/assets/theme/night_forest.scss", - "bundleName": "night_forest", - "inject": false - }, - "src/styles.scss", - "src/assets/theme/antd.less", - "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" - ], - "customWebpackConfig": { - "path": "./angular.webpack.js", - "replaceDuplicatePlugins": true - }, - "allowedCommonJsDependencies": [ - "brace", - "qs", - "rxjs" - ] - }, - "configurations": { - "dev": { - "optimization": false, - "outputHashing": "none", - "sourceMap": true, - "namedChunks": false, - "aot": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false, - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.dev.ts" - } - ] - }, - "web": { - "optimization": false, - "outputHashing": "none", - "sourceMap": true, - "namedChunks": false, - "aot": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false, - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.web.ts" - } - ] - }, - "webaot": { - "optimization": false, - "outputHashing": "none", - "sourceMap": true, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": false, - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.web.ts" - } - ] - }, - "production": { - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "aot": true, - "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" - }, - "web": { - "browserTarget": "eoapi:build:web" - }, - "webaot": { - "browserTarget": "eoapi:build:web" - }, - "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" - } - } -} \ No newline at end of file +{"$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/@ant-design/icons-angular/src/inline-svg/","output":"/assets/"}],"styles":[{"input":"src/assets/theme/classic_forest.scss","bundleName":"classic_forest","inject":false},"src/styles.scss","src/assets/theme/antd.less","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"],"customWebpackConfig":{"path":"./angular.webpack.js","replaceDuplicatePlugins":true},"allowedCommonJsDependencies":["brace","qs","rxjs"]},"configurations":{"dev":{"optimization":false,"outputHashing":"none","sourceMap":true,"namedChunks":false,"extractLicenses":true,"vendorChunk":false,"buildOptimizer":false,"fileReplacements":[{"replace":"src/environments/environment.ts","with":"src/environments/environment.dev.ts"}]},"web":{"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.web.ts"}]},"webCn":{"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.web.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"},"web":{"browserTarget":"eoapi:build:web"},"webCn":{"browserTarget":"eoapi:build:webCn"},"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"}}} \ No newline at end of file diff --git a/src/workbench/browser/build/build.js b/src/workbench/browser/build/build.js new file mode 100644 index 00000000..703b6e90 --- /dev/null +++ b/src/workbench/browser/build/build.js @@ -0,0 +1,56 @@ +//change angular.json +const fs = require('fs'); +const { execSync } = require('child_process'); + +class webPlatformBuilder { + resetBuildConfig(json) { + delete json.projects.eoapi.i18n.sourceLocale.baseHref; + Object.keys(json.projects.eoapi.i18n.locales).forEach((val) => { + delete json.projects.eoapi.i18n.locales[val].baseHref; + }); + return json; + } + executeBuild() { + execSync('ng build -c production', { stdio: 'inherit' }); + } +} +class appPlatformBuilder { + resetBuildConfig(json) { + json.projects.eoapi.i18n.sourceLocale.baseHref = ''; + Object.keys(json.projects.eoapi.i18n.locales).forEach((val) => { + json.projects.eoapi.i18n.locales[val].baseHref = ''; + }); + return json; + } + executeBuild() { + execSync('ng build --base-href ./', { stdio: 'inherit' }); + } +} +class PlatformBuilder { + constructor(platForm) { + switch (platForm) { + case 'web': { + this.instance = new webPlatformBuilder(); + break; + } + case 'app': { + this.instance = new appPlatformBuilder(); + break; + } + } + } + build() { + let buildConfigJson = require( '../angular.json'); + buildConfigJson = this.instance.resetBuildConfig(buildConfigJson); + let that=this; + fs.writeFile('./angular.json', JSON.stringify(buildConfigJson), function (err) { + if (err) { + console.error('build/beforeBuild.js:', err); + } + that.instance.executeBuild(); + }); + } +} +let platform = process.argv[2] || 'app'; +let platformBuilder = new PlatformBuilder(platform); +platformBuilder.build(); diff --git a/src/workbench/browser/package.json b/src/workbench/browser/package.json index 095fe539..0be6de13 100644 --- a/src/workbench/browser/package.json +++ b/src/workbench/browser/package.json @@ -4,13 +4,12 @@ "private": true, "scripts": { "ng": "ng", - "start": "ng serve -c web", - "build": "ng build --base-href ./", - "build:dev": "yarn build -- -c dev", - "build:prod": "yarn build -- -c production", - "build:web": "ng build -c production", + "start": "ng serve -c web -o", + "start:zh": "ng serve -c webCn -o", + "build": "node ./build/build.js", + "build:web": "node ./build/build.js web", "ng:serve": "ng serve -c web -o", - "ng:web": "ng serve -c webaot -o", + "lang:gen": "ng extract-i18n --output-path src/locale", "test": "ng test --watch=false", "test:watch": "ng test", "e2e": "yarn build:prod && playwright test -c e2e/playwright.config.ts e2e/", @@ -19,69 +18,70 @@ "lint": "ng lint" }, "dependencies": { - "@angular/animations": "13.3.6", - "@angular/cdk": "13.3.6", - "@angular/common": "13.3.6", - "@angular/compiler": "13.3.6", - "@angular/core": "13.3.6", - "@angular/forms": "13.3.6", - "@angular/language-service": "13.3.6", - "@angular/platform-browser": "13.3.6", - "@angular/platform-browser-dynamic": "13.3.6", - "@angular/router": "13.3.6", - "@angular/upgrade": "^13.3.6", + "@angular-cli/base-href-webpack": "1.0.16", + "@angular/animations": "14.0.3", + "@angular/cdk": "14.0.3", + "@angular/common": "14.0.3", + "@angular/compiler": "14.0.3", + "@angular/core": "14.0.3", + "@angular/forms": "14.0.3", + "@angular/language-service": "14.0.3", + "@angular/platform-browser": "14.0.3", + "@angular/platform-browser-dynamic": "14.0.3", + "@angular/router": "14.0.3", + "@angular/upgrade": "^14.0.3", "@ant-design/icons-angular": "13.1.0", - "@babel/runtime": "7.18.0", - "@ngxs/store": "3.7.3", + "@babel/runtime": "7.18.3", + "@ngxs/store": "3.7.4", "angular": "1.8.2", "brace": "0.11.1", - "js-beautify": "1.14.3", + "js-beautify": "1.14.4", "markdown-it": "13.0.1", - "ng-zorro-antd": "13.2.1", + "ng-zorro-antd": "13.3.2", "ngx-ace-wrapper": "12.0.0", - "qs": "6.10.3", + "qs": "6.11.0", "rxjs": "7.5.5", "tslib": "^2.4.0", - "zone.js": "~0.11.5" + "zone.js": "~0.11.6" }, "devDependencies": { - "@angular-builders/custom-webpack": "13.1.0", - "@angular-devkit/build-angular": "13.3.5", - "@angular-eslint/builder": "13.2.1", - "@angular-eslint/eslint-plugin": "13.2.1", - "@angular-eslint/eslint-plugin-template": "13.2.1", - "@angular-eslint/schematics": "13.2.1", - "@angular-eslint/template-parser": "13.2.1", - "@angular/cli": "13.3.5", - "@angular/compiler-cli": "13.3.6", + "@angular-builders/custom-webpack": "14.0.0", + "@angular-devkit/build-angular": "14.0.3", + "@angular-eslint/builder": "14.0.0", + "@angular-eslint/eslint-plugin": "14.0.0", + "@angular-eslint/eslint-plugin-template": "14.0.0", + "@angular-eslint/schematics": "14.0.0", + "@angular-eslint/template-parser": "14.0.0", + "@angular/cli": "14.0.3", + "@angular/compiler-cli": "14.0.3", + "@angular/localize": "14.0.3", "@ngx-translate/core": "14.0.0", "@ngx-translate/http-loader": "7.0.0", "@types/jasmine": "4.0.3", "@types/jasminewd2": "2.0.10", "@types/markdown-it": "12.2.3", - "@types/node": "17.0.32", - "@typescript-eslint/eslint-plugin": "5.23.0", - "@typescript-eslint/parser": "5.23.0", + "@types/node": "18.0.0", + "@typescript-eslint/eslint-plugin": "5.29.0", + "@typescript-eslint/parser": "5.29.0", "autoprefixer": "10.4.7", "conventional-changelog-cli": "2.2.2", - "dexie": "3.2.1", - "eslint": "8.15.0", + "dexie": "3.2.2", + "eslint": "8.18.0", "eslint-plugin-import": "2.26.0", - "eslint-plugin-jsdoc": "39.2.9", + "eslint-plugin-jsdoc": "39.3.3", "eslint-plugin-prefer-arrow": "1.2.3", - "jasmine-core": "4.1.1", + "jasmine-core": "4.2.0", "jasmine-spec-reporter": "7.0.0", - "karma": "6.3.19", + "karma": "6.4.0", "karma-coverage-istanbul-reporter": "3.0.3", - "karma-jasmine": "5.0.0", - "karma-jasmine-html-reporter": "1.7.0", - "lodash": "4.17.21", - "node-polyfill-webpack-plugin": "1.1.4", + "karma-jasmine": "5.1.0", + "karma-jasmine-html-reporter": "2.0.0", + "node-polyfill-webpack-plugin": "2.0.0", "postcss": "8.4.14", - "tailwindcss": "3.0.24", - "ts-node": "10.7.0", - "typescript": "~4.6.4", - "webpack": "5.72.1" + "tailwindcss": "3.1.4", + "ts-node": "10.8.1", + "typescript": "~4.7.4", + "webpack": "5.73.0" }, "engines": { "node": ">=14.17.0" diff --git a/src/workbench/browser/src/app/app.component.ts b/src/workbench/browser/src/app/app.component.ts index 8d1c7b8d..d4ee2930 100644 --- a/src/workbench/browser/src/app/app.component.ts +++ b/src/workbench/browser/src/app/app.component.ts @@ -31,7 +31,7 @@ export class AppComponent { this.remoteService.switchDataSource(); }, 5000); this.modal.info({ - nzContent: '无法连接到远程数据源,请检查后重新连接,为了不影响使用,程序将帮您跳转到本地', + nzContent: $localize `:{can not connect}:Unable to connect to remote data sources, please check and reconnect. In order not to affect use, the app will help you jump to local`, nzFooter: null, nzCentered: true, nzClosable: false, diff --git a/src/workbench/browser/src/app/app.module.ts b/src/workbench/browser/src/app/app.module.ts index 41aeaea7..cbbaeb83 100644 --- a/src/workbench/browser/src/app/app.module.ts +++ b/src/workbench/browser/src/app/app.module.ts @@ -1,6 +1,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NgModule } from '@angular/core'; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, LOCALE_ID } 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'; @@ -16,12 +16,14 @@ import { EnvState } from './shared/store/env.state'; import { UpgradeModule } from '@angular/upgrade/static'; import { MessageService } from './shared/services/message'; import { IndexedDBStorage } from 'eo/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/'; -import { HttpStorage, BaseUrlInterceptor } from 'eo/workbench/browser/src/app/shared/services/storage/http/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 { RemoteService } from 'eo/workbench/browser/src/app/shared/services/remote/remote.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'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { BaseUrlInterceptor } from 'eo/workbench/browser/src/app/shared/services/storage/http/lib/baseUrl.service'; @NgModule({ declarations: [AppComponent], @@ -37,9 +39,10 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; NgxsModule.forRoot([EnvState]), ], providers: [ + SettingService, + StorageService, RemoteService, MessageService, - StorageService, IndexedDBStorage, HttpStorage, NzMessageService, @@ -51,6 +54,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; }, { provide: HTTP_INTERCEPTORS, useClass: BaseUrlInterceptor, multi: true }, ], + schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA], bootstrap: [AppComponent], }) export class AppModule { diff --git a/src/workbench/browser/src/app/app.service.ts b/src/workbench/browser/src/app/app.service.ts index 9feb1de2..a0e0fbce 100644 --- a/src/workbench/browser/src/app/app.service.ts +++ b/src/workbench/browser/src/app/app.service.ts @@ -16,18 +16,18 @@ export class AppService { this.ipcRenderer.on('getMockApiList', async (event, req = {}) => { const sender = event.sender; console.log('req', req); - const isEnabledMatchType = window.eo?.getModuleSettings?.('eoapi-features.mock.matchType'); + const isEnabledMatchType = window.eo?.getModuleSettings?.('eoapi-features.mock.matchType') !== false; // console.log('wo接收到了哇', event, message); const { mockID } = req.query; if (Number.isInteger(Number(mockID))) { try { const mock = await this.getMockByMockID(Number(mockID)); const apiData = await this.getApiData(Number(mock.apiDataID)); - if (isEnabledMatchType) { + if (!mock && isEnabledMatchType) { const result = await this.matchApiData(1, req); return sender.send('getMockApiList', result); } else { - mock.response = this.generateResponse(apiData.responseBody) || mock.response; + mock.response = mock?.response ?? this.generateResponse(apiData.responseBody); } sender.send('getMockApiList', mock); } catch (error) { @@ -42,7 +42,7 @@ export class AppService { const response = await this.matchApiData(1, req); sender.send('getMockApiList', response); } else { - sender.send('getMockApiList', { response: { message: `没有找到ID为${mockID}的mock!` }, url: req.url }); + sender.send('getMockApiList', { response: { message: $localize `No mock found with ID ${mockID}` }, url: req.url }); } }); } diff --git a/src/workbench/browser/src/app/core/services/settings/settings.service.ts b/src/workbench/browser/src/app/core/services/settings/settings.service.ts new file mode 100644 index 00000000..c6043586 --- /dev/null +++ b/src/workbench/browser/src/app/core/services/settings/settings.service.ts @@ -0,0 +1,73 @@ +import { Injectable, Inject } from '@angular/core'; + +export const LOCAL_SETTINGS_KEY = 'LOCAL_SETTINGS_KEY'; + +export const getSettings = () => { + try { + return JSON.parse(localStorage.getItem(LOCAL_SETTINGS_KEY) || '{}'); + } catch (error) { + return {}; + } +}; + +@Injectable({ + providedIn: 'root', +}) +export class SettingService { + get settings() { + return this.getSettings(); + } + + /** get local settings */ + getSettings() { + return getSettings(); + } + + putSettings(settings: Record = {}) { + this.saveSetting({ ...this.settings, ...settings }); + } + + deleteSettings(keys: string[]) { + const settings = { ...this.settings }; + keys.forEach((key) => Reflect.deleteProperty(settings, key)); + this.saveSetting(settings); + } + + saveSetting(settings: string | Record = {}) { + if (typeof settings === 'object') { + localStorage.setItem(LOCAL_SETTINGS_KEY, JSON.stringify(settings)); + } else { + localStorage.setItem(LOCAL_SETTINGS_KEY, settings); + } + } + + /** + * Get the value of the corresponding configuration according to the key path + * + * @param key + * @returns + */ + getConfiguration = (keyPath: string) => { + const localSettings = this.getSettings(); + + if (Reflect.has(localSettings, keyPath)) { + return Reflect.get(localSettings, keyPath); + } + + const keys = Object.keys(localSettings); + const filterKeys = keys.filter((n) => n.startsWith(keyPath)); + if (filterKeys.length) { + return filterKeys.reduce((pb, ck) => { + const keyArr = ck.replace(`${keyPath}.`, '').split('.'); + const targetKey = keyArr.pop(); + const target = keyArr.reduce((p, v) => { + p[v] ??= {}; + return p[v]; + }, pb); + target[targetKey] = localSettings[ck]; + return pb; + }, {}); + } + return undefined; + }; +} diff --git a/src/workbench/browser/src/app/core/services/theme/theme.model.ts b/src/workbench/browser/src/app/core/services/theme/theme.model.ts index 406310a5..eda747b6 100644 --- a/src/workbench/browser/src/app/core/services/theme/theme.model.ts +++ b/src/workbench/browser/src/app/core/services/theme/theme.model.ts @@ -1,9 +1,9 @@ export const THEMES = [ { - title: '经典', + title: $localize`Classic`, lists: [ { - key: '森林', + key: $localize`Forest`, value: 'classic_forest', }, // { diff --git a/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.html b/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.html index 74c06f54..55673917 100644 --- a/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.html +++ b/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.html @@ -1,7 +1,7 @@
- - 内容类型 + + Content Type diff --git a/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.ts b/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.ts index 38c947a0..78fe7d35 100644 --- a/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.ts +++ b/src/workbench/browser/src/app/eoui/editor/eo-editor/eo-editor.component.ts @@ -16,27 +16,27 @@ type EventType = 'format' | 'copy' | 'search' | 'replace' | 'type' | 'download' const eventHash = new Map() .set('format', { - label: '整理格式', + label: $localize`Format`, icon: 'deployment-unit', }) .set('copy', { - label: '复制', + label: $localize`:@@Copy:Copy`, icon: 'copy', }) .set('search', { - label: '搜索', + label: $localize`:@@Search:Search`, icon: 'search', }) .set('download', { - label: '下载', + label: $localize`Download`, icon: 'download', }) .set('newTab', { - label: '新开标签', + label: $localize`New Tab`, icon: 'file-text', }) .set('replace', { - label: '替换', + label: $localize`Replace`, icon: 'security-scan', }); @@ -155,7 +155,7 @@ export class EoEditorComponent implements AfterViewInit, OnInit, OnChanges { const value = session.getValue(); if (navigator.clipboard) { navigator.clipboard.writeText(value); - this.message.success('复制成功'); + this.message.success($localize`Copied`); return; } break; diff --git a/src/workbench/browser/src/app/eoui/eoui.module.ts b/src/workbench/browser/src/app/eoui/eoui.module.ts index 97cad2a6..1b195001 100644 --- a/src/workbench/browser/src/app/eoui/eoui.module.ts +++ b/src/workbench/browser/src/app/eoui/eoui.module.ts @@ -16,13 +16,14 @@ import { EoMessageComponent } from './message/eo-message.component'; // ! Directive import { CellDirective } from './table/eo-table/cell.directive'; +import { EoIconparkIconModule } from 'eo/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.module'; const antdModules = [NzTableModule, NzIconModule, NzButtonModule, NzInputModule, NzSelectModule]; const DEFAULT_ACE_CONFIG: AceConfigInterface = {}; @NgModule({ declarations: [EoTableComponent, EoEditorComponent, EoMessageComponent, CellDirective], - imports: [CommonModule, FormsModule, AceModule, ...antdModules], + imports: [CommonModule, FormsModule, AceModule, EoIconparkIconModule, ...antdModules], exports: [EoTableComponent, EoEditorComponent, EoMessageComponent, CellDirective], providers: [ { diff --git a/src/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.component.ts b/src/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.component.ts new file mode 100644 index 00000000..e19395c2 --- /dev/null +++ b/src/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.component.ts @@ -0,0 +1,17 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + // standalone: true, + selector: 'eo-iconpark-icon', + template: ``, + styleUrls: [], + host: { + class: 'inline-flex', + }, +}) +export class EoIconparkIconComponent { + @Input() name: string; + @Input() size = '18px'; + + constructor() {} +} diff --git a/src/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.module.ts b/src/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.module.ts new file mode 100644 index 00000000..c01b7024 --- /dev/null +++ b/src/workbench/browser/src/app/eoui/iconpark-icon/eo-iconpark-icon.module.ts @@ -0,0 +1,11 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { EoIconparkIconComponent } from './eo-iconpark-icon.component'; + +@NgModule({ + declarations: [EoIconparkIconComponent], + imports: [CommonModule], + exports: [EoIconparkIconComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], +}) +export class EoIconparkIconModule {} diff --git a/src/workbench/browser/src/app/ng1/ng1.module.ts b/src/workbench/browser/src/app/ng1/ng1.module.ts index 1777f752..d384ae7e 100644 --- a/src/workbench/browser/src/app/ng1/ng1.module.ts +++ b/src/workbench/browser/src/app/ng1/ng1.module.ts @@ -1,10 +1,6 @@ import { NgModule } from '@angular/core'; -import { - ListBlockCommonComponent, -} from './component.ajs'; -const declarations: Array = [ - ListBlockCommonComponent -]; +import { ListBlockCommonComponent } from './component.ajs'; +const declarations: Array = [ListBlockCommonComponent]; @NgModule({ declarations: declarations, imports: [], diff --git a/src/workbench/browser/src/app/pages/api/api.component.html b/src/workbench/browser/src/app/pages/api/api.component.html index 1dfd0aaf..8f452e9b 100644 --- a/src/workbench/browser/src/app/pages/api/api.component.html +++ b/src/workbench/browser/src/app/pages/api/api.component.html @@ -1,25 +1,72 @@ - -
- -
+ + + + + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + +
+
-
+
-
- +
+ + + + + + Manage Environment +
- {{ tab.title }} + {{ tab.title }} diff --git a/src/workbench/browser/src/app/pages/api/api.component.scss b/src/workbench/browser/src/app/pages/api/api.component.scss index 2d8674aa..1c4dab65 100644 --- a/src/workbench/browser/src/app/pages/api/api.component.scss +++ b/src/workbench/browser/src/app/pages/api/api.component.scss @@ -2,20 +2,41 @@ nz-content { background-color: var(--MAIN_BG); } +:lang(zh) { + nz-select { + width: 132px; + } +} + +:lang(en) { + nz-select { + width: 162px; + } +} + nz-sider { width: 250px; background-color: #fff; } -::ng-deep { + ::ng-deep { .ant-layout-sider-children { border-right: 1px solid #f0f0f0; - padding: 5px; + padding: 0 10px; .group-btn-add, .group-search { margin-bottom: 12px; } } + .api-tabs .ant-tabs-nav-list { + width: 100%; + justify-content: space-evenly; + .ant-tabs-tab { + flex: 1; + justify-content: center; + margin: 0; + } + } .tabs-bar { background-color: var(--SEC_BG); padding: 5px 10px 0 15px; @@ -23,9 +44,6 @@ nz-sider { margin-bottom: -1px; align-items: center; justify-content: space-between; - .env { - height: 36px; - } } .content_container { border-top: 1px solid var(--BORDER); @@ -56,3 +74,22 @@ nz-sider { color: var(--BLACK_TAG_BG); } } + +.api-tabs { + ::ng-deep .ant-tabs-tab { + padding: 0.2em 0; + } +} + +.fix-mt { + margin-top: -6px; +} + +nz-divider { + margin: 0.1em 0; +} + +.manager-env { + color: #00785a; +} + diff --git a/src/workbench/browser/src/app/pages/api/api.component.ts b/src/workbench/browser/src/app/pages/api/api.component.ts index fc5379f1..595cb1fc 100644 --- a/src/workbench/browser/src/app/pages/api/api.component.ts +++ b/src/workbench/browser/src/app/pages/api/api.component.ts @@ -1,9 +1,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { StorageRes, StorageResStatus } from '../../shared/services/storage/index.model'; import { Subject, takeUntil } from 'rxjs'; +import { Store } from '@ngxs/store'; import { Message, MessageService } from '../../shared/services/message'; import { ApiService } from './api.service'; import { StorageService } from '../../shared/services/storage'; +import { Change } from '../../shared/store/env.state'; import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/remote/remote.service'; @Component({ @@ -20,17 +23,22 @@ export class ApiComponent implements OnInit, OnDestroy { TABS = [ { routerLink: 'detail', - title: '文档', + title: $localize`:@@API Detail:Preview`, }, { routerLink: 'edit', - title: '编辑', + title: $localize`Edit`, }, { routerLink: 'test', - title: '测试', + title: $localize`Test`, }, ]; + isOpen = false; + envInfo: any = {}; + envList: Array = []; + activeUuid: number | string = 0; + tabsIndex = 0; private destroy$: Subject = new Subject(); constructor( @@ -38,9 +46,23 @@ export class ApiComponent implements OnInit, OnDestroy { private apiService: ApiService, private messageService: MessageService, private storage: StorageService, - private remoteService: RemoteService + private remoteService: RemoteService, + private store: Store ) {} + get envUuid(): number { + return Number(localStorage.getItem('env:selected')) || 0; + } + set envUuid(value) { + this.activeUuid = value; + if (value !== null) { + localStorage.setItem('env:selected', value == null ? '' : value.toString()); + } else { + localStorage.removeItem('env:selected'); + } + this.changeStoreEnv(value); + } + ngOnInit(): void { this.watchChangeRouter(); this.watchApiAction(); @@ -51,6 +73,18 @@ export class ApiComponent implements OnInit, OnDestroy { title: 'Mock', }); } + this.envUuid = Number(localStorage.getItem('env:selected')); + // * load All env + this.getAllEnv().then((result: any[]) => { + this.envList = result || []; + }); + this.messageService.get().subscribe(({ type }) => { + if (type === 'updateEnv') { + this.getAllEnv().then((result: any[]) => { + this.envList = result || []; + }); + } + }); } ngOnDestroy() { this.destroy$.next(); @@ -98,6 +132,38 @@ export class ApiComponent implements OnInit, OnDestroy { }); } clickContentMenu(data) { - this.messageService.send({ type: 'beforeChangeRouter', data: data }); + this.messageService.send({ type: 'beforeChangeRouter', data }); } + + gotoEnvManager() { + // * switch to env + this.tabsIndex = 2; + } + + getAllEnv(uuid?: number) { + const projectID = 1; + return new Promise((resolve) => { + this.storage.run('environmentLoadAllByProjectID', [projectID], async (result: StorageRes) => { + if (result.status === StorageResStatus.success) { + return resolve(result.data || []); + } + return resolve([]); + }); + }); + } + + private changeStoreEnv(uuid) { + if (uuid == null) { + this.store.dispatch(new Change(null)); + return; + } + this.storage.run('environmentLoadAllByProjectID', [1], (result: StorageRes) => { + if (result.status === StorageResStatus.success) { + const data = result.data.find((val) => val.uuid === Number(uuid)); + this.store.dispatch(new Change(data)); + } + }); + } + + handleEnvSelectStatus(event: boolean) {} } diff --git a/src/workbench/browser/src/app/pages/api/api.module.ts b/src/workbench/browser/src/app/pages/api/api.module.ts index 6377745e..94000605 100644 --- a/src/workbench/browser/src/app/pages/api/api.module.ts +++ b/src/workbench/browser/src/app/pages/api/api.module.ts @@ -30,6 +30,7 @@ import { NzInputModule } from 'ng-zorro-antd/input'; import { NzDropDownModule } from 'ng-zorro-antd/dropdown'; import { NzDividerModule } from 'ng-zorro-antd/divider'; import { NzCardModule } from 'ng-zorro-antd/card'; +import { NzSelectModule } from 'ng-zorro-antd/select'; import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm'; @@ -38,8 +39,10 @@ import { ApiTabComponent } from './tab/api-tab.component'; import { ApiService } from './api.service'; import { ElectronService } from '../../core/services'; import { ApiOverviewComponent } from './overview/api-overview.component'; +import { HistoryComponent } from './history/eo-history.component'; import { ApiMockComponent } from './mock/api-mock.component'; import { IndexedDBStorage } from 'eo/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/'; +import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module'; const COMPONENTS = [ ApiComponent, @@ -52,6 +55,7 @@ const COMPONENTS = [ ImportApiComponent, ExtensionSelectComponent, ApiMockComponent, + HistoryComponent, ]; @NgModule({ imports: [ @@ -79,7 +83,9 @@ const COMPONENTS = [ EnvModule, NzCardModule, NzModalModule, + NzSelectModule, NzPopconfirmModule, + SharedModule, ], declarations: [...COMPONENTS], exports: [], diff --git a/src/workbench/browser/src/app/pages/api/api.service.ts b/src/workbench/browser/src/app/pages/api/api.service.ts index 71ad7a15..b13498ff 100644 --- a/src/workbench/browser/src/app/pages/api/api.service.ts +++ b/src/workbench/browser/src/app/pages/api/api.service.ts @@ -20,10 +20,10 @@ export class ApiService { delete({ name, uuid }: ApiData): void { this.nzModalService.confirm({ - nzTitle: '删除确认?', - nzContent: `确认要删除数据 ${ + nzTitle: $localize`Deletion Confirmation?`, + nzContent: $localize`Are you sure you want to delete the data ${ name.length > 50 ? name.slice(0, 50) + '...' : name - } 吗?删除后不可恢复!`, + } ? You cannot restore it once deleted!`, nzOnOk: () => { this.storage.run('apiDataRemove', [uuid], (result: StorageRes) => { if (result.status === StorageResStatus.success) { diff --git a/src/workbench/browser/src/app/pages/api/detail/api-detail.component.html b/src/workbench/browser/src/app/pages/api/detail/api-detail.component.html index bac9ba58..d1cdb10d 100644 --- a/src/workbench/browser/src/app/pages/api/detail/api-detail.component.html +++ b/src/workbench/browser/src/app/pages/api/detail/api-detail.component.html @@ -6,37 +6,45 @@

{{ apiData.name }}

-

请求头部

+

Request Headers

-

Query 参数

+

Query

-

Rest 参数

+

REST

- Body 请求参数{{ CONST.BODY_TYPE[apiData.requestBodyType] }} - 最外层结构为:{{ - CONST.JSON_ROOT_TYPE[apiData.requestBodyJsonType] }} + Body + {{ CONST.BODY_TYPE[apiData.requestBodyType] }} + The outermost structure is: {{ CONST.JSON_ROOT_TYPE[apiData.requestBodyJsonType] }}
- +
-

返回头部

+

Response Headers

-

返回参数

- +

Response

+
-

MOCK

+

MOCK

diff --git a/src/workbench/browser/src/app/pages/api/detail/api-detail.service.ts b/src/workbench/browser/src/app/pages/api/detail/api-detail.service.ts index 57d158ac..83191e06 100644 --- a/src/workbench/browser/src/app/pages/api/detail/api-detail.service.ts +++ b/src/workbench/browser/src/app/pages/api/detail/api-detail.service.ts @@ -57,7 +57,7 @@ export class ApiDetailService { } } initListConf(opts) { - opts.title = opts.title || '参数'; + opts.title = opts.title || $localize`Param`; return { setting: { draggable: true, @@ -66,22 +66,22 @@ export class ApiDetailService { itemStructure: Object.assign({}, opts.itemStructure), tdList: [ { - thKey: opts.nameTitle || `${opts.title}名`, + thKey: opts.nameTitle || $localize`${opts.title} Name`, type: 'text', modelKey: 'name', - placeholder: opts.nameTitle || `${opts.title}名`, + placeholder: opts.nameTitle || $localize`${opts.title} Name`, width: 250, mark: 'name', }, { - thKey: '必填', + thKey: $localize`Required`, type: 'html', - html: '{{item.required?"是":""}}', + html: $localize`{{item.required?"True":""}}`, width: 80, mark: 'require', }, { - thKey: '说明', + thKey: $localize`:@@Description:Description`, type: 'text', modelKey: 'description', width: 250, @@ -89,7 +89,7 @@ export class ApiDetailService { }, { - thKey: '示例', + thKey: $localize`Example`, type: 'text', modelKey: 'example', width: 200, @@ -116,28 +116,28 @@ export class ApiDetailService { itemStructure: Object.assign({}, opts.itemStructure), tdList: [ { - thKey: '参数名', + thKey: $localize`Param Name`, type: 'depthHtml', html: '{{item.name}}', width: 260, mark: 'name', }, { - thKey: '类型', + thKey: $localize`Type`, type: 'text', modelKey: 'type', mark: 'type', width: 80, }, { - thKey: '必填', + thKey: $localize`Required`, type: 'html', - html: '{{item.required?"是":""}}', + html: $localize`{{item.required?"True":""}}`, width: 60, mark: 'require', }, { - thKey: '说明', + thKey: $localize`:@@Description:Description`, type: 'text', modelKey: 'description', width: 260, @@ -145,20 +145,20 @@ export class ApiDetailService { }, { - thKey: '示例', + thKey: $localize`Example`, type: 'text', modelKey: 'example', width: 200, mark: 'example', }, { - thKey: ``, + thKey: $localize``, type: 'html', - html: `{{item.isClick?"收缩":"展开"}}`, + (item.enum && item.enum.length > 0 && item.enum[0].value)">{{item.isClick?"Shrink":"Expand"}}`, mark: 'fn_btn', width: '100px', class: 'undivide_line_lbcc', diff --git a/src/workbench/browser/src/app/pages/api/detail/body/api-detail-body.component.scss b/src/workbench/browser/src/app/pages/api/detail/body/api-detail-body.component.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src/workbench/browser/src/app/pages/api/detail/body/api-detail-body.component.ts b/src/workbench/browser/src/app/pages/api/detail/body/api-detail-body.component.ts index d5b04197..2c7c14fe 100644 --- a/src/workbench/browser/src/app/pages/api/detail/body/api-detail-body.component.ts +++ b/src/workbench/browser/src/app/pages/api/detail/body/api-detail-body.component.ts @@ -4,8 +4,7 @@ import { ApiEditBody, ApiBodyType, JsonRootType } from '../../../../shared/servi import { ApiDetailService } from '../api-detail.service'; @Component({ selector: 'eo-api-detail-body', - templateUrl: './api-detail-body.component.html', - styleUrls: ['./api-detail-body.component.scss'], + templateUrl: './api-detail-body.component.html' }) export class ApiDetailBodyComponent implements OnInit, OnChanges, OnDestroy { @Input() model: string | ApiEditBody[] | any; @@ -55,7 +54,6 @@ export class ApiDetailBodyComponent implements OnInit, OnChanges, OnDestroy { } private initListConf() { this.listConf = this.apiDetail.initBodyListConf({ - title: '参数', itemStructure: this.itemStructure, }); } diff --git a/src/workbench/browser/src/app/pages/api/detail/header/api-detail-header.component.ts b/src/workbench/browser/src/app/pages/api/detail/header/api-detail-header.component.ts index 3ce51e23..00dd0b81 100644 --- a/src/workbench/browser/src/app/pages/api/detail/header/api-detail-header.component.ts +++ b/src/workbench/browser/src/app/pages/api/detail/header/api-detail-header.component.ts @@ -22,8 +22,8 @@ export class ApiDetailHeaderComponent implements OnInit, OnChanges { private initListConf() { this.listConf = this.detailService.initListConf({ dragCacheVar: 'DRAG_VAR_API_EDIT_HEADER', - title: '头部', - nameTitle: '标签', + title: $localize`:@@Header:Header`, + nameTitle: $localize`Key`, }); } } diff --git a/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.html b/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.html index 24c83c0c..ecb7f12e 100644 --- a/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.html +++ b/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.html @@ -1,6 +1,6 @@ - + {{ scope.url }} diff --git a/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.ts b/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.ts index b664bc45..7acebda0 100644 --- a/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.ts +++ b/src/workbench/browser/src/app/pages/api/detail/mock/api-detail-mock.component.ts @@ -26,12 +26,12 @@ export class ApiDetailMockComponent implements OnInit, OnChanges { listConf: object = {}; isVisible = false; createWayMap = { - system: '系统自动创建', - custom: '手动创建', + system: $localize `System creation`, + custom: $localize `Manual creation`, }; mockListColumns = [ - { title: '名称', key: 'name' }, - { title: '创建方式', slot: 'createWay' }, + { title: $localize`Name`, key: 'name' }, + { title: $localize`Created Type`, slot: 'createWay' }, { title: 'URL', slot: 'url' }, ]; private destroy$: Subject = new Subject(); @@ -140,6 +140,6 @@ export class ApiDetailMockComponent implements OnInit, OnChanges { async copyText(text: string) { await copyText(text); - this.message.success('复制成功'); + this.message.success($localize`Copied`); } } diff --git a/src/workbench/browser/src/app/pages/api/edit/api-edit.component.html b/src/workbench/browser/src/app/pages/api/edit/api-edit.component.html index f9ca9ed5..0b03fd17 100644 --- a/src/workbench/browser/src/app/pages/api/edit/api-edit.component.html +++ b/src/workbench/browser/src/app/pages/api/edit/api-edit.component.html @@ -1,7 +1,7 @@
- +
@@ -14,22 +14,22 @@ - + - 分组 / API 名称 + Group / API Name - + - + @@ -37,17 +37,18 @@ - + - {{ panel.nzActive ? '收缩' : '展开' }} + {{ panel.nzActive ? 'Shrink' : 'Expand' }} - + - 请求头部 - {{ + Request Headers + {{ apiData.requestHeaders | apiParamsNum }} @@ -56,7 +57,7 @@ - 请求体 + Body - Query 参数 - {{ + Query + {{ apiData.queryParams | apiParamsNum }} @@ -78,8 +79,8 @@ - REST 参数 - {{ + REST + {{ apiData.restParams | apiParamsNum }} @@ -90,16 +91,17 @@ - + - {{ panelRes.nzActive ? '收缩' : '展开' }} + {{ panelRes.nzActive ? 'Shrink' : 'Expand' }} - 返回头部 - {{ + Response Headers + {{ apiData.responseHeaders | apiParamsNum }} @@ -108,7 +110,7 @@ - 返回结果 + Response { this.showMore(inputArg, { @@ -210,14 +210,14 @@ export class ApiEditService { itemExpression: `eo-attr-tip-placeholder="more_setting_btn" `, }, { - key: '插入', + key: $localize`Insert`, operateName: 'insert', - itemExpression: `ng-if="!($ctrl.mainObject.setting.munalHideOperateColumn&&$first)"` + itemExpression: `ng-if="!($ctrl.mainObject.setting.munalHideOperateColumn&&$first)"`, }, { - key: '删除', + key: $localize`:@@Delete:Delete`, operateName: 'delete', - itemExpression: 'ng-if="!($ctrl.mainObject.setting.munalHideOperateColumn&&$first)"' + itemExpression: 'ng-if="!($ctrl.mainObject.setting.munalHideOperateColumn&&$first)"', }, ], }, diff --git a/src/workbench/browser/src/app/pages/api/edit/body/api-edit-body.component.html b/src/workbench/browser/src/app/pages/api/edit/body/api-edit-body.component.html index f2c0b048..c06ea81c 100644 --- a/src/workbench/browser/src/app/pages/api/edit/body/api-edit-body.component.html +++ b/src/workbench/browser/src/app/pages/api/edit/body/api-edit-body.component.html @@ -9,7 +9,7 @@ [rootType]="jsonRootType">
-

JSON 根类型:

+

JSON Root Type:

diff --git a/src/workbench/browser/src/app/pages/api/edit/extra-setting/api-params-extra-setting.component.ts b/src/workbench/browser/src/app/pages/api/edit/extra-setting/api-params-extra-setting.component.ts index 7e0c1705..d1eace03 100644 --- a/src/workbench/browser/src/app/pages/api/edit/extra-setting/api-params-extra-setting.component.ts +++ b/src/workbench/browser/src/app/pages/api/edit/extra-setting/api-params-extra-setting.component.ts @@ -14,23 +14,23 @@ export class ApiParamsExtraSettingComponent implements OnInit { }, tdList: [ { - thKey: '参数名', + thKey: $localize`Param Name`, type: 'text', modelKey: 'name', }, { - thKey: '必填', + thKey: $localize`Required`, type: 'html', - html: '{{item.required?"是":"否"}}', + html: '{{item.required?"True":"False"}}', class: 'w_100', }, { - thKey: '说明', + thKey: $localize`:@@Description:Description`, type: 'text', modelKey: 'description', }, { - thKey: '类型', + thKey: $localize`Type`, type: 'text', modelKey: 'type', }, diff --git a/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.html b/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.html index 64a37803..ff903f07 100644 --- a/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.html +++ b/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.ts b/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.ts index bbd6eab6..fd1f143f 100644 --- a/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.ts +++ b/src/workbench/browser/src/app/pages/api/edit/header/api-edit-header.component.ts @@ -33,8 +33,8 @@ export class ApiEditHeaderComponent implements OnInit, OnChanges, AfterViewCheck this.listConf = this.editService.initListConf({ dragCacheVar: 'DRAG_VAR_API_EDIT_HEADER', itemStructure: this.itemStructure, - title: '头部', - nameTitle: '标签', + title: $localize`:@@Header:Header`, + nameTitle: $localize`Key`, nzOnOkMoreSetting: (inputArg) => { this.model[inputArg.$index] = inputArg.item; }, diff --git a/src/workbench/browser/src/app/pages/api/edit/query/api-edit-query.component.html b/src/workbench/browser/src/app/pages/api/edit/query/api-edit-query.component.html index 28cb1b4c..696a029e 100644 --- a/src/workbench/browser/src/app/pages/api/edit/query/api-edit-query.component.html +++ b/src/workbench/browser/src/app/pages/api/edit/query/api-edit-query.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.html b/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.html index dd0fbbc8..e7285b9a 100644 --- a/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.html +++ b/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.html @@ -1,13 +1,14 @@
- 分组名称 - + Group Name +
-

- 删除 - {{ group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name }} - 后,该分组下的数据都会删除。该操作无法撤销,确认删除吗? +

+ Data from{{ + group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name + }} + will be deleted. This cannot be undone. Are you sure you want to delete?

diff --git a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.html b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.html index d894d53a..971a50f2 100644 --- a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.html +++ b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.html @@ -1,48 +1,38 @@ -
- -
-
- - - - - - -
-
- - - - - - - -
-
-
+
+ +
+ +
+ + +
-
+
+ -
-
- +
+
+ + + + {{ node.title }}
-
+
+ +
-
+
- {{ node.title }}
@@ -64,16 +53,16 @@ @@ -98,15 +87,15 @@ diff --git a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.scss b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.scss index ea6a7217..9904f8b3 100644 --- a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.scss +++ b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.scss @@ -1,4 +1,38 @@ +.input { + width: 0px; + height: 30px; + border: none; + background-color: rgba(0,0,0,0.05); + border-radius: 3px; + &::placeholder { + color: rgba(0,0,0,0.5); + font-size: 0.9em; + } +} +.btn { + width: 32px; + background-color: var(--BTN_PRIMARY_BG); + color: #fff; + border-radius: 3px; + cursor: pointer; +} ::ng-deep { + .ant-tree .ant-tree-treenode { + padding: 0; + margin: 2px 0; + border-radius: 3px; + transition: all 0.3s, border 0s, line-height 0s, box-shadow 0s; + &:hover { + background-color: #f5f5f5; + } + } + .ant-tree-treenode-selected { + background-color: var(--NAVBAR_BTN_BG); + } + .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected { + background-color: transparent; + transition: none; + } .ant-tree-node-selected * { // color: var(--TEXT_ACTIVE); } @@ -19,6 +53,7 @@ height: calc(100vh - var(--NAVBAR_HEIGHT) - 88px); .ant-tree-switcher { width: 12px; + padding-left: 4px; } .ant-tree-indent-unit { width: 8px; @@ -28,9 +63,6 @@ padding: 0 2px; } } - .ng-star-inserted { - padding-left: 0; - } } .ant-dropdown-menu { min-width: 100px; @@ -39,6 +71,7 @@ max-width: 50px; min-width: 32px; font-size: 11px; + font-weight: 300; } .node_title { max-width: 145px; diff --git a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts index b5e87edf..808bbd22 100644 --- a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts +++ b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts @@ -50,7 +50,7 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { treeNodes: GroupTreeItem[] | NzTreeNode[] | any; fixedTreeNode: GroupTreeItem[] | NzTreeNode[] = [ { - title: '概况', + title: $localize `:@@API Index:Index`, key: 'overview', weight: 0, parentID: '0', @@ -246,7 +246,7 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { * Create group. */ addGroup() { - this.groupModal('添加分组', { group: this.buildGroupModel(), action: 'new' }); + this.groupModal($localize`Add Group`, { group: this.buildGroupModel(), action: 'new' }); } /** @@ -255,7 +255,7 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { * @param node NzTreeNode */ addSubGroup(node: NzTreeNode) { - this.groupModal('添加子分组', { group: this.buildGroupModel(node.key), action: 'sub' }); + this.groupModal($localize`Add Subgroup`, { group: this.buildGroupModel(node.key), action: 'sub' }); } /** @@ -264,7 +264,7 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { * @param node NzTreeNode */ editGroup(node: NzTreeNode) { - this.groupModal('编辑分组', { group: this.nodeToGroup(node), action: 'edit' }); + this.groupModal($localize`Edit Group`, { group: this.nodeToGroup(node), action: 'edit' }); } /** @@ -273,7 +273,7 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { * @param node NzTreeNode */ deleteGroup(node: NzTreeNode) { - this.groupModal('删除分组', { group: this.nodeToGroup(node), treeItems: this.treeItems, action: 'delete' }); + this.groupModal($localize`Delete Group`, { group: this.nodeToGroup(node), treeItems: this.treeItems, action: 'delete' }); } /** diff --git a/src/workbench/browser/src/app/pages/api/history/eo-history.component.html b/src/workbench/browser/src/app/pages/api/history/eo-history.component.html new file mode 100644 index 00000000..cce16041 --- /dev/null +++ b/src/workbench/browser/src/app/pages/api/history/eo-history.component.html @@ -0,0 +1,20 @@ +
+
+ History +
+ + + + +
+
+
+ {{ + item.request.method + }} + {{ item.request.uri }} +
+
diff --git a/src/workbench/browser/src/app/pages/api/history/eo-history.component.scss b/src/workbench/browser/src/app/pages/api/history/eo-history.component.scss new file mode 100644 index 00000000..993dbb90 --- /dev/null +++ b/src/workbench/browser/src/app/pages/api/history/eo-history.component.scss @@ -0,0 +1,30 @@ +.method_type { + &.green { + color: green + } + &.red { + color: red + } + &.blue { + color: blue + } + &.pink { + color: pink + } +} + +.icon { + width: 30px; + height: 30px; + color: rgba(0,0,0,0.5); + border-radius: 3px; + font-size: 1.3em; + transition: all .4s ease; + &:hover { + background-color: rgba(0,0,0,0.05); + } +} + +.history-title { + border-bottom: 1px solid #eee; +} \ No newline at end of file diff --git a/src/workbench/browser/src/app/pages/api/history/eo-history.component.ts b/src/workbench/browser/src/app/pages/api/history/eo-history.component.ts new file mode 100644 index 00000000..c41e25e5 --- /dev/null +++ b/src/workbench/browser/src/app/pages/api/history/eo-history.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit } from '@angular/core'; +import { IndexedDBStorage } from '../../../../../../../workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index'; +import { MessageService } from '../../../shared/services/message'; + +@Component({ + selector: 'eo-history', + templateUrl: './eo-history.component.html', + styleUrls: ['./eo-history.component.scss'], +}) +export class HistoryComponent implements OnInit { + historyList = []; + colorHash = new Map().set('get', 'green').set('post', 'blue').set('delete', 'red').set('put', 'pink'); + constructor(public storageInstance: IndexedDBStorage, private message: MessageService) {} + ngOnInit() { + const observer = this.loadAllTest(); + observer.subscribe((result: any) => { + // console.log(result.data); + this.historyList = result.data; + }); + this.message.get().subscribe(({ type }) => { + if (type === 'updateHistory') { + this.loadAllTest().subscribe((result: any) => { + this.historyList = result.data; + }); + } + }); + } + + loadAllTest() { + return this.storageInstance.apiTestHistoryLoadAllByProjectID(1); + } + + methodColor(type) { + return this.colorHash.get(type.toLowerCase()); + } + + gotoTestHistory(data) { + // this.message.send({ type: 'gotoApiTest', data }); + this.message.send({ + type: 'gotoApiTest', + data: { + ...data, + origin: { method: data.request.method.toUpperCase(), title: '测试历史', key: `history_${data.uuid}` }, + }, + }); + } + + clearAllHistory() { + const uuids = this.historyList.map((it) => it.uuid); + this.historyList = []; + this.storageInstance.apiTestHistoryBulkRemove(uuids); + } + cancel() {} +} diff --git a/src/workbench/browser/src/app/pages/api/mock/api-mock.component.html b/src/workbench/browser/src/app/pages/api/mock/api-mock.component.html index cdc4ae5f..6def8aa4 100644 --- a/src/workbench/browser/src/app/pages/api/mock/api-mock.component.html +++ b/src/workbench/browser/src/app/pages/api/mock/api-mock.component.html @@ -1,13 +1,20 @@
- +
-
{{scope.name}}
+
{{ scope.name }}
- + {{ scope.url }} @@ -16,11 +23,21 @@
@@ -32,25 +49,33 @@
- Mock 名称 + Mock Name - + - 返回值 + Response - +
- \ No newline at end of file + diff --git a/src/workbench/browser/src/app/pages/api/mock/api-mock.component.ts b/src/workbench/browser/src/app/pages/api/mock/api-mock.component.ts index 56bfa5ac..f1e887a4 100644 --- a/src/workbench/browser/src/app/pages/api/mock/api-mock.component.ts +++ b/src/workbench/browser/src/app/pages/api/mock/api-mock.component.ts @@ -25,18 +25,18 @@ export class ApiMockComponent implements OnInit, OnChanges { } get modalTitle() { return `${ - this.currentEditMockIndex === -1 ? '添加' : this.currentEditMock.createWay === 'system' ? '预览' : '编辑' + this.currentEditMockIndex === -1 ? $localize`Add` : this.currentEditMock.createWay === 'system' ? $localize`Preview` : $localize`Edit` } Mock`; } mocklList: ApiMockEntity[] = []; apiData: ApiData; createWayMap = { - system: '系统自动创建', - custom: '手动创建', + system: $localize `System creation`, + custom: $localize `Manual creation`, }; mockListColumns = [ - { title: '名称', slot: 'name', width: '20%' }, - { title: '创建方式', slot: 'createWay', width: '15%' }, + { title: $localize`Name`, slot: 'name', width: '20%' }, + { title: $localize`Created Type`, slot: 'createWay', width: '15%' }, { title: 'URL', slot: 'url', width: '50%' }, { title: '', slot: 'action', width: '15%', fixed: true }, ]; @@ -223,7 +223,7 @@ export class ApiMockComponent implements OnInit, OnChanges { await this.removeMock(Number(target.uuid)); this.mocklList.splice(index, 1)[0]; this.mocklList = [...this.mocklList]; - this.message.success('删除成功'); + this.message.success($localize`Delete Succeeded`); } async handleSave() { this.isVisible = false; @@ -232,12 +232,12 @@ export class ApiMockComponent implements OnInit, OnChanges { if (this.isEdit) { await this.updateMock(this.currentEditMock, Number(this.currentEditMock.uuid)); - this.message.success('修改成功'); + this.message.success($localize`Edited successfully`); this.mocklList[this.currentEditMockIndex] = this.currentEditMock; } else { const result = await this.createMock(this.currentEditMock); Object.assign(this.currentEditMock, result.data); - this.message.success('新增成功'); + this.message.success($localize`Added successfully`); this.mocklList.push(this.currentEditMock); } this.currentEditMock.url = this.getApiUrl(this.currentEditMock); @@ -258,6 +258,6 @@ export class ApiMockComponent implements OnInit, OnChanges { async copyText(text: string) { await copyText(text); - this.message.success('复制成功'); + this.message.success($localize`Copied`); } } diff --git a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html index 53239104..f1d76a0a 100644 --- a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html +++ b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html @@ -1,5 +1,5 @@
-

概况

+

Index

diff --git a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts index 197005b0..4d29dbde 100644 --- a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts +++ b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts @@ -24,21 +24,21 @@ export class ApiOverviewComponent implements OnDestroy { constructor(private modalService: ModalService, private router: Router, private message: EoMessageService) {} overviewList = [ { - title: '导入', + title: $localize`Import`, icon: 'import', - desc: '导入 API 数据', + desc: $localize`Import API data`, type: 'import', }, { - title: '导出', + title: $localize`Export`, icon: 'export', - desc: '导出 API 数据', + desc: $localize`Export API data`, type: 'export', }, { - title: '推送', + title: $localize`Push`, icon: 'sync', - desc: '将 API 推送/同步到其他平台', + desc: $localize`Push/Sync API to other platforms`, type: 'push', }, ]; @@ -52,10 +52,10 @@ export class ApiOverviewComponent implements OnDestroy { nzOnOk: () => { this.modal.componentInstance.submit((status) => { if (status) { - this.message.success(`${title}成功`); + this.message.success($localize`${title} successfully`); this.modal.destroy(); } else { - this.message.error(`${title}失败`); + this.message.error($localize`Failed to ${title}`); } }); }, diff --git a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html index 4f3a4475..c018fd59 100644 --- a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html +++ b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html @@ -1,5 +1,11 @@ - + {{ tab.method }} @@ -16,11 +22,11 @@
    -
  • 关闭所有标签(当前标签除外)
  • -
  • 关闭所有标签
  • +
  • Close All Tags (excluding current tabs)
  • +
  • Close All Tabs
  • -
  • 关闭左侧标签页
  • -
  • 关闭右侧标签页
  • +
  • Close Tabs To The Left
  • +
  • Close Tabs to Right
diff --git a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.ts b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.ts index 049979b6..3acf79c8 100644 --- a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.ts +++ b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.ts @@ -23,11 +23,11 @@ export class ApiTabComponent implements OnInit, OnDestroy { * Default tabs of api. */ defaultTabs = { - edit: { path: '/home/api/edit', title: '新 API' }, - test: { path: '/home/api/test', title: '新 API' }, - detail: { path: '/home/api/detail', title: 'API 详情' }, - overview: { path: '/home/api/overview', title: '概况', key: 'overview' }, - mock: { path: '/home/api/mock', title: 'mock', key: 'mock' }, + edit: { path: '/home/api/edit', title: $localize`New API` }, + test: { path: '/home/api/test', title: $localize`New API` }, + detail: { path: '/home/api/detail', title: $localize`:@@API Detail:Preview` }, + overview: { path: '/home/api/overview', title: $localize`:@@API Index:Index`, key: 'overview' }, + mock: { path: '/home/api/mock', title: 'Mock', key: 'mock' }, }; MAX_TAB_LIMIT = 15; @@ -234,17 +234,24 @@ export class ApiTabComponent implements OnInit, OnDestroy { this.appendOrSwitchTab('detail', inArg.data.origin); break; case 'detailOverview': { - console.log(inArg.data.origin); + // console.log(inArg.data.origin); this.appendOrSwitchTab('overview', inArg.data.origin); break; } + case 'gotoApiTest': + this.appendOrSwitchTab('test', inArg.data.origin); + // ! It is bad way for delay render detail of api history. + setTimeout(() => { + this.messageService.send({ type: 'renderHistory', data: inArg.data }); + }, 20); + break; case 'gotoEditApi': this.appendOrSwitchTab('edit', inArg.data.origin); break; case 'copyApi': this.storage.run('apiDataCreate', [{ ...inArg.data }, inArg.data.uuid], (result: StorageRes) => { if (result.status === StorageResStatus.success) { - this.message.success('复制成功'); + this.message.success($localize`Copied successfully`); this.appendOrSwitchTab('edit', { ...inArg.data, ...result.data, @@ -253,7 +260,7 @@ export class ApiTabComponent implements OnInit, OnDestroy { }); this.messageService.send({ type: `copyApiSuccess`, data: result.data }); } else { - this.message.success('失败'); + this.message.success($localize`Failed to copy`); } }); break; diff --git a/src/workbench/browser/src/app/pages/api/tab/api-tab.service.ts b/src/workbench/browser/src/app/pages/api/tab/api-tab.service.ts index 6fee9624..fd6ae396 100644 --- a/src/workbench/browser/src/app/pages/api/tab/api-tab.service.ts +++ b/src/workbench/browser/src/app/pages/api/tab/api-tab.service.ts @@ -4,7 +4,7 @@ import { AppModule } from '../../../app.module'; import { TabItem } from './tab.model'; @Injectable({ - providedIn:AppModule + providedIn: AppModule, }) export class ApiTabService { tabs: Array = []; @@ -30,13 +30,15 @@ export class ApiTabService { this.tabCache[inData.tab.uuid] = inData.data; } removeData(tabID) { - if (!this.tabCache.hasOwnProperty(tabID)) return; + if (!this.tabCache.hasOwnProperty(tabID)) { + return; + } delete this.tabCache[tabID]; } - destroy(){ + destroy() { this.saveTabData$.complete(); this.tabChange$.complete(); - this.tabs=[]; - this.tabCache={}; + this.tabs = []; + this.tabCache = {}; } } diff --git a/src/workbench/browser/src/app/pages/api/test/api-test.component.html b/src/workbench/browser/src/app/pages/api/test/api-test.component.html index 19310282..eecc7bd9 100644 --- a/src/workbench/browser/src/app/pages/api/test/api-test.component.html +++ b/src/workbench/browser/src/app/pages/api/test/api-test.component.html @@ -16,34 +16,35 @@
- + - + - 请求头部 - {{ + Request Headers + {{ apiData.requestHeaders | apiParamsNum }} - + - 请求体 + Body - Query 参数 - {{ + Query + {{ apiData.queryParams | apiParamsNum }} @@ -66,35 +67,35 @@ - REST 参数 - {{ + REST + {{ apiData.restParams | apiParamsNum }} - + - - + +
- - + +
- + - + - - - -
+ diff --git a/src/workbench/browser/src/app/pages/api/test/api-test.component.ts b/src/workbench/browser/src/app/pages/api/test/api-test.component.ts index 15603f9b..d568586c 100644 --- a/src/workbench/browser/src/app/pages/api/test/api-test.component.ts +++ b/src/workbench/browser/src/app/pages/api/test/api-test.component.ts @@ -43,7 +43,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { }; status: 'start' | 'testing' | 'tested' = 'start'; waitSeconds = 0; - tabIndexRes: number = 0; + tabIndexRes = 0; testResult: any = { response: {}, request: {}, @@ -90,7 +90,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { * @param item test history data */ restoreHistory(item) { - let result = this.apiTest.getTestDataFromHistory(item); + const result = this.apiTest.getTestDataFromHistory(item); console.log('restoreHistory', result); //restore request this.apiData = result.testData; @@ -108,7 +108,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { }); } saveTestDataToApi() { - let apiData = this.apiTest.getApiFromTestData({ + const apiData = this.apiTest.getApiFromTestData({ history: this.testResult, testData: this.apiData, }); @@ -134,6 +134,11 @@ export class ApiTestComponent implements OnInit, OnDestroy { this.initApi(Number(this.route.snapshot.queryParams.uuid)); this.watchTabChange(); this.watchEnvChange(); + this.messageService.get().subscribe(({ type, data }) => { + if (type === 'renderHistory') { + this.restoreHistory(data); + } + }); } ngOnDestroy() { this.destroy$.next(); @@ -168,6 +173,8 @@ export class ApiTestComponent implements OnInit, OnDestroy { }, id ); + // console.log('test'); + this.messageService.send({ type: 'updateHistory', data: {} }); } } /** @@ -175,7 +182,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { */ private receiveMessage(message) { console.log('receiveMessage', message); - let tmpHistory = { + const tmpHistory = { general: message.general, request: message.report.request, response: message.response, @@ -183,7 +190,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { // other tab test finish,support multiple tab test same time if (message.id && this.apiTab.tabID !== message.id) { this.apiTab.tabCache[message.id].testResult = tmpHistory; - let tab = this.apiTab.tabs.find((val) => val.uuid === message.id); + const tab = this.apiTab.tabs.find((val) => val.uuid === message.id); if (tab) { this.addHistory(message, tab.key); } @@ -199,7 +206,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { */ private changeStatus(status) { this.status = status; - let that = this; + const that = this; switch (status) { case 'testing': { this.timer$ = interval(1000) @@ -229,7 +236,7 @@ export class ApiTestComponent implements OnInit, OnDestroy { this.initBasicForm(); //recovery from tab if (this.apiTab.currentTab && this.apiTab.tabCache[this.apiTab.tabID]) { - let tabData = this.apiTab.tabCache[this.apiTab.tabID]; + const tabData = this.apiTab.tabCache[this.apiTab.tabID]; this.apiData = tabData.apiData; this.testResult = tabData.testResult; return; @@ -290,7 +297,9 @@ export class ApiTestComponent implements OnInit, OnDestroy { request: {}, }; this.status$.next('start'); - if (this.timer$) this.timer$.unsubscribe(); + if (this.timer$) { + this.timer$.unsubscribe(); + } this.waitSeconds = 0; this.tabIndexRes = 0; } diff --git a/src/workbench/browser/src/app/pages/api/test/api-test.service.ts b/src/workbench/browser/src/app/pages/api/test/api-test.service.ts index 79652c56..341e971c 100644 --- a/src/workbench/browser/src/app/pages/api/test/api-test.service.ts +++ b/src/workbench/browser/src/app/pages/api/test/api-test.service.ts @@ -8,9 +8,9 @@ import { text2UiData } from '../../../utils/data-transfer/data-transfer.utils'; export class ApiTestService { constructor() {} initListConf(opts) { - opts.title = opts.title || '参数'; - opts.nameTitle = opts.nameTitle || `${opts.title}名`; - opts.valueTitle = opts.valueTitle || `${opts.title}值`; + opts.title = opts.title || $localize`Param`; + opts.nameTitle = opts.nameTitle || $localize`${opts.title} Name`; + opts.valueTitle = opts.valueTitle || $localize`${opts.title} Value`; return { setting: { // draggable: true, @@ -50,7 +50,7 @@ export class ApiTestService { class: 'w_250', btnList: [ { - key: '删除', + key: $localize`:@@Delete:Delete`, operateName: 'delete', }, ], @@ -93,15 +93,15 @@ export class ApiTestService { mark: 'require', }, { - thKey: '参数名', + thKey: $localize`Param Name`, type: 'depthInput', modelKey: 'name', - placeholder: '参数名', + placeholder: $localize`Param Name`, width: 300, mark: 'name', }, { - thKey: '类型', + thKey: $localize`Type`, type: 'select', key: 'key', value: 'value', @@ -114,12 +114,12 @@ export class ApiTestService { }, { - thKey: '参数值', + thKey: $localize`Value`, type: 'autoCompleteAndFile', modelKey: 'value', switchVar: 'type', swicthFile: 'file', - placeholder: '参数值', + placeholder: $localize`Value`, width: 300, mark: 'value', }, @@ -128,12 +128,12 @@ export class ApiTestService { class: 'w_250', btnList: [ { - key: '添加子字段', + key: $localize`Add Child`, operateName: 'addChild', itemExpression: `eo-attr-tip-placeholder="add_child_btn" ng-if="$ctrl.mainObject.setting.isLevel"`, }, { - key: '删除', + key: $localize`:@@Delete:Delete`, operateName: 'delete', itemExpression: 'ng-if="!($ctrl.mainObject.setting.munalHideOperateColumn&&$first)"', }, diff --git a/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.html b/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.html index 823dcf7d..98712981 100644 --- a/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.html +++ b/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.html @@ -9,7 +9,7 @@ [rootType]="jsonRootType">
-

JSON 根类型:

+

JSON Root Type:

@@ -24,5 +24,5 @@ - diff --git a/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.ts b/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.ts index 5f29f2cd..91e8262b 100644 --- a/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.ts +++ b/src/workbench/browser/src/app/pages/api/test/body/api-test-body.component.ts @@ -167,7 +167,7 @@ export class ApiTestBodyComponent implements OnInit, OnChanges, OnDestroy { } private initListConf() { this.listConf = this.apiTest.initBodyListConf({ - title: '参数', + title: $localize`Param`, itemStructure: this.itemStructure, watchFormLastChange: () => { this.modelChange.emit(this.model); @@ -180,7 +180,7 @@ export class ApiTestBodyComponent implements OnInit, OnChanges, OnDestroy { var val = inputArg.file[i]; if (val.size > 2 * 1024 * 1024) { inputArg.item.value = ''; - this.message.error('文件大小均需小于2M'); + this.message.error($localize`File size must be less than 2M`); return; } } diff --git a/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.html b/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.html index 64a37803..ff903f07 100644 --- a/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.html +++ b/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.ts b/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.ts index f5de4b3a..450da6cf 100644 --- a/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.ts +++ b/src/workbench/browser/src/app/pages/api/test/header/api-test-header.component.ts @@ -47,9 +47,9 @@ export class ApiTestHeaderComponent implements OnInit, OnChanges { this.listConf = this.editService.initListConf({ dragCacheVar: 'DRAG_VAR_API_HEADER', itemStructure: this.itemStructure, - title: '头部', - nameTitle: '标签', - valueTitle: '内容', + title: $localize`:@@Header:Header`, + nameTitle: $localize`Key`, + valueTitle: $localize`Value`, watchFormLastChange: () => { this.modelChange$.next(); }, diff --git a/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.html b/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.html index 8e56d8c7..a893f94b 100644 --- a/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.html +++ b/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.html @@ -1,17 +1,19 @@ -
- - \ No newline at end of file + + + diff --git a/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.ts b/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.ts index 8509b3a3..e11999e8 100644 --- a/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.ts +++ b/src/workbench/browser/src/app/pages/api/test/history/api-test-history.component.ts @@ -55,9 +55,9 @@ export class ApiTestHistoryComponent implements OnInit { this.storage.run('apiTestHistoryBulkRemove', [this.model.map((val) => val.uuid)], (result: StorageRes) => { if (result.status === StorageResStatus.success) { this.model = []; - this.message.success('删除成功'); + this.message.success($localize`Delete Succeeded`); } else { - this.message.error('删除失败'); + this.message.error($localize`Failed to delete`); console.error(result.data); } }); @@ -83,24 +83,24 @@ export class ApiTestHistoryComponent implements OnInit { }, tdList: [ { - thKey: '测试时间', + thKey: $localize`Test Time`, type: 'text', modelKey: 'testTime', class: 'pl20 w_180', }, { - thKey: '请求地址', + thKey: $localize`URL`, type: 'html', html: '{{item.request.method}}{{item.request.uri}}', }, { - thKey: '返回状态', + thKey: $localize`Status Code`, type: 'html', class: 'w_100', html: `{{item.response.statusCode}}`, }, { - thKey: '请求时长(ms)', + thKey: $localize`Request Time(ms)`, type: 'html', html: '{{item.response.testDeny}}', class: 'w_120', @@ -110,7 +110,7 @@ export class ApiTestHistoryComponent implements OnInit { class: 'w_100', btnList: [ { - key: '删除', + key: $localize`:@@Delete:Delete`, operateName: 'delete', fun: (inArg) => { this.delete(inArg); @@ -126,9 +126,9 @@ export class ApiTestHistoryComponent implements OnInit { this.storage.run('apiTestHistoryRemove', [inArg.item.uuid], (result: StorageRes) => { if (result.status === StorageResStatus.success) { this.model.splice(inArg.$index, 1); - this.message.success('删除成功'); + this.message.success($localize`Delete Succeeded`); } else { - this.message.success('删除失败'); + this.message.success($localize`Failed to delete`); console.error(result.data); } }); diff --git a/src/workbench/browser/src/app/pages/api/test/query/api-test-query.component.html b/src/workbench/browser/src/app/pages/api/test/query/api-test-query.component.html index 28cb1b4c..1acfaaf1 100644 --- a/src/workbench/browser/src/app/pages/api/test/query/api-test-query.component.html +++ b/src/workbench/browser/src/app/pages/api/test/query/api-test-query.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/workbench/browser/src/app/pages/api/test/result-header/api-test-result-header.component.html b/src/workbench/browser/src/app/pages/api/test/result-header/api-test-result-header.component.html index b81ade94..026e9a1b 100644 --- a/src/workbench/browser/src/app/pages/api/test/result-header/api-test-result-header.component.html +++ b/src/workbench/browser/src/app/pages/api/test/result-header/api-test-result-header.component.html @@ -1,6 +1,6 @@ - 暂无头部 + No Headers
    diff --git a/src/workbench/browser/src/app/pages/api/test/result-request-body/api-test-result-request-body.component.html b/src/workbench/browser/src/app/pages/api/test/result-request-body/api-test-result-request-body.component.html index 2f4e4e9b..b3f4d747 100644 --- a/src/workbench/browser/src/app/pages/api/test/result-request-body/api-test-result-request-body.component.html +++ b/src/workbench/browser/src/app/pages/api/test/result-request-body/api-test-result-request-body.component.html @@ -1,6 +1,6 @@ - 暂无请求体 + No Request Body
    diff --git a/src/workbench/browser/src/app/pages/api/test/result-response/api-test-result-response.component.html b/src/workbench/browser/src/app/pages/api/test/result-response/api-test-result-response.component.html index c760fffe..1991bf0c 100644 --- a/src/workbench/browser/src/app/pages/api/test/result-response/api-test-result-response.component.html +++ b/src/workbench/browser/src/app/pages/api/test/result-response/api-test-result-response.component.html @@ -1,7 +1,7 @@
    - 点击发送按钮获取测试报告 + Click the Send button to get a test report
    @@ -14,21 +14,25 @@
-
- 无法预览非文本类型的数据,您可以 - - ,并用其他程序打开。 +
+ Unable to preview non-text type data, you canand open it with other programs.
-
- 响应结果超出可预览的大小,您可以 - - + and open it with other programs. -->
-
- - 返回列表 - +
+
+ +
-
-
-
- +
+
+
- {{ extensionDetail?.moduleName }} - 作者: {{ extensionDetail?.author }} - - 版本: {{ extensionDetail?.version }} + {{ extensionDetail?.moduleName }}

{{ extensionDetail?.description }}

-
- - -
- +
-
- - {{ extensionDetail?.description }} - - - {{ extensionDetail?.author }} - {{ extensionDetail?.version }} - - Issue - - - - - +
+
+

Intro

+ +
+ + + + +
+
+
+
+

Install

+
+ +
+ + + + +
+ The extensions can only be installed on the client at present. Please download the client first~ +
+
+
+ + +

Support

+ + {{ extensionDetail?.author }} + {{ extensionDetail?.version }} + + +
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss index 59206ea5..9fc3b616 100644 --- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss +++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss @@ -1,5 +1,26 @@ ::ng-deep { - eo-extension-detail .ant-tabs-content-holder { - padding-top: 20px; + eo-extension-detail { + .ant-tabs-content-holder { + padding-top: 20px; + } + } +} +::ng-deep .extension-detail { + +.markdown-desc { + overflow: auto; + &::-webkit-scrollbar { + width:0; + } + &:hover::-webkit-scrollbar { + width:8px; + } +} + +.ant-descriptions-row > td { + padding-bottom: 8px; +} + .ant-descriptions-item-content,.ant-descriptions-item-label { + color: #999; } } diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts index 6866e1a4..98b9e9cf 100644 --- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts +++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts @@ -1,6 +1,8 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ElectronService } from 'eo/workbench/browser/src/app/core/services'; import { EoExtensionInfo } from '../extension.model'; +import { ResourceInfo } from '../../../shared/models/client.model'; import { ExtensionService } from '../extension.service'; @Component({ @@ -10,16 +12,87 @@ import { ExtensionService } from '../extension.service'; }) export class ExtensionDetailComponent implements OnInit { isOperating = false; + introLoading = false; extensionDetail: EoExtensionInfo; - constructor(private extensionService: ExtensionService, private route: ActivatedRoute) { + resourceInfo = ResourceInfo; + get isElectron() { + return this.electronService.isElectron; + } + constructor( + private extensionService: ExtensionService, + private route: ActivatedRoute, + private router: Router, + private electronService: ElectronService + ) { this.getDetail(); + this.getInstaller(); } async getDetail() { - this.extensionDetail = await this.extensionService.getDetail(this.route.snapshot.queryParams.id,this.route.snapshot.queryParams.name); + this.extensionDetail = await this.extensionService.getDetail( + this.route.snapshot.queryParams.id, + this.route.snapshot.queryParams.name + ); + if (!this.extensionDetail?.installed) { + await this.fetchReadme(); + } + this.extensionDetail.introduction ||= $localize`This plugin has no documentation yet.`; } + + async fetchReadme() { + try { + this.introLoading = true; + const response = await fetch(`https://unpkg.com/${this.extensionDetail.name}/README.md`); + if (response.status === 200) { + this.extensionDetail.introduction = await response.text(); + } + } catch (error) { + } finally { + this.introLoading = false; + } + } + + private findLinkInSingleAssets(assets, item) { + let result = ''; + const assetIndex = assets.findIndex( + (asset) => + new RegExp(`${item.suffix}$`, 'g').test(asset.browser_download_url) && + (!item.keyword || asset.browser_download_url.includes(item.keyword)) + ); + if (assetIndex === -1) { + return result; + } + result = assets[assetIndex].browser_download_url; + assets.splice(assetIndex, 1); + return result; + } + + private findLink(allAssets, item) { + let result = ''; + allAssets.some((assets) => { + result = this.findLinkInSingleAssets(assets, item); + return result; + }); + return result; + } + + getInstaller() { + fetch('https://api.github.com/repos/eolinker/eoapi/releases') + .then((response) => response.json()) + .then((data) => { + [...this.resourceInfo] + .sort((a1, a2) => a2.suffix.length - a1.suffix.length) + .forEach((item) => { + item.link = this.findLink( + data.map((val) => val.assets), + item + ); + }); + }); + } + manageExtension(operate: string, id) { this.isOperating = true; - console.log(this.isOperating) + console.log(this.isOperating); /** * * WARNING:Sending a synchronous message will block the whole * renderer process until the reply is received, so use this method only as a last @@ -29,14 +102,25 @@ export class ExtensionDetailComponent implements OnInit { switch (operate) { case 'install': { this.extensionDetail.installed = this.extensionService.install(id); + this.getDetail(); break; } case 'uninstall': { this.extensionDetail.installed = !this.extensionService.uninstall(id); + this.fetchReadme(); + break; } } this.isOperating = false; }, 100); } ngOnInit(): void {} + + backToList() { + this.router.navigate(['/home/extension/list'], { + queryParams: { + type: this.route.snapshot.queryParams.type, + }, + }); + } } diff --git a/src/workbench/browser/src/app/pages/extension/extension.component.html b/src/workbench/browser/src/app/pages/extension/extension.component.html index 7dfbced6..5d7e76cb 100644 --- a/src/workbench/browser/src/app/pages/extension/extension.component.html +++ b/src/workbench/browser/src/app/pages/extension/extension.component.html @@ -1,16 +1,42 @@
-
-
插件分类
-