mirror of
https://gitee.com/eolink_admin/postcat.git
synced 2024-11-29 18:28:09 +08:00
Group UI optimization (#89)
* fix: can not navigate to extensions page * feat: add iconpark * feat: sidebar done * feat: update style * fix: theme components show in bottom * feat: homepage almost done * feat: extension detail readme * feat: ignore eo-setting * feat: ignore eo-setting * feat: extension detail support text * fix: build error * feat: mock extension preview in web * update css style * feat: extension detail support web preview * pref: update css style * feat: update setting component * feat: update style * feat: ignore * remove diver * feat: add link in github logo * fix: build ts error * fix: search extension * feat: change bind event of click * fix: setting button can not click * feat: system setting support web * fix: flex style issue * feat: icon button width * chore: switch data source btn move to setting modal * feat: i18n * fix: module inject error * fix: some css style * feat: hide cloud icon * add logo to settingModal * feat: update settingModal UI * chore: serve support i18n * feat: base env * fix: some error message * fix: build error * feat: history done * feat: done env * feat: settings logic * fix: env modal auto open while page load * pref: api-tabs css style * fix: api-tabs css style issus * pref: update settingsModal css style * fix: set default language * feat: update env * feat: add goto env callback * fix: remote source for web * fix: remote source for web * fix: rename API to REST * feat: fix tips text * feat: fix icon size * fix: make api-tabs space evenly * fix: about component descritions issues * fix: style of env select position * fix: settings modal navigate to extension page faile * feat: modal footer left * fix: mock some logic * feat: history icon style * fix: extension list css style * update extension css style * fix: env save failed * chore: build i18n destop app * fix: rename event name * feat: change language * chore * test vercel * test vercel * delete vercel.json * fix: lose baseHref * chore: change angular.json beforeBuild * fix: 2 bug about env * perf: extension detail scrollbar style * feat: add ts.code-snippets * feat: initial i18n * fix: open test history * feat: add uuid in history tab * fix: custom iconpark component * test merge * fix: center plus icon * fix: setttings should auto save * translate: en * feat: change lang * feat: translate en * translate en * translate en * translate en * delete package.lock * fix: root directory run error * vercel.json * vercel redirects * vercel.json * change * change * route by accept-language * vercel.json * feat:redirect * vercel rewrites * vercel * chore: translate chinese comment to english * translate: cn * push zh.xlf * fix: manage environment * translate zh * fix: some css style issues * feat: extension detail support web preview * fix: update extension page css style * ci: support mac pack * fix: ci trigger condition * ci: set time zone * ci: set time zone * ci: remove build step * ci: remove set time zone * feat: add upload.js * chore: delete .yarnc * ci: update trigger condion * v1.2.3 * fix: release:m1 comand * feat: add notarize after sign * Update upload.js * feat: test * feat: delete upload.js in release command * feat: test v1.1.3 * feat: test v1.2.5 * fix: can not minimize with windows * fix: testResult.response can be null * feat: test 1.2.6 * feat: upload.js * test upload * feat: add test file * test upload * test upload * test upload * test upload * test upload * feat: update upload.js * feat: add log * feat: update * feat: delete upload.yml * feat: delete useless file * refactor: add settings service * feat: use custom update url * feat: v1.2.2 * ci: auto update by qiniu cdn * chore: release 1.2.3 * feat: v1.2.4 * remove yaml devp * chore: release 1.2.4 * update upload.js * update electron builder config Co-authored-by: buqiyuan <1743369777@qq.com> Co-authored-by: 夜鹰 <17kungfuboy@gmail.com> Co-authored-by: renqian805 <84910084+renqian805@users.noreply.github.com>
This commit is contained in:
parent
f2ba7b9156
commit
84c5d65f94
46
.github/workflows/release-win.yml
vendored
46
.github/workflows/release-win.yml
vendored
@ -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 }}
|
94
.github/workflows/release.yml
vendored
Normal file
94
.github/workflows/release.yml
vendored
Normal file
@ -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 }}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,6 +14,7 @@ out/
|
||||
!/api/*.js
|
||||
!/build/*.js
|
||||
!*.config.js
|
||||
!upload.js
|
||||
|
||||
scripts/notarize.js
|
||||
# dependencies
|
||||
|
28
.vscode/ts.code-snippets
vendored
Normal file
28
.vscode/ts.code-snippets
vendored
Normal file
@ -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: `<div></div>`,",
|
||||
"styleUrls: []",
|
||||
"})",
|
||||
"export class $2Component implements OnInit {",
|
||||
"constructor() {}",
|
||||
"ngOnInit() {}",
|
||||
"}"
|
||||
],
|
||||
"description": "Create a new Angular Component"
|
||||
}
|
||||
}
|
8
build/entitlements.mac.plist
Normal file
8
build/entitlements.mac.plist
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
3
build/notarize.js
Normal file
3
build/notarize.js
Normal file
@ -0,0 +1,3 @@
|
||||
exports.default = function notarizing(context) {
|
||||
return context;
|
||||
};
|
@ -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"]
|
||||
|
38
package.json
38
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": {
|
||||
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
|
@ -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();
|
||||
|
@ -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';
|
||||
|
@ -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) => {
|
||||
|
@ -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<T = any>(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();
|
||||
|
@ -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) {
|
||||
|
@ -26,6 +26,8 @@ export interface ModuleInfo {
|
||||
version: string;
|
||||
// 模块描述
|
||||
description: string;
|
||||
// 详细说明
|
||||
introduction: string;
|
||||
// 模块ID,用于关联
|
||||
moduleID: string;
|
||||
// 模块名称,用于显示
|
||||
|
@ -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<boolean>('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);
|
||||
});
|
||||
});
|
||||
|
1
src/workbench/browser/.gitignore
vendored
1
src/workbench/browser/.gitignore
vendored
@ -7,6 +7,7 @@ dist/
|
||||
/app-builds
|
||||
/release
|
||||
src/**/*.js
|
||||
!build/*.js
|
||||
!src/karma.conf.js
|
||||
!src/ng1/**/*.js
|
||||
*.js.map
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
{"$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"}}}
|
56
src/workbench/browser/build/build.js
Normal file
56
src/workbench/browser/build/build.js
Normal file
@ -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();
|
@ -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"
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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<string, any> = {}) {
|
||||
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<string, any> = {}) {
|
||||
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;
|
||||
};
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
export const THEMES = [
|
||||
{
|
||||
title: '经典',
|
||||
title: $localize`Classic`,
|
||||
lists: [
|
||||
{
|
||||
key: '森林',
|
||||
key: $localize`Forest`,
|
||||
value: 'classic_forest',
|
||||
},
|
||||
// {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div>
|
||||
<div class="button_list">
|
||||
<span *ngIf="eventList.includes('type') && !hiddenList.includes('type')">
|
||||
内容类型
|
||||
<span *ngIf="eventList.includes('type') && !hiddenList.includes('type')" i18n>
|
||||
Content Type
|
||||
<nz-select [(ngModel)]="editorType">
|
||||
<nz-option *ngFor="let item of typeList" [nzValue]="item.value" [nzLabel]="item.label"></nz-option>
|
||||
</nz-select>
|
||||
|
@ -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;
|
||||
|
@ -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: [
|
||||
{
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
// standalone: true,
|
||||
selector: 'eo-iconpark-icon',
|
||||
template: `<iconpark-icon [name]="name" [ngStyle]="{ fontSize: size }"></iconpark-icon>`,
|
||||
styleUrls: [],
|
||||
host: {
|
||||
class: 'inline-flex',
|
||||
},
|
||||
})
|
||||
export class EoIconparkIconComponent {
|
||||
@Input() name: string;
|
||||
@Input() size = '18px';
|
||||
|
||||
constructor() {}
|
||||
}
|
@ -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 {}
|
@ -1,10 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import {
|
||||
ListBlockCommonComponent,
|
||||
} from './component.ajs';
|
||||
const declarations: Array<any> = [
|
||||
ListBlockCommonComponent
|
||||
];
|
||||
import { ListBlockCommonComponent } from './component.ajs';
|
||||
const declarations: Array<any> = [ListBlockCommonComponent];
|
||||
@NgModule({
|
||||
declarations: declarations,
|
||||
imports: [],
|
||||
|
@ -1,25 +1,72 @@
|
||||
<nz-layout class="layout">
|
||||
<nz-sider nzTheme="light" nzWidth="250">
|
||||
<nz-content>
|
||||
<div class="inner-content">
|
||||
<eo-api-group-tree></eo-api-group-tree>
|
||||
</div>
|
||||
<!-- <div class="side-container"> -->
|
||||
<nz-content class="api-tabs">
|
||||
<nz-tabset nzCentered [nzAnimated]="false" [(nzSelectedIndex)]="tabsIndex">
|
||||
<nz-tab [nzTitle]="apiTitle">
|
||||
<ng-template #apiTitle>
|
||||
<span i18n-nzTooltipTitle nzTooltipTitle="Collections" nz-tooltip class="text-lg">
|
||||
<eo-iconpark-icon name="folder-open"></eo-iconpark-icon>
|
||||
</span>
|
||||
</ng-template>
|
||||
<div class="inner-content">
|
||||
<eo-api-group-tree></eo-api-group-tree>
|
||||
</div>
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="historyTitle">
|
||||
<ng-template #historyTitle>
|
||||
<span i18n-nzTooltipTitle nzTooltipTitle="History" nz-tooltip class="text-lg">
|
||||
<eo-iconpark-icon name="history"></eo-iconpark-icon>
|
||||
</span>
|
||||
</ng-template>
|
||||
<eo-history></eo-history>
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="envTitle">
|
||||
<ng-template #envTitle>
|
||||
<span i18n-nzTooltipTitle nzTooltipTitle="Environment" nz-tooltip class="text-lg">
|
||||
<eo-iconpark-icon name="instruction"></eo-iconpark-icon>
|
||||
</span>
|
||||
</ng-template>
|
||||
<eo-env></eo-env>
|
||||
</nz-tab>
|
||||
</nz-tabset>
|
||||
</nz-content>
|
||||
<!-- </div> -->
|
||||
</nz-sider>
|
||||
<nz-layout class="right-layout">
|
||||
<nz-content>
|
||||
<div class="inner-content">
|
||||
<div class="tabs-bar f_row">
|
||||
<div class="flex items-center tabs-bar">
|
||||
<eo-api-tab class="fg1"></eo-api-tab>
|
||||
<div class="env">
|
||||
<eo-env></eo-env>
|
||||
<div class="flex items-center fix-mt">
|
||||
<nz-select
|
||||
[(ngModel)]="envUuid"
|
||||
[(nzOpen)]="isOpen"
|
||||
(nzOpenChange)="handleEnvSelectStatus($event)"
|
||||
[nzDropdownRender]="renderTemplate"
|
||||
nzAllowClear
|
||||
i18n-nzPlaceHolder="Environment Dropdown placeholder"
|
||||
nzPlaceHolder="Environment"
|
||||
>
|
||||
<nz-option *ngFor="let item of envList" [nzValue]="item.uuid" [nzLabel]="item.name"></nz-option>
|
||||
</nz-select>
|
||||
<ng-template #renderTemplate>
|
||||
<nz-divider></nz-divider>
|
||||
<a class="text-sx manager-env" nz-button nzType="link" (click)="gotoEnvManager()" i18n>Manage Environment</a>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content_container {{ this.id ? 'has_tab_page' : '' }}">
|
||||
<nz-tabset class="inside_page_tab" [nzAnimated]="false" *ngIf="this.id" nzLinkRouter>
|
||||
<nz-tab *ngFor="let tab of TABS">
|
||||
<a *nzTabLink nz-tab-link (click)="clickContentMenu(tab)" [routerLink]="[tab.routerLink]"
|
||||
queryParamsHandling="merge">{{ tab.title }}</a>
|
||||
<a
|
||||
*nzTabLink
|
||||
nz-tab-link
|
||||
(click)="clickContentMenu(tab)"
|
||||
[routerLink]="[tab.routerLink]"
|
||||
queryParamsHandling="merge"
|
||||
>{{ tab.title }}</a
|
||||
>
|
||||
</nz-tab>
|
||||
</nz-tabset>
|
||||
<router-outlet></router-outlet>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<any> = [];
|
||||
activeUuid: number | string = 0;
|
||||
tabsIndex = 0;
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
@ -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: [],
|
||||
|
@ -20,10 +20,10 @@ export class ApiService {
|
||||
|
||||
delete({ name, uuid }: ApiData): void {
|
||||
this.nzModalService.confirm({
|
||||
nzTitle: '删除确认?',
|
||||
nzContent: `确认要删除数据 <strong title="${name}">${
|
||||
nzTitle: $localize`Deletion Confirmation?`,
|
||||
nzContent: $localize`Are you sure you want to delete the data <strong title="${name}">${
|
||||
name.length > 50 ? name.slice(0, 50) + '...' : name
|
||||
}</strong> 吗?删除后不可恢复!`,
|
||||
}</strong> ? You cannot restore it once deleted!`,
|
||||
nzOnOk: () => {
|
||||
this.storage.run('apiDataRemove', [uuid], (result: StorageRes) => {
|
||||
if (result.status === StorageResStatus.success) {
|
||||
|
@ -6,37 +6,45 @@
|
||||
</div>
|
||||
<p class="api_name text_omit">{{ apiData.name }}</p>
|
||||
<div *ngIf="apiData.requestHeaders && apiData.requestHeaders.length">
|
||||
<p class="api_line">请求头部</p>
|
||||
<p class="api_line" i18n>Request Headers</p>
|
||||
<eo-api-detail-header [model]="apiData.requestHeaders"></eo-api-detail-header>
|
||||
</div>
|
||||
<div *ngIf="apiData.queryParams && apiData.queryParams.length">
|
||||
<p class="api_line">Query 参数</p>
|
||||
<p class="api_line" i18n>Query</p>
|
||||
<eo-api-detail-query [model]="apiData.queryParams"></eo-api-detail-query>
|
||||
</div>
|
||||
<div *ngIf="apiData.restParams && apiData.restParams.length">
|
||||
<p class="api_line">Rest 参数</p>
|
||||
<p class="api_line" i18n>REST</p>
|
||||
<eo-api-detail-rest [model]="apiData.restParams"></eo-api-detail-rest>
|
||||
</div>
|
||||
<div *ngIf="apiData.requestBody?.length">
|
||||
<div class="api_line">
|
||||
Body 请求参数<nz-tag class="ml10" nzColor="default">{{ CONST.BODY_TYPE[apiData.requestBodyType] }}</nz-tag>
|
||||
<nz-tag *ngIf="apiData.requestBodyType==='json'" nzColor="default">最外层结构为:{{
|
||||
CONST.JSON_ROOT_TYPE[apiData.requestBodyJsonType] }}</nz-tag>
|
||||
<span i18n>Body</span>
|
||||
<nz-tag class="ml10" nzColor="default">{{ CONST.BODY_TYPE[apiData.requestBodyType] }}</nz-tag>
|
||||
<nz-tag *ngIf="apiData.requestBodyType === 'json'" nzColor="default" i18n
|
||||
>The outermost structure is: {{ CONST.JSON_ROOT_TYPE[apiData.requestBodyJsonType] }}</nz-tag
|
||||
>
|
||||
</div>
|
||||
<eo-api-detail-body [bodyType]="apiData.requestBodyType" [model]="apiData.requestBody"
|
||||
[jsonRootType]="apiData.requestBodyJsonType"></eo-api-detail-body>
|
||||
<eo-api-detail-body
|
||||
[bodyType]="apiData.requestBodyType"
|
||||
[model]="apiData.requestBody"
|
||||
[jsonRootType]="apiData.requestBodyJsonType"
|
||||
></eo-api-detail-body>
|
||||
</div>
|
||||
<div *ngIf="apiData.responseHeaders && apiData.responseHeaders.length">
|
||||
<p class="api_line">返回头部</p>
|
||||
<p class="api_line" i18n>Response Headers</p>
|
||||
<eo-api-detail-header [model]="apiData.responseHeaders"></eo-api-detail-header>
|
||||
</div>
|
||||
<div *ngIf="apiData.responseBody && apiData.responseBody.length">
|
||||
<p class="api_line">返回参数</p>
|
||||
<eo-api-detail-body [bodyType]="apiData.responseBodyType" [model]="apiData.responseBody"
|
||||
[jsonRootType]="apiData.responseBodyJsonType"></eo-api-detail-body>
|
||||
<p class="api_line" i18n>Response</p>
|
||||
<eo-api-detail-body
|
||||
[bodyType]="apiData.responseBodyType"
|
||||
[model]="apiData.responseBody"
|
||||
[jsonRootType]="apiData.responseBodyJsonType"
|
||||
></eo-api-detail-body>
|
||||
</div>
|
||||
<div *ngIf="isElectron">
|
||||
<p class="api_line">MOCK </p>
|
||||
<p class="api_line">MOCK</p>
|
||||
<eo-api-detail-mock [apiData]="apiData"></eo-api-detail-mock>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -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: '<span class="param-name-span">{{item.name}}</span>',
|
||||
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: `<button type="button" class="eo-operate-btn" ng-click="$ctrl.data.isSpreedBtnClick=!$ctrl.data.isSpreedBtnClick;$ctrl.data.isSpreed=true;$ctrl.mainObject.baseFun.spreedAll($event);$ctrl.data.isSpreed=false;">{{$ctrl.data.isSpreedBtnClick?"全部收缩":"全部展开"}}</button>`,
|
||||
thKey: $localize`<button type="button" class="eo-operate-btn" ng-click="$ctrl.data.isSpreedBtnClick=!$ctrl.data.isSpreedBtnClick;$ctrl.data.isSpreed=true;$ctrl.mainObject.baseFun.spreedAll($event);$ctrl.data.isSpreed=false;">{{$ctrl.data.isSpreedBtnClick?"Shrink All":"Expand All"}}</button>`,
|
||||
type: 'html',
|
||||
html: `<span class="eo-operate-btn fs12" ng-show="item.minimum ||
|
||||
html: $localize`<span class="eo-operate-btn fs12" ng-show="item.minimum ||
|
||||
item.maximum ||
|
||||
item.minLength ||
|
||||
item.maxLength ||
|
||||
(item.enum && item.enum.length > 0 && item.enum[0].value)">{{item.isClick?"收缩":"展开"}}</span>`,
|
||||
(item.enum && item.enum.length > 0 && item.enum[0].value)">{{item.isClick?"Shrink":"Expand"}}</span>`,
|
||||
mark: 'fn_btn',
|
||||
width: '100px',
|
||||
class: 'undivide_line_lbcc',
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<eo-table [(model)]="mocklList" [columns]="mockListColumns" [dataModel]="{ name: '', value: '', description: '' }">
|
||||
<ng-template cell="url" let-scope="scope" let-index="index">
|
||||
<span nzTooltipTitle="点击复制" nzTooltipPlacement="top" nz-tooltip (click)="copyText(scope.url )" class="truncate">
|
||||
<span i18n-nzTooltipTitle nzTooltipTitle="Click to Copy" nzTooltipPlacement="top" nz-tooltip (click)="copyText(scope.url )" class="truncate">
|
||||
{{ scope.url }}
|
||||
</span>
|
||||
</ng-template>
|
||||
|
@ -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<void> = new Subject<void>();
|
||||
@ -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`);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<eo-message></eo-message>
|
||||
<div class="!pt-0 p15">
|
||||
<div class="sticky top-0 z-10 pt-6 bg-white">
|
||||
<button type="submit" nz-button nztype="primary" class="eo_theme_btn_success" (click)="saveApi()">保存</button>
|
||||
<button type="submit" nz-button nztype="primary" class="eo_theme_btn_success" (click)="saveApi()" i18n>Save</button>
|
||||
<nz-divider></nz-divider>
|
||||
</div>
|
||||
<form nz-form [nzLayout]="'vertical'" [formGroup]="validateForm">
|
||||
@ -14,22 +14,22 @@
|
||||
<nz-option *ngFor="let item of REQUEST_METHOD" [nzLabel]="item.key" [nzValue]="item.value"></nz-option>
|
||||
</nz-select>
|
||||
<nz-form-item nz-col class="fg1">
|
||||
<nz-form-control nzErrorTip="请输入 API Path">
|
||||
<nz-form-control i18n-nzErrorTip nzErrorTip="Please enter API Path">
|
||||
<input type="text" [(ngModel)]="apiData.uri" name="uri" id="uri" nz-input formControlName="uri" />
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</nz-input-group>
|
||||
<nz-form-label nzFor="name">分组 / API 名称 </nz-form-label>
|
||||
<nz-form-label nzFor="name" i18n>Group / API Name</nz-form-label>
|
||||
<nz-input-group nzCompact>
|
||||
<nz-form-item nz-col>
|
||||
<nz-form-control class="w_250" nzErrorTip="请选择 API 分组">
|
||||
<nz-form-control class="w_250" i18n-nzErrorTip nzErrorTip="Please select an API group">
|
||||
<nz-tree-select nzAllowClear="false" [nzExpandedKeys]="expandKeys" [nzDropdownMatchSelectWidth]="false"
|
||||
[nzNodes]="groups" [(ngModel)]="apiData.groupID" [nzShowSearch]="true" #apiGroup formControlName="groupID">
|
||||
</nz-tree-select>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item nz-col class="fg1">
|
||||
<nz-form-control nzErrorTip="请输入 API 名称">
|
||||
<nz-form-control i18n-nzErrorTip nzErrorTip="Please enter API name">
|
||||
<input type="text" name="name" id="name" nz-input formControlName="name" />
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
@ -37,17 +37,18 @@
|
||||
</form>
|
||||
<!-- 请求参数 -->
|
||||
<nz-collapse class="eo_collapse mt20" [nzGhost]="true">
|
||||
<nz-collapse-panel #panel [nzActive]="true" nzHeader="请求参数" nzShowArrow="false" [nzExtra]="extraTpl">
|
||||
<nz-collapse-panel #panel [nzActive]="true" i18n-nzHeader nzHeader="Request" nzShowArrow="false"
|
||||
[nzExtra]="extraTpl">
|
||||
<ng-template #extraTpl>
|
||||
{{ panel.nzActive ? '收缩' : '展开' }}
|
||||
<span i18n>{{ panel.nzActive ? 'Shrink' : 'Expand' }}</span>
|
||||
<span class="iconfont icon-chevron-{{ panel.nzActive ? 'up' : 'down' }}"></span>
|
||||
</ng-template>
|
||||
<nz-tabset [nzAnimated]="false" [nzSelectedIndex]="1" class="mt10">
|
||||
<!-- 请求头部 -->
|
||||
<!-- Request Headers -->
|
||||
<nz-tab [nzTitle]="headerTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #headerTitleTmp>
|
||||
请求头部
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.requestHeaders)">{{
|
||||
<span i18n>Request Headers</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.requestHeaders)">{{
|
||||
apiData.requestHeaders | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
@ -56,7 +57,7 @@
|
||||
<!-- 请求体 -->
|
||||
<nz-tab [nzTitle]="bodyTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #bodyTitleTmp>
|
||||
请求体
|
||||
<span i18n>Body</span>
|
||||
<span class="iconfont icon-circle eo-tab-theme-icon" *ngIf="
|
||||
['formData', 'json', 'xml'].includes(apiData.requestBodyType)
|
||||
? bindGetApiParamNum(apiData.requestBody)
|
||||
@ -69,8 +70,8 @@
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="queryTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #queryTitleTmp>
|
||||
Query 参数
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.queryParams)">{{
|
||||
<span i18n>Query</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.queryParams)">{{
|
||||
apiData.queryParams | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
@ -78,8 +79,8 @@
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="restTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #restTitleTmp>
|
||||
REST 参数
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.restParams)">{{
|
||||
<span i18n>REST</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.restParams)">{{
|
||||
apiData.restParams | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
@ -90,16 +91,17 @@
|
||||
</nz-collapse>
|
||||
<!-- 响应内容 -->
|
||||
<nz-collapse class="eo_collapse mt40" [nzGhost]="true">
|
||||
<nz-collapse-panel #panelRes [nzActive]="true" nzHeader="响应内容" nzShowArrow="false" [nzExtra]="extraTplRes">
|
||||
<nz-collapse-panel #panelRes [nzActive]="true" i18n-nzHeader nzHeader="Response" nzShowArrow="false"
|
||||
[nzExtra]="extraTplRes">
|
||||
<ng-template #extraTplRes>
|
||||
{{ panelRes.nzActive ? '收缩' : '展开' }}
|
||||
<span i18n>{{ panelRes.nzActive ? 'Shrink' : 'Expand' }}</span>
|
||||
<span class="iconfont icon-chevron-{{ panelRes.nzActive ? 'up' : 'down' }}"></span>
|
||||
</ng-template>
|
||||
<nz-tabset [nzAnimated]="false" [nzSelectedIndex]="1" class="mt10">
|
||||
<nz-tab [nzTitle]="responseHeaderTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #responseHeaderTitleTmp>
|
||||
返回头部
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.responseHeaders)">{{
|
||||
<span i18n>Response Headers</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.responseHeaders)">{{
|
||||
apiData.responseHeaders | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
@ -108,7 +110,7 @@
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="responseTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #responseTitleTmp>
|
||||
返回结果
|
||||
<span i18n>Response</span>
|
||||
<span class="iconfont icon-circle eo-tab-theme-icon" *ngIf="
|
||||
['formData', 'json', 'xml'].includes(apiData.responseBodyType)
|
||||
? bindGetApiParamNum(apiData.responseBody)
|
||||
@ -122,3 +124,4 @@
|
||||
</nz-tabset>
|
||||
</nz-collapse-panel>
|
||||
</nz-collapse>
|
||||
</div>
|
||||
|
@ -59,7 +59,7 @@ export class ApiEditComponent implements OnInit, OnDestroy {
|
||||
this.groups = [];
|
||||
const treeItems: any = [
|
||||
{
|
||||
title: '根目录',
|
||||
title: $localize`Root directory`,
|
||||
//!actually is 0,but 0 will hidden in nz component,so use -1 replace 0
|
||||
key: '-1',
|
||||
weight: 0,
|
||||
|
@ -6,7 +6,7 @@ export class ApiEditService {
|
||||
constructor(private modalService: ModalService) {}
|
||||
showMore(inputArg, opts: { nzOnOk: (result: any) => void; title: string }) {
|
||||
const modal = this.modalService.create({
|
||||
nzTitle: `${opts.title}详情`,
|
||||
nzTitle: $localize`${opts.title} Detail`,
|
||||
nzContent: ApiParamsExtraSettingComponent,
|
||||
nzClosable: false,
|
||||
nzWidth: '60%',
|
||||
@ -144,15 +144,15 @@ export class ApiEditService {
|
||||
itemExpression: 'ng-if="$index+1!==$ctrl.list.length"',
|
||||
},
|
||||
{
|
||||
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',
|
||||
@ -164,26 +164,26 @@ export class ApiEditService {
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
thKey: '必填',
|
||||
thKey: $localize`Required`,
|
||||
type: 'checkbox',
|
||||
modelKey: 'required',
|
||||
width: 80,
|
||||
mark: 'require',
|
||||
},
|
||||
{
|
||||
thKey: '说明',
|
||||
thKey: $localize`:@@Description:Description`,
|
||||
type: 'input',
|
||||
modelKey: 'description',
|
||||
placeholder: '参数说明',
|
||||
placeholder: $localize`Param Description`,
|
||||
width: 300,
|
||||
mark: 'description',
|
||||
},
|
||||
|
||||
{
|
||||
thKey: '示例',
|
||||
thKey: $localize`Example`,
|
||||
type: 'input',
|
||||
modelKey: 'example',
|
||||
placeholder: '参数示例',
|
||||
placeholder: $localize`Param Example`,
|
||||
width: 200,
|
||||
hide: 1,
|
||||
mark: 'example',
|
||||
@ -193,12 +193,12 @@ export class ApiEditService {
|
||||
class: 'w_250',
|
||||
btnList: [
|
||||
{
|
||||
key: '添加子字段',
|
||||
key: $localize`Add Child`,
|
||||
operateName: 'addChild',
|
||||
itemExpression: `ng-if="$ctrl.mainObject.setting.isLevel"`,
|
||||
},
|
||||
{
|
||||
key: '更多设置',
|
||||
key: $localize`More Settings`,
|
||||
operateName: 'more',
|
||||
fun: (inputArg) => {
|
||||
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)"',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -9,7 +9,7 @@
|
||||
[rootType]="jsonRootType"></params-import>
|
||||
</div>
|
||||
<div *ngIf="bodyType === 'json'">
|
||||
<p class="fs12 c999 mb5">JSON 根类型:</p>
|
||||
<p class="fs12 c999 mb5" i18n>JSON Root Type:</p>
|
||||
<nz-select class="w_100 mb10" [(ngModel)]="jsonRootType" (ngModelChange)="jsonRootTypeChange.emit(jsonRootType)">
|
||||
<nz-option *ngFor="let item of CONST.JSON_ROOT_TYPE" [nzLabel]="item.key" [nzValue]="item.value"></nz-option>
|
||||
</nz-select>
|
||||
|
@ -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',
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="param_header">
|
||||
<params-import [(baseData)]="model" contentType="formData" modalTitle="头部"></params-import>
|
||||
<params-import [(baseData)]="model" contentType="formData" i18n-modalTitle="@@Header" modalTitle="Header"></params-import>
|
||||
</div>
|
||||
<list-block-common-component [mainObject]="listConf" [(list)]="model"></list-block-common-component>
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="param_header">
|
||||
<params-import [(baseData)]="model" contentType="query" modalTitle="Query参数"></params-import>
|
||||
<params-import [(baseData)]="model" contentType="query" modalTitle="Query"></params-import>
|
||||
</div>
|
||||
<list-block-common-component [mainObject]="listConf" [(list)]="model"></list-block-common-component>
|
||||
|
@ -1,13 +1,14 @@
|
||||
<form nz-form [nzLayout]="'vertical'" [formGroup]="validateForm" (ngSubmit)="submit()" *ngIf="!isDelete">
|
||||
<nz-form-item nz-col class="fg1">
|
||||
<nz-form-label nzFor="name">分组名称 </nz-form-label>
|
||||
<nz-form-control nzErrorTip="请输入分组名称">
|
||||
<nz-form-label nzFor="name" i18n>Group Name</nz-form-label>
|
||||
<nz-form-control i18n-nzErrorTip nzErrorTip="Please enter group name">
|
||||
<input type="text" autofocus nz-input id="name" formControlName="name" [(ngModel)]="group.name" />
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
<p *ngIf="isDelete">
|
||||
删除
|
||||
<strong title="{{ group.name }}">{{ group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name }}</strong>
|
||||
后,该分组下的数据都会删除。该操作无法撤销,确认删除吗?
|
||||
<p *ngIf="isDelete" i18n>
|
||||
Data from<strong title="{{ group.name }}">{{
|
||||
group.name.length > 50 ? group.name.slice(0, 50) + '...' : group.name
|
||||
}}</strong>
|
||||
will be deleted. This cannot be undone. Are you sure you want to delete?
|
||||
</p>
|
||||
|
@ -1,48 +1,38 @@
|
||||
<header class="group_header">
|
||||
<nz-input-group>
|
||||
<div nz-row [nzGutter]="8">
|
||||
<div nz-col nzFlex="4">
|
||||
<nz-input-group [nzPrefix]="prefixIcon" nzCompact>
|
||||
<input type="text" nz-input placeholder="搜索" [(ngModel)]="searchValue" />
|
||||
</nz-input-group>
|
||||
<ng-template #prefixIcon>
|
||||
<i nz-icon nzType="search"></i>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div nz-col nzFlex="2">
|
||||
<nz-button-group>
|
||||
<button nzType="primary" nz-button (click)="operateApiEvent({ event: $event, eventName: 'gotoAddApi' })">
|
||||
<i nz-icon nzType="plus"></i>
|
||||
API
|
||||
</button>
|
||||
<button nzType="primary" nz-button nz-dropdown [nzDropdownMenu]="menu" nzPlacement="bottomRight">
|
||||
<i nz-icon nzType="caret-down" nzTheme="outline"></i>
|
||||
</button>
|
||||
</nz-button-group>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<li nz-menu-item (click)="operateApiEvent({ event: $event, eventName: 'gotoAddApi' })"><a>新建API</a></li>
|
||||
<li nz-menu-item (click)="addGroup()"><a>新建分组</a></li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</div>
|
||||
</div>
|
||||
</nz-input-group>
|
||||
<header class="flex py-2">
|
||||
<input type="text" class="flex-1 px-3 input" i18n-placeholder="@@Search" placeholder="Search"
|
||||
[(ngModel)]="searchValue" />
|
||||
<div class="flex items-center justify-center ml-3 text-base btn shrink-0" nzType="primary" nz-button nz-dropdown
|
||||
[nzDropdownMenu]="menu" nzPlacement="bottomRight"
|
||||
(click)="operateApiEvent({ event: $event, eventName: 'gotoAddApi' })">
|
||||
<eo-iconpark-icon name="plus"></eo-iconpark-icon>
|
||||
</div>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<li nz-menu-item (click)="operateApiEvent({ event: $event, eventName: 'gotoAddApi' })" i18n><a>New API</a></li>
|
||||
<li nz-menu-item (click)="addGroup()"><a i18n>New Group</a></li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</header>
|
||||
<!-- Fixed Group -->
|
||||
<div class="group_container fixed_group_tree pt10" *ngIf="electron.isElectron">
|
||||
<div class="group_container fixed_group_tree pt10">
|
||||
<!-- <div class="group_container fixed_group_tree pt10" *ngIf="electron.isElectron"> -->
|
||||
<nz-tree [nzData]="fixedTreeNode" [nzSelectedKeys]="nzSelectedKeys" nzBlockNode (nzClick)="clickTreeItem($event)"
|
||||
[nzTreeTemplate]="nzFixedTreeTemplate"></nz-tree>
|
||||
<ng-template #nzFixedTreeTemplate let-node let-origin="origin">
|
||||
<div class="pl5 tree_node" *ngIf="node.origin?.isFixed">
|
||||
<div class="f_row_ac">
|
||||
<i class="mr10" nz-icon nzType="home" nzTheme="outline"></i>
|
||||
<div class="tree_node" *ngIf="node.origin?.isFixed">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-2 text-sm">
|
||||
<eo-iconpark-icon name="home">
|
||||
</eo-iconpark-icon>
|
||||
</span>
|
||||
<span class="text_omit node_title">{{ node.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="bbd" *ngIf="electron.isElectron"></div>
|
||||
<div class="bbd"></div>
|
||||
<!-- <div class="bbd" *ngIf="electron.isElectron"></div> -->
|
||||
|
||||
<!-- Custom Group -->
|
||||
<div class="group_container group_tree pt10">
|
||||
<nz-tree [nzData]="treeNodes" [nzSelectedKeys]="nzSelectedKeys" #apiGroup [nzSearchValue]="searchValue"
|
||||
@ -50,11 +40,10 @@
|
||||
(nzExpandChange)="toggleExpand()" nzDraggable nzBlockNode (nzOnDrop)="treeItemDrop($event)"
|
||||
[nzTreeTemplate]="nzTreeTemplate"></nz-tree>
|
||||
<ng-template #nzTreeTemplate let-node let-origin="origin">
|
||||
<div class="pl5">
|
||||
<div>
|
||||
<!-- Folder -->
|
||||
<div class="tree_node f_row f_js_ac" *ngIf="!node.isLeaf">
|
||||
<div class="f_row_ac">
|
||||
<span class="iconfont icon-folder-outline fs16 mr5"></span>
|
||||
<span class="text_omit node_title">{{ node.title }}</span>
|
||||
</div>
|
||||
<span class="tree_node_operate">
|
||||
@ -64,16 +53,16 @@
|
||||
<nz-dropdown-menu #groupMenu="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<li nz-menu-item (click)="operateApiEvent({ event: $event, eventName: 'gotoAddApi', node: node })">
|
||||
<a>添加 API</a>
|
||||
<a i18n>Add API</a>
|
||||
</li>
|
||||
<li nz-menu-item (click)="addSubGroup(node)">
|
||||
<a>新建子分组</a>
|
||||
<a i18n>Add Subgroup</a>
|
||||
</li>
|
||||
<li nz-menu-item (click)="editGroup(node)">
|
||||
<a>编辑</a>
|
||||
<a i18n>Edit</a>
|
||||
</li>
|
||||
<li nz-menu-item (click)="deleteGroup(node)">
|
||||
<a>删除</a>
|
||||
<a i18n="@@Delete">Delete</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
@ -98,15 +87,15 @@
|
||||
<nz-dropdown-menu #apiDataMenu="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<li nz-menu-item (click)="operateApiEvent({ event: $event, eventName: 'gotoEditApi', node: node })">
|
||||
<a>编辑</a>
|
||||
<a i18n>Edit</a>
|
||||
</li>
|
||||
<li nz-menu-item
|
||||
(click)="operateApiEvent({ event: $event, eventName: 'gotoCopyApi', node: apiDataItems[node.key] })">
|
||||
<a>复制</a>
|
||||
<a i18n="@@Copy">Copy</a>
|
||||
</li>
|
||||
<li nz-menu-item
|
||||
(click)="operateApiEvent({ event: $event, eventName: 'gotoDeleteApi', node: apiDataItems[node.key] })">
|
||||
<a>删除</a>
|
||||
<a i18n="@@Delete">Delete</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
|
@ -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;
|
||||
|
@ -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' });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,20 @@
|
||||
<div>
|
||||
<div class="flex items-center justify-between h-10 px-2 history-title">
|
||||
<span class="font-bold" i18n>History</span>
|
||||
<div class="flex items-center justify-center cursor-pointer shrink-0 h-7" nzTooltipTitle="Clear All" nz-tooltip
|
||||
nz-popconfirm nzPopconfirmTitle="Are you sure delete all history?" (nzOnConfirm)="clearAllHistory()"
|
||||
(nzOnCancel)="cancel()" nzOkText="Yes" nzCancelText="No" nzPopconfirmPlacement="topRight">
|
||||
<span class="flex items-center justify-center icon">
|
||||
<eo-iconpark-icon name="delete">
|
||||
</eo-iconpark-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngFor="let item of historyList" class="flex items-center h-8 p-2 text-xs cursor-pointer hover:bg-gray-100"
|
||||
(click)="gotoTestHistory(item)">
|
||||
<span class="block w-12 font-light method_type" [ngClass]="methodColor(item.request.method)">{{
|
||||
item.request.method
|
||||
}}</span>
|
||||
<span class="flex-1 overflow-hidden text-gray-600 truncate">{{ item.request.uri }}</span>
|
||||
</div>
|
||||
</div>
|
@ -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;
|
||||
}
|
@ -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() {}
|
||||
}
|
@ -1,13 +1,20 @@
|
||||
<div class="p-[18px]">
|
||||
<button nz-button nzType="primary" (click)="addOrEditModal()">添加 Mock</button>
|
||||
<button nz-button nzType="primary" (click)="addOrEditModal()" i18n>Add Mock</button>
|
||||
|
||||
<div class="mt-[20px]">
|
||||
<eo-table [(model)]="mocklList" [columns]="mockListColumns" [dataModel]="{ name: '', value: '', description: '' }">
|
||||
<ng-template cell="name" let-scope="scope" let-index="index">
|
||||
<div class="truncate w-[120px]">{{scope.name}}</div>
|
||||
<div class="truncate w-[120px]">{{ scope.name }}</div>
|
||||
</ng-template>
|
||||
<ng-template cell="url" let-scope="scope" let-index="index">
|
||||
<span nzTooltipTitle="点击复制" nzTooltipPlacement="top" nz-tooltip (click)="copyText(scope.url )" class="truncate">
|
||||
<span
|
||||
i18n-nzTooltipTitle
|
||||
nzTooltipTitle="Click to Copy"
|
||||
nzTooltipPlacement="top"
|
||||
nz-tooltip
|
||||
(click)="copyText(scope.url)"
|
||||
class="truncate"
|
||||
>
|
||||
{{ scope.url }}
|
||||
</span>
|
||||
</ng-template>
|
||||
@ -16,11 +23,21 @@
|
||||
</ng-template>
|
||||
<ng-template cell="action" let-scope="scope" let-index="index">
|
||||
<div class="flex justify-evenly">
|
||||
<a nz-button nzType="link" *ngIf="scope.name || scope.url" (click)="addOrEditModal(index)">
|
||||
{{ scope.createWay === 'system' ? '预览' : '编辑' }}</a>
|
||||
<a nz-button nzType="link" *ngIf="(scope.name || scope.url) && scope.createWay !== 'system'" nz-popconfirm
|
||||
nzPopconfirmTitle="您确定要删除此Mock吗?" nzPopconfirmPlacement="topRight"
|
||||
(nzOnConfirm)="handleDeleteMockItem(index)">删除</a>
|
||||
<a nz-button nzType="link" *ngIf="scope.name || scope.url" (click)="addOrEditModal(index)" i18n>
|
||||
{{ scope.createWay === 'system' ? 'Preview' : 'Edit' }}</a
|
||||
>
|
||||
<a
|
||||
nz-button
|
||||
nzType="link"
|
||||
*ngIf="(scope.name || scope.url) && scope.createWay !== 'system'"
|
||||
nz-popconfirm
|
||||
i18n-nzPopconfirmTitle
|
||||
nzPopconfirmTitle="Are you sure you want to delete this Mock?"
|
||||
nzPopconfirmPlacement="topRight"
|
||||
(nzOnConfirm)="handleDeleteMockItem(index)"
|
||||
i18n="@@Delete"
|
||||
>Delete</a
|
||||
>
|
||||
</div>
|
||||
</ng-template>
|
||||
</eo-table>
|
||||
@ -32,25 +49,33 @@
|
||||
<div class="w-full main-content">
|
||||
<form nz-form nzLayout="vertical">
|
||||
<nz-form-item>
|
||||
<nz-form-label nzFor="currentEditMock.name">Mock 名称</nz-form-label>
|
||||
<nz-form-label i18n nzFor="currentEditMock.name">Mock Name</nz-form-label>
|
||||
<nz-form-control>
|
||||
<input nz-input name="name" type="text" [(ngModel)]="currentEditMock.name"
|
||||
[readonly]="currentEditMock.createWay=== 'system'" />
|
||||
<input
|
||||
nz-input
|
||||
name="name"
|
||||
type="text"
|
||||
[(ngModel)]="currentEditMock.name"
|
||||
[readonly]="currentEditMock.createWay === 'system'"
|
||||
/>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
<nz-form-item>
|
||||
<nz-form-label nzFor="currentEditMock.response">返回值</nz-form-label>
|
||||
<nz-form-label i18n nzFor="currentEditMock.response">Response</nz-form-label>
|
||||
<nz-form-control>
|
||||
<eo-editor [(code)]="responseStr" (codeChange)="rawDataChange()"
|
||||
[disabled]="currentEditMock.createWay=== 'system'"
|
||||
[eventList]="['type', 'format', 'copy', 'download', 'newTab', 'search', 'replace']"></eo-editor>
|
||||
<eo-editor
|
||||
[(code)]="responseStr"
|
||||
(codeChange)="rawDataChange()"
|
||||
[disabled]="currentEditMock.createWay === 'system'"
|
||||
[eventList]="['type', 'format', 'copy', 'download', 'newTab', 'search', 'replace']"
|
||||
></eo-editor>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
<div *nzModalFooter class="footer">
|
||||
<button nz-button nzType="primary" (click)="handleSave()">保存</button>
|
||||
<button nz-button nzType="default" (click)="handleCancel()">取消</button>
|
||||
<button nz-button nzType="primary" i18n (click)="handleSave()">Save</button>
|
||||
<button nz-button nzType="default" i18n (click)="handleCancel()">Cancel</button>
|
||||
</div>
|
||||
</nz-modal>
|
||||
</nz-modal>
|
||||
|
@ -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`);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="overview_container">
|
||||
<h1 class="fs20 fwb">概况</h1>
|
||||
<h1 class="fs20 fwb" i18n="@@API Index">Index</h1>
|
||||
<nz-divider></nz-divider>
|
||||
<section class="grid gap-8 grid-cols-2 xl:grid-cols-3">
|
||||
<div class="card_item" *ngFor="let item of overviewList">
|
||||
|
@ -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}`);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -1,5 +1,11 @@
|
||||
<nz-tabset [(nzSelectedIndex)]="selectedIndex" nzType="editable-card" nzHideAdd="true" (nzClose)="closeTab($event)"
|
||||
(nzSelectChange)="pickTab()" [nzTabBarExtraContent]="extraTemplate">
|
||||
<nz-tabset
|
||||
[(nzSelectedIndex)]="selectedIndex"
|
||||
nzType="editable-card"
|
||||
nzHideAdd="true"
|
||||
(nzClose)="closeTab($event)"
|
||||
(nzSelectChange)="pickTab()"
|
||||
[nzTabBarExtraContent]="extraTemplate"
|
||||
>
|
||||
<nz-tab *ngFor="let tab of tabSerive.tabs; let i = index" nzClosable [nzTitle]="titleTemplate">
|
||||
<ng-template #titleTemplate>
|
||||
<span class="mr5 method_text_{{ tab.method }}" *ngIf="tab.method">{{ tab.method }}</span>
|
||||
@ -16,11 +22,11 @@
|
||||
</a>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<li nz-menu-item (click)="operateTab('closeOther')">关闭所有标签(当前标签除外)</li>
|
||||
<li nz-menu-item (click)="operateTab('closeAll')">关闭所有标签</li>
|
||||
<li nz-menu-item (click)="operateTab('closeOther')" i18n>Close All Tags (excluding current tabs)</li>
|
||||
<li nz-menu-item (click)="operateTab('closeAll')" i18n>Close All Tabs</li>
|
||||
<!-- <li nz-menu-item>关闭已保存</li> -->
|
||||
<li nz-menu-item (click)="operateTab('closeLeft')">关闭左侧标签页</li>
|
||||
<li nz-menu-item (click)="operateTab('closeRight')">关闭右侧标签页</li>
|
||||
<li nz-menu-item (click)="operateTab('closeLeft')" i18n>Close Tabs To The Left</li>
|
||||
<li nz-menu-item (click)="operateTab('closeRight')" i18n>Close Tabs to Right</li>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</ng-template>
|
||||
|
@ -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;
|
||||
|
@ -4,7 +4,7 @@ import { AppModule } from '../../../app.module';
|
||||
import { TabItem } from './tab.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn:AppModule
|
||||
providedIn: AppModule,
|
||||
})
|
||||
export class ApiTabService {
|
||||
tabs: Array<TabItem> = [];
|
||||
@ -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 = {};
|
||||
}
|
||||
}
|
||||
|
@ -16,34 +16,35 @@
|
||||
<input type="text" name="uri" nz-input formControlName="uri" [(ngModel)]="apiData.uri"
|
||||
(change)="changeUri()" />
|
||||
<button type="submit" nz-button nzType="primary" class="ml10" (click)="clickTest()">
|
||||
{{ status === 'testing' ? '终止' : '发送' }}
|
||||
<span *ngIf="status === 'testing'" i18n>Abort</span>
|
||||
<span *ngIf="status !== 'testing'" i18n>Send</span>
|
||||
<span *ngIf="status === 'testing' && waitSeconds" class="ml5">{{ waitSeconds }}</span>
|
||||
</button>
|
||||
<button type="button" *ngIf="!apiData.uuid" nz-button nzType="default" (click)="saveTestDataToApi()"
|
||||
class="ml10">
|
||||
保存为新 API
|
||||
class="ml10" i18n>
|
||||
Save as API
|
||||
</button>
|
||||
</nz-input-group>
|
||||
</nz-form-control>
|
||||
</nz-form-item>
|
||||
</form>
|
||||
<div class="scroll_container">
|
||||
<!-- 请求参数 -->
|
||||
<!-- Request Info -->
|
||||
<nz-tabset [nzTabBarStyle]="{ 'padding-left': '10px' }" [nzAnimated]="false" [nzSelectedIndex]="1">
|
||||
<!-- 请求头部 -->
|
||||
<!-- Request Headers -->
|
||||
<nz-tab [nzTitle]="headerTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #headerTitleTmp>
|
||||
请求头部
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.requestHeaders)">{{
|
||||
<span i18n>Request Headers</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.requestHeaders)">{{
|
||||
apiData.requestHeaders | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
<eo-api-test-header class="eo_theme_iblock bbd" [(model)]="apiData.requestHeaders"></eo-api-test-header>
|
||||
</nz-tab>
|
||||
<!-- 请求信息 -->
|
||||
<!--Request Info -->
|
||||
<nz-tab [nzTitle]="bodyTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #bodyTitleTmp>
|
||||
请求体
|
||||
<span i18n>Body</span>
|
||||
<span class="iconfont icon-circle eo-tab-theme-icon" *ngIf="
|
||||
['formData', 'json', 'xml'].includes(apiData.requestBodyType)
|
||||
? bindGetApiParamNum(apiData.requestBody)
|
||||
@ -56,8 +57,8 @@
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="queryTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #queryTitleTmp>
|
||||
Query 参数
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.queryParams)">{{
|
||||
<span i18n>Query</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.queryParams)">{{
|
||||
apiData.queryParams | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
@ -66,35 +67,35 @@
|
||||
</nz-tab>
|
||||
<nz-tab [nzTitle]="restTitleTmp" [nzForceRender]="true">
|
||||
<ng-template #restTitleTmp>
|
||||
REST 参数
|
||||
<span class="eo-tab-icon" *ngIf="bindGetApiParamNum(apiData.restParams)">{{
|
||||
<span i18n>REST</span>
|
||||
<span class="eo-tab-icon ml-[4px]" *ngIf="bindGetApiParamNum(apiData.restParams)">{{
|
||||
apiData.restParams | apiParamsNum
|
||||
}}</span>
|
||||
</ng-template>
|
||||
<eo-api-test-rest class="eo_theme_iblock bbd" [model]="apiData.restParams"></eo-api-test-rest>
|
||||
</nz-tab>
|
||||
</nz-tabset>
|
||||
<!-- 响应信息 -->
|
||||
<!-- Response -->
|
||||
<nz-tabset [nzTabBarStyle]="{ 'padding-left': '10px' }" [(nzSelectedIndex)]="tabIndexRes" [nzAnimated]="false"
|
||||
class="mt10 response_container">
|
||||
<nz-tab nzTitle="返回结果">
|
||||
<eo-api-test-result-response [model]="testResult.response"></eo-api-test-result-response>
|
||||
<nz-tab i18n-nzTitle nzTitle="Response">
|
||||
<eo-api-test-result-response [model]="testResult?.response"></eo-api-test-result-response>
|
||||
</nz-tab>
|
||||
<div>
|
||||
<nz-tab nzTitle="返回头部">
|
||||
<eo-api-test-result-header [model]="testResult.response?.headers"></eo-api-test-result-header>
|
||||
<nz-tab i18n-nzTitle nzTitle="Response Headers">
|
||||
<eo-api-test-result-header [model]="testResult?.response?.headers"></eo-api-test-result-header>
|
||||
</nz-tab>
|
||||
</div>
|
||||
<nz-tab nzTitle="请求内容">
|
||||
<nz-tab i18n-nzTitle nzTitle="Body">
|
||||
<eo-api-test-result-request-body [model]="testResult.request?.requestBody"></eo-api-test-result-request-body>
|
||||
</nz-tab>
|
||||
<nz-tab nzTitle="请求头部">
|
||||
<nz-tab i18n-nzTitle nzTitle="Request Headers">
|
||||
<eo-api-test-result-header [model]="testResult.request?.requestHeaders"></eo-api-test-result-header>
|
||||
</nz-tab>
|
||||
<nz-tab nzTitle="测试历史">
|
||||
<eo-api-test-history [apiID]="apiData.uuid" (clickItem)="restoreHistory($event)" #historyComponent>
|
||||
</eo-api-test-history>
|
||||
</nz-tab>
|
||||
</nz-tabset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="invisible">
|
||||
<eo-api-test-history [apiID]="apiData.uuid" (clickItem)="restoreHistory($event)" #historyComponent>
|
||||
</eo-api-test-history>
|
||||
</div>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)"',
|
||||
},
|
||||
|
@ -9,7 +9,7 @@
|
||||
[rootType]="jsonRootType"></params-import>
|
||||
</div>
|
||||
<div *ngIf="bodyType === 'json'">
|
||||
<p class="fs12 c999 mb5">JSON 根类型:</p>
|
||||
<p class="fs12 c999 mb5" i18n>JSON Root Type:</p>
|
||||
<nz-select class="w_100 mb10" [(ngModel)]="jsonRootType">
|
||||
<nz-option *ngFor="let item of CONST.JSON_ROOT_TYPE" [nzLabel]="item.key" [nzValue]="item.value"></nz-option>
|
||||
</nz-select>
|
||||
@ -24,5 +24,5 @@
|
||||
<eo-editor [(code)]="model" (codeChange)="rawDataChange()" *ngIf="bodyType === 'raw'"
|
||||
[eventList]="['type', 'format', 'copy', 'download', 'newTab', 'search', 'replace']"></eo-editor>
|
||||
<!-- Binary -->
|
||||
<textarea class="btd" rows="4" *ngIf="bodyType === 'binary'" nzBorderless placeholder="参数描述" nz-input
|
||||
<textarea class="btd" rows="4" *ngIf="bodyType === 'binary'" nzBorderless i18n-placeholder="@@Description" placeholder="Description" nz-input
|
||||
[(ngModel)]="model"></textarea>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="param_header">
|
||||
<params-import [(baseData)]="model" contentType="formData" modalTitle="头部"></params-import>
|
||||
<params-import [(baseData)]="model" contentType="formData" i18n-modalTitle="@@Header" modalTitle="Header"></params-import>
|
||||
</div>
|
||||
<list-block-common-component [mainObject]="listConf" [(list)]="model"></list-block-common-component>
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -1,17 +1,19 @@
|
||||
|
||||
<header>
|
||||
<button
|
||||
nz-popconfirm
|
||||
nzPopconfirmTitle="此操作无法恢复,确认操作?"
|
||||
i18n-nzPopconfirmTitle
|
||||
nzPopconfirmTitle="This operation cannot be restored, do you want to do it?"
|
||||
(nzOnConfirm)="deleteAll()"
|
||||
nzPopconfirmPlacement="topLeft"
|
||||
nz-button
|
||||
nzType="primary"
|
||||
nzDanger
|
||||
i18n
|
||||
>
|
||||
清空测试历史
|
||||
Empty Test History
|
||||
</button>
|
||||
</header>
|
||||
<nz-divider></nz-divider>
|
||||
<list-block-common-component class="api_test_history_list" [(list)]="model" [mainObject]="listConf"> </list-block-common-component>
|
||||
<nz-empty class="pb20" *ngIf="!model||!model.length" nzNotFoundImage="simple"></nz-empty>
|
||||
<list-block-common-component class="api_test_history_list" [(list)]="model" [mainObject]="listConf">
|
||||
</list-block-common-component>
|
||||
<nz-empty class="pb20" *ngIf="!model || !model.length" nzNotFoundImage="simple"></nz-empty>
|
||||
|
@ -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: '<span class="method_text_{{item.request.method}} method_label mr5">{{item.request.method}}</span>{{item.request.uri}}',
|
||||
},
|
||||
{
|
||||
thKey: '返回状态',
|
||||
thKey: $localize`Status Code`,
|
||||
type: 'html',
|
||||
class: 'w_100',
|
||||
html: `<span class="{{item.codeClass}}">{{item.response.statusCode}}</span>`,
|
||||
},
|
||||
{
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="param_header">
|
||||
<params-import [(baseData)]="model" contentType="query" modalTitle="Query参数"></params-import>
|
||||
<params-import [(baseData)]="model" contentType="query" i18n-modalTitle modalTitle="Query"></params-import>
|
||||
</div>
|
||||
<list-block-common-component [mainObject]="listConf" [(list)]="model"></list-block-common-component>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<nz-empty *ngIf="!model || !model.length" nzNotFoundImage="simple" [nzNotFoundContent]="contentTpl">
|
||||
<ng-template #contentTpl>
|
||||
<span>暂无头部</span>
|
||||
<span i18n>No Headers</span>
|
||||
</ng-template>
|
||||
</nz-empty>
|
||||
<ul *ngIf="model && model.length" class="p20">
|
||||
|
@ -1,6 +1,6 @@
|
||||
<nz-empty *ngIf="!model || !model.length" nzNotFoundImage="simple" [nzNotFoundContent]="contentTpl">
|
||||
<ng-template #contentTpl>
|
||||
<span>暂无请求体</span>
|
||||
<span i18n>No Request Body</span>
|
||||
</ng-template>
|
||||
</nz-empty>
|
||||
<div *ngIf="model && model.length" class="p20">
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="pb15" *ngIf="!model.responseType">
|
||||
<nz-empty nzNotFoundImage="simple" [nzNotFoundContent]="contentTpl">
|
||||
<ng-template #contentTpl>
|
||||
<span> 点击发送按钮获取测试报告 </span>
|
||||
<span i18n>Click the Send button to get a test report</span>
|
||||
</ng-template>
|
||||
</nz-empty>
|
||||
</div>
|
||||
@ -14,21 +14,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center" *ngSwitchCase="'stream'">
|
||||
<div *ngIf="!responseIsImg">
|
||||
无法预览非文本类型的数据,您可以
|
||||
<button class="eo_theme_btn_default mlr5" type="button" (click)="downloadResponseText()">下载返回结果</button>
|
||||
,并用其他程序打开。
|
||||
<div *ngIf="!responseIsImg" i18n>
|
||||
Unable to preview non-text type data, you can<button
|
||||
class="eo_theme_btn_default mlr5"
|
||||
type="button"
|
||||
(click)="downloadResponseText()"
|
||||
>
|
||||
download back result</button
|
||||
>and open it with other programs.
|
||||
</div>
|
||||
<!-- <div class="mt20" *ngIf="responseIsImg">
|
||||
<img class="maw_100percent" [src]="model.blobUrl" />
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="text-center" *ngSwitchCase="'longText'">
|
||||
响应结果超出可预览的大小,您可以
|
||||
<button class="eo_theme_btn_default mlr5" type="button" (click)="downloadResponseText()">下载返回结果</button>
|
||||
<!-- 或者
|
||||
<div class="text-center" *ngSwitchCase="'longText'" i18n>
|
||||
Unable to preview non-text type data, you can
|
||||
<button class="eo_theme_btn_default mlr5" type="button" (click)="downloadResponseText()">download back result</button>
|
||||
<!-- or
|
||||
<button class="eo_theme_btn_default" type="button" (click)="newTabResponseText()">在新标签页中显示返回结果</button>
|
||||
并用其他程序打开。 -->
|
||||
and open it with other programs. -->
|
||||
</div>
|
||||
<eo-editor
|
||||
*ngSwitchDefault
|
||||
|
@ -1,63 +1,73 @@
|
||||
<div class="py-3">
|
||||
<div class="py-4">
|
||||
<a nz-button [routerLink]="['/home/extension/list']" nzType="link">
|
||||
<i nz-icon nzType="left" nzTheme="outline"></i>返回列表
|
||||
</a>
|
||||
<div class="py-3 extension-detail">
|
||||
<div class="sticky top-0 z-50 bg-white">
|
||||
<div class="pb-3">
|
||||
<a nz-button nzType="link" (click)="backToList()">
|
||||
<i nz-icon nzType="left" nzTheme="outline"></i><span i18n>Back</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="bbd"></div>
|
||||
</div>
|
||||
<div class="bbd"></div>
|
||||
<section class="">
|
||||
<div class="flex p-8">
|
||||
<i
|
||||
class="bd_all block border rounded-lg h-40 w-40 bg-cover bg-center bg-no-repeat mr-8"
|
||||
[ngStyle]="{ 'background-image': 'url(' + (extensionDetail?.logo || '') + ')' }"
|
||||
></i>
|
||||
<section class="h-full p-4 max-w-[80vw] mx-auto">
|
||||
<div class="flex">
|
||||
<i class="block w-24 h-24 mr-8 bg-center bg-no-repeat bg-cover border rounded-lg bd_all"
|
||||
[ngStyle]="{ 'background-image': 'url(' + (extensionDetail?.logo || '') + ')' }"></i>
|
||||
<div class="flex flex-col flex-1">
|
||||
<div class="flex flex-col">
|
||||
<span class="text-xl mb-2 font-bold">{{ extensionDetail?.moduleName }}</span>
|
||||
<span>作者: {{ extensionDetail?.author }}</span>
|
||||
<!-- <span class="mb-4">Tags: {{ extensionDetail?.keywords }}</span> -->
|
||||
<span class="mb-2">版本: {{ extensionDetail?.version }}</span>
|
||||
<span class="mb-2 text-xl font-bold">{{ extensionDetail?.moduleName }}</span>
|
||||
<p class="w-full h-20">{{ extensionDetail?.description }}</p>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="flex items-center" *ngIf="!extensionDetail?.installed">
|
||||
<button
|
||||
nz-button
|
||||
nzType="primary"
|
||||
[nzLoading]="isOperating"
|
||||
(click)="manageExtension('install', extensionDetail?.name)"
|
||||
>
|
||||
安装
|
||||
</button>
|
||||
<!-- <span class="text-gray-500">安装完成后需要重启</span> -->
|
||||
</div>
|
||||
<button
|
||||
*ngIf="extensionDetail?.installed"
|
||||
nz-button
|
||||
nzType="primary"
|
||||
nzDanger
|
||||
[nzLoading]="isOperating"
|
||||
(click)="manageExtension('uninstall', extensionDetail?.name)"
|
||||
>
|
||||
卸载
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<nz-tabset [nzAnimated]="false">
|
||||
<nz-tab nzTitle="概述"> {{ extensionDetail?.description }} </nz-tab>
|
||||
<nz-tab nzTitle="更多信息">
|
||||
<nz-descriptions [nzColumn]="1" nzTitle="">
|
||||
<nz-descriptions-item nzTitle="作者">{{ extensionDetail?.author }}</nz-descriptions-item>
|
||||
<nz-descriptions-item nzTitle="版本">{{ extensionDetail?.version }}</nz-descriptions-item>
|
||||
<nz-descriptions-item nzTitle="反馈">
|
||||
<a class="eo_link" target="_blank" [href]="extensionDetail?.bugs?.url">Issue</a>
|
||||
</nz-descriptions-item>
|
||||
</nz-descriptions>
|
||||
</nz-tab>
|
||||
<!-- <nz-tab nzTitle="设置" *ngIf="extensionDetail?.configuration && extensionDetail?.configuration.properties">Content of Tab Pane 3</nz-tab> -->
|
||||
</nz-tabset>
|
||||
<div class="flex w-full mt-6 h-[calc(100vh-350px)]">
|
||||
<div class="flex-auto">
|
||||
<h2 class="text-lg font-bold" i18n>Intro</h2>
|
||||
<!-- <nz-divider></nz-divider> -->
|
||||
<div class="h-full overflow-auto markdown-desc">
|
||||
<nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
|
||||
<eo-shadow-dom [text]="extensionDetail?.introduction" [options]="{ html: true }">
|
||||
</eo-shadow-dom>
|
||||
</nz-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-[1px] bg-[#f2f2f2] mx-[10px]"></div>
|
||||
<div class="w-[200px] 2xl:w-[250px] overflow-auto h-full">
|
||||
<h2 class="text-lg font-bold" i18n>Install</h2>
|
||||
<div class="flex items-center mt-[22px]" *ngIf="!extensionDetail?.installed">
|
||||
<button *ngIf="isElectron" nz-button nzType="primary" nzBlock nzSize="large" [nzLoading]="isOperating"
|
||||
(click)="manageExtension('install', extensionDetail?.name)">
|
||||
Install
|
||||
</button>
|
||||
<div *ngIf="!isElectron">
|
||||
<button nz-button nzType="primary" nz-dropdown [nzDropdownMenu]="download" class="!w-full" i18n>Download Client
|
||||
</button>
|
||||
<nz-dropdown-menu #download="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<ng-container *ngFor="let item of resourceInfo; let index = index">
|
||||
<a [href]="item.link" nz-menu-item>{{ item.name }}</a>
|
||||
<li nz-menu-divider *ngIf="index !== resourceInfo.length - 1"></li>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
<div class="bg-[#FF1744] p-[6px] mt-[14px] rounded-[3px] text-white" i18n>
|
||||
The extensions can only be installed on the client at present. Please download the client first~
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button *ngIf="extensionDetail?.installed" nz-button nzBlock nzType="primary" nzDanger nzSize="large"
|
||||
[nzLoading]="isOperating" class="mt-[12px]" (click)="manageExtension('uninstall', extensionDetail?.name)" i18n>
|
||||
Uninstall
|
||||
</button>
|
||||
|
||||
<h2 class="text-lg font-bold mt-[30px]" i18n>Support</h2>
|
||||
<nz-descriptions [nzColumn]="1" nzTitle="">
|
||||
<nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail?.author }}</nz-descriptions-item>
|
||||
<nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail?.version }}
|
||||
</nz-descriptions-item>
|
||||
</nz-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,42 @@
|
||||
<section class="main">
|
||||
<section class="left flex-shrink-0">
|
||||
<div class="mb-2"><i class="mr-2" nz-icon nzType="appstore" nzTheme="fill"></i>插件分类</div>
|
||||
<div
|
||||
class="plugin-link px-1 py-2"
|
||||
[ngClass]="{ active: selectGroup === item.id }"
|
||||
*ngFor="let item of groups"
|
||||
(click)="clickGroup(item.id)"
|
||||
>
|
||||
{{ item.title }}<span *ngIf="item.showNum"> ({{ extensionService.extensionIDs.length }})</span>
|
||||
<section class="flex-shrink-0 p-0 left">
|
||||
<!-- <input type="text" nz-input [(ngModel)]="keyword" (ngModelChange)="onSeachChange($event)" placeholder="search" /> -->
|
||||
<div class="mb-2">
|
||||
<input type="text" class="flex-1 w-full px-3 input" i18n-placeholder="@@Search" placeholder="Search"
|
||||
[(ngModel)]="keyword" (ngModelChange)="onSeachChange($event)" />
|
||||
</div>
|
||||
<!-- Fixed Group -->
|
||||
<div class="group_container ">
|
||||
<nz-tree [nzData]="fixedTreeNode" [nzSelectedKeys]="nzSelectedKeys" nzBlockNode (nzClick)="clickTreeItem($event)"
|
||||
[nzTreeTemplate]="nzFixedTreeTemplate"></nz-tree>
|
||||
<ng-template #nzFixedTreeTemplate let-node let-origin="origin">
|
||||
<div class="pl-[18px] tree_node" *ngIf="node.origin?.isFixed">
|
||||
<div class="f_row_ac">
|
||||
<i class="mr10" nz-icon nzType="home" nzTheme="outline"></i>
|
||||
<span class="text_omit node_title">{{ node.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<nz-divider class="!mt-[5px] !mb-[7px]"></nz-divider>
|
||||
<nz-tree [nzData]="treeNodes" [nzSelectedKeys]="nzSelectedKeys" #apiGroup [nzHideUnMatched]="true"
|
||||
(nzClick)="clickTreeItem($event)" nzBlockNode [nzTreeTemplate]="nzTreeTemplate"></nz-tree>
|
||||
<ng-template #nzTreeTemplate let-node let-origin="origin">
|
||||
<div class="pl-[18px]">
|
||||
<div class="tree_node f_row f_js_ac" *ngIf="!node.origin?.isFixed && node.isLeaf">
|
||||
<div class="overflow-hidden f_row_ac text-ellipsis">
|
||||
<b class="method_text method_text_{{ node.origin.method }} mr5" *ngIf="node.origin.method">
|
||||
{{ node.origin.method }}
|
||||
</b>
|
||||
<span class="text_omit node_title">{{ node.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div *ngIf="node.component?.showIndicator" class="ant-tree-drop-indicator ng-star-inserted"
|
||||
style="bottom: -3px; left: 4px; right: 0px;"></div> -->
|
||||
</ng-template>
|
||||
</section>
|
||||
<section class="right fg1 px-4">
|
||||
<section class="px-4 right fg1">
|
||||
<router-outlet></router-outlet>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -1,10 +1,50 @@
|
||||
.main {
|
||||
::ng-deep .main {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
.input {
|
||||
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;
|
||||
}
|
||||
}
|
||||
.tree_node {
|
||||
font-size: 12px;
|
||||
}
|
||||
.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 .ant-tree-node-content-wrapper{
|
||||
border-radius: 0;
|
||||
&.ant-tree-node-selected {
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tree-switcher {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.left {
|
||||
background: #f8f8f8;
|
||||
border-right: 1px solid #f8f8f8;
|
||||
width: 250px;
|
||||
padding: 10px;
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { ElectronService } from 'eo/workbench/browser/src/app/core/services';
|
||||
import { GroupTreeItem } from 'eo/workbench/browser/src/app/shared/models';
|
||||
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { NzFormatEmitEvent, NzTreeNode, NzTreeNodeOptions } from 'ng-zorro-antd/tree';
|
||||
import { filter, Subject } from 'rxjs';
|
||||
import { ExtensionGroupType } from './extension.model';
|
||||
import { ExtensionService } from './extension.service';
|
||||
|
||||
@ -9,24 +14,34 @@ import { ExtensionService } from './extension.service';
|
||||
styleUrls: ['./extension.component.scss'],
|
||||
})
|
||||
export class ExtensionComponent implements OnInit {
|
||||
groups = [
|
||||
keyword = '';
|
||||
nzSelectedKeys: (number | string)[] = [];
|
||||
treeNodes: NzTreeNodeOptions[] = [
|
||||
{
|
||||
id: 'all',
|
||||
title: '全部插件',
|
||||
},
|
||||
{
|
||||
id: 'official',
|
||||
title: '官方插件',
|
||||
},
|
||||
{
|
||||
id: 'installed',
|
||||
title: '已安装',
|
||||
showNum: true
|
||||
key: 'official',
|
||||
title: $localize`Official`,
|
||||
isLeaf: true,
|
||||
},
|
||||
];
|
||||
selectGroup: ExtensionGroupType|string = ExtensionGroupType.all;
|
||||
constructor(public extensionService: ExtensionService, private router: Router) {
|
||||
}
|
||||
fixedTreeNode: GroupTreeItem[] | NzTreeNode[] = [
|
||||
{
|
||||
title: $localize`All`,
|
||||
key: 'all',
|
||||
weight: 0,
|
||||
parentID: '0',
|
||||
isLeaf: true,
|
||||
isFixed: true,
|
||||
},
|
||||
];
|
||||
selectGroup: ExtensionGroupType | string = ExtensionGroupType.all;
|
||||
|
||||
constructor(
|
||||
public extensionService: ExtensionService,
|
||||
private router: Router,
|
||||
public electron: ElectronService,
|
||||
private route: ActivatedRoute,
|
||||
private messageService: MessageService
|
||||
) {}
|
||||
clickGroup(id) {
|
||||
this.selectGroup = id;
|
||||
this.router
|
||||
@ -35,5 +50,46 @@ export class ExtensionComponent implements OnInit {
|
||||
})
|
||||
.finally();
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
ngOnInit(): void {
|
||||
this.watchRouterChange();
|
||||
this.setSelectedKeys();
|
||||
}
|
||||
|
||||
onSeachChange(keyword) {
|
||||
this.messageService.send({ type: 'searchPluginByKeyword', data: keyword });
|
||||
}
|
||||
|
||||
private watchRouterChange() {
|
||||
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((res: any) => {
|
||||
this.setSelectedKeys();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Group tree item click.
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
clickTreeItem(event: NzFormatEmitEvent): void {
|
||||
const eventName = event.node?.origin.isFixed ? 'clickFixedItem' : 'clickItem';
|
||||
|
||||
switch (eventName) {
|
||||
case 'clickFixedItem': {
|
||||
this.clickGroup(event.node.key);
|
||||
break;
|
||||
}
|
||||
case 'clickItem': {
|
||||
this.clickGroup(event.node.key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setSelectedKeys() {
|
||||
if (this.route.snapshot.queryParams.type) {
|
||||
this.nzSelectedKeys = [this.route.snapshot.queryParams.type];
|
||||
} else {
|
||||
this.nzSelectedKeys = [this.fixedTreeNode[0].key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,30 @@ import { NzInputModule } from 'ng-zorro-antd/input';
|
||||
import { NzTabsModule } from 'ng-zorro-antd/tabs';
|
||||
import { NzDescriptionsModule } from 'ng-zorro-antd/descriptions';
|
||||
import { NzTagModule } from 'ng-zorro-antd/tag';
|
||||
|
||||
import { NzDividerModule } from 'ng-zorro-antd/divider';
|
||||
import { NzTreeModule } from 'ng-zorro-antd/tree';
|
||||
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
|
||||
import { NzSkeletonModule } from 'ng-zorro-antd/skeleton';
|
||||
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [FormsModule,NzTabsModule,NzTagModule,NzDescriptionsModule,NzInputModule,NzButtonModule,NzIconModule,ExtensionRoutingModule, CommonModule],
|
||||
providers:[ExtensionService],
|
||||
imports: [
|
||||
SharedModule,
|
||||
FormsModule,
|
||||
NzTabsModule,
|
||||
NzTagModule,
|
||||
NzDescriptionsModule,
|
||||
NzInputModule,
|
||||
NzButtonModule,
|
||||
NzIconModule,
|
||||
ExtensionRoutingModule,
|
||||
CommonModule,
|
||||
NzDividerModule,
|
||||
NzTreeModule,
|
||||
NzDropDownModule,
|
||||
NzSkeletonModule,
|
||||
],
|
||||
providers: [ExtensionService],
|
||||
declarations: [ExtensionComponent, ExtensionListComponent, ExtensionDetailComponent],
|
||||
})
|
||||
export class ExtensionModule {}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { isElectron } from 'eo/shared/common/common';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { ModuleInfo } from '../../utils/module-loader';
|
||||
import { EoExtensionInfo } from './extension.model';
|
||||
@ -14,7 +15,7 @@ export class ExtensionService {
|
||||
this.getInstalledList();
|
||||
}
|
||||
getInstalledList() {
|
||||
this.localModules = window.eo.getModules();
|
||||
this.localModules = window.eo?.getModules() || new Map();
|
||||
this.updateExtensionIDs();
|
||||
}
|
||||
public async requestList() {
|
||||
|
@ -1,51 +1,24 @@
|
||||
<div class="px-3">
|
||||
<div class="py-4">
|
||||
<nz-input-group class="w-60" [nzPrefix]="prefixTemplateSearch">
|
||||
<input
|
||||
type="text"
|
||||
nz-input
|
||||
[(ngModel)]="keyword"
|
||||
(ngModelChange)="onSeachChange($event)"
|
||||
placeholder="搜索关键字"
|
||||
/>
|
||||
</nz-input-group>
|
||||
</div>
|
||||
<ng-template #prefixTemplateSearch><i nz-icon nzType="search"></i></ng-template>
|
||||
<div class="bbd"></div>
|
||||
<div class="list-block grid gap-6 py-5 grid-cols-4">
|
||||
|
||||
<div class="grid grid-cols-2 gap-6 py-5 list-block">
|
||||
<div
|
||||
class="bd_all w-full h-76 py-2 px-3 rounded-lg flex flex-col flex-wrap items-center plugin-block"
|
||||
*ngFor="let it of renderList"
|
||||
(click)="clickExtension(it)"
|
||||
>
|
||||
<span class="h-8 w-full flex justify-between items-center">
|
||||
<nz-tag *ngIf="extensionService.localModules.has(it.moduleID)" [nzColor]="'var(--MAIN_THEME_COLOR)'"
|
||||
>已安装</nz-tag
|
||||
>
|
||||
<span
|
||||
*ngIf="!extensionService.localModules.has(it.moduleID)"
|
||||
class="text-xs p-1 bd_all rounded-sm text-green-700 border-green-700"
|
||||
>未安装</span
|
||||
>
|
||||
<!-- <i
|
||||
nz-icon
|
||||
nzType="setting"
|
||||
(click)="handleSetingPlugin(it)"
|
||||
*ngIf="
|
||||
extensionService.localModules.has(it.moduleID) &&
|
||||
extensionService.localModules.get(it.moduleID).configuration
|
||||
"
|
||||
nzTheme="outline"
|
||||
></i> -->
|
||||
</span>
|
||||
<i
|
||||
class="block w-20 h-20 my-3 rounded-lg bg-cover bg-center bg-no-repeat"
|
||||
[ngClass]="{ 'bg-gray-100': it.logo }"
|
||||
[ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }"
|
||||
></i>
|
||||
<span class="text-lg font-bold">{{ it.moduleName }}</span>
|
||||
<span class="text-gray-400 my-2">{{ it.author }}</span>
|
||||
<span class="text-gray-500 my-1 desc">{{ it.description }}</span>
|
||||
class="bd_all w-full min-h-[140px] p-5 rounded-lg flex flex-col flex-wrap items-center plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
|
||||
*ngFor="let it of renderList" (click)="clickExtension(it)">
|
||||
<div class="flex w-full">
|
||||
<div class=" block w-[40px] h-[40px] rounded-lg bg-cover bg-center bg-no-repeat mr-[20px]"
|
||||
[ngClass]="{ 'bg-gray-100': it.logo }" [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }">
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col flex-auto">
|
||||
<span class="text-lg font-bold">{{ it.moduleName }}</span>
|
||||
<span class="my-2 text-gray-400">{{ it.author }}</span>
|
||||
<span class="my-1 text-gray-500 desc">{{ it.description }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span *ngIf="isElectron ? extensionService.localModules.has(it.moduleID) : it.installed"
|
||||
class="p-1 text-xs text-green-700 border-green-700 rounded-sm bd_all" i18n>Installed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { debounceTime, distinctUntilChanged, lastValueFrom, Subject } from 'rxjs';
|
||||
import { isElectron } from 'eo/shared/common/common';
|
||||
import { Message, MessageService } from 'eo/workbench/browser/src/app/shared/services/message';
|
||||
import { debounceTime, distinctUntilChanged, takeUntil, Subject } from 'rxjs';
|
||||
import { ExtensionGroupType } from '../extension.model';
|
||||
import { ExtensionService } from '../extension.service';
|
||||
class ExtensionList {
|
||||
@ -10,7 +12,7 @@ class ExtensionList {
|
||||
}
|
||||
search(keyword: string) {
|
||||
return this.list.filter(
|
||||
(it) => it.moduleID.includes(keyword) || it.name.includes(keyword) || it.keywords.includes(keyword)
|
||||
(it) => it.moduleID.includes(keyword) || it.name.includes(keyword) || it.keywords?.includes(keyword)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -23,8 +25,16 @@ export class ExtensionListComponent implements OnInit {
|
||||
type: ExtensionGroupType = ExtensionGroupType.all;
|
||||
keyword = '';
|
||||
renderList = [];
|
||||
isElectron = isElectron();
|
||||
seachChanged$: Subject<string> = new Subject<string>();
|
||||
constructor(public extensionService: ExtensionService, private route: ActivatedRoute, private router: Router) {
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
public extensionService: ExtensionService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private messageService: MessageService
|
||||
) {
|
||||
this.type = this.route.snapshot.queryParams.type;
|
||||
this.seachChanged$.pipe(debounceTime(500), distinctUntilChanged()).subscribe(async (keyword) => {
|
||||
this.renderList = await this.searchPlugin(keyword);
|
||||
@ -32,6 +42,7 @@ export class ExtensionListComponent implements OnInit {
|
||||
}
|
||||
async ngOnInit() {
|
||||
this.watchSearchConditionChange();
|
||||
this.watchSearchKeywordChange();
|
||||
}
|
||||
async searchPlugin(keyword = '') {
|
||||
if (this.type === 'installed') {
|
||||
@ -54,7 +65,12 @@ export class ExtensionListComponent implements OnInit {
|
||||
clickExtension(item) {
|
||||
this.router
|
||||
.navigate(['home/extension/detail'], {
|
||||
queryParams: { id: item.moduleID, name: item.name, jump: 'setting' },
|
||||
queryParams: {
|
||||
type: this.route.snapshot.queryParams.type,
|
||||
id: item.moduleID,
|
||||
name: item.name,
|
||||
jump: 'setting',
|
||||
},
|
||||
})
|
||||
.finally();
|
||||
}
|
||||
@ -64,4 +80,18 @@ export class ExtensionListComponent implements OnInit {
|
||||
this.renderList = await this.searchPlugin();
|
||||
});
|
||||
}
|
||||
|
||||
private watchSearchKeywordChange() {
|
||||
this.messageService
|
||||
.get()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((inArg: Message) => {
|
||||
switch (inArg.type) {
|
||||
case 'searchPluginByKeyword': {
|
||||
this.onSeachChange(inArg.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
<div class="eo_navbar f_row f_js_ac">
|
||||
<div>
|
||||
<img class="logo" src="assets/images/logo.svg" />
|
||||
<a href="https://github.com/eolinker/eoapi" target="_blank">
|
||||
<img class="mx-4" src="https://img.shields.io/github/stars/eolinker/eoapi?style=social" alt="" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="icon mx-1 flex items-center justify-center" i18n-title title="Open Settings" (click)="handleShowModal()">
|
||||
<eo-iconpark-icon name="setting-two"></eo-iconpark-icon>
|
||||
</span>
|
||||
<!-- <span
|
||||
i18n-title
|
||||
class="icon mx-1 flex items-center justify-center"
|
||||
title="{{ dataSourceText }}数据源"
|
||||
(click)="switchDataSource()"
|
||||
>
|
||||
<eo-iconpark-icon [name]="isRemote ? 'link-cloud-sucess' : 'link-cloud-faild'"></eo-iconpark-icon>
|
||||
</span> -->
|
||||
<span class="icon mx-1 flex items-center justify-center" nz-dropdown [nzDropdownMenu]="menu">
|
||||
<eo-iconpark-icon name="help">
|
||||
</eo-iconpark-icon>
|
||||
</span>
|
||||
<nz-dropdown-menu #menu="nzDropdownMenu">
|
||||
<ul nz-menu nzSelectable>
|
||||
<a href="https://eoapi.io/" target="_blank" nz-menu-item i18n>Document</a>
|
||||
<li nz-menu-divider></li>
|
||||
<a href="https://github.com/eolinker/eoapi/issues/new" target="_blank" nz-menu-item i18n>Report Issue</a>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
<div *ngIf="!OS_TYPE.includes('mac') && isElectron">
|
||||
<span nz-tooltip i18n-nzTooltipTitle nzTooltipTitle="Minimize" nzTooltipPlacement="left" class="iconfont icon-jianhao mr10 fs24 cp"
|
||||
(click)="minimize()">
|
||||
</span>
|
||||
<span nz-tooltip i18n-nzTooltipTitle [nzTooltipTitle]="isMaximized ? 'Restore' : 'Maximize'" nzTooltipPlacement="left"
|
||||
class="iconfont icon-{{ isMaximized ? 'copy' : 'duoxuanweixuanzhong' }} mr10 fs24 cp"
|
||||
(click)="toggleMaximize()">
|
||||
</span>
|
||||
<span nz-tooltip i18n-nzTooltipTitle nzTooltipTitle="Close" nzTooltipPlacement="left" class="iconfont icon-guanbi pr15 fs24 cp"
|
||||
(click)="close()">
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="!isElectron">
|
||||
<div class="btn py-1.5 px-2 mx-1 flex items-center" nz-dropdown i18n [nzDropdownMenu]="download">Download</div>
|
||||
<nz-dropdown-menu #download="nzDropdownMenu">
|
||||
<ul nz-menu>
|
||||
<ng-container *ngFor="let item of resourceInfo; let index = index">
|
||||
<a [href]="item.link" nz-menu-item>{{ item.name }}</a>
|
||||
<li nz-menu-divider *ngIf="index !== resourceInfo.length - 1"></li>
|
||||
</ng-container>
|
||||
</ul>
|
||||
</nz-dropdown-menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<eo-setting [(isShowModal)]="isSettingVisible"></eo-setting>
|
@ -1,3 +1,27 @@
|
||||
.btn {
|
||||
border-radius: 3px;
|
||||
background-color: var(--BTN_PRIMARY_BG);
|
||||
color: #fff;
|
||||
font-size: 0.9em;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.icon {
|
||||
-webkit-app-region: no-drag;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 1.5em;
|
||||
color: rgba(0,0,0,0.5);
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
transition: all .4s ease;
|
||||
&:hover {
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
}
|
||||
}
|
||||
.iconfont{
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
.eo_navbar {
|
||||
-webkit-app-region: drag;
|
||||
position: sticky;
|
||||
@ -5,6 +29,7 @@
|
||||
z-index: 11;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0 8px;
|
||||
// background-color: var(--NAVBAR_BG);
|
||||
// color: var(--NAVBAR_TEXT);
|
||||
// border-bottom: 1px solid var(--NAVBAR_BORDER_BOTTOM);
|
@ -1,10 +1,10 @@
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { ElectronService } from '../../../core/services';
|
||||
import { ElectronService } from '../../core/services';
|
||||
import { ModuleInfo } from 'eo/platform/node/extension-manager';
|
||||
import { MessageService } from '../../../shared/services/message';
|
||||
import { MessageService } from '../../shared/services/message';
|
||||
import { NzConfigService } from 'ng-zorro-antd/core/config';
|
||||
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/remote/remote.service';
|
||||
|
||||
import { ResourceInfo } from '../../shared/models/client.model';
|
||||
@Component({
|
||||
selector: 'eo-navbar',
|
||||
templateUrl: './navbar.component.html',
|
||||
@ -13,6 +13,7 @@ import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/remo
|
||||
export class NavbarComponent implements OnInit {
|
||||
isMaximized = false;
|
||||
isElectron = false;
|
||||
isSettingVisible = false;
|
||||
messageTop;
|
||||
@ViewChild('notificationTemplate', { static: true })
|
||||
notificationTemplate!: TemplateRef<{}>;
|
||||
@ -29,30 +30,7 @@ export class NavbarComponent implements OnInit {
|
||||
}
|
||||
OS_TYPE = navigator.platform.toLowerCase();
|
||||
modules: Map<string, ModuleInfo>;
|
||||
resourceInfo = [
|
||||
{
|
||||
id: 'win',
|
||||
name: 'Windows 客户端',
|
||||
icon: 'windows',
|
||||
keyword: 'Setup',
|
||||
suffix: 'exe',
|
||||
link: '',
|
||||
},
|
||||
{
|
||||
id: 'mac',
|
||||
name: 'macOS(Intel) 客户端',
|
||||
icon: 'mac',
|
||||
suffix: 'dmg',
|
||||
link: '',
|
||||
},
|
||||
{
|
||||
id: 'mac',
|
||||
name: 'macOS(M1) 客户端',
|
||||
icon: 'mac',
|
||||
suffix: 'arm64.dmg',
|
||||
link: '',
|
||||
},
|
||||
];
|
||||
resourceInfo = ResourceInfo;
|
||||
|
||||
constructor(
|
||||
private electron: ElectronService,
|
||||
@ -124,6 +102,10 @@ export class NavbarComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
handleShowModal() {
|
||||
this.isSettingVisible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* switch data
|
||||
*/
|
14
src/workbench/browser/src/app/pages/navbar/navbar.module.ts
Normal file
14
src/workbench/browser/src/app/pages/navbar/navbar.module.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
|
||||
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
|
||||
import { NavbarComponent } from 'eo/workbench/browser/src/app/pages/navbar/navbar.component';
|
||||
import { SettingModule } from 'eo/workbench/browser/src/app/shared/components/setting/setting.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, NzDropDownModule, NzToolTipModule, SettingModule, SharedModule],
|
||||
declarations: [NavbarComponent],
|
||||
exports: [NavbarComponent],
|
||||
})
|
||||
export class NavbarModule {}
|
@ -3,7 +3,6 @@ import { NgModule } from '@angular/core';
|
||||
|
||||
import { PagesComponent } from './pages.component';
|
||||
import { PageBlankComponent } from '../shared/components/page-blank/page-blank.component';
|
||||
import { PageFeaturePreviewComponent } from '../shared/components/page-feature-preview/page-feature-preview.component';
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
@ -18,10 +17,6 @@ const routes: Routes = [
|
||||
path: 'blank',
|
||||
component: PageBlankComponent,
|
||||
},
|
||||
{
|
||||
path: 'preview',
|
||||
component: PageFeaturePreviewComponent,
|
||||
},
|
||||
{
|
||||
path: 'api',
|
||||
loadChildren: () => import('./api/api.module').then((m) => m.ApiModule),
|
||||
|
@ -2,19 +2,29 @@
|
||||
<div [style.--remote-notification-height]="isShowNotification && isElectron ? '50px' : '0px'">
|
||||
<div *ngIf="isShowNotification && isElectron" class="remote-notification">
|
||||
<i nz-icon [nzType]="isRemote ? 'cloud' : 'exclamation-circle'" nzTheme="outline" class="text-[13px] mr-[2px]"></i>
|
||||
目前数据储存在{{dataSourceText}},如需协作请切换
|
||||
<a class="eo-blod" (click)="switchDataSource()">{{ isRemote ? '本地' : '远程' }}数据源</a>
|
||||
<i nz-icon nzType="close" nzTheme="outline" class="absolute right-[20px] cursor-pointer"
|
||||
(click)="closeNotification()"></i>
|
||||
<span i18n>Current data storage exists{{ dataSourceText }},please switch if you want to collaborate</span>
|
||||
<a class="eo-blod" (click)="switchDataSource()" i18n>{{ isRemote ? 'Remote Server' : 'Localhost' }} Data Storage</a>
|
||||
<i
|
||||
nz-icon
|
||||
nzType="close"
|
||||
nzTheme="outline"
|
||||
class="absolute right-[20px] cursor-pointer"
|
||||
(click)="closeNotification()"
|
||||
></i>
|
||||
</div>
|
||||
<div class="home_container f_row">
|
||||
<eo-setting></eo-setting>
|
||||
<eo-sidebar></eo-sidebar>
|
||||
<div class="home fg1">
|
||||
<router-outlet *ngIf="!loadedIframe"></router-outlet>
|
||||
<iframe *ngIf="!this.sidebar.currentModule.isOffical" id="app_iframe" frameborder="no" border="0"
|
||||
style="width: calc(100vw - 90px);height: calc(100vh - var(--NAVBAR_HEIGHT) - 4px);"></iframe>
|
||||
<iframe
|
||||
*ngIf="!this.sidebar.currentModule.isOffical"
|
||||
id="app_iframe"
|
||||
frameborder="no"
|
||||
border="0"
|
||||
style="width: calc(100vw - 90px); height: calc(100vh - var(--NAVBAR_HEIGHT) - 4px)"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <eo-toolbar></eo-toolbar> -->
|
||||
<eo-toolbar></eo-toolbar>
|
||||
|
@ -3,11 +3,10 @@ import { SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { SidebarService } from 'eo/workbench/browser/src/app/shared/components/sidebar/sidebar.service';
|
||||
import { Message } from 'eo/workbench/browser/src/app/shared/services/message/message.model';
|
||||
import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { Subject, takeUntil, debounceTime } from 'rxjs';
|
||||
import { isElectron } from 'eo/shared/common/common';
|
||||
import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/remote/remote.service';
|
||||
import { IS_SHOW_REMOTE_SERVER_NOTIFICATION } from 'eo/workbench/browser/src/app/shared/services/storage/storage.service';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
@Component({
|
||||
selector: 'eo-pages',
|
||||
@ -26,6 +25,7 @@ export class PagesComponent implements OnInit {
|
||||
return this.remoteService.dataSourceText;
|
||||
}
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
private rawChange$: Subject<string> = new Subject<string>();
|
||||
get isShowNotification() {
|
||||
return !this.isRemote && this.isShow;
|
||||
}
|
||||
@ -35,11 +35,15 @@ export class PagesComponent implements OnInit {
|
||||
public sidebar: SidebarService,
|
||||
private messageService: MessageService,
|
||||
private remoteService: RemoteService
|
||||
) {}
|
||||
) {
|
||||
this.rawChange$.pipe(debounceTime(500), takeUntil(this.destroy$)).subscribe(() => {
|
||||
this.updateState();
|
||||
});
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.watchSidebarItemChange();
|
||||
this.watchRemoteServerChange();
|
||||
this.updateState();
|
||||
this.rawChange$.next('');
|
||||
}
|
||||
private watchSidebarItemChange() {
|
||||
this.sidebar.appChanged$.subscribe(() => {
|
||||
@ -48,7 +52,7 @@ export class PagesComponent implements OnInit {
|
||||
setTimeout(() => {
|
||||
//add loading
|
||||
this.loadedIframe = false;
|
||||
let iframe = document.getElementById('app_iframe') as HTMLIFrameElement;
|
||||
const iframe = document.getElementById('app_iframe') as HTMLIFrameElement;
|
||||
//load resource
|
||||
iframe.src = this.sidebar.currentModule.main;
|
||||
//loading finish
|
||||
@ -65,7 +69,7 @@ export class PagesComponent implements OnInit {
|
||||
this.remoteService.switchDataSource();
|
||||
};
|
||||
|
||||
updateState = debounce(async () => {
|
||||
updateState = async () => {
|
||||
if (!this.isRemote && localStorage.getItem(IS_SHOW_REMOTE_SERVER_NOTIFICATION) !== 'false') {
|
||||
const [isSuccess] = await this.remoteService.pingRmoteServerUrl();
|
||||
this.isShow = isSuccess;
|
||||
@ -73,7 +77,7 @@ export class PagesComponent implements OnInit {
|
||||
// if (!) {
|
||||
// this.isClose = false;
|
||||
// }
|
||||
}, 500);
|
||||
};
|
||||
|
||||
private watchRemoteServerChange() {
|
||||
this.messageService
|
||||
@ -82,7 +86,7 @@ export class PagesComponent implements OnInit {
|
||||
.subscribe((inArg: Message) => {
|
||||
switch (inArg.type) {
|
||||
case 'onDataSourceChange': {
|
||||
this.updateState();
|
||||
this.rawChange$.next(inArg.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ import { SettingModule } from '../shared/components/setting/setting.module';
|
||||
import { PagesComponent } from './pages.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { NzIconModule } from 'ng-zorro-antd/icon';
|
||||
import { NavbarModule } from 'eo/workbench/browser/src/app/pages/navbar/navbar.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [PagesRoutingModule, SettingModule, CommonModule, SharedModule, NzIconModule, NavbarModule],
|
||||
declarations: [PagesComponent],
|
||||
imports: [PagesRoutingModule, SettingModule, CommonModule, SharedModule, NzIconModule],
|
||||
exports: [],
|
||||
})
|
||||
export class PagesModule {}
|
||||
|
@ -1,5 +0,0 @@
|
||||
<div class="about">
|
||||
<nz-descriptions nzTitle="关于" [nzColumn]="1">
|
||||
<nz-descriptions-item *ngFor="let item of list" [nzTitle]="item.label">{{item.value}}</nz-descriptions-item>
|
||||
</nz-descriptions>
|
||||
</div>
|
@ -1,8 +0,0 @@
|
||||
.about ::ng-deep .ant-descriptions-item-label {
|
||||
width: 84px;
|
||||
position: relative;
|
||||
&::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AboutComponent } from './about.component';
|
||||
|
||||
describe('AboutComponent', () => {
|
||||
let component: AboutComponent;
|
||||
let fixture: ComponentFixture<AboutComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [AboutComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AboutComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,104 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ElectronService } from '../../../core/services';
|
||||
import pkg from '../../../../../../../../package.json';
|
||||
|
||||
const dependencies = {
|
||||
...pkg.dependencies,
|
||||
...pkg.devDependencies,
|
||||
} as const;
|
||||
|
||||
type DescriptionsItem = {
|
||||
readonly id: string;
|
||||
readonly label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const descriptions: DescriptionsItem[] = [
|
||||
{
|
||||
id: 'version',
|
||||
label: '当前版本号',
|
||||
value: pkg.version,
|
||||
},
|
||||
{
|
||||
id: 'publishTime',
|
||||
label: '发布日期',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
id: 'homeDir',
|
||||
label: '安装目录',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
id: 'electron',
|
||||
label: 'Electron',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
id: 'chrome',
|
||||
label: 'Chromium',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
id: 'node',
|
||||
label: 'Node.js',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
id: 'v8',
|
||||
label: 'V8',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
id: 'os',
|
||||
label: 'OS',
|
||||
value: '',
|
||||
},
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'eo-about',
|
||||
templateUrl: './about.component.html',
|
||||
styleUrls: ['./about.component.scss'],
|
||||
})
|
||||
export class AboutComponent implements OnInit {
|
||||
list = descriptions;
|
||||
|
||||
constructor(private electron: ElectronService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
fetch('https://api.github.com/repos/eolinker/eoapi/releases')
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const publishTime = data.find((n) => n.tag_name.slice(1) === pkg.version)?.published_at;
|
||||
const publishObj = this.list.find((n) => n.id === 'publishTime');
|
||||
if (publishTime) {
|
||||
publishObj.value = new Intl.DateTimeFormat('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
weekday: 'long',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
.format(new Date(publishTime))
|
||||
.replace(/星期[^]?/, '');
|
||||
} else {
|
||||
publishObj.value = `当前版本(v${pkg.version})尚未发布`;
|
||||
}
|
||||
});
|
||||
const systemInfo = this.getSystemInfo();
|
||||
this.list.forEach((item) => {
|
||||
if (item.id in systemInfo) {
|
||||
item.value = systemInfo[item.id];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getSystemInfo() {
|
||||
const systemInfo = this.electron.ipcRenderer.sendSync('get-system-info');
|
||||
return systemInfo;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user